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 <[email protected]>
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> {

Reply via email to