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/03 00:36:32 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-5523, GROOVY-7507, GROOVY-9972, GROOVY-9983: STC ternary operator

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

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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new cf08b38c9e GROOVY-5523, GROOVY-7507, GROOVY-9972, GROOVY-9983: STC ternary operator
cf08b38c9e is described below

commit cf08b38c9e7e5a6d42a73f96ae242f0566ee3e5c
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun May 16 13:47:04 2021 -0500

    GROOVY-5523, GROOVY-7507, GROOVY-9972, GROOVY-9983: STC ternary operator
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 144 ++++++++++------
 src/test/groovy/bugs/Groovy7753.groovy             | 101 +++++++++++
 src/test/groovy/bugs/Groovy7753Bug.groovy          |  92 ----------
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  89 ++++++++++
 .../transform/stc/TernaryOperatorSTCTest.groovy    | 190 ++++++++++++++-------
 5 files changed, 410 insertions(+), 206 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 244df89ce2..38c4bbadbe 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -36,6 +36,7 @@ import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
 import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.DynamicVariable;
 import org.codehaus.groovy.ast.FieldNode;
@@ -58,6 +59,7 @@ import org.codehaus.groovy.ast.expr.ClosureListExpression;
 import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
 import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.ElvisOperatorExpression;
 import org.codehaus.groovy.ast.expr.EmptyExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.FieldExpression;
@@ -4125,88 +4127,120 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     @Override
     public void visitTernaryExpression(final TernaryExpression expression) {
         Map<VariableExpression, List<ClassNode>> oldTracker = pushAssignmentTracking();
-        // create a new temporary element in the if-then-else type info
-        typeCheckingContext.pushTemporaryTypeInfo();
-        expression.getBooleanExpression().visit(this);
+        typeCheckingContext.pushTemporaryTypeInfo(); // capture instanceof restriction
+        if (!(expression instanceof ElvisOperatorExpression)) {
+            expression.getBooleanExpression().visit(this);
+        }
         Expression trueExpression = expression.getTrueExpression();
-        Expression falseExpression = expression.getFalseExpression();
         ClassNode typeOfTrue = findCurrentInstanceOfClass(trueExpression, null);
         trueExpression.visit(this);
-        if (typeOfTrue == null)
-            typeOfTrue = getType(trueExpression);
-        // pop if-then-else temporary type info
-        typeCheckingContext.popTemporaryTypeInfo();
+        if (typeOfTrue == null) typeOfTrue = getType(trueExpression);
+        typeCheckingContext.popTemporaryTypeInfo(); // instanceof doesn't apply to false branch
+        Expression falseExpression = expression.getFalseExpression();
         falseExpression.visit(this);
-        ClassNode resultType;
         ClassNode typeOfFalse = getType(falseExpression);
-        // handle instanceof cases
-        if (hasInferredReturnType(falseExpression)) {
-            typeOfFalse = getInferredReturnType(falseExpression);
-        }
-        if (hasInferredReturnType(trueExpression)) {
-            typeOfTrue = getInferredReturnType(trueExpression);
-        }
-        typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
-        typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
-        if (isNullConstant(trueExpression) || isNullConstant(falseExpression)) {
-            BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
-            if (enclosingBinaryExpression != null && enclosingBinaryExpression.getRightExpression() == expression) {
-                resultType = getType(enclosingBinaryExpression.getLeftExpression());
-            } else if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) {
-                resultType = OBJECT_TYPE;
-            } else if (isNullConstant(trueExpression)) {
-                resultType = wrapTypeIfNecessary(typeOfFalse);
-            } else {
-                resultType = wrapTypeIfNecessary(typeOfTrue);
-            }
+
+        ClassNode resultType;
+        if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { // GROOVY-5523
+            resultType = checkForTargetType(trueExpression, UNKNOWN_PARAMETER_TYPE);
+        } else if (isNullConstant(trueExpression) || (isEmptyCollection(trueExpression)
+                && isOrImplements(typeOfTrue, typeOfFalse))) { // [] : List/Collection/Iterable
+            resultType = wrapTypeIfNecessary(checkForTargetType(falseExpression, typeOfFalse));
+        } else if (isNullConstant(falseExpression) || (isEmptyCollection(falseExpression)
+                && isOrImplements(typeOfFalse, typeOfTrue))) { // List/Collection/Iterable : []
+            resultType = wrapTypeIfNecessary(checkForTargetType(trueExpression, typeOfTrue));
         } else {
-            // store type information
+            typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
+            typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
             resultType = lowestUpperBound(typeOfTrue, typeOfFalse);
         }
         storeType(expression, resultType);
         popAssignmentTracking(oldTracker);
     }
 
