Paul King created GROOVY-12541:
----------------------------------

             Summary: @CompileStatic silently drops type annotations on local 
variables
                 Key: GROOVY-12541
                 URL: https://issues.apache.org/jira/browse/GROOVY-12541
             Project: Groovy
          Issue Type: Bug
    Affects Versions: 6.0.0
            Reporter: Paul King


h3. Problem

Under {{@CompileStatic}}, {{TYPE_USE}} annotations on local variables are not 
written to the class file (no {{RuntimeVisibleTypeAnnotations}} entry with 
target {{LOCAL_VARIABLE}}). The same source compiled dynamically emits them 
correctly. No error or warning is given.

{code:groovy}
@groovy.transform.CompileStatic
class Foo {
    static void m() {
        @TU Integer n = 0
        for (@TU Integer i = 0; i < 3; i++) { }
        println n
    }
}
{code}

||Code||Dynamic||{{@CompileStatic}}||
|{{for (@TU Integer i = 0; ...)}}|emitted|*not emitted*|
|{{@TU Integer n = 0}}|emitted|*not emitted*|

The same happens whether the annotation targets only {{TYPE_USE}} or 
{{LOCAL_VARIABLE, TYPE_USE}}. Tested on 6.0.0-beta-2 and master. (Primitive 
locals currently crash instead; see the related {{GroovyBugError}} issue. Once 
that is fixed, they show this same drop under {{@CompileStatic}}.)

Tools that read local variable type annotations from bytecode (e.g. TornadoVM's 
{{@Parallel}} loop marker) therefore never see them in statically compiled 
Groovy.

h3. Cause

{{CompileStack}} writes local variable type annotations from 
{{BytecodeVariable.getType()}}, the slot type passed to {{defineVariable}}. For 
declarations, {{BinaryExpressionHelper.evaluateEqual}} gets that type from the 
type chooser. The dynamic chooser returns the declared type node, which carries 
the annotations. {{StaticTypesTypeChooser}} returns the type checker's inferred 
type, a different node without them.

h3. Proposed fix

Record the declared type separately from the slot type and emit the annotations 
from it:

* {{BytecodeVariable}}: new {{declaredType}} property; {{getDeclaredType()}} 
falls back to {{getType()}} when none was recorded.
* {{CompileStack.defineVariable(Variable, ClassNode, boolean)}}: 
{{answer.setDeclaredType(v.getOriginType())}}.
* {{CompileStack}} local variable table emission: read type annotations from 
{{v.getDeclaredType()}} instead of {{v.getType()}}.

Regression test added to {{TypeAnnotationsTest}} 
({{testTypeAnnotationsForLocalVariablesCompileStatic}}): a {{@CompileStatic}} 
{{for (@A int i ...)}} loop and an annotated {{Integer}} local both get 
{{LOCAL_VARIABLE}} type annotations.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to