[
https://issues.apache.org/jira/browse/GROOVY-12106?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18091631#comment-18091631
]
Eric Milles edited comment on GROOVY-12106 at 6/25/26 2:41 PM:
---------------------------------------------------------------
I think the transformation installed for GROOVY-11985 created an object
expression that is not easy to handle. The Groovy 5 code -- for GROOVY-7213,
GROOVY-7214, GROOVY-8282, GROOVY-8859, GROOVY-10106, GROOVY-10312 --
transformed "this.m\(x)" into "(this or T$Trait$Helper).m($self or $static$self
or (Class)$self.getClass(), x)". The static method "m(Class,Args)" is directly
available by lookup and so STC is straightforward.
The GROOVY-11985 change writes "this.m\(x)" as "($self or $static$self or
$self.getClass()).m\(x)". This is a bit harder to locate the proper method. I
had experimented with adding some STC metadata once. I forget ATM if it helped.
So instead of this sequence in TraitReceiverTransformer:
{code:java}
Expression implClass =
ClassHelper.isClassType(weaved.getOriginType()) ? varX(weaved) :
castX(ClassHelper.CLASS_Type.getPlainNodeReference(), callX(varX(weaved),
"getClass"));
newCall = callX(implClass, method, transform(arguments));
newCall.setImplicitThis(false);
newCall.putNodeMetaData(TraitASTTransformation.DO_DYNAMIC,
methodNode.getReturnType());
{code}
Add some more hints:
{code:java}
// this.m(x) --> ($static$self or $self.getClass()).m(x)
Expression selfClass;
if (ClassHelper.isClassType(weaved.getOriginType())) {
selfClass = varX(weaved); // $static$self
} else {
newCall = callX(varX(weaved), "getClass"); //
$self.getClass()
newCall.setImplicitThis(false);
newCall.setMethodTarget(ClassHelper.OBJECT_TYPE.getGetterMethod("getClass",false));
selfClass = newCall;
}
newCall = callX(selfClass, method, transform(arguments));
newCall.setImplicitThis(false);
// weaved static method is available via the implementing
class, which is not checkable
newCall.putNodeMetaData(TraitASTTransformation.DO_DYNAMIC,
methodNode.getReturnType());
{code}
Looking at what I have here, I seem to recall now that there is not enough info
to indicate the generics for the Class cases (aka "$static$self" or
"$self.getClass()"). That is why dynamic is on.
was (Author: emilles):
I think the transformation installed for GROOVY-11985 created an object
expression that is not easy to handle. The Groovy 5 code -- for GROOVY-7213,
GROOVY-7214, GROOVY-8282, GROOVY-8859, GROOVY-10106, GROOVY-10312 --
transformed "this.m(x)" into "(this or T$Trait$Helper).m($self or $static$self
or (Class)$self.getClass(), x)". The static method "m(Class,Args)" is directly
available by lookup and so STC is straightforward.
The GROOVY-11985 change writes "this.m(x)" as "($self or $static$self or
$self.getClass()).m(x)". This is a bit harder to locate the proper method. I
had experimented with adding some STC metadata once. I forget ATM if it helped.
So instead of this sequence in TraitReceiverTransformer:
{code:java}
Expression implClass =
ClassHelper.isClassType(weaved.getOriginType()) ? varX(weaved) :
castX(ClassHelper.CLASS_Type.getPlainNodeReference(), callX(varX(weaved),
"getClass"));
newCall = callX(implClass, method, transform(arguments));
newCall.setImplicitThis(false);
newCall.putNodeMetaData(TraitASTTransformation.DO_DYNAMIC,
methodNode.getReturnType());
{code}
Add some more hints:
{code:java}
// this.m(x) --> ($static$self or $self.getClass()).m(x)
Expression selfClass;
if (ClassHelper.isClassType(weaved.getOriginType())) {
selfClass = varX(weaved); // $static$self
} else {
newCall = callX(varX(weaved), "getClass"); //
$self.getClass()
newCall.setImplicitThis(false);
newCall.setMethodTarget(ClassHelper.OBJECT_TYPE.getGetterMethod("getClass",false));
selfClass = newCall;
}
newCall = callX(selfClass, method, transform(arguments));
newCall.setImplicitThis(false);
// weaved static method is available via the implementing
class, which is not checkable
newCall.putNodeMetaData(TraitASTTransformation.DO_DYNAMIC,
methodNode.getReturnType());
{code}
Looking at what I have here, I seem to recall now that there is not enough info
to indicate the generics for the Class cases (aka "$static$self" or
"$self.getClass()"). That is why dynamic is on.
> 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
> 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)