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/09/05 13:04:36 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-10749: STC: closure/lambda/reference parameter(s) as type witness

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new 5515c2b9fd GROOVY-10749: STC: closure/lambda/reference parameter(s) as type witness
5515c2b9fd is described below

commit 5515c2b9fd3bd53573f032e637e3dd3c282c94d2
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Sep 4 11:39:21 2022 -0500

    GROOVY-10749: STC: closure/lambda/reference parameter(s) as type witness
    
    3_0_X backport
---
 .../java/org/codehaus/groovy/ast/Parameter.java    |  2 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 53 +++-------------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 73 ++++++++++++++++++++++
 3 files changed, 84 insertions(+), 44 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/Parameter.java b/src/main/java/org/codehaus/groovy/ast/Parameter.java
index ce49781ab7..bf69a34ecf 100644
--- a/src/main/java/org/codehaus/groovy/ast/Parameter.java
+++ b/src/main/java/org/codehaus/groovy/ast/Parameter.java
@@ -52,7 +52,7 @@ public class Parameter extends AnnotatedNode implements Variable {
     }
 
     public String toString() {
-        return super.toString() + "[name:" + name + ((type == null) ? "" : " type: " + type.getName()) + ", hasDefaultValue: " + this.hasInitialExpression() + "]";
+        return super.toString() + "[name: " + name + (type == null ? "" : ", type: " + type.toString(false)) + ", hasDefaultValue: " + this.hasInitialExpression() + "]";
     }
 
     public String getName() {
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 198b113f4b..11eb97ea20 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -278,7 +278,6 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isPowe
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isShiftOperation;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isTraitSelf;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUsingGenericsOrIsArrayUsingGenerics;
-import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUsingUncheckedGenerics;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isVargs;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isWildcardLeftHandSide;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.lastArgMatchesVarg;
@@ -5518,14 +5517,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      * @param samType     the type into which the closure is coerced into
      * @return SAM type augmented using information from the argument expression
      */
-    private static ClassNode convertClosureTypeToSAMType(final Expression expression, final ClassNode closureType, final MethodNode sam, final ClassNode samType, final Map<GenericsTypeName, GenericsType> placeholders) {
+    private static ClassNode convertClosureTypeToSAMType(Expression expression, final ClassNode closureType, final MethodNode sam, final ClassNode samType, final Map<GenericsTypeName, GenericsType> placeholders) {
         // use the generics information from Closure to further specify the type
-        if (closureType.isUsingGenerics()) {
+        if (isClosureWithType(closureType)) {
             ClassNode closureReturnType = closureType.getGenericsTypes()[0].getType();
 
             Parameter[] parameters = sam.getParameters();
-            if (parameters.length > 0 && expression instanceof MethodPointerExpression
-                    && isUsingUncheckedGenerics(closureReturnType)) { // needs resolve
+            if (parameters.length > 0 && expression instanceof MethodPointerExpression) {
                 MethodPointerExpression mp = (MethodPointerExpression) expression;
                 MethodNode mn = chooseMethod(mp, () ->
                     applyGenericsContext(placeholders, extractTypesFromParameters(parameters))
@@ -5541,6 +5539,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     closureReturnType = applyGenericsContext(connections, closureReturnType);
                     // apply known generics connections to the placeholders of the return type
                     closureReturnType = applyGenericsContext(placeholders, closureReturnType);
+
+                    expression = new ClosureExpression(Arrays.stream(pTypes).map(t -> new Parameter(t,"")).toArray(Parameter[]::new), null);
                 }
             }
 
@@ -5549,44 +5549,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
             // repeat the same for each parameter given in the ClosureExpression
             if (parameters.length > 0 && expression instanceof ClosureExpression) {
-                List<ClassNode[]> genericsToConnect = new ArrayList<>();
-                Parameter[] closureParams = ((ClosureExpression) expression).getParameters();
-                ClassNode[] closureParamTypes = expression.getNodeMetaData(CLOSURE_ARGUMENTS);
-                if (closureParamTypes == null) closureParamTypes = extractTypesFromParameters(closureParams);
-
-                for (int i = 0, n = parameters.length; i < n; i += 1) {
-                    Parameter parameter = parameters[i];
-                    if (parameter.getOriginType().isUsingGenerics() && closureParamTypes.length > i) {
-                        genericsToConnect.add(new ClassNode[]{closureParamTypes[i], parameter.getOriginType()});
-                    }
-                }
-                for (ClassNode[] classNodes : genericsToConnect) {
-                    ClassNode found = classNodes[0];
-                    ClassNode expected = classNodes[1];
-                    if (!isAssignableTo(found, expected)) {
-                        // probably facing a type mismatch
-                        continue;
-                    }
-                    ClassNode generifiedType = GenericsUtils.parameterizeType(found, expected);
-                    while (expected.isArray()) {
-                        expected = expected.getComponentType();
-                        generifiedType = generifiedType.getComponentType();
-                    }
-                    if (expected.isGenericsPlaceHolder()) {
-                        placeholders.put(new GenericsTypeName(expected.getGenericsTypes()[0].getName()), new GenericsType(generifiedType));
-                    } else {
-                        GenericsType[] expectedGenericsTypes = expected.getGenericsTypes();
-                        GenericsType[] foundGenericsTypes = generifiedType.getGenericsTypes();
-
-                        for (int i = 0, n = expectedGenericsTypes.length; i < n; i += 1) {
-                            GenericsType type = expectedGenericsTypes[i];
-                            if (type.isPlaceholder()) {
-                                String name = type.getName();
-                                placeholders.put(new GenericsTypeName(name), foundGenericsTypes[i]);
-                            }
-                        }
-                    }
-                }
+                ClassNode[] paramTypes = applyGenericsContext(placeholders, extractTypesFromParameters(parameters));
+                int i = 0;
+                // GROOVY-10054, GROOVY-10699, GROOVY-10749, et al.
+                for (Parameter p : getParametersSafe((ClosureExpression) expression))
+                    if (!p.isDynamicTyped()) extractGenericsConnections(placeholders, p.getType(), paramTypes[i++]);
             }
         }
 
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index f00967f7f5..1d6e8ab3e5 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -719,6 +719,79 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    @NotYetImplemented // GROOVY-10646
+    void testReturnTypeInferenceWithMethodGenerics28() {
+        String types = '''
+            class Model {
+            }
+            interface Output<T> {
+                T getT()
+            }
+            abstract class WhereDSL<Type> {
+                abstract Type where()
+            }
+            abstract class Input<T> extends WhereDSL<ReferencesOuterClassTP> {
+                class ReferencesOuterClassTP implements Output<T> {
+                    @Override T getT() { return null }
+                }
+            }
+        '''
+        assertScript types + '''
+            void m(Input<Model> input) {
+                def output = input.where()
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE).toString(false) == 'Model'
+                })
+                def result = output.getT()
+            }
+        '''
+        assertScript types + '''
+            @FunctionalInterface
+            interface Xform extends java.util.function.Function<Input<Model>, Output<Model>> {
+            }
+
+            void select(Xform xform) {
+            }
+
+            select { input ->
+                def result = input.where()
+                return result // Cannot return value of type Input$ReferencesOuterClassTP for closure expecting Output<Model>
+            }
+        '''
+    }
+
+    // GROOVY-10749
+    void testReturnTypeInferenceWithMethodGenerics29() {
+        String named = 'class Named { String name }'
+
+        for (expr in ['Named.&getName', 'Named::getName', '{Named named -> named.getName()}', '(Named named) -> named.getName()']) {
+            assertScript named + """
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    def type = node.getNodeMetaData(INFERRED_TYPE)
+                    assert type.toString(false) == 'java.util.stream.Collector <Named, ?, java.util.Map>'
+                })
+                def collector = java.util.stream.Collectors.groupingBy($expr)
+            """
+        }
+
+        // explicit type args
+        assertScript named + '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.toString(false) == 'java.util.stream.Collector <Named, ?, java.util.Map>'
+            })
+            def c1 = java.util.stream.Collectors.<Named,String>groupingBy(named -> named.getName())
+            //                                   ^^^^^^^^^^^^^^
+
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.toString(false) == 'java.util.stream.Collector <Named, ?, java.util.Map>'
+            })
+            def c2 = java.util.stream.Collectors.<Named,String,Named>toMap(named -> named.getName(), named -> named)
+            //                                   ^^^^^^^^^^^^^^^^^^^^
+        '''
+    }
+
     void testDiamondInferrenceFromConstructor1() {
         assertScript '''
             class Foo<U> {