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/05 10:04:33 UTC
[groovy] 02/04: GROOVY-9635: resolve "V" in T=V;
"T" may have been reused as type param (closes #1359)
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit d0dfbce53c1dd86026dfb1b5dd00b6ce51230ec5
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 (closes #1359)
---
.../transform/stc/StaticTypeCheckingSupport.java | 55 ++++++++++------------
.../groovy/transform/stc/GenericsSTCTest.groovy | 32 +++++++++----
2 files changed, 47 insertions(+), 40 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 959f7a3..061942f 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1624,43 +1624,36 @@ public abstract class StaticTypeCheckingSupport {
static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final Map<GenericsTypeName, GenericsType> resolvedPlaceholders) {
if (connections == null) return;
int count = 0;
- while (count < 10000) {
- count += 1;
- boolean checkForMorePlaceHolders = false;
+ while (count++ < 10000) {
+ boolean checkForMorePlaceholders = false;
for (Map.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);
+ // 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(final GenericsType gt) {
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 896f3cc..e30ebb1 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1529,7 +1529,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
// GROOVY-6455
void testDelegateWithGenerics() {
assertScript '''
- @groovy.transform.CompileStatic
class IntList {
@Delegate List<Integer> delegate = new ArrayList<Integer>()
}
@@ -1813,13 +1812,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)
@@ -1839,13 +1836,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)
@@ -1864,7 +1859,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
assertScript '''
import groovy.transform.*
- @CompileStatic
@TupleConstructor(includeFields=true)
abstract class A<N extends Number> {
protected final N number
@@ -1874,7 +1868,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
- @CompileStatic
class C<L extends Long> extends A<L> {
C(L longNumber) {
super(longNumber)
@@ -1894,13 +1887,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)
@@ -1915,6 +1906,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 {