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/05/16 19:32:24 UTC

[groovy] 01/03: GROOVY-7419, GROOVY-9948, GROOVY-9956: STC: infer <> from argument types

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

commit e6016c634d0ab92f892216ef52be0a7f52457630
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Feb 17 15:05:15 2021 -0600

    GROOVY-7419, GROOVY-9948, GROOVY-9956: STC: infer <> from argument types
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 32 +++++++++++++-----
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 38 +++++-----------------
 2 files changed, 32 insertions(+), 38 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 3631754ce6..35f6cd29d4 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1051,19 +1051,33 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     protected void inferDiamondType(final ConstructorCallExpression cce, final ClassNode lType) {
-        ClassNode cceType = cce.getType();
+        ClassNode cceType = cce.getType(), inferredType = lType;
         // check if constructor call expression makes use of the diamond operator
         if (cceType.getGenericsTypes() != null && cceType.getGenericsTypes().length == 0) {
-            ArgumentListExpression argumentListExpression = InvocationWriter.makeArgumentList(cce.getArguments());
-            if (argumentListExpression.getExpressions().isEmpty()) {
-                adjustGenerics(lType, cceType);
-            } else {
-                ClassNode type = getType(argumentListExpression.getExpression(0));
-                if (type.isUsingGenerics()) {
-                    adjustGenerics(type, cceType);
+            ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(cce.getArguments());
+            ConstructorNode constructor = cce.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
+            if (!argumentList.getExpressions().isEmpty() && constructor != null) {
+                ClassNode type = GenericsUtils.parameterizeType(cceType, cceType);
+                type = inferReturnTypeGenerics(type, constructor, argumentList);
+                if (lType.getGenericsTypes() != null // GROOVY-10367: nothing to inspect
+                        // GROOVY-6232, GROOVY-9956: if cce not assignment compatible, process target as additional type witness
+                        && 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(rhs[i].getType()))) {
+                        type = pType; // lType proved to be a viable type witness
+                    }
                 }
+                inferredType = type;
             }
-            // store inferred type on CCE
+
+            adjustGenerics(inferredType, cceType);
             storeType(cce, cceType);
         }
     }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 2582791dfa..461bcd0cae 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -494,7 +494,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-8638
     void testReturnTypeInferenceWithMethodGenerics18() {
         assertScript '''
-            @Grab('com.google.guava:guava:30.1.1-jre')
+            @Grab('com.google.guava:guava:31.1-jre')
             import com.google.common.collect.*
 
             ListMultimap<String, Integer> mmap = ArrayListMultimap.create()
@@ -633,21 +633,13 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             new HashSet<>(Arrays.asList(0L))
         '''
 
-        // not diamond inference, but tests compatible assignment
         assertScript '''
-            Set<Number> set = new HashSet<Number>(Arrays.asList(0L))
-        '''
-
-        shouldFailWithMessages '''
             Set<Number> set = new HashSet<>(Arrays.asList(0L))
-        ''',
-        'Cannot assign java.util.HashSet <java.lang.Long> to: java.util.Set <Number>'
-    }
+        '''
 
-    @NotYetImplemented
-    void testDiamondInferrenceFromConstructor3a() {
+        // not diamond inference, but tests compatible assignment
         assertScript '''
-            Set<Number> set = new HashSet<>(Arrays.asList(0L))
+            Set<Number> set = new HashSet<Number>(Arrays.asList(0L))
         '''
 
         assertScript '''
@@ -659,7 +651,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    @NotYetImplemented // GROOVY-7419
+    // GROOVY-7419
     void testDiamondInferrenceFromConstructor4() {
         assertScript '''
             Map<Thread.State, Object> map = new EnumMap<>(Thread.State)
@@ -773,7 +765,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    @NotYetImplemented
     void testDiamondInferrenceFromConstructor9() {
         assertScript '''
             abstract class A<X> { }
@@ -798,12 +789,12 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
             A<String> ax = new C<>(new D())
         ''',
-        'Incompatible generic argument types. Cannot assign C<D> to: A<java.lang.String>'
+        '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<java.lang.Number>> to: java.util.Set<java.util.List<java.lang.String>>'
+        'Incompatible generic argument types. Cannot assign java.util.HashSet <java.util.ArrayList> to: java.util.Set <List>'
     }
 
     // GROOVY-9972
@@ -832,7 +823,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             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(defaults=false)
             class C<T> {
@@ -849,7 +840,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             }
             assert cd.p.f.toLowerCase() == 'd#f'
         '''
-*/
+
         assertScript '''
             @groovy.transform.TupleConstructor
             class C {
@@ -3182,17 +3173,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    // GROOVY-6232
-    void testDiamond() {
-        assertScript '''
-            class Foo<T>{  Foo(T a, T b){} }
-            def bar() {
-                Foo<Object> f = new Foo<>("a",new Object())
-            }
-            bar()
-        '''
-    }
-
     // GROOVY-6233
     void testConstructorArgumentsAgainstGenerics() {
         shouldFailWithMessages '''