You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/09/10 06:38:45 UTC

[groovy] 01/02: GROOVY-9635: resolve "V" in T=V; "T" may have been reused as type param (port to 2_5_X)

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

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

commit f847b1da21b9bdf736ce205646a300631560d0e4
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Aug 31 17:34:50 2020 -0500

    GROOVY-9635: resolve "V" in T=V; "T" may have been reused as type param (port to 2_5_X)
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 58 ++++++++++------------
 src/test/groovy/bugs/Groovy9706.groovy             |  2 -
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 32 ++++++++----
 3 files changed, 49 insertions(+), 43 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 2f481b2..ef9846d 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1673,43 +1673,37 @@ public abstract class StaticTypeCheckingSupport {
     ) {
         if (connections == null) return;
         int count = 0;
-        while (count < 10000) {
-            count++;
-            boolean checkForMorePlaceHolders = false;
-            for (Entry<GenericsTypeName, GenericsType> entry : resolvedPlaceholders.entrySet()) {
-                GenericsTypeName name = entry.getKey();
-                GenericsType replacement = connections.get(name);
-                if (replacement == null) {
-                    GenericsType value = entry.getValue();
-                    GenericsType newValue = applyGenericsContext(connections, value);
-                    entry.setValue(newValue);
-                    checkForMorePlaceHolders = checkForMorePlaceHolders || !equalIncludingGenerics(value, newValue);
-                    continue;
-                }
-                GenericsType original = entry.getValue();
-                if (!original.isWildcard() && !original.isPlaceholder()) {
-                    continue;
-                }
-                boolean placeholderReplacement = replacement.isPlaceholder();
-                if (placeholderReplacement) {
-                    GenericsType connectedType = resolvedPlaceholders.get(name);
-                    if (replacement == connectedType) continue;
-                }
-                // GROOVY-6787: Don't override the original if the replacement placeholder doesn't respect the bounds,
-                // otherwise the original bounds are lost which can result in accepting an incompatible type as an
-                // argument, for example.
-                ClassNode replacementType = extractType(replacement);
-                if (original.isCompatibleWith(replacementType)) {
-                    entry.setValue(replacement);
-                    if (placeholderReplacement) {
-                        checkForMorePlaceHolders = checkForMorePlaceHolders || !equalIncludingGenerics(original, replacement);
+
+        while (count++ < 10000) {
+            boolean checkForMorePlaceholders = false;
+            for (Map.Entry<GenericsTypeName, GenericsType> entry : resolvedPlaceholders.entrySet()) {
+                // entry could be T=T, T=T extends U, T=V, T=String, T=? extends String, etc.
+                GenericsType oldValue = entry.getValue();
+                if (oldValue.isPlaceholder()) { // T=T or V, not T=String or ? ...
+                    GenericsTypeName name = new GenericsTypeName(oldValue.getName());
+                    GenericsType newValue = connections.get(name); // find "V" in T=V
+                    if (newValue == oldValue) continue;
+                    if (newValue == null) {
+                        entry.setValue(newValue = applyGenericsContext(connections, oldValue));
+                        checkForMorePlaceholders = checkForMorePlaceholders || !equalIncludingGenerics(oldValue, newValue);
+                    } else if (!newValue.isPlaceholder() || newValue != resolvedPlaceholders.get(name)) {
+                        // GROOVY-6787: Don't override the original if the replacement doesn't respect the bounds otherwise
+                        // the original bounds are lost, which can result in accepting an incompatible type as an argument.
+                        ClassNode replacementType = extractType(newValue);
+                        if (oldValue.isCompatibleWith(replacementType)) {
+                            entry.setValue(newValue);
+                            if (newValue.isPlaceholder()) {
+                                checkForMorePlaceholders = checkForMorePlaceholders || !equalIncludingGenerics(oldValue, newValue);
+                            }
+                        }
                     }
                 }
             }
-            if (!checkForMorePlaceHolders) break;
+            if (!checkForMorePlaceholders) break;
         }
-        if (count >= 10000)
+        if (count >= 10000) {
             throw new GroovyBugError("unable to handle generics in " + resolvedPlaceholders + " with connections " + connections);
+        }
     }
 
     private static ClassNode extractType(GenericsType gt) {
diff --git a/src/test/groovy/bugs/Groovy9706.groovy b/src/test/groovy/bugs/Groovy9706.groovy
index 19ca427..84519f1 100644
--- a/src/test/groovy/bugs/Groovy9706.groovy
+++ b/src/test/groovy/bugs/Groovy9706.groovy
@@ -18,8 +18,6 @@
  */
 package groovy.bugs
 
-import groovy.test.GroovyTestCase
-
 class Groovy9706 extends GroovyTestCase {
     void testVarargsPrimitive() {
         assertScript '''
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index f2d87e7..614957a 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1502,7 +1502,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-6455
     void testDelegateWithGenerics() {
         assertScript '''
-            @groovy.transform.CompileStatic
             class IntList {
                 @Delegate List<Integer> delegate = new ArrayList<Integer>()
             }
@@ -1786,13 +1785,11 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         assertScript '''
             import groovy.transform.*
 
-            @CompileStatic
             @TupleConstructor(includeFields=true)
             abstract class A<N extends Number> {
                 protected final N number
             }
 
-            @CompileStatic
             class C<L extends Long> extends A<L> { // further restriction of type parameter
                 C(L longNumber) {
                     super(longNumber)
@@ -1812,13 +1809,11 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         assertScript '''
             import groovy.transform.*
 
-            @CompileStatic
             @TupleConstructor
             abstract class A<N extends Number> {
                 final N number
             }
 
-            @CompileStatic
             class C<L extends Long> extends A<L> {
                 C(L longNumber) {
                     super(longNumber)
@@ -1837,7 +1832,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         assertScript '''
             import groovy.transform.*
 
-            @CompileStatic
             @TupleConstructor(includeFields=true)
             abstract class A<N extends Number> {
                 protected final N number
@@ -1847,7 +1841,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 }
             }
 
-            @CompileStatic
             class C<L extends Long> extends A<L> {
                 C(L longNumber) {
                     super(longNumber)
@@ -1867,13 +1860,11 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         assertScript '''
             import groovy.transform.*
 
-            @CompileStatic
             @TupleConstructor(includeFields=true)
             abstract class A<N extends Number> {
                 final N number
             }
 
-            @CompileStatic
             class C<L extends Long> extends A<L> {
                 C(L longNumber) {
                     super(longNumber)
@@ -1888,6 +1879,29 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-9635
+    void testCovariantReturnTypeInferredFromMethod3() {
+        assertScript '''
+            import java.util.function.Function
+
+            class C<R extends Number> {
+                def <V> V m(Function<C, V> f) { // R from C is confused with R->V from Function
+                    V result = f.apply(this)
+                    return result
+                }
+            }
+
+            def ret = new C().m(new Function<C, String>() {
+                @Override
+                String apply(C that) {
+                    return 'foo'
+                }
+            })
+
+            assert ret == 'foo'
+        '''
+    }
+
     void testReturnTypeChecking() {
         shouldFailWithMessages '''
             class Foo {