[
https://issues.apache.org/jira/browse/GROOVY-12608?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18135620#comment-18135620
]
ASF GitHub Bot commented on GROOVY-12608:
-----------------------------------------
codeconsole opened a new pull request, #3135:
URL: https://github.com/apache/groovy/pull/3135
Under `@CompileStatic`, a closure or lambda in trait code that reads a super
trait's static accessor or static property as a property calls the super trait
interface's static forwarder. The forwarder passes the trait itself to the
trait helper as the receiver class, so an accessor that reads trait state fails:
```groovy
@groovy.transform.CompileStatic
trait T { private static int count = 1; static int getCnt() { count } }
@groovy.transform.CompileStatic
trait U extends T { String m() { def c = { -> cnt }; c() } }
class C implements U {}
assert new C().m() == '1' // MissingMethodException: No signature of static
method: T__count$get for class: T
```
Writing the property in a closure (`cnt = n`, `cnt++`, `s = 'y'`) is
rejected under `@TypeChecked` and `@CompileStatic` with "Cannot set read-only
property". Dynamic Groovy and the method form `getCnt()` work.
A name like this inside a closure is left unchanged by the trait
transformation (GROOVY-8049, GROOVY-9386), so that the closure's delegate can
still provide it. That is kept.
- **Closures:** when the type checker resolves the name to a static method
of a trait through the closure's owner, `StaticInvocationWriter` now calls the
trait helper (the GROOVY-12590 path). It passes the class of the closure's
`thisObject`, which the trait transformation sets to `$self`, or `$static$self`
in a static trait method.
- **Lambdas:** a lambda has no delegate or `thisObject`, so
`TraitReceiverTransformer` rewrites the name to `$self.name` or
`$static$self.name` there, as for a property of the trait itself, and the
lambda captures it.
- **Setters:** in trait code, `StaticTypeCheckingVisitor` now also finds the
static setters of a super trait, since a static interface method is not
inherited by the receiver type.
`p.with { cnt }` still reads `p.cnt` when `p` has the property. This PR is
stacked on #3115 (GROOVY-12590).
## Tests
`TraitASTTransformationTest.testSuperTraitStaticAccessorAsPropertyInClosure`
runs dynamic, `@TypeChecked` and `@CompileStatic`. It covers getters, an isser,
setters, `++`, `+=`, nested closures, static and instance methods, a
grand-super trait, static properties, lambdas, per-class state and
delegate-first resolution. The `@TypeChecked` and `@CompileStatic` variants
fail without the change, and each of the three main-code changes is needed for
them to pass. The `groovy.transform.stc`,
`org.codehaus.groovy.classgen.asm.sc`, `org.codehaus.groovy.transform.traitx`,
`gls`, `groovy.bugs` and `bugs` packages and `groovy.ThisAndSuperTest` pass.
The Checkstyle and CodeNarc gates pass.
> SC calls a super trait's static accessor, read as a property in a closure or
> lambda, with the trait instead of the implementing class
> -------------------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12608
> URL: https://issues.apache.org/jira/browse/GROOVY-12608
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> {code:groovy}
> @groovy.transform.CompileStatic
> trait T { private static int count = 1; static int getCnt() { count } }
> @groovy.transform.CompileStatic
> trait U extends T { String m() { def c = { -> cnt }; c() } }
> class C implements U {}
> assert new C().m() == '1'
> {code}
> Actual: MissingMethodException: No signature of static method: T__count$get
> for class: T is applicable for argument types: () values: []
> The same happens in a lambda, in a static trait method, and for a super
> trait's static property such as static int shared = 3. Writing such a
> property in a closure (cnt = n, cnt++, s = 'y') fails to compile under
> @TypeChecked and @CompileStatic: "Cannot set read-only property: cnt".
> Expected: the assertion passes, as it does in dynamic Groovy and with
> getCnt(). A delegate that has the property still comes first (p.with ... cnt).
> The static compiler calls the static method on the super trait interface,
> which passes the trait itself to the trait helper as the receiver class. The
> type checker looks for setters only on the receiver type, where a static
> interface method is not inherited.
> Seen on master with PR #3115 (GROOVY-12590) applied, which fixes the same
> call outside closures. 6.0.0 fails the same way.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)