Paul King created GROOVY-12702:
----------------------------------

             Summary: TYPE_USE annotations on a multiple-assignment declaration 
are not applied to the declared variables' types
                 Key: GROOVY-12702
                 URL: https://issues.apache.org/jira/browse/GROOVY-12702
             Project: Groovy
          Issue Type: Bug
          Components: Compiler
            Reporter: Paul King


h3. Description

A TYPE_USE annotation written in declaration position applies to the type of 
the declared variable, and is written to the class file as a local-variable 
type annotation ({{RuntimeVisibleTypeAnnotations}}, target {{LOCAL_VARIABLE}}). 
This works for a single declaration and for several declarators, but not for a 
multiple-assignment declaration:

{code:groovy}
import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface TO {}

class C {
    def single()      { @TO String a = 'x'; a }                         // 1 
local-variable type annotation
    def declarators() { @TO String a = 'x', b = 'y'; a + b }            // 2
    def multiple()    { @TO def (String a, String b) = ['x', 'y']; a + b } // 
none
    def typePosition(){ def (@TO String a, String b) = ['x', 'y']; a + b } // 
1, for a
}
{code}

{{javap -v C.class}} shows {{RuntimeVisibleTypeAnnotations}} entries with 
target {{LOCAL_VARIABLE}} for {{a}} in {{single()}}, for {{a}} and {{b}} in 
{{declarators()}}, and for {{a}} in {{typePosition()}}, but none in 
{{multiple()}}. The same holds with {{@TypeChecked}}. An annotation allowed on 
both TYPE_USE and LOCAL_VARIABLE ({{@Target([TYPE_USE, LOCAL_VARIABLE])}}) 
behaves the same way.

Under {{@CompileStatic}} the result depends on how the multiple assignment is 
compiled. Static compilation rewrites {{def (a, b) = [x, y]}} into single 
declarations when its value is not used, and with GROOVY-12698 those carry the 
annotations of the original declaration. So, once GROOVY-12698 and GROOVY-12700 
are in:
* as a statement, {{multiple()}} emits an annotation for both {{a}} and {{b}}, 
like {{declarators()}};
* where its value is used (for example as the last statement of a method or 
closure), it is not rewritten and emits none.

So the same declaration produces different class files depending on the 
compilation mode and on where it appears.

h3. Cause

{{ExtendedVerifier#visitDeclarationExpression}} handles the two forms 
differently:

{code:java}
visitAnnotations(expression, LOCAL_VARIABLE_TARGET);
if (expression.isMultipleAssignmentDeclaration()) {
    expression.getTupleExpression().forEach(e -> 
visitTypeAnnotations(((VariableExpression) e).getOriginType()));
} else {
    ClassNode type = expression.getVariableExpression().getOriginType();
    visitTypeAnnotations(type);
    extractTypeUseAnnotations(expression.getAnnotations(), type, 
LOCAL_VARIABLE_TARGET);
}
{code}

For a single declaration, {{extractTypeUseAnnotations}} moves the TYPE_USE 
annotations of the declaration onto the declared type, from where 
{{CompileStack}} writes them as local-variable type annotations. For a 
multiple-assignment declaration, only annotations already on each variable's 
type (written in type position) are checked, and the annotations of the 
declaration itself are never moved onto the variables' types.

h3. Suggested fix

In the multiple-assignment branch, add the TYPE_USE annotations of the 
declaration to the origin type of every variable on the left-hand side, as the 
single-declaration branch does for its one variable. Annotations allowed only 
on TYPE_USE should then be removed from the declaration once, after all the 
variables have been handled. Calling {{extractTypeUseAnnotations}} per variable 
as it stands would remove them while handling the first variable, so the helper 
needs splitting into "add to type" and "remove from declaration", or a variant 
that takes several target types. The existing duplicate check in 
{{extractTypeUseAnnotations}} (GROOVY-12434) also guards against adding the 
same annotation twice when variables share a type node.

With that change, the multiple-assignment form emits the same annotations as 
the declarator form in every mode and position, and the class files no longer 
depend on whether static compilation rewrote the declaration.



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

Reply via email to