[
https://issues.apache.org/jira/browse/GROOVY-12741?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18140831#comment-18140831
]
ASF GitHub Bot commented on GROOVY-12741:
-----------------------------------------
testlens-app[bot] commented on PR #3276:
URL: https://github.com/apache/groovy/pull/3276#issuecomment-6725678736
## ✅ All tests passed ✅
🏷️ Commit: 136028e8a5987a2a53015e1169069c5aa145251e
▶️ Tests: 118108 executed
⚪️ Checks: 26/26 completed
---
_Learn more about TestLens at
[testlens.app/docs](https://testlens.app/docs/features/pr-comment/)._
> groovy-contracts: a contract annotation on a trait or a trait method with a
> body crashes the compiler or fails at run time
> --------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12741
> URL: https://issues.apache.org/jira/browse/GROOVY-12741
> Project: Groovy
> Issue Type: Bug
> Components: groovy-contracts
> Affects Versions: 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> h3. Problem
> A {{@Requires}} on a trait method with a parameter crashes class generation:
> {code:groovy}
> import groovy.contracts.*
> trait Bank {
> BigDecimal balance = 0
> @Requires({ amount > 0 })
> void deposit(BigDecimal amount) { balance += amount }
> }
> class Account implements Bank {}
> new Account().deposit(1); println 'ok'
> {code}
> {noformat}
> General error during instruction selection: Index 1 out of bounds for length 1
> java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
> at
> org.codehaus.groovy.classgen.asm.InvocationWriter.loadArguments(InvocationWriter.java:379)
> at
> org.codehaus.groovy.classgen.asm.InvocationWriter.writeDirectMethodCall(InvocationWriter.java:292)
> at
> org.codehaus.groovy.classgen.asm.InvocationWriter.makeDirectCall(InvocationWriter.java:436)
> at
> org.codehaus.groovy.classgen.asm.InvocationWriter.makeCall(InvocationWriter.java:191)
> at
> org.codehaus.groovy.classgen.asm.InvocationWriter.writeInvokeMethod(InvocationWriter.java:622)
> at
> org.codehaus.groovy.classgen.AsmClassGenerator.visitMethodCallExpression(AsmClassGenerator.java:1244)
> {noformat}
> The same happens with {{@CompileStatic}} on the trait (in
> {{StaticInvocationWriter.loadArguments}}). The other contract annotations do
> not crash but do not work either:
> * {{@Requires}} on a trait method without parameters, and {{@Ensures}} on any
> trait method: the class compiles, then every call fails with
> {{MissingPropertyException: No such property: $GCONTRACTS_ENABLED for class:
> Bank$Trait$Helper}}.
> * {{@Invariant}} on a trait: inherited by the implementing class like an
> interface invariant, it fails at construction, while the trait's fields are
> still being initialised (the condition sees {{balance}} as null).
> A contract on an abstract trait method works (it applies to the implementing
> class like an interface contract), and so does one on an interface method a
> trait implements.
> Reproduced on Groovy 6.0.0 and on master (df1fe66ed1).
> h3. Cause
> The contracts transform runs in SEMANTIC_ANALYSIS before the trait transform
> and treats the trait as a class: it generates the condition class with the
> trait method's parameters and marks the trait {{@Contracted}}. The trait
> transform then moves each method with a body into the helper class as a
> static method with a leading {{$self}} parameter, keeping the annotations,
> and copies the class annotations to the helper. In INSTRUCTION_SELECTION the
> module weaves the helper (it carries {{@Contracted}};
> {{CandidateChecks.isContractsCandidate}} is not consulted), building the
> {{doCall}} argument list from the helper method's parameters, one more than
> the condition class accepts; without parameters the extra argument is dropped
> and the woven {{$GCONTRACTS_ENABLED}} guard has no field to refer to in the
> helper.
> h3. Fix
> Supporting contracts on trait methods needs a design (where the check runs,
> what {{this}} is, how a trait invariant interacts with trait field
> initialisation, private and static trait methods). For now the module reports
> a compile error instead of crashing:
> {noformat}
> [groovy-contracts] @Requires is not supported on a trait method with a body
> (Bank.deposit); only an abstract trait method may carry a contract.
> [groovy-contracts] @Invariant is not supported on a trait (Bank).
> {noformat}
> {{@ThrowsIf}} and {{@Decreases}} on a trait method with a body are reported
> the same way (without the abstract-method hint, since on an abstract method
> they stay informational). Their transforms run after the trait transform has
> moved the method into the helper class and rewritten the body to reach trait
> state through {{$self}}; the guard or measure woven there is not rewritten,
> so a condition that refers to trait state fails at run time with a missing
> property on the helper class, and only a condition that refers to parameters
> alone happened to work (once the separate core crash in {{TraitComposer}} for
> annotations with a closure member, GROOVY-12743, is fixed).
> {noformat}
> [groovy-contracts] @ThrowsIf is not supported on a trait method with a body
> (Bank.withdraw).
> [groovy-contracts] @Decreases is not supported on a trait method with a body
> (Counting.sumUp).
> {noformat}
> A contract on an abstract trait method keeps working. Pull request to follow.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)