+    /**
+     * @param expr true or false branch of ternary expression
+     * @param type the inferred type of {@code expr}
+     */
     private ClassNode checkForTargetType(final Expression expr, final ClassNode type) {
-        BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
-        if (enclosingBinaryExpression instanceof DeclarationExpression
-                && isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
-            VariableExpression target = (VariableExpression) enclosingBinaryExpression.getLeftExpression();
-            return adjustForTargetType(target.getType(), type);
+        ClassNode sourceType = type;
+        ClassNode targetType = null;
+        MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
+        BinaryExpression enclosingExpression = typeCheckingContext.getEnclosingBinaryExpression();
+        if (enclosingExpression != null
+                && isAssignment(enclosingExpression.getOperation().getType())
+                && isTypeSource(expr, enclosingExpression.getRightExpression())) {
+            targetType = getDeclaredOrInferredType(enclosingExpression.getLeftExpression());
+        } else if (enclosingMethod != null
+                && !enclosingMethod.isAbstract()
+                && !enclosingMethod.isVoidMethod()
+                && isTypeSource(expr, enclosingMethod)) {
+             targetType = enclosingMethod.getReturnType();
         }
-        if (currentField != null) {
-            return adjustForTargetType(currentField.getType(), type);
+
+        if (expr instanceof VariableExpression && hasInferredReturnType(expr)) {
+            sourceType = getInferredReturnType(expr);
         }
-        if (currentProperty != null) {
-            return adjustForTargetType(currentProperty.getType(), type);
+
+        if (targetType == null) return sourceType;
+
+        if (expr instanceof ConstructorCallExpression) {
+            inferDiamondType((ConstructorCallExpression) expr, targetType);
+        } else if (!isPrimitiveType(getUnwrapper(targetType)) && !OBJECT_TYPE.equals(targetType)
+                    && !sourceType.isGenericsPlaceHolder() && missesGenericsTypes(sourceType)) {
+            // unchecked assignment with ternary/elvis, like "List<T> list = listOfT ?: []"
+            // the inferred type is the RHS type "completed" with generics information from LHS
+            return GenericsUtils.parameterizeType(targetType, sourceType.getPlainNodeReference());
         }
-        MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
-        if (enclosingMethod != null) {
-            return adjustForTargetType(enclosingMethod.getReturnType(), type);
+
+        return sourceType != UNKNOWN_PARAMETER_TYPE ? sourceType : targetType;
+    }
+
+    private static boolean isTypeSource(final Expression expr, final Expression right) {
+        if (right instanceof TernaryExpression) {
+            return isTypeSource(expr, ((TernaryExpression) right).getTrueExpression())
+                || isTypeSource(expr, ((TernaryExpression) right).getFalseExpression());
         }
-        return type;
+        return expr == right;
     }
 
-    private static ClassNode adjustForTargetType(final ClassNode targetType, final ClassNode resultType) {
-        if (targetType.isUsingGenerics() && missesGenericsTypes(resultType)) {
-            // unchecked assignment within ternary/elvis
-            // examples:
-            // List<A> list = existingAs ?: []
-            // in that case, the inferred type of the RHS is the type of the RHS
-            // "completed" with generics type information available in the LHS
-            return GenericsUtils.parameterizeType(targetType, resultType.getPlainNodeReference());
+    private static boolean isTypeSource(final Expression expr, final MethodNode mNode) {
+        final boolean[] returned = new boolean[1];
+
+        mNode.getCode().visit(new CodeVisitorSupport() {
+            @Override
+            public void visitReturnStatement(final ReturnStatement returnStatement) {
+                if (isTypeSource(expr, returnStatement.getExpression())) {
+                    returned[0] = true;
+                }
+            }
+            @Override
+            public void visitClosureExpression(final ClosureExpression expression) {
+            }
+        });
+
+        if (!returned[0]) {
+            new ReturnAdder(new ReturnAdder.ReturnStatementListener() {
+                @Override
+                public void returnStatementAdded(final ReturnStatement returnStatement) {
+                    if (isTypeSource(expr, returnStatement.getExpression())) {
+                        returned[0] = true;
+                    }
+                }
+            }).visitMethod(mNode);
         }
-        return resultType;
+
+        return returned[0];
     }
 
-    private static boolean isEmptyCollection(Expression expr) {
+    private static boolean isEmptyCollection(final Expression expr) {
         return (expr instanceof ListExpression && ((ListExpression) expr).getExpressions().isEmpty())
                 || (expr instanceof MapExpression && ((MapExpression) expr).getMapEntryExpressions().isEmpty());
     }
 
-    private static boolean hasInferredReturnType(Expression expression) {
+    private static boolean hasInferredReturnType(final Expression expression) {
         ClassNode type = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
-        return type != null && !type.getName().equals("java.lang.Object");
+        return type != null && !type.getName().equals(ClassHelper.OBJECT);
     }
 
     @Override
diff --git a/src/test/groovy/bugs/Groovy7753.groovy b/src/test/groovy/bugs/Groovy7753.groovy
new file mode 100644
index 0000000000..e3213db3ef
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy7753.groovy
@@ -0,0 +1,101 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy7753 {
+
+    @Test
+    void testGroovy7753() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                x ? [x] : Collections.emptyList()
+            }
+
+            getStrings()
+        '''
+    }
+
+    @Test
+    void testGroovy7753WithReturn() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                return x ? [x] : Collections.emptyList()
+            }
+
+            getStrings()
+        '''
+    }
+
+    @Test
+    void testGroovy7753WithReturn2() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                return x ? Collections.emptyList() : [x]
+            }
+
+            getStrings()
+        '''
+    }
+
+    @Test
+    void test2() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                Collections.emptyList()
+            }
+
+            getStrings()
+        '''
+    }
+
+    @Test
+    void test3() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                [x]
+            }
+
+            getStrings()
+        '''
+    }
+}
diff --git a/src/test/groovy/bugs/Groovy7753Bug.groovy b/src/test/groovy/bugs/Groovy7753Bug.groovy
deleted file mode 100644
index 8134eee756..0000000000
--- a/src/test/groovy/bugs/Groovy7753Bug.groovy
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package groovy.bugs
-
-class Groovy7753Bug extends GroovyTestCase {
-
-    void testGroovy7753() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            x ? [x] : Collections.emptyList()
-        }
-        
-        getStrings()
-        '''
-    }
-
-    void testGroovy7753WithReturn() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            return x ? [x] : Collections.emptyList()
-        }
-        
-        getStrings()
-        '''
-    }
-
-    void testGroovy7753WithReturn2() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            return x ? Collections.emptyList() : [x]
-        }
-        
-        getStrings()
-        '''
-    }
-
-    void test2() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            Collections.emptyList()
-        }
-        
-        getStrings()
-        '''
-    }
-
-    void test3() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            [x]
-        }
-        
-        getStrings()
-        '''
-    }
-}
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 35a2da9c31..7ef859a5a5 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -547,6 +547,95 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-9972
+    void testDiamondInferrenceFromConstructor10() {
+        assertScript '''
+            @groovy.transform.TupleConstructor
+            class C<T> {
+                T p
+            }
+            class D {
+                public String f = 'D#f'
+            }
+            C<D> cd = true ? new C<>(new D()) : new C<>(new D())
+            assert cd.p.f.toLowerCase() == 'd#f'
+        '''
+
+        assertScript '''
+            @groovy.transform.TupleConstructor
+            class C<T> {
+                T p
+            }
+            class D {
+                public String f = 'D#f'
+            }
+            boolean b = false
+            C<D> cd = b ? new C<>(new D()) : (b ? new C<>((D) null) : new C<>(new D()))
+            assert cd.p.f.toLowerCase() == 'd#f'
+        '''
+
+        assertScript '''
+            @groovy.transform.TupleConstructor
+            class C {
+                List<D> list
+            }
+            class D {
+            }
+            List test(C... array) {
+                // old code used "List<D> many" as target for guts of closure
+                List<D> many = array.collectMany { it.list ?: [] }
+            }
+            def result = test(new C(), new C(list:[new D()]))
+            assert result.size() == 1
+        '''
+    }
+
+    // GROOVY-9983
+    void testDiamondInferrenceFromConstructor11() {
+        String types = '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class A<T> {
+                T p
+            }
+            class B {
+            }
+            class C {
+                static m(A<B> a_of_b) {
+                }
+            }
+        '''
+
+        assertScript types + '''
+            class E extends B {
+            }
+            boolean flag = true
+
+            A<B> v = new A<>(null)
+            A<B> w = new A<>(new B())
+            A<B> x = new A<>(new E())
+            A<B> y = flag ? new A<>(new B()) : new A<>(new B())
+            A<B> z = flag ? new A<>(new B()) : new A<>(new E())
+
+            C.m(new A<>(null))
+            C.m(new A<>(new B()))
+            C.m(new A<>(new E()))
+            C.m(flag ? new A<>(new B()) : new A<>(new B()))
+            C.m(flag ? new A<>(new B()) : new A<>(new E())) // Cannot call m(A<B>) with arguments [A<? extends B>]
+        '''
+
+        /*shouldFailWithMessages types + '''
+            A<B> x = new A<>(new Object())
+            A<B> y = true ? new A<>(new B()) : new A<>(new Object())
+
+            C.m(new A<>(new Object()))
+            C.m(true ? new A<>(new B()) : new A<>(new Object()))
+        ''',
+        'Cannot assign A<java.lang.Object> to: A<B>',
+        'Cannot assign A<? extends java.lang.Object> to: A<B>',
+        'Cannot call C#m(A<B>) with arguments [A<java.lang.Object>]',
+        'Cannot call C#m(A<B>) with arguments [A<? extends java.lang.Object>]'*/
+    }
+
     // GROOVY-10280
     void testTypeArgumentPropagation() {
         assertScript '''
