[
https://issues.apache.org/jira/browse/GROOVY-12106?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18092072#comment-18092072
]
Paul King commented on GROOVY-12106:
------------------------------------
Scenario: child trait extends parent trait, calling the inherited parent-trait
static, with a subtype argument, under @CompileStatic
|| Row || Form || Case || master || 5.0.7-SNAPSHOT || GEP-22 says ||
| 16 | unqualified {{m(...)}} | (b) | (x) | (x) | *Silent* |
| 16b | {{this.m(...)}} | (b) | (x) | (x) | *Silent* |
| 16q | qualified {{Parent.m(...)}} | (a) | (/) | (x) | *Covered — supported* |
| - | {{Parent.super.m(...)}} | escape | (/) | (/) | *Covered* |
| - | unqualified, _exact_ {{Object}} arg | - | (/) | (/) | *Silent* |
| D | plain class {{Child extends Parent}} | control | (/) | (/) | *n/a* |
What the spec says for each verdict:
* *(b) — Silent.* Every static-dispatch rule in _§ Static members_ is phrased
for a static _"declared in a trait"_ called from _"the body of a trait method"_
resolving to _"the trait's own copy."_ None address a static *inherited from a
super-trait* called from a *sub-trait* body. But the model implies they should
work: _"a trait should be reasoned about as if it were a superclass of the
implementing class"_ + _"declarer-bound by default: a call of the form
{{m(...)}} or {{this.m(...)}} ... resolves to the trait's own copy."_ So (b) is
a spec-violation-by-implication, not intended behaviour.
* *exact-{{Object}} arg — Silent (and the split is the bug).* The spec never
makes static resolution depend on argument subtyping; that the exact type
resolves while a subtype fails is nowhere in the spec.
* *(a) qualified — Covered/supported.* _"Public static methods declared in a
trait are promoted onto the generated trait interface as JVM-native interface
statics ... External calls of both forms — {{Trait.staticMethod(...)}} ... —
are supported"_ (GROOVY-12111). Master matches the spec; 5.0.7 fails only
because it lacks the interface promotion → a pure backport gap.
* *{{Parent.super.m(...)}} — Covered.* _"A qualified {{T.super.m(...)}} call
resolves to trait {{T}}'s implementation ... the explicit override form"_ (item
3); item 4 names it as the form to use for a trait's own static.
* *plain class (row D) — n/a.* GEP-22 governs traits; the plain-class control
is outside its scope (and works everywhere — localising the defect to trait
machinery).
*Net:* the spec is silent exactly where the bug lives (inherited super-trait
statics via unqualified/{{this.}}), explicitly supports the form that works on
master (qualified), and is n/a for the control. The GROOVY-12106 fix therefore
has two spec-aligned parts: (b) needs a code fix _and_ a spec clause making
inherited-super-trait static resolution explicit; (a) needs only the 5.0.x
backport (already spec-covered).
> STC cannot resolve inherited static trait method from sub-trait body
> --------------------------------------------------------------------
>
> Key: GROOVY-12106
> URL: https://issues.apache.org/jira/browse/GROOVY-12106
> Project: Groovy
> Issue Type: Bug
> Components: Static Type Checker
> Affects Versions: 5.0.7
> Reporter: James Fredley
> Assignee: Paul King
> Priority: Major
> Labels: static-method, sts, traits
>
> h2. Summary
> Groovy 5 static type checking does not resolve a static method declared by a
> parent trait when the method is called from a sub-trait body.
> This is independent of Grails and can be reproduced with plain Groovy traits
> under @CompileStatic.
> This was re-tested on Groovy 5.0.7-SNAPSHOT. Both an unqualified call and a
> qualified call fail static type checking:
> {code:groovy}
> inheritedStaticMethod(value)
> {code}
> {code:groovy}
> ParentTrait.inheritedStaticMethod(value)
> {code}
> The failure is distinct from GROOVY-11985, which fixed
> static-override-via-this. This issue is about resolving a parent trait's
> static method from a sub-trait body.
> h2. Standalone reproducer
> {code:groovy}
> import groovy.transform.CompileStatic
> @CompileStatic
> trait ParentTrait {
> static void configure(Closure closure, Object target) {
> if (closure != null) {
> closure.delegate = target
> closure.resolveStrategy = Closure.DELEGATE_ONLY
> closure.call()
> }
> }
> }
> @CompileStatic
> trait ChildTrait extends ParentTrait {
> void applyConfig(Closure closure, Object target) {
> configure(closure, target)
> }
> }
> @CompileStatic
> class Example implements ChildTrait {
> }
> {code}
> h2. Expected result
> The code should pass static type checking. A sub-trait should be able to
> resolve a static method declared by a parent trait.
> h2. Actual result
> Static type checking fails on the call from the sub-trait body:
> {code}
> Cannot find matching method ChildTrait#configure(groovy.lang.Closure,
> java.lang.Object)
> {code}
> Using a qualified call also fails:
> {code:groovy}
> ParentTrait.configure(closure, target)
> {code}
> h2. Real-world use case
> Apache Grails has the same pattern in the GraphQL DSL helper traits.
> Parent trait method:
> {code}
> grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ExecutesClosures.groovy
> line 34
> {code}
> {code:groovy}
> @CompileStatic
> trait ExecutesClosures {
> static void withDelegate(@DelegatesTo(strategy = Closure.DELEGATE_ONLY)
> Closure closure, Object delegate) {
> ...
> }
> }
> {code}
> Sub-traits that should be able to call the inherited helper:
> {code}
> grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/Arguable.groovy
> grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ComplexTyped.groovy
> {code}
> Grails currently works around this by inlining the parent trait helper body
> into each sub-trait. Once this Groovy STC issue is fixed, that inline
> workaround can be removed.
> h2. Workaround
> Inline the static helper body in each sub-trait, or avoid calling the
> inherited static trait method from the sub-trait.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)