You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/07/22 21:03:07 UTC
[groovy] branch master updated: GROOVY-10699: STC: `<>` ctor call closure/lambda parameter type checking
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 188d8f1ca5 GROOVY-10699: STC: `<>` ctor call closure/lambda parameter type checking
188d8f1ca5 is described below
commit 188d8f1ca501cd9584dc0a630cf4b8df75a667bf
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Jul 22 15:40:47 2022 -0500
GROOVY-10699: STC: `<>` ctor call closure/lambda parameter type checking
---
.../transform/stc/StaticTypeCheckingVisitor.java | 82 ++++++++++++----------
.../groovy/transform/stc/GenericsSTCTest.groovy | 50 ++++++++++++-
2 files changed, 92 insertions(+), 40 deletions(-)
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 11e12da0ff..c5d306b819 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2295,8 +2295,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ctor = findMethodOrFail(call, receiver, "<init>", argumentTypes);
if (ctor != null) {
Parameter[] parameters = ctor.getParameters();
- if (asBoolean(receiver.getGenericsTypes())) { // GROOVY-10283, GROOVY-10316, GROOVY-10482, GROOVY-10624, et al.
- Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, ctor, argumentList);
+ if (asBoolean(receiver.getGenericsTypes())) { // GROOVY-10283, GROOVY-10316, GROOVY-10482, GROOVY-10624
+ Map<GenericsTypeName, GenericsType> context = extractPlaceHolders(receiver, ctor.getDeclaringClass());
parameters = Arrays.stream(parameters).map(p -> new Parameter(applyGenericsContext(context, p.getType()), p.getName())).toArray(Parameter[]::new);
}
resolvePlaceholdersFromImplicitTypeHints(argumentTypes, argumentList, parameters);
@@ -2653,8 +2653,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected void addTypeCheckingInfoAnnotation(final MethodNode node) {
- // TypeChecked$TypeCheckingInfo can not be applied on constructors
- if (node instanceof ConstructorNode) return;
+ // TypeChecked$TypeCheckingInfo cannot be applied on constructors
+ if (node.isConstructor()) return;
// if a returned inferred type is available and no @TypeCheckingInfo is on node, then add an
// annotation to the method node
@@ -2944,23 +2944,30 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
doInferClosureParameterTypes(receiver, arguments, expression, method, value, conflictResolver, options);
}
} else if (isSAMType(target.getOriginType())) { // SAM-type coercion
- Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
- GenericsType[] typeParameters = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
+ boolean isConstructor = method.isConstructor();
+ boolean hasTypeArguments = asBoolean(receiver.getGenericsTypes());
+ Map<GenericsTypeName, GenericsType> context = isConstructor && !hasTypeArguments ? new HashMap<>() // GROOVY-10699
+ : extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
+ GenericsType[] typeParameters = isConstructor ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
if (typeParameters != null) {
boolean typeParametersResolved = false;
// first check for explicit type arguments
- Expression emc = typeCheckingContext.getEnclosingMethodCall();
- if (emc instanceof MethodCallExpression) {
- MethodCallExpression mce = (MethodCallExpression) emc;
- if (mce.getArguments() == arguments) {
- GenericsType[] typeArguments = mce.getGenericsTypes();
- if (typeArguments != null) {
- int n = typeParameters.length;
- if (n == typeArguments.length) {
- typeParametersResolved = true;
- for (int i = 0; i < n; i += 1) {
- context.put(new GenericsTypeName(typeParameters[i].getName()), typeArguments[i]);
+ if (isConstructor) {
+ typeParametersResolved = hasTypeArguments;
+ } else {
+ Expression emc = typeCheckingContext.getEnclosingMethodCall();
+ if (emc instanceof MethodCallExpression) {
+ MethodCallExpression mce = (MethodCallExpression) emc;
+ if (mce.getArguments() == arguments) {
+ GenericsType[] typeArguments = mce.getGenericsTypes();
+ if (typeArguments != null) {
+ int n = typeParameters.length;
+ if (n == typeArguments.length) {
+ typeParametersResolved = true;
+ for (int i = 0; i < n; i += 1) {
+ context.put(new GenericsTypeName(typeParameters[i].getName()), typeArguments[i]);
+ }
}
}
}
@@ -3149,26 +3156,26 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));
- MethodNode dummyMN = selectedMethod instanceof ExtensionMethodNode ? ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode() : selectedMethod;
- dummyMN = new MethodNode(dummyMN.getName(), dummyMN.getModifiers(), returnType, dummyMN.getParameters(), dummyMN.getExceptions(), null);
- dummyMN.setDeclaringClass(selectedMethod.getDeclaringClass());
- dummyMN.setGenericsTypes(selectedMethod.getGenericsTypes());
+ MethodNode methodNode = selectedMethod instanceof ExtensionMethodNode ? ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode() : selectedMethod;
+ methodNode = new MethodNode("$$", methodNode.getModifiers(), returnType, methodNode.getParameters(), methodNode.getExceptions(), null);
+ methodNode.setDeclaringClass(selectedMethod.getDeclaringClass());
+ methodNode.setGenericsTypes(selectedMethod.getGenericsTypes());
if (selectedMethod instanceof ExtensionMethodNode) {
- dummyMN = new ExtensionMethodNode(
- dummyMN,
- dummyMN.getName(),
- dummyMN.getModifiers(),
+ methodNode = new ExtensionMethodNode(
+ methodNode,
+ methodNode.getName(),
+ methodNode.getModifiers(),
returnType,
selectedMethod.getParameters(),
selectedMethod.getExceptions(),
null,
((ExtensionMethodNode) selectedMethod).isStaticExtension()
);
- dummyMN.setDeclaringClass(selectedMethod.getDeclaringClass());
- dummyMN.setGenericsTypes(selectedMethod.getGenericsTypes());
+ methodNode.setDeclaringClass(selectedMethod.getDeclaringClass());
+ methodNode.setGenericsTypes(selectedMethod.getGenericsTypes());
}
- returnType = inferReturnTypeGenerics(receiver, dummyMN, arguments);
+ returnType = inferReturnTypeGenerics(receiver, methodNode, arguments);
GenericsType[] returnTypeGenerics = returnType.getGenericsTypes();
for (int i = 0, n = returnTypeGenerics.length; i < n; i += 1) {
GenericsType gt = returnTypeGenerics[i];
@@ -4866,14 +4873,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
MethodNode stubbed;
- if ("<init>".equals(method.getName())) {
+ if (method.isConstructor()) {
stubbed = new ConstructorNode(
method.getModifiers(),
newParams,
method.getExceptions(),
GENERATED_EMPTY_STATEMENT
);
-
} else {
stubbed = new MethodNode(
method.getName(),
@@ -4973,8 +4979,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
- if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
- // lookup in DGM methods too
+ if (!name.endsWith("init>")) { // also search for extension methods
findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), receiver, name, args, methods);
}
methods = filterMethodsByVisibility(methods, typeCheckingContext.getEnclosingClassNode());
@@ -5330,7 +5335,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* @param explicitTypeHints type arguments (optional), for example {@code Collections.<String>emptyList()}
*/
protected ClassNode inferReturnTypeGenerics(final ClassNode receiver, final MethodNode method, final Expression arguments, final GenericsType[] explicitTypeHints) {
- ClassNode returnType = method instanceof ConstructorNode ? method.getDeclaringClass() : method.getReturnType();
+ ClassNode returnType = method.isConstructor() ? method.getDeclaringClass() : method.getReturnType();
if (!GenericsUtils.hasUnresolvedGenerics(returnType)) {
// GROOVY-7538: replace "Type<?>" with "Type<? extends/super X>" for any "Type<T extends/super X>"
if (getGenericsWithoutArray(returnType) != null) returnType = boundUnboundedWildcards(returnType);
@@ -5344,9 +5349,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return inferReturnTypeGenerics(receiver, extension, args, explicitTypeHints);
}
- Map<GenericsTypeName, GenericsType> context = method.isStatic() || method instanceof ConstructorNode
+ Map<GenericsTypeName, GenericsType> context = method.isStatic() || method.isConstructor()
? null : extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
- GenericsType[] methodGenericTypes = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
+ GenericsType[] methodGenericTypes = method.isConstructor() ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
// 1) resolve type parameters of method
@@ -5690,8 +5695,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
todo = new ClassNode[] {!isPrimitiveType(declaringClass) ? wrapTypeIfNecessary(receiver) : receiver};
}
for (ClassNode type : todo) {
- ClassNode current = type;
- while (current != null) {
+ for (ClassNode current = type; current != null; ) {
Map<GenericsTypeName, GenericsType> placeHolders = new HashMap<>();
if (current.isGenericsPlaceHolder())
// GROOVY-10622: type param bound "T extends Set<Type>"
@@ -5749,8 +5753,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
result = new HashMap<>();
} else {
ClassNode declaring = method.getDeclaringClass();
- if (argument instanceof ArgumentListExpression) { // resolve extension method class
- List<Expression> arguments = ((ArgumentListExpression) argument).getExpressions();
+ if (argument instanceof TupleExpression) { // resolve extension method class
+ List<Expression> arguments = ((TupleExpression) argument).getExpressions();
if (!arguments.isEmpty()) {
ClassNode cn = arguments.get(0).getNodeMetaData(ExtensionMethodDeclaringClass.class);
if (cn != null)
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index b61d831a71..0dc1f6995f 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1154,7 +1154,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
class B<T1 extends Number, T2 extends A<C, ? extends T1>> {
T2 t
B(T2 t) {
- this.t = t
+ this.t = t // Cannot assign value of type A<C,? extends T1> to variable of type T2
}
}
class C {
@@ -1422,6 +1422,54 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10699
+ void testDiamondInferrenceFromConstructor33() {
+ assertScript '''
+ class A<T> {
+ A(B<T> b_of_t) {
+ }
+ }
+ class B<T> {
+ B(T t) {
+ }
+ }
+
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'A<java.lang.String>'
+ })
+ def x = new A<>(new B<>('str'))
+ '''
+
+ assertScript '''import java.util.function.Consumer
+ class C<T> {
+ C(Consumer<T> consumer_of_t) {
+ consumer_of_t.accept(null)
+ }
+ }
+
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'C<java.lang.Object>' // TODO: String
+ })
+ def y = new C<>((String s) -> { print(s); }) // error: Expected type Object for lambda parameter: s
+ '''
+
+ assertScript '''import java.util.function.Supplier
+ class D<T> {
+ D(Supplier<T> supplier_of_t) {
+ T t = supplier_of_t.get()
+ }
+ }
+
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'D<java.lang.String>'
+ })
+ def z = new D<>(() -> 'str')
+ '''
+ }
+
// GROOVY-10280
void testTypeArgumentPropagation() {
assertScript '''