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/04 16:48:28 UTC

[groovy] branch master 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 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 3b9e31cce6 GROOVY-10749: STC: closure/lambda/reference parameter(s) as type witness
3b9e31cce6 is described below

commit 3b9e31cce6c1aa1aea1947afd6dd16abb77eaf13
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
---
 .../java/org/codehaus/groovy/ast/Parameter.java    |  2 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 19 +++++++-----
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 34 +++++++++++++++++++++-
 3 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/Parameter.java b/src/main/java/org/codehaus/groovy/ast/Parameter.java
index 0ca914a07c..5c25900f2a 100644
--- a/src/main/java/org/codehaus/groovy/ast/Parameter.java
+++ b/src/main/java/org/codehaus/groovy/ast/Parameter.java
@@ -53,7 +53,7 @@ public class Parameter extends AnnotatedNode implements Variable {
 
     @Override
     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() + "]";
     }
 
     @Override
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 2fb02586d5..8511cdd36a 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -5628,7 +5628,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      * @param samType     the target type for the argument expression
      * @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) {
+    private static ClassNode convertClosureTypeToSAMType(Expression expression, final ClassNode closureType, final MethodNode sam, final ClassNode samType) {
         Map<GenericsTypeName, GenericsType> samTypeConnections = GenericsUtils.extractPlaceholders(samType);
         samTypeConnections.replaceAll((xx, gt) -> // GROOVY-9762, GROOVY-9803: reduce "? super T" to "T"
             Optional.ofNullable(gt.getLowerBound()).map(GenericsType::new).orElse(gt)
@@ -5636,9 +5636,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         ClassNode closureReturnType = closureType.getGenericsTypes()[0].getType();
 
         Parameter[] parameters = sam.getParameters();
-        if (parameters.length > 0
-                && expression instanceof MethodPointerExpression
-                && GenericsUtils.hasUnresolvedGenerics(closureReturnType)) {
+        if (parameters.length > 0 && expression instanceof MethodPointerExpression) {
             // try to resolve referenced method type parameters in return type
             MethodPointerExpression mp = (MethodPointerExpression) expression;
             List<MethodNode> candidates = mp.getNodeMetaData(MethodNode.class);
@@ -5658,6 +5656,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     closureReturnType = applyGenericsContext(connections, closureReturnType);
                     // apply known generics connections to the SAM's placeholders in the return type
                     closureReturnType = applyGenericsContext(samTypeConnections, closureReturnType);
+
+                    expression = new ClosureExpression(Arrays.stream(matchTypes).map(t -> new Parameter(t,"")).toArray(Parameter[]::new), null);
                 }
             }
         }
@@ -5667,7 +5667,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
         // repeat the same for each parameter given in the ClosureExpression
         if (parameters.length > 0 && expression instanceof ClosureExpression) {
-            return closureType; // TODO
+            ClassNode[] paramTypes = applyGenericsContext(samTypeConnections, extractTypesFromParameters(parameters));
+            int i = 0;
+            // GROOVY-10054, GROOVY-10699, GROOVY-10749, et al.
+            for (Parameter p : getParametersSafe((ClosureExpression) expression))
+                if (!p.isDynamicTyped()) extractGenericsConnections(samTypeConnections, p.getType(), paramTypes[i++]);
         }
 
         return applyGenericsContext(samTypeConnections, samType.redirect());
@@ -5698,8 +5702,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             return false;
         }
         for (int i = 0; i < n; i += 1) {
-            // for method closure, SAM parameters act like arguments
-            if (!isAssignableTo(providerTypes[i], receiverTypes[i])) {
+            // for method closure SAM parameters act like arguments
+            if (!isAssignableTo(providerTypes[i], receiverTypes[i])
+                    && !providerTypes[i].isGenericsPlaceHolder()) {
                 return false;
             }
         }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 9727aa89e5..6b569a3e17 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -761,6 +761,38 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // 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<java.lang.String, java.util.List<Named>>>'
+                })
+                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<java.lang.String, java.util.List<Named>>>'
+            })
+            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<java.lang.String, Named>>'
+            })
+            def c2 = java.util.stream.Collectors.<Named,String,Named>toMap(named -> named.getName(), named -> named)
+            //                                   ^^^^^^^^^^^^^^^^^^^^
+        '''
+    }
+
     void testDiamondInferrenceFromConstructor1() {
         assertScript '''
             class Foo<U> {
@@ -1499,7 +1531,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 def type = node.getNodeMetaData(INFERRED_TYPE)
-                assert type.toString(false) == 'C<java.lang.Object>' // TODO: String
+                assert type.toString(false) == 'C<java.lang.String>'
             })
             def y = new C<>((String s) -> { print(s); }) // error: Expected type Object for lambda parameter: s
         '''