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/03/30 19:46:34 UTC
[groovy] 02/03: GROOVY-10556: "T" vs "C>" as bound of wildcard
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 a8e89b2ae6cba74476e172c6803eeb333dfced1f
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Mar 30 09:37:52 2022 -0500
GROOVY-10556: "T" vs "C<T extends C<?>>" as bound of wildcard
---
.../java/org/codehaus/groovy/ast/GenericsType.java | 12 ++--
.../transform/stc/StaticTypeCheckingSupport.java | 48 +++++++---------
.../groovy/transform/stc/GenericsSTCTest.groovy | 64 +++++++++++++++++++---
3 files changed, 82 insertions(+), 42 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index 36d5c8c..cfb4047 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -308,13 +308,13 @@ public class GenericsType extends ASTNode {
* @return true if generics are compatible
*/
private static boolean compareGenericsWithBound(final ClassNode classNode, final ClassNode bound) {
- if (classNode == null) {
- return false;
- }
- if (bound.getGenericsTypes() == null || (classNode.getGenericsTypes() == null && classNode.redirect().getGenericsTypes() != null)) {
- // if the bound is not using generics or the class node is a raw type, there's nothing to compare with
+ if (classNode == null) return false;
+ if (bound.getGenericsTypes() == null
+ || classNode.isGenericsPlaceHolder() // GROOVY-10556: "T" vs "C<T extends C<?>>" bound
+ || (classNode.getGenericsTypes() == null && classNode.redirect().getGenericsTypes() != null))
+ // if the bound is not using generics or the class node is a raw type, there's nothing to compare
return true;
- }
+
if (!classNode.equals(bound)) {
// the class nodes are on different types
// in this situation, we must choose the correct execution path : either the bound
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 c83f128..e782330 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1859,27 +1859,30 @@ public abstract class StaticTypeCheckingSupport {
if (type.isArray()) {
return applyGenericsContext(spec, type.getComponentType()).makeArray();
}
- ClassNode newType = type.getPlainNodeReference();
+
GenericsType[] gt = type.getGenericsTypes();
if (asBoolean(spec)) {
gt = applyGenericsContext(spec, gt);
}
- newType.setGenericsTypes(gt);
- if (type.isGenericsPlaceHolder()) {
- boolean nonTrivial = hasNonTrivialBounds(gt[0]);
- if (nonTrivial || !gt[0].isPlaceholder()) {
- return getCombinedBoundType(gt[0]);
- }
- String placeholderName = gt[0].getName();
- if (!placeholderName.equals(newType.getUnresolvedName())) {
- ClassNode clean = make(placeholderName);
- clean.setGenericsTypes(gt);
- clean.setRedirect(newType);
- newType = clean;
- }
- newType.setGenericsPlaceHolder(true);
+ if (!type.isGenericsPlaceHolder()) { // convert Type<T> to Type<...>
+ ClassNode cn = type.getPlainNodeReference();
+ cn.setGenericsTypes(gt);
+ return cn;
}
- return newType;
+
+ if (!gt[0].isPlaceholder()) { // convert T to Type or Type<...>
+ return getCombinedBoundType(gt[0]);
+ }
+
+ if (type.getGenericsTypes()[0] != gt[0]) { // convert T to X
+ ClassNode cn = make(gt[0].getName());
+ cn.setRedirect(gt[0].getType());
+ cn.setGenericsPlaceHolder(true);
+ cn.setGenericsTypes(gt);
+ return cn;
+ }
+
+ return type; // nothing to do
}
static ClassNode getCombinedBoundType(final GenericsType genericsType) {
@@ -1893,19 +1896,6 @@ public abstract class StaticTypeCheckingSupport {
return genericsType.getType();
}
- private static void applyContextGenerics(final Map<GenericsTypeName, GenericsType> resolvedPlaceholders, final Map<GenericsTypeName, GenericsType> placeholdersFromContext) {
- if (placeholdersFromContext == null) return;
- for (Map.Entry<GenericsTypeName, GenericsType> entry : resolvedPlaceholders.entrySet()) {
- GenericsType gt = entry.getValue();
- if (gt.isPlaceholder()) {
- GenericsTypeName name = new GenericsTypeName(gt.getName());
- GenericsType outer = placeholdersFromContext.get(name);
- if (outer == null) continue;
- entry.setValue(outer);
- }
- }
- }
-
private static Map<GenericsTypeName, GenericsType> getGenericsParameterMapOfThis(final ClassNode cn) {
if (cn == null) return null;
Map<GenericsTypeName, GenericsType> map = null;
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 70da3e4..545c892 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1035,7 +1035,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- @NotYetImplemented // GROOVY-9995
+ // GROOVY-9995
void testDiamondInferrenceFromConstructor15() {
[
['Closure<A<Long>>', 'java.util.concurrent.Callable<A<Long>>'],
@@ -2345,15 +2345,18 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
println arg1 == arg2
}
printEqual(1, 'foo')
- ''', '#printEqual(T, T) with arguments [int, java.lang.String]'
+ ''',
+ '#printEqual(T, T) with arguments [int, java.lang.String]'
}
+
void testIncompatibleGenericsForTwoArgumentsUsingEmbeddedPlaceholder() {
shouldFailWithMessages '''
public <T> void printEqual(T arg1, List<T> arg2) {
println arg1 == arg2
}
printEqual(1, ['foo'])
- ''', '#printEqual(T, java.util.List <T>) with arguments [int, java.util.List <java.lang.String>]'
+ ''',
+ '#printEqual(T, java.util.List <T>) with arguments [int, java.util.List <java.lang.String>]'
}
// GROOVY-9902
@@ -2595,7 +2598,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
static <T extends List<? extends CharSequence>> void bar(T a) {}
}
Foo.bar([new Object()])
- ''', 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List <java.lang.Object>]'
+ ''',
+ 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List <java.lang.Object>]'
}
void testOutOfBoundsBySuperGenericParameterType() {
@@ -2604,7 +2608,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
static <T extends List<? super CharSequence>> void bar(T a) {}
}
Foo.bar(['abc'])
- ''', 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List <java.lang.String>]'
+ ''',
+ 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List <java.lang.String>]'
}
void testOutOfBoundsByExtendsPlaceholderParameterType() {
@@ -2615,7 +2620,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
def <U extends List<Object>> void baz(U list) {
Foo.bar(list)
}
- ''', 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
+ ''',
+ 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
}
void testOutOfBoundsBySuperPlaceholderParameterType() {
@@ -2626,7 +2632,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
def <U extends List<String>> void baz(U list) {
Foo.bar(list)
}
- ''', 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
+ ''',
+ 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
}
// GROOVY-5721
@@ -3834,6 +3841,49 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
+ @NotYetImplemented // GROOVY-10055
+ void testSelfReferentialTypeParameter2() {
+ assertScript '''
+ abstract class A<Self extends A<Self>> {
+ Self foo(inputs) {
+ // ...
+ this
+ }
+ }
+ abstract class B<Self extends B<Self>> extends A<Self> {
+ Self bar(inputs) {
+ // ...
+ this
+ }
+ }
+ class C<Self extends C<Self>> extends B<Self> { // see org.testcontainers.containers.PostgreSQLContainer
+ Self baz(inputs) {
+ // ...
+ this
+ }
+ }
+
+ new C<>()
+ .foo('x')
+ .bar('y') // error
+ .baz('z') // error
+ '''
+ }
+
+ // GROOVY-10556
+ void testSelfReferentialTypeParameter3() {
+ ['(B) this', 'this as B'].each { self ->
+ assertScript """
+ abstract class A<B extends A<B,X>,X> {
+ B m() {
+ $self
+ }
+ }
+ (new A(){}).m()
+ """
+ }
+ }
+
// GROOVY-7804
void testParameterlessClosureToGenericSAMTypeArgumentCoercion() {
assertScript '''