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/08/01 20:25:03 UTC
[groovy] branch GROOVY_4_0_X updated: GROOVY-10646: STC: non-static inner class and outer class type parameter
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new c9c890962a GROOVY-10646: STC: non-static inner class and outer class type parameter
c9c890962a is described below
commit c9c890962ac26993c22861d597b1c662ca707022
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Aug 1 14:47:33 2022 -0500
GROOVY-10646: STC: non-static inner class and outer class type parameter
---
.../java/org/codehaus/groovy/ast/GenericsType.java | 4 +-
.../codehaus/groovy/ast/tools/GenericsUtils.java | 4 +-
.../transform/stc/StaticTypeCheckingSupport.java | 43 +++++++++++++++++-----
.../groovy/transform/stc/GenericsSTCTest.groovy | 41 +++++++++++++++++++++
4 files changed, 79 insertions(+), 13 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index ae469aec9b..8ffa393148 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -125,7 +125,9 @@ public class GenericsType extends ASTNode {
if (Modifier.isStatic(theType.getModifiers()) || theType.isInterface()) {
sb.append(parentClassNodeName);
} else {
- sb.append(genericsBounds(theType.getOuterClass(), new HashSet<>()));
+ ClassNode outerClass = theType.getNodeMetaData("outer.class");
+ if (outerClass == null) outerClass = theType.getOuterClass();
+ sb.append(genericsBounds(outerClass, new HashSet<>()));
}
sb.append('.');
sb.append(theType.getName(), parentClassNodeName.length() + 1, theType.getName().length());
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 2cbb5ced6c..cbce5ff302 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -482,9 +482,9 @@ public class GenericsUtils {
// class C<T extends Number> extends A<T,Object,String> { }
// the type "A<T,Object,String> -> A<X,Y,Z>" will produce [X:Number,Y:Object,Z:String]
+ ClassNode oc = type.getNodeMetaData("outer.class"); // GROOVY-10646: outer class type parameters
+ Map<String, ClassNode> newSpec = oc != null ? createGenericsSpec(oc, oldSpec) : new HashMap<>();
GenericsType[] gt = type.getGenericsTypes(), rgt = type.redirect().getGenericsTypes();
-
- Map<String, ClassNode> newSpec = new HashMap<>();
if (gt != null && rgt != null) {
for (int i = 0, n = gt.length; i < n; i += 1) {
newSpec.put(rgt[i].getName(), correctToGenericsSpec(oldSpec, gt[i]));
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 557cdb1805..9ae9b315a6 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1304,7 +1304,7 @@ public abstract class StaticTypeCheckingSupport {
}
/**
- * Returns true if a class node makes use of generic types. If the class node represents an
+ * Returns true if a class makes use of generic types. If node represents an
* array type, then checks if the component type is using generics.
*
* @param cn a class node for which to check if it is using generics
@@ -1314,7 +1314,17 @@ public abstract class StaticTypeCheckingSupport {
if (cn.isArray()) {
return isUsingGenericsOrIsArrayUsingGenerics(cn.getComponentType());
}
- return (cn.isUsingGenerics() && cn.getGenericsTypes() != null);
+ if (cn.isUsingGenerics()) {
+ if (cn.getGenericsTypes() != null) {
+ return true;
+ }
+ ClassNode oc = cn.getOuterClass();
+ if (oc != null && oc.getGenericsTypes() != null
+ && (cn.getModifiers() & Opcodes.ACC_STATIC) == 0) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -1735,10 +1745,11 @@ public abstract class StaticTypeCheckingSupport {
} else if (type.equals(CLOSURE_TYPE) && isSAMType(target)) {
// GROOVY-9974, GROOVY-10052: Lambda, Closure, Pointer or Reference for SAM-type receiver
ClassNode returnType = StaticTypeCheckingVisitor.wrapTypeIfNecessary(GenericsUtils.parameterizeSAM(target).getV2());
- extractGenericsConnections(connections, type.getGenericsTypes(), new GenericsType[] {returnType.asGenericsType()});
+ extractGenericsConnections(connections, type.getGenericsTypes(), new GenericsType[]{ returnType.asGenericsType() });
} else if (type.equals(target)) {
extractGenericsConnections(connections, type.getGenericsTypes(), target.getGenericsTypes());
+ extractGenericsConnections(connections, type.getNodeMetaData("outer.class"), target.getOuterClass()); //GROOVY-10646
} else if (implementsInterfaceOrIsSubclassOf(type, target)) {
ClassNode superClass = getNextSuperClass(type, target);
@@ -1825,32 +1836,44 @@ public abstract class StaticTypeCheckingSupport {
}
private static GenericsType applyGenericsContext(final Map<GenericsTypeName, GenericsType> spec, final GenericsType gt) {
+ ClassNode type = gt.getType();
+
if (gt.isPlaceholder()) {
GenericsTypeName name = new GenericsTypeName(gt.getName());
GenericsType specType = spec.get(name);
if (specType != null) return specType;
if (hasNonTrivialBounds(gt)) {
- GenericsType newGT = new GenericsType(gt.getType(), applyGenericsContext(spec, gt.getUpperBounds()), applyGenericsContext(spec, gt.getLowerBound()));
+ GenericsType newGT = new GenericsType(type, applyGenericsContext(spec, gt.getUpperBounds()), applyGenericsContext(spec, gt.getLowerBound()));
newGT.setPlaceholder(true);
return newGT;
}
return gt;
- } else if (gt.isWildcard()) {
- GenericsType newGT = new GenericsType(gt.getType(), applyGenericsContext(spec, gt.getUpperBounds()), applyGenericsContext(spec, gt.getLowerBound()));
+ }
+
+ if (gt.isWildcard()) {
+ GenericsType newGT = new GenericsType(type, applyGenericsContext(spec, gt.getUpperBounds()), applyGenericsContext(spec, gt.getLowerBound()));
newGT.setWildcard(true);
return newGT;
}
- ClassNode type = gt.getType();
+
ClassNode newType;
if (type.isArray()) {
newType = applyGenericsContext(spec, type.getComponentType()).makeArray();
+ } else if (type.getGenericsTypes() == null//type.isGenericsPlaceHolder()
+ && type.getOuterClass() == null) {
+ return gt;
} else {
- if (type.getGenericsTypes() == null) {
- return gt;
- }
newType = type.getPlainNodeReference();
newType.setGenericsPlaceHolder(type.isGenericsPlaceHolder());
newType.setGenericsTypes(applyGenericsContext(spec, type.getGenericsTypes()));
+
+ // GROOVY-10646: non-static inner class + outer class type parameter
+ if ((type.getModifiers() & Opcodes.ACC_STATIC) == 0) {
+ Optional.ofNullable(type.getOuterClass())
+ .filter(oc -> oc.getGenericsTypes()!=null)
+ .map(oc -> applyGenericsContext(spec, oc))
+ .ifPresent(oc -> newType.putNodeMetaData("outer.class", oc));
+ }
}
return new GenericsType(newType);
}
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 35e8642d02..78a2311a99 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -712,6 +712,47 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10646
+ void testReturnTypeInferenceWithMethodGenerics28() {
+ String types = '''
+ class Model {
+ }
+ interface Output<T> {
+ T getT()
+ }
+ abstract class WhereDSL<Type> {
+ abstract Type where()
+ }
+ abstract class Input<T> extends WhereDSL<ReferencesOuterClassTP> {
+ class ReferencesOuterClassTP implements Output<T> {
+ @Override T getT() { return null }
+ }
+ }
+ '''
+ assertScript types + '''
+ void m(Input<Model> input) {
+ def output = input.where()
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ assert node.getNodeMetaData(INFERRED_TYPE).toString(false) == 'Model'
+ })
+ def result = output.getT()
+ }
+ '''
+ assertScript types + '''
+ @FunctionalInterface
+ interface Xform extends java.util.function.Function<Input<Model>, Output<Model>> {
+ }
+
+ void select(Xform xform) {
+ }
+
+ select { input ->
+ def result = input.where()
+ return result // Cannot return value of type Input$ReferencesOuterClassTP for closure expecting Output<Model>
+ }
+ '''
+ }
+
void testDiamondInferrenceFromConstructor1() {
assertScript '''
class Foo<U> {