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> {