diff --git a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
index d7c7da6d02..b477460409 100644
--- a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
@@ -18,162 +18,234 @@
  */
 package groovy.transform.stc
 
+import groovy.transform.NotYetImplemented
+
 /**
- * Unit tests for static type checking : ternary operator tests.
+ * Unit tests for static type checking : ternary operator.
  */
 class TernaryOperatorSTCTest extends StaticTypeCheckingTestCase {
 
-    void testByteByteTernary() {
+    void testByteByte() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == byte_TYPE
             })
             def y = true?(byte)1:(byte)0
         '''
     }
-    void testShortShortTernary() {
+
+    void testShortShort() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == short_TYPE
             })
             def y = true?(short)1:(short)0
         '''
     }
 
-    void testIntIntTernary() {
+    void testIntInt() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == int_TYPE
             })
             def y = true?1:0
         '''
     }
 
-    void testLongLongTernary() {
+    void testLongLong() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == long_TYPE
             })
             def y = true?1L:0L
         '''
     }
 
-    void testFloatFloatTernary() {
+    void testFloatFloat() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == float_TYPE
             })
             def y = true?1f:0f
         '''
     }
 
-    void testDoubleDoubleTernary() {
+    void testDoubleDouble() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == double_TYPE
             })
             def y = true?1d:0d
         '''
     }
 
