GROOVY-11479: STC: closure or lambda parameter type must remain mutable
do not use the type from the SAM directory
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
index 4292491..ceb280c 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
@@ -33,6 +33,7 @@
import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
import static org.codehaus.groovy.ast.ClassHelper.isDynamicTyped;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.hasUnresolvedGenerics;
import static org.codehaus.groovy.classgen.asm.BytecodeHelper.getClassInternalName;
import static org.codehaus.groovy.classgen.asm.BytecodeHelper.getMethodDescriptor;
@@ -113,12 +114,14 @@
throw new RuntimeParserException("The inferred type[" + inferredType.redirect() + "] is not compatible with the parameter type[" + parameterType.redirect() + "]", parameterType);
}
- ClassNode type = inferredType;
+ ClassNode type;
if (isPrimitiveType(parameterType)) {
if (!isPrimitiveType(inferredType)) {
// The non-primitive type and primitive type are not allowed to mix since Java 9+
// java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: class java.lang.Integer is not a subtype of int
- type = getUnwrapper(inferredType);
+ type = getUnwrapper(inferredType).getPlainNodeReference(false);
+ } else {
+ type = inferredType.getPlainNodeReference(false);
}
} else if (isPrimitiveType(inferredType)) {
// GROOVY-9790: bootstrap method initialization exception raised when lambda parameter type is wrong
@@ -128,11 +131,21 @@
&& (parameterType.equals(getUnwrapper(parameterType)) || inferredType.equals(getWrapper(inferredType)))) { // (2)
// The non-primitive type and primitive type are not allowed to mix since Java 9+
// java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: int is not a subtype of class java.lang.Object
- type = getWrapper(inferredType);
+ type = getWrapper(inferredType).getPlainNodeReference();
+ } else {
+ type = inferredType.getPlainNodeReference(false);
}
- }
- if (type.isGenericsPlaceHolder()) {
- type = type.redirect();
+ } else {
+ type = inferredType;
+ // GROOVY-11304: no placeholders
+ if (hasUnresolvedGenerics(type)) type = type.redirect();
+ // GROOVY-11479: mutable for node metadata or type annotations
+ if (type.toString(false).equals(parameterType.toString(false))) {
+ type = parameterType;
+ } else {
+ // TODO: deep copy if type args set
+ type = type.getPlainNodeReference();
+ }
}
return type;
}
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index db5446a..1fbaef6 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1035,7 +1035,10 @@
for (int i = 0; i < n; i += 1) {
Parameter parameter = closureParameters[i];
if (parameter.isDynamicTyped()) {
- parameter.setType(expectedTypes[i]); // GROOVY-11083, GROOVY-11085, et al.
+ ClassNode type = expectedTypes[i].getPlainNodeReference(false); // GROOVY-11479
+ if (!expectedTypes[i].isGenericsPlaceHolder())
+ type.setGenericsTypes(expectedTypes[i].getGenericsTypes());
+ parameter.setType(type); // GROOVY-11083, GROOVY-11085, et al.
} else {
checkParamType(parameter, expectedTypes[i], i == n-1, rhsExpression instanceof LambdaExpression);
}
diff --git a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
index 74746e3..d8758d9 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
@@ -257,6 +257,42 @@
])
}
+ // GROOVY-11479
+ void testTypeAnnotationsForClosure() {
+ def bytecode = compile(classNamePattern: 'Foo\\$_closure1', method: 'doCall', imports + '''
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+
+ @groovy.transform.CompileStatic
+ class Foo {
+ @TypeAnno0 java.util.function.IntUnaryOperator bar = { @TypeAnno1 def i -> 1 }
+ }
+ ''')
+ assert bytecode.hasStrictSequence([
+ 'public doCall(I)Ljava/lang/Integer;',
+ '@LTypeAnno1;() : METHOD_FORMAL_PARAMETER 0, null',
+ 'L0'
+ ])
+ }
+
+ // GROOVY-11479
+ void testTypeAnnotationsForLambda() {
+ def bytecode = compile(classNamePattern: 'Foo\\$_lambda1', method: 'doCall', imports + '''
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+
+ @groovy.transform.CompileStatic
+ class Foo {
+ @TypeAnno0 java.util.function.IntUnaryOperator bar = (@TypeAnno1 int i) -> 1
+ }
+ ''')
+ assert bytecode.hasStrictSequence([
+ 'public doCall(I)I',
+ '@LTypeAnno1;() : METHOD_FORMAL_PARAMETER 0, null',
+ 'L0'
+ ])
+ }
+
void testTypeAnnotationsForField1() {
def bytecode = compile(classNamePattern: 'Foo', field: 'foo', imports + '''
@Retention(RUNTIME) @Target(FIELD) @interface FieldAnno { String value() }