Skip to content
Permalink
Browse files
GROOVY-10622: STC: type parameter with parameterized bound
  • Loading branch information
eric-milles committed May 15, 2022
1 parent 468379f commit a585ab3079640e1c11e5117ca292b1624f8298a5
Showing 3 changed files with 59 additions and 17 deletions.
@@ -1494,13 +1494,24 @@ private boolean resolveGenericsType(final GenericsType genericsType) {
if (genericsType.isResolved()) return true;
currentClass.setUsingGenerics(true);
ClassNode type = genericsType.getType();
// save name before redirect
GenericsTypeName name = new GenericsTypeName(type.getName());
visitTypeAnnotations(type);
ClassNode[] bounds = genericsType.getUpperBounds();
if (!genericParameterNames.containsKey(name)) {
if (bounds != null) {
for (ClassNode upperBound : bounds) {
visitTypeAnnotations(type); // JSR-308 support
GenericsType tp = genericParameterNames.get(new GenericsTypeName(type.getName()));
if (tp != null) {
ClassNode[] bounds = tp.getUpperBounds();
if (bounds != null && (bounds.length > 1 || (bounds[0].isRedirectNode()
&& bounds[0].redirect().getGenericsTypes() != null))) {
// GROOVY-10622: bounds are too complex for a redirect-only representation
//genericsType.setUpperBounds(bounds);
type.setGenericsPlaceHolder(true);
type.setRedirect(bounds[0]);
} else {
type.setRedirect(tp.getType());
}
genericsType.setPlaceholder(true);
} else {
ClassNode[] upperBounds = genericsType.getUpperBounds();
if (upperBounds != null) {
for (ClassNode upperBound : upperBounds) {
resolveOrFail(upperBound, genericsType);
type.setRedirect(upperBound);
resolveGenericsTypes(upperBound.getGenericsTypes());
@@ -1510,16 +1521,10 @@ private boolean resolveGenericsType(final GenericsType genericsType) {
} else {
resolveOrFail(type, genericsType);
}
} else {
GenericsType gt = genericParameterNames.get(name);
type.setRedirect(gt.getType());
genericsType.setPlaceholder(true);
}

if (genericsType.getLowerBound() != null) {
resolveOrFail(genericsType.getLowerBound(), genericsType);
}

if (resolveGenericsTypes(type.getGenericsTypes())) {
genericsType.setResolved(genericsType.getType().isResolved());
}
@@ -5313,8 +5313,9 @@ protected ClassNode inferReturnTypeGenerics(final ClassNode receiver, final Meth
if (context != null) {
returnType = applyGenericsContext(context, returnType);

if (receiver.getGenericsTypes() == null && receiver.redirect().getGenericsTypes() != null && GenericsUtils.hasUnresolvedGenerics(returnType)) {
returnType = returnType.getPlainNodeReference(); // GROOVY-10049: do not return "Stream<E>" for raw type "List#stream()"
if (receiver.getGenericsTypes() == null && receiver.redirect().getGenericsTypes() != null
&& !receiver.isGenericsPlaceHolder() && GenericsUtils.hasUnresolvedGenerics(returnType)) {
returnType = returnType.getPlainNodeReference(); // GROOVY-10049: not "Stream<E>" for raw type
}
}

@@ -5640,8 +5641,11 @@ private static Map<GenericsTypeName, GenericsType> extractPlaceHolders(final Cla
ClassNode current = type;
while (current != null) {
Map<GenericsTypeName, GenericsType> placeHolders = new HashMap<>();
// GROOVY-10055: handle diamond or raw
if (current.getGenericsTypes() != null
if (current.isGenericsPlaceHolder())
// GROOVY-10622: type param bound "T extends Set<Type>"
current = current.asGenericsType().getUpperBounds()[0];
// GROOVY-10055: handle diamond or raw type
else if (current.getGenericsTypes() != null
? current.getGenericsTypes().length == 0
: current.redirect().getGenericsTypes() != null) {
for (GenericsType gt : current.redirect().getGenericsTypes()) {
@@ -662,6 +662,39 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}

// GROOVY-10622
void testReturnTypeInferenceWithMethodGenerics26() {
String types = '''
@groovy.transform.TupleConstructor(defaults=false)
class A<X> {
X x
}
@groovy.transform.TupleConstructor(defaults=false)
class B<Y> {
Y y
}
class C {
C(byte b) { }
}
'''
assertScript types + '''
def <T extends A<Byte>> void test(B<T> b_of_t) {
def t = b_of_t.getY()
def x = t.getX()
new C(x)
}
test(new B<>(new A<>((byte)1)))
'''
assertScript types + '''
def <T extends A<Byte>> void test(B<T> b_of_t) {
def t = b_of_t.y
def x = t.x
new C(x)
}
test(new B<>(new A<>((byte)1)))
'''
}

void testDiamondInferrenceFromConstructor1() {
assertScript '''
class Foo<U> {

0 comments on commit a585ab3

Please sign in to comment.