-    void testBoolBoolTernary() {
+    void testBoolBool() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == boolean_TYPE
             })
             def y = true?true:false
         '''
     }
 
-    void testDoubleFloatTernary() {
+    void testDoubleFloat() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == double_TYPE
             })
             def y = true?1d:1f
         '''
     }
 
-    void testDoubleDoubleTernaryWithBoxedTypes() {
+    void testDoubleDoubleWithBoxedTypes() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Double_TYPE
             })
             def y = true?new Double(1d):new Double(1f)
         '''
     }
 
-    void testDoubleFloatTernaryWithBoxedTypes() {
+    void testDoubleFloatWithBoxedTypes() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Double_TYPE
             })
             def y = true?new Double(1d):new Float(1f)
         '''
     }
 
-    void testDoubleFloatTernaryWithOneBoxedType() {
+    void testDoubleFloatWithOneBoxedType1() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Double_TYPE
             })
             def y = true?1d:new Float(1f)
         '''
     }
 
-    void testDoubleFloatTernaryWithOneBoxedType2() {
+    void testDoubleFloatWithOneBoxedType2() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Double_TYPE
             })
             def y = true?new Double(1d):1f
         '''
     }
 
+    @NotYetImplemented // GROOVY-10357
+    void testAbstractMethodDefault() {
+        assertScript '''
+            import org.apache.groovy.internal.util.Function
+
+            abstract class A {
+                abstract long m(Function<Boolean,Integer> f = { Boolean b -> b ? +1 : -1 })
+            }
+
+            def a = new A() {
+                @Override
+                long m(Function<Boolean,Integer> f) {
+                    f(true).longValue()
+                }
+            }
+            assert a.m() == 1L
+        '''
+    }
+
     // GROOVY-5523
