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 2023/11/30 16:47:50 UTC

(groovy) branch GROOVY_3_0_X updated: GROOVY-10271, GROOVY-10272: STC: process closure in ternary expression

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 61287d6dc0 GROOVY-10271, GROOVY-10272: STC: process closure in ternary expression
61287d6dc0 is described below

commit 61287d6dc0dffa9460163d8be33d424fa3e2cd6f
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Nov 30 10:41:31 2023 -0600

    GROOVY-10271, GROOVY-10272: STC: process closure in ternary expression
    
    3_0_X backport
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 89 ++++++++++++----------
 .../transform/stc/TernaryOperatorSTCTest.groovy    | 47 +++++++++++-
 2 files changed, 95 insertions(+), 41 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 4853c9c575..11a110a173 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -768,11 +768,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 } else {
                     lType = getOriginalDeclarationType(leftExpression);
 
-                    if (isFunctionalInterface(lType)) {
-                        processFunctionalInterfaceAssignment(lType, rightExpression);
-                    } else if (isClosureWithType(lType) && rightExpression instanceof ClosureExpression) {
-                        storeInferredReturnType(rightExpression, getCombinedBoundType(lType.getGenericsTypes()[0]));
-                    }
+                    applyTargetType(lType, rightExpression);
                 }
                 rightExpression.visit(this);
             }
@@ -908,21 +904,28 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
-        if (rhsExpression instanceof ClosureExpression) {
-            inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) rhsExpression);
-        } else if (rhsExpression instanceof MapExpression) { // GROOVY-7141
-            List<MapEntryExpression> spec = ((MapExpression) rhsExpression).getMapEntryExpressions();
-            if (spec.size() == 1 && spec.get(0).getValueExpression() instanceof ClosureExpression
-                    && findSAM(lhsType).getName().equals(spec.get(0).getKeyExpression().getText())) {
-                inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) spec.get(0).getValueExpression());
+    private void applyTargetType(final ClassNode target, final Expression source) {
+        if (isClosureWithType(target)) {
+            if (source instanceof ClosureExpression) {
+                GenericsType returnType = target.getGenericsTypes()[0];
+                storeInferredReturnType(source, getCombinedBoundType(returnType));
             }
-        } else if (rhsExpression instanceof MethodReferenceExpression) {
-            LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lhsType, (MethodReferenceExpression) rhsExpression);
+        } 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 lambda = constructLambdaExpressionForMethodReference(target, (MethodReferenceExpression) source);
 
-            inferParameterAndReturnTypesOfClosureOnRHS(lhsType, lambdaExpression);
-            rhsExpression.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
-            rhsExpression.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
+                inferParameterAndReturnTypesOfClosureOnRHS(target, lambda);
+                source.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambda);
+                source.putNodeMetaData(CLOSURE_ARGUMENTS, extractTypesFromParameters(lambda.getParameters()));
+            }
         }
     }
 
@@ -1952,16 +1955,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    private void visitInitialExpression(final Expression value, final Expression target, final ASTNode position) {
+    private void visitInitialExpression(final Expression value, final Expression target, final ASTNode origin) {
         if (value != null) {
             ClassNode lType = target.getType();
-            if (isFunctionalInterface(lType)) { // GROOVY-9977
-                processFunctionalInterfaceAssignment(lType, value);
-            } else if (isClosureWithType(lType) && value instanceof ClosureExpression) {
-                storeInferredReturnType(value, getCombinedBoundType(lType.getGenericsTypes()[0]));
-            }
+            applyTargetType(lType, value); // GROOVY-9977
 
-            typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, position));
+            typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, origin));
 
             value.visit(this);
             ClassNode rType = getType(value);
