You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/03/02 23:59:38 UTC

[groovy] branch master updated: GROOVY-6232, GROOVY-9956: check LHS type witness for diamond constructor

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

sunlan 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 715c34e  GROOVY-6232, GROOVY-9956: check LHS type witness for diamond constructor
715c34e is described below

commit 715c34edc82261544b8715bc2253b0b3f05bdbad
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Feb 27 15:29:44 2021 -0600

    GROOVY-6232, GROOVY-9956: check LHS type witness for diamond constructor
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 15 ++++++
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 57 +++++++++++-----------
 2 files changed, 43 insertions(+), 29 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 0a77404..1ef88a0 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1028,6 +1028,20 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 ClassNode type = GenericsUtils.parameterizeType(cceType, cceType);
                 type = inferReturnTypeGenerics(type, constructor, argumentList);
                 if (type.isUsingGenerics()) {
+                    // GROOVY-6232, GROOVY-9956: if cce not assignment compatible, process target as additional type witness
+                    if (checkCompatibleAssignmentTypes(lType, type, cce) && !GenericsUtils.buildWildcardType(lType).isCompatibleWith(type)) {
+                        // allow covariance of each type parameter, but maintain semantics for nested generics
+
+                        ClassNode pType = GenericsUtils.parameterizeType(lType, type);
+                        GenericsType[] lhs = pType.getGenericsTypes(), rhs = type.getGenericsTypes();
+                        if (lhs == null || rhs == null || lhs.length != rhs.length) throw new GroovyBugError(
+                                "Parameterization failed: " + prettyPrintType(pType) + " ~ " + prettyPrintType(type));
+
+                        if (java.util.stream.IntStream.range(0, lhs.length).allMatch(i ->
+                                GenericsUtils.buildWildcardType(getCombinedBoundType(lhs[i])).isCompatibleWith(getCombinedBoundType(rhs[i])))) {
+                            type = pType; // lType proved to be a viable type witness
+                        }
+                    }
                     inferredType = type;
                 }
             }
@@ -3100,6 +3114,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      * @return a class node usable as an inferred type
      */
     private static ClassNode createUsableClassNodeFromGenericsType(final GenericsType genericsType) {
+        // TODO: Merge with StaticTypeCheckingSupport#getCombinedBoundType(GenericsType)?
         ClassNode value = genericsType.getType();
         if (genericsType.isPlaceholder()) {
             value = OBJECT_TYPE;
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 12b5e70..4c28a82 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -223,9 +223,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     void testDiamondInferrenceFromConstructor3() {
-        shouldFailWithMessages '''
+        assertScript '''
             Set<Number> set = new HashSet<>(Arrays.asList(0L))
-        ''', 'Cannot assign java.util.HashSet <java.lang.Long> to: java.util.Set <Number>'
+        '''
 
         // not diamond inference, but tests compatible assignment
         assertScript '''
@@ -241,16 +241,13 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-6232
     void testDiamondInferrenceFromConstructor5() {
-        [/*'"a",new Object()',*/ 'new Object(),"b"', /*'"a","b"'*/].each { args ->
+        ['"a",new Object()', 'new Object(),"b"', '"a","b"'].each { args ->
             assertScript """
                 class C<T> {
                     C(T x, T y) {
                     }
                 }
-                void test() {
-                    C<Object> c = new C<>($args)
-                }
-                test()
+                C<Object> c = new C<>($args)
             """
         }
     }
@@ -265,11 +262,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 }
             }
 
-            void test() {
-                C<Integer> c = new C<>(1)
-                assert c.p < 10
-            }
-            test()
+            C<Integer> c = new C<>(1)
+            assert c.p < 10
         '''
     }
 
@@ -281,15 +275,12 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 T p
             }
 
-            void test() {
-                C<Integer> c = new C<>(1)
-                assert c.p < 10
-            }
-            test()
+            C<Integer> c = new C<>(1)
+            assert c.p < 10
         '''
     }
 
-    @NotYetImplemented // GROOVY-9956
+    // GROOVY-9956
     void testDiamondInferrenceFromConstructor8() {
         assertScript '''
             @groovy.transform.TupleConstructor
@@ -299,16 +290,11 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             interface I { }
             class D implements I { }
 
-            void test() {
-                C<I> ci;
-                ci = new C<I>(new D())
-                ci = new C<>(new D()) // infers C<D> on RHS
-            }
-            test()
+            C<I> ci = new C<I>(new D())
+            ci = new C<>(new D()) // infers C<D> on RHS
         '''
     }
 
-    @NotYetImplemented
     void testDiamondInferrenceFromConstructor9() {
         assertScript '''
             abstract class A<X> { }
@@ -319,11 +305,24 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             interface I { }
             class D implements I { }
 
-            void test() {
-                A<I> ai = new C<>(new D())
-            }
-            test()
+            A<I> ai = new C<>(new D())
         '''
+
+        shouldFailWithMessages '''
+            abstract class A<X> { }
+            @groovy.transform.TupleConstructor
+            class C<T> extends A<T> {
+                T p
+            }
+            interface I { }
+            class D implements I { }
+
+            A<String> ax = new C<>(new D())
+        ''', 'Incompatible generic argument types. Cannot assign C <D> to: A <String>'
+
+        shouldFailWithMessages '''
+            Set<List<String>> strings = new HashSet<>([new ArrayList<Number>()])
+        ''', 'Incompatible generic argument types. Cannot assign java.util.HashSet <java.util.ArrayList> to: java.util.Set <List>'
     }
 
     void testLinkedListWithListArgument() {