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 2021/03/24 03:50:15 UTC

[groovy] branch master updated: GROOVY-9998: STC: connect "? super T" with "X" not as "T=X" but as "T=?"

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

emilles 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 e6ce56f  GROOVY-9998: STC: connect "? super T" with "X" not as "T=X" but as "T=?"
e6ce56f is described below

commit e6ce56fc27e7640664946eb1a99bdee6fb63547e
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Mar 23 22:43:26 2021 -0500

    GROOVY-9998: STC: connect "? super T" with "X" not as "T=X" but as "T=?"
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 21 +++---
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 21 ++++++
 .../classgen/asm/sc/bugs/Groovy6564Bug.groovy      | 82 +++++++++++-----------
 3 files changed, 71 insertions(+), 53 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 875777a..6270dd0 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1579,7 +1579,7 @@ public abstract class StaticTypeCheckingSupport {
     }
 
     static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final Map<GenericsTypeName, GenericsType> resolvedPlaceholders) {
-        if (connections == null) return;
+        if (connections == null || connections.isEmpty()) return;
         int count = 0;
         while (count++ < 10000) {
             boolean checkForMorePlaceholders = false;
@@ -1598,7 +1598,7 @@ public abstract class StaticTypeCheckingSupport {
                         // 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);
+                            entry.setValue(newValue.isWildcard() ? new GenericsType(replacementType) : newValue); // GROOVY-9998
                             if (newValue.isPlaceholder()) {
                                 checkForMorePlaceholders = checkForMorePlaceholders || !equalIncludingGenerics(oldValue, newValue);
                             }
@@ -1736,8 +1736,7 @@ public abstract class StaticTypeCheckingSupport {
 
         // both have generics
         for (int i = 0, n = usage.length; i < n; i += 1) {
-            GenericsType ui = usage[i];
-            GenericsType di = declaration[i];
+            GenericsType ui = usage[i], di = declaration[i];
             if (di.isPlaceholder()) {
                 connections.put(new GenericsTypeName(di.getName()), ui);
             } else if (di.isWildcard()) {
@@ -1745,13 +1744,13 @@ public abstract class StaticTypeCheckingSupport {
                     extractGenericsConnections(connections, ui.getLowerBound(), di.getLowerBound());
                     extractGenericsConnections(connections, ui.getUpperBounds(), di.getUpperBounds());
                 } else {
-                    ClassNode cu = ui.getType();
-                    extractGenericsConnections(connections, cu, di.getLowerBound());
-                    ClassNode[] upperBounds = di.getUpperBounds();
-                    if (upperBounds != null) {
-                        for (ClassNode cn : upperBounds) {
-                            extractGenericsConnections(connections, cu, cn);
-                        }
+                    ClassNode boundType = getCombinedBoundType(di);
+                    if (boundType.isGenericsPlaceHolder()) { // GROOVY-9998
+                        String placeholderName = boundType.getUnresolvedName();
+                        ui = new GenericsType(ui.getType()); ui.setWildcard(true);
+                        connections.put(new GenericsTypeName(placeholderName), ui);
+                    } else { // di like "? super Collection<T>" and ui like "List<Type>"
+                        extractGenericsConnections(connections, ui.getType(), boundType);
                     }
                 }
             } else {
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 087eaab..dc1a4ae 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2543,6 +2543,27 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-9998
+    void testContravariantMethodResolution2() {
+        assertScript '''
+            import groovy.transform.*
+            @TupleConstructor(defaults=false)
+            class A {
+              final int order
+            }
+            @InheritConstructors @ToString(includeSuperProperties=true)
+            class B extends A {
+            }
+
+            Comparator<A> comparator = { a1, a2 -> Integer.compare(a1.order, a2.order) }
+
+            def input = [new B(2), new B(3), new B(1), new B(0)]
+            // sorted(Comparator<? super B>) using Comparator<A>
+            def result = input.stream().sorted(comparator).toList()
+            assert result.toString().equals("[B(0), B(1), B(2), B(3)]")
+        '''
+    }
+
     void testContravariantMethodResolutionWithImplicitCoercion() {
         assertScript '''
             interface Function<T, R> {
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy6564Bug.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy6564Bug.groovy
index 0697069..b15a994 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy6564Bug.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy6564Bug.groovy
@@ -16,10 +16,6 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
-
-
-
 package org.codehaus.groovy.classgen.asm.sc.bugs
 
 import groovy.transform.stc.StaticTypeCheckingTestCase
@@ -28,46 +24,48 @@ import org.codehaus.groovy.classgen.asm.sc.StaticCompilationTestSupport
 class Groovy6564Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
 
     void testShouldNotRequireIntermediateVariableToPass() {
-        assertScript '''class Stream<T> implements Iterable<T> {
-    public static Stream<String> from(BufferedReader reader) { new Stream(data: ['a', 'b', 'c']) }
-
-    List<T> data
-
-    public Iterator<T> iterator() { data.iterator() }
-
-    public <U> Stream<U> flatMap(Closure<? extends Collection<U>> closure) {
-        new Stream(data: data.collect(closure).flatten() as List)
-    }
-}
-
-Map<String, Integer> frequencies = [:].withDefault { 0 }
-BufferedReader r = null
-Stream.from(r)
-    .flatMap { String it -> it.toList() }
-    .each { String it -> frequencies[it.toUpperCase()]++ }
-    assert frequencies == [A:1, B:1, C:1]
-'''
+        assertScript '''
+            class Stream<T> implements Iterable<T> {
+                public static Stream<String> from(BufferedReader reader) { new Stream(data: ['a', 'b', 'c']) }
+
+                List<T> data
+
+                public Iterator<T> iterator() { data.iterator() }
+
+                public <U> Stream<U> flatMap(Closure<? extends Collection<U>> closure) {
+                    new Stream(data: data.collect(closure).flatten() as List)
+                }
+            }
+
+            Map<String, Integer> frequencies = [:].withDefault { 0 }
+            BufferedReader r = null
+            Stream.from(r)
+                .flatMap { String it -> it.toList() }
+                .each { String it -> frequencies[it.toUpperCase()]++ }
+            assert frequencies == [A:1, B:1, C:1]
+        '''
     }
 
     void testShouldNotRequireIntermediateVariableToPassWithEachParamInference() {
-        assertScript '''class Stream<T> implements Iterable<T> {
-    public static Stream<String> from(BufferedReader reader) { new Stream(data: ['a', 'b', 'c']) }
-
-    List<T> data
-
-    public Iterator<T> iterator() { data.iterator() }
-
-    public <U> Stream<U> flatMap(Closure<? extends Collection<U>> closure) {
-        new Stream(data: data.collect(closure).flatten() as List)
-    }
-}
-
-Map<String, Integer> frequencies = [:].withDefault { 0 }
-BufferedReader r = null
-Stream.from(r)
-    .flatMap { String it -> it.toList() }
-    .each { println it; frequencies[it.toUpperCase()]++ }
-    assert frequencies == [A:1, B:1, C:1]
-'''
+        assertScript '''
+            class Stream<T> implements Iterable<T> {
+                public static Stream<String> from(BufferedReader reader) { new Stream(data: ['a', 'b', 'c']) }
+
+                List<T> data
+
+                public Iterator<T> iterator() { data.iterator() }
+
+                public <U> Stream<U> flatMap(Closure<? extends Collection<U>> closure) {
+                    new Stream(data: data.collect(closure).flatten() as List)
+                }
+            }
+
+            Map<String, Integer> frequencies = [:].withDefault { 0 }
+            BufferedReader r = null
+            Stream.from(r)
+                .flatMap { String it -> it.toList() }
+                .each { frequencies[it.toUpperCase()]++ }
+            assert frequencies == [A:1, B:1, C:1]
+        '''
     }
 }