@@ -4215,18 +4214,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     @Override
     public void visitCastExpression(final CastExpression expression) {
-        ClassNode type = expression.getType();
-        Expression target = expression.getExpression();
-        if (isFunctionalInterface(type)) { // GROOVY-9997
-            processFunctionalInterfaceAssignment(type, target);
-        } else if (isClosureWithType(type) && target instanceof ClosureExpression) {
-            storeInferredReturnType(target, getCombinedBoundType(type.getGenericsTypes()[0]));
-        }
+        ClassNode target = expression.getType();
+        Expression source = expression.getExpression();
+        applyTargetType(target, source); // GROOVY-9997
 
-        target.visit(this);
+        source.visit(this);
 
-        if (!expression.isCoerce() && !checkCast(type, target) && !isDelegateOrOwnerInClosure(target)) {
-            addStaticTypeError("Inconvertible types: cannot cast " + prettyPrintType(getType(target)) + " to " + prettyPrintType(type), expression);
+        if (!expression.isCoerce() && !checkCast(target, source) && !isDelegateOrOwnerInClosure(source)) {
+            addStaticTypeError("Inconvertible types: cannot cast " + prettyPrintType(getType(source)) + " to " + prettyPrintType(target), expression);
         }
     }
 
@@ -4273,12 +4268,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
         Expression trueExpression = expression.getTrueExpression();
         ClassNode typeOfTrue = findCurrentInstanceOfClass(trueExpression, null);
-        trueExpression.visit(this);
-        if (typeOfTrue == null) typeOfTrue = getType(trueExpression);
+        typeOfTrue = Optional.ofNullable(typeOfTrue).orElse(visitValueExpression(trueExpression));
         typeCheckingContext.popTemporaryTypeInfo(); // instanceof doesn't apply to false branch
         Expression falseExpression = expression.getFalseExpression();
-        falseExpression.visit(this);
-        ClassNode typeOfFalse = getType(falseExpression);
+        ClassNode typeOfFalse = visitValueExpression(falseExpression);
 
         ClassNode resultType;
         if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { // GROOVY-5523
@@ -4298,6 +4291,18 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         popAssignmentTracking(oldTracker);
     }
 
+    /**
+     * @param expr true or false branch of ternary expression
+     * @return the inferred type of {@code expr}
+     */
+    private ClassNode visitValueExpression(final Expression expr) {
+        if (expr instanceof ClosureExpression) {
+            applyTargetType(checkForTargetType(expr, null), expr);
+        }
+        expr.visit(this);
+        return getType(expr);
+    }
+
     /**
      * @param expr true or false branch of ternary expression
      * @param type the inferred type of {@code expr}
@@ -4322,6 +4327,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
              targetType = enclosingMethod.getReturnType();
         }
 
+        if (expr instanceof ClosureExpression) { // GROOVY-10271, GROOVY-10272
+            return isSAMType(targetType) ? targetType : type;
+        }
+
         if (targetType == null)
             targetType = type.getPlainNodeReference();
         if (type == UNKNOWN_PARAMETER_TYPE) return targetType;
@@ -4351,7 +4360,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 // GROOVY-6126, GROOVY-6558, GROOVY-6564, et al.
                 if (!targetType.isGenericsPlaceHolder()) return targetType;
             } else {
-                // GROOVY-5640, GROOVY-9033, GROOVY-10220, GROOVY-10235, GROOVY-10688, et al.
+                // GROOVY-5640, GROOVY-9033, GROOVY-10220, GROOVY-10235, GROOVY-10688, GROOVY-11192, et al.
                 Map<GenericsTypeName, GenericsType> gt = new HashMap<>();
                 ClassNode sc = resultType;
                 for (;;) {
diff --git a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
index 698dbbbdb9..929eb8fd9c 100644
--- a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
@@ -180,8 +180,53 @@ class TernaryOperatorSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-10688
+    void testTypeParameterTypeParameter3() {
+        assertScript '''
+            class A<T,U> {
+            }
+            <T> void test(
+                A<Double, ? extends T> x) {
+                A<Double, ? extends T> y = x
+                A<Double, ? extends T> z = true ? y : x
+            }
+            test(null)
+        '''
+    }
+
+    // GROOVY-10271
+    void testFunctionalInterfaceTarget1() {
+        for (flag in ['true', 'false']) {
+            assertScript """import java.util.function.Supplier
+
+                Supplier<Integer> x = { -> 1 }
+                Supplier<Integer> y = $flag ? x : { -> 2 }
+
+                assert y.get() == ($flag ? 1 : 2)
+            """
+        }
+    }
+
+    // GROOVY-10272
+    void testFunctionalInterfaceTarget2() {
+        assertScript '''
+            import java.util.function.Function
+
+            Function<Integer, Long> x
+            if (true) {
+                x = { a -> a.longValue() }
+            } else {
+                x = { Integer b -> (Long)b }
+            }
+            assert x.apply(42) == 42L
+
+            Function<Integer, Long> y = (true ? { a -> a.longValue() } : { Integer b -> (Long)b })
+            assert y.apply(42) == 42L
+        '''
+    }
+
     // GROOVY-10701
-    void testFunctionalInterfaceTarget() {
+    void testFunctionalInterfaceTarget3() {
         for (type in ['Function<T,T>', 'UnaryOperator<T>']) {
             assertScript """import java.util.function.*