This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 7a7e7b2ef8 GROOVY-11479: STC: closure or lambda parameter type must
remain mutable
7a7e7b2ef8 is described below
commit 7a7e7b2ef8a40b7cf648017f57c1ea0ddd8ba87f
Author: Eric Milles <[email protected]>
AuthorDate: Wed Sep 25 10:51:50 2024 -0500
GROOVY-11479: STC: closure or lambda parameter type must remain mutable
do not use the type from the SAM directory
---
.../asm/sc/AbstractFunctionalInterfaceWriter.java | 25 +++++++++++----
.../transform/stc/StaticTypeCheckingVisitor.java | 5 ++-
.../groovy/classgen/asm/TypeAnnotationsTest.groovy | 36 ++++++++++++++++++++++
3 files changed, 59 insertions(+), 7 deletions(-)
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 4292491afa..ceb280c43b 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.getUnwrapper;
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 @@ public interface AbstractFunctionalInterfaceWriter {
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 @@ public interface AbstractFunctionalInterfaceWriter {
&& (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);
+ }
+ } 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();
}
- }
- if (type.isGenericsPlaceHolder()) {
- type = type.redirect();
}
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 db5446a4e5..1fbaef6a7a 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 @@ out: if ((samParameterTypes.length == 1 &&
isOrImplements(samParameterTypes[0
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 74746e3eae..d8758d9de3 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 @@ final class TypeAnnotationsTest extends
AbstractBytecodeTestCase {
])
}
+ // 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() }