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/11/01 16:33:59 UTC

[groovy] branch master updated: GROOVY-7141: STC: infer param types for closure map to SAM-type coercion

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 db1e25b468 GROOVY-7141: STC: infer param types for closure map to SAM-type coercion
db1e25b468 is described below

commit db1e25b4682d665d589caaaf18d6867de64d2612
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Nov 1 11:28:09 2022 -0500

    GROOVY-7141: STC: infer param types for closure map to SAM-type coercion
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 30 +++++++++++++-------
 .../stc/ClosureParamTypeInferenceSTCTest.groovy    | 32 +++++++++++++++++++++-
 2 files changed, 51 insertions(+), 11 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 e3997e0303..37d7934f21 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -711,7 +711,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
             ClassNode inferredType = localVariable.getNodeMetaData(INFERRED_TYPE);
             inferredType = getInferredTypeFromTempInfo(localVariable, inferredType);
-            if (inferredType != null && !isObjectType(inferredType) && !inferredType.equals(accessedVariable.getType())) {
+            if (inferredType != null && !isObjectType(inferredType) && !inferredType.equals(accessedVariable.getOriginType())) {
                 vexp.putNodeMetaData(INFERRED_TYPE, inferredType);
             }
         }
@@ -964,9 +964,20 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private void applyTargetType(final ClassNode target, final Expression source) {
-        if (isFunctionalInterface(target)) {
+        if (isClosureWithType(target)) {
+            if (source instanceof ClosureExpression) {
+                GenericsType returnType = target.getGenericsTypes()[0];
+                storeInferredReturnType(source, getCombinedBoundType(returnType));
+            }
+        } else if (isFunctionalInterface(target)) {
             if (source instanceof ClosureExpression) {
                 inferParameterAndReturnTypesOfClosureOnRHS(target, (ClosureExpression) source);
+            } else if (source instanceof MapExpression) { // GROOVY-7141
+                List<MapEntryExpression> spec = ((MapExpression) source).getMapEntryExpressions();
+                if (spec.size() == 1 && spec.get(0).getValueExpression() instanceof ClosureExpression
+                        && findSAM(target).getName().equals(spec.get(0).getKeyExpression().getText())) {
+                    inferParameterAndReturnTypesOfClosureOnRHS(target, (ClosureExpression) spec.get(0).getValueExpression());
+                }
             } else if (source instanceof MethodReferenceExpression) {
                 LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(target, (MethodReferenceExpression) source);
 
@@ -974,11 +985,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 source.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
                 source.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
             }
-        } else if (isClosureWithType(target)) {
-            if (source instanceof ClosureExpression) {
-                GenericsType returnType = target.getGenericsTypes()[0];
-                storeInferredReturnType(source, getCombinedBoundType(returnType));
-            }
         }
     }
 
@@ -987,13 +993,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         Parameter[] closureParameters = getParametersSafe(rhsExpression);
         ClassNode[] samParameterTypes = typeInfo.getV1();
 
-        int n = closureParameters.length, m = samParameterTypes.length;
-        if (n == m || (1 == m && hasImplicitParameter(rhsExpression))) {
+        if (samParameterTypes.length == 1 && hasImplicitParameter(rhsExpression)) {
+            Variable it = rhsExpression.getVariableScope().getDeclaredVariable("it"); // GROOVY-7141
+            closureParameters = new Parameter[]{it instanceof Parameter ? (Parameter) it : new Parameter(dynamicType(), "")};
+        }
+
+        int n = closureParameters.length;
+        if (n == samParameterTypes.length) {
             for (int i = 0; i < n; i += 1) {
                 Parameter parameter = closureParameters[i];
                 if (parameter.isDynamicTyped()) {
                     parameter.setType(samParameterTypes[i]);
-                    parameter.setOriginType(samParameterTypes[i]);
                 } else {
                     checkParamType(parameter, samParameterTypes[i], i == n-1, rhsExpression instanceof LambdaExpression);
                 }
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index e1e889f7e4..2537b14226 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -1293,7 +1293,37 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         'Incorrect number of parameters. Expected 1 but found 2'
     }
 
-    void testInferenceWithSAMTypeCoercion() {
+    // GROOVY-7141
+    void testInferenceWithSAMTypeCoercion1() {
+        String sam = '''
+            @FunctionalInterface
+            interface I {
+                String foo(String s)
+            }
+        '''
+        assertScript sam + '''
+            def impl = [foo: { it.toUpperCase() }] as I
+            String result = impl.foo('bar')
+            assert result == 'BAR'
+        '''
+        assertScript sam + '''
+            def impl = [foo: { s -> s.toUpperCase() }] as I
+            String result = impl.foo('bar')
+            assert result == 'BAR'
+        '''
+        assertScript sam + '''
+            def impl = [foo: { String s -> s.toUpperCase() }] as I
+            String result = impl.foo('bar')
+            assert result == 'BAR'
+        '''
+        assertScript '''
+            def impl = [apply: { it.toUpperCase() }] as java.util.function.Function<String,String>
+            String result = impl.apply('bar')
+            assert result == 'BAR'
+        '''
+    }
+
+    void testInferenceWithSAMTypeCoercion2() {
         assertScript '''
             interface Action<T> {
                 void execute(T thing)