-    void testTernaryOperatorWithNull() {
-        assertScript '''File findFile() {
-            String str = ""
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
-                assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
-            })
-            File f = str ? new File(str) : null
-        }
+    void testNull1() {
+        assertScript '''
+            def findFile() {
+                String str = ""
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
+                })
+                File f = str ? new File(str) : null
+            }
         '''
-        assertScript '''File findFile() {
-            String str = ""
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
-                assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
-            })
-            File f = str ? null : new File(str)
-        }
+        assertScript '''
+            def findFile() {
+                String str = ""
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
+                })
+                File f = str ? null : new File(str)
+            }
         '''
-        assertScript '''File findFile() {
-            String str = ""
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
-                assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
-            })
-            File f = str ? null : null
-        }
+        assertScript '''
+            def findFile() {
+                String str = ""
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
+                })
+                File f = str ? null : null
+            }
         '''
     }
-    void testElvisOperatorWithNull() {
-        assertScript '''String findString() {
-            String str = ""
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
-                assert node.getNodeMetaData(INFERRED_TYPE) == STRING_TYPE
-            })
-            String f = str ?: null
-        }
+
+    void testNull2() {
+        assertScript '''
+            def test(String str) {
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE) == STRING_TYPE
+                })
+                String s = str ?: null
+            }
+
+            assert test('x') == 'x'
+            assert test('') == null
         '''
     }
 
     // GROOVY-5734
-    void testNullInTernary() {
+    void testNull3() {
         assertScript '''
-            Integer someMethod() { (false) ? null : 8 }
-            assert someMethod() == 8
+            Integer test() { false ? null : 42 }
+
+            assert test() == 42
+        '''
+    }
+
+    // GROOVY-7507
+    void testNull4() {
+        assertScript '''
+            class B {
+            }
+            class C {
+                B b
+            }
+            boolean check = true
+            C c = new C()
+            c.b = check ? new B() : null // Cannot assign value of type Object to variable of type Bar
         '''
     }
-}
 
+    // GROOVY-10226
+    void testNull5() {
+        assertScript '''
+            class A<T> {
+            }
+            def <T extends A<String>> T test() {
+                final T x = null
+                true ? (T) null : x
+            }
+            assert test() == null
+        '''
+    }
+
+    // GROOVY-10158
+    void testNull6() {
+        assertScript '''
+            class A<T> {
+            }
+            class B<T extends A<String>> {
+                T m() {
+                    final T x = null
+                    final T y = null
+                    ( true ? x : y )
+                }
+            }
+            assert new B<A<String>>().m() == null
+        '''
+    }
+}