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)