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/23 01:53:53 UTC

[groovy] 02/02: 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 GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 9c7bb5d34c43efd345b5664498ef81c6a6f4df66
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   | 52 ++++++++++++----------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 50 ++++++++++++++++++++-
 2 files changed, 78 insertions(+), 24 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 cd15553533..99fba116aa 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2300,8 +2300,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             && parameters.length == argumentTypes.length - 1) {
                         ctor = typeCheckMapConstructor(call, receiver, arguments);
                     } else {
-                        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);
@@ -2658,8 +2658,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
@@ -2943,23 +2943,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]);
+                                    }
                                 }
                             }
                         }
@@ -4820,7 +4827,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     }
                 }
                 MethodNode stubbed;
-                if ("<init>".equals(method.getName())) {
+                if (method.isConstructor()) {
                     stubbed = new ConstructorNode(
                             method.getModifiers(),
                             newParams,
@@ -4926,8 +4933,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());
@@ -5295,7 +5301,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);
@@ -5309,9 +5315,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
 
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index eaa5a038e1..ee17a5d5f3 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 '''