[
https://issues.apache.org/jira/browse/GROOVY-12550?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18141064#comment-18141064
]
ASF GitHub Bot commented on GROOVY-12550:
-----------------------------------------
paulk-asert merged PR #3079:
URL: https://github.com/apache/groovy/pull/3079
> STC: a closure's doCall drops the type arguments of a return type whose class
> is in the same compilation unit
> -------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12550
> URL: https://issues.apache.org/jira/browse/GROOVY-12550
> Project: Groovy
> Issue Type: Bug
> Components: Static compilation
> Affects Versions: 4.0.33, 5.1.3
> Reporter: James Daugherty
> Priority: Major
>
> With static type checking, the {{doCall}} method of a closure is declared
> with the closure's inferred return type. When that type is a parameterized
> type whose class is in the same compilation unit, its type arguments are
> dropped. When the class is read from the class path, they are kept. The same
> source therefore compiles to a closure class with a different signature
> depending on what else is compiled, which incremental builds and build caches
> see as a change.
> {code:groovy}
> interface Up {}
> class Res<T> { List<T> items = [] }
> @groovy.transform.CompileStatic
> class Svc {
> static <V> V timeIt(Closure<V> body) { body.call() }
> def <U extends Up> Res<U> read(Class<U> type) { new Res<U>() }
> Res<Up> load(Class<Up> type) { timeIt { -> read(type) } }
> }
> {code}
> {noformat}
> Svc compiled together with Res: public Res doCall()
> Svc compiled against a compiled Res: public Res<Up> doCall()
> {noformat}
> Reproduced with 4.0.33, 5.1.3 and master. A reproducer with a groovyc script
> and a Gradle build:
> https://github.com/jdaugherty/groovy-closure-return-type-signature
> Grails applications hit this in incremental builds: {{@Transactional}} and
> similar transformations move a method body into a new method without the
> method's type parameters, so a closure there returns a concrete parameterized
> type.
> *Cause*
> {{ClosureWriter.createClosureClass}} normalises the inferred return type with:
> {code:java}
> else if (returnType.isPrimaryClassNode()) returnType =
> returnType.getPlainNodeReference();
> {code}
> {{isPrimaryClassNode()}} is true for the class node of a class in the
> compilation unit and for every reference to it, including a parameterized one
> such as {{Res<Up>}}, and {{getPlainNodeReference()}} drops the type
> arguments. A reference to a class read from bytecode is not primary, so it
> keeps them unless they are unresolved. On master,
> {{ClosureWriter.inferredClosureReturnType}} does the same for packed closures.
> *Proposed fix*
> Only replace the class node itself, which carries the declared type
> parameters, by its plain reference: {{returnType.isPrimaryClassNode() &&
> !returnType.isRedirectNode()}}. A parameterized reference then goes through
> the same check for unresolved generics as any other type, and both builds
> produce {{Res<Up> doCall()}}, the signature a closure already gets for a
> parameterized library type such as {{List<String>}}.
> Branches with the fix and a test, {{ClosureReturnTypeSignatureTest}}, that
> compiles the example both ways and compares the {{doCall}} signatures. The
> full core test suite passes on both:
> * master:
> https://github.com/jdaugherty/groovy/tree/closure-return-type-signature
> * GROOVY_5_1_X:
> https://github.com/jdaugherty/groovy/tree/closure-return-type-signature-5_1_X
> Related: GROOVY-10932 and GROOVY-12544, other differences between classes of
> the compilation unit and pre-compiled classes that affect incremental builds.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)