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/05/17 15:26:17 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-10365: type argument/parameter relationships: no type param bound

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


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new d9f6e9870a GROOVY-10365: type argument/parameter relationships: no type param bound
d9f6e9870a is described below

commit d9f6e9870a2f3cedb1eba8748ba75d9ef5288952
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue May 17 09:45:48 2022 -0500

    GROOVY-10365: type argument/parameter relationships: no type param bound
    
    3_0_X backport
---
 .../java/org/codehaus/groovy/ast/GenericsType.java |  4 +-
 .../codehaus/groovy/ast/tools/GenericsUtils.java   | 64 +++++++++++++---------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 21 +++++++
 3 files changed, 63 insertions(+), 26 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index ccef754fb4..97749967b4 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -115,6 +115,8 @@ public class GenericsType extends ASTNode {
     private static StringBuilder appendName(final ClassNode theType, final StringBuilder sb) {
         if (theType.isArray()) {
             appendName(theType.getComponentType(), sb).append("[]");
+        } else if (theType.isGenericsPlaceHolder()) {
+            sb.append(theType.getUnresolvedName());
         } else if (theType.getOuterClass() != null) {
             String parentClassNodeName = theType.getOuterClass().getName();
             if (Modifier.isStatic(theType.getModifiers()) || theType.isInterface()) {
@@ -125,7 +127,7 @@ public class GenericsType extends ASTNode {
             sb.append('.');
             sb.append(theType.getName(), parentClassNodeName.length() + 1, theType.getName().length());
         } else {
-            sb.append(theType.isGenericsPlaceHolder() ? theType.getUnresolvedName() : theType.getName());
+            sb.append(theType.getName());
         }
         return sb;
     }
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 ff571acdb8..35390dc31b 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -40,6 +40,7 @@ import org.codehaus.groovy.runtime.memoize.ConcurrentSoftCache;
 import org.codehaus.groovy.runtime.memoize.EvictableCache;
 
 import java.lang.ref.SoftReference;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -141,6 +142,11 @@ public class GenericsUtils {
         return gt;
     }
 
+    /**
+     * Returns the type parameter/argument relationships of the specified type.
+     *
+     * @param type the class node to check
+     */
     public static Map<GenericsType.GenericsTypeName, GenericsType> extractPlaceholders(final ClassNode type) {
         Map<GenericsType.GenericsTypeName, GenericsType> placeholders = new HashMap<>();
         extractPlaceholders(type, placeholders);
@@ -148,8 +154,8 @@ public class GenericsUtils {
     }
 
     /**
-     * For a given classnode, fills in the supplied map with the parameterized
-     * types it defines.
+     * Populates the supplied map with the type parameter/argument relationships
+     * of the specified type.
      *
      * @param type the class node to check
      * @param placeholders the generics type information collector
@@ -163,45 +169,53 @@ public class GenericsUtils {
         }
 
         if (!type.isUsingGenerics() || !type.isRedirectNode()) return;
-        GenericsType[] parameterized = type.getGenericsTypes();
-        if (parameterized == null || parameterized.length == 0) return;
+        GenericsType[] parameterized = type.getGenericsTypes(); int n;
+        if (parameterized == null || (n = parameterized.length) == 0) return;
+
+        // GROOVY-8609, GROOVY-10067, etc.
+        if (type.isGenericsPlaceHolder()) {
+            GenericsType gt = parameterized[0];
+            placeholders.putIfAbsent(new GenericsType.GenericsTypeName(gt.getName()), gt);
+            return;
+        }
+
         GenericsType[] redirectGenericsTypes = type.redirect().getGenericsTypes();
-        if (redirectGenericsTypes == null || (type.isGenericsPlaceHolder() // GROOVY-8609
-                                              && redirectGenericsTypes.length != parameterized.length)) {
+        if (redirectGenericsTypes == null) {
             redirectGenericsTypes = parameterized;
-        } else if (redirectGenericsTypes.length != parameterized.length) {
+        } else if (redirectGenericsTypes.length != n) {
             throw new GroovyBugError("Expected earlier checking to detect generics parameter arity mismatch" +
                     "\nExpected: " + type.getName() + toGenericTypesString(redirectGenericsTypes) +
                     "\nSupplied: " + type.getName() + toGenericTypesString(parameterized));
         }
 
-        List<GenericsType> valueList = new LinkedList<>();
-        for (int i = 0, n = redirectGenericsTypes.length; i < n; i += 1) {
+        List<GenericsType> typeArguments = new ArrayList<>(n);
+        for (int i = 0; i < n; i += 1) {
             GenericsType rgt = redirectGenericsTypes[i];
             if (rgt.isPlaceholder()) {
-                GenericsType.GenericsTypeName name = new GenericsType.GenericsTypeName(rgt.getName());
-                if (!placeholders.containsKey(name)) {
-                    GenericsType value = parameterized[i];
-                    placeholders.put(name, value);
-                    valueList.add(value);
-                }
+                GenericsType typeArgument = parameterized[i];
+                placeholders.computeIfAbsent(new GenericsType.GenericsTypeName(rgt.getName()), name -> {
+                    typeArguments.add(typeArgument);
+                    return typeArgument;
+                });
             }
         }
 
-        for (GenericsType value : valueList) {
-            if (value.isWildcard()) {
-                ClassNode lowerBound = value.getLowerBound();
+        // examine non-placeholder type args
+        for (GenericsType gt : typeArguments) {
+            if (gt.isWildcard()) {
+                ClassNode lowerBound = gt.getLowerBound();
                 if (lowerBound != null) {
                     extractPlaceholders(lowerBound, placeholders);
-                }
-                ClassNode[] upperBounds = value.getUpperBounds();
-                if (upperBounds != null) {
-                    for (ClassNode upperBound : upperBounds) {
-                        extractPlaceholders(upperBound, placeholders);
+                } else {
+                    ClassNode[] upperBounds = gt.getUpperBounds();
+                    if (upperBounds != null) {
+                        for (ClassNode upperBound : upperBounds) {
+                            extractPlaceholders(upperBound, placeholders);
+                        }
                     }
                 }
-            } else if (!value.isPlaceholder()) {
-                extractPlaceholders(value.getType(), placeholders);
+            } else if (!gt.isPlaceholder()) {
+                extractPlaceholders(gt.getType(), placeholders);
             }
         }
     }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 137b1568ab..c593d72357 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2819,6 +2819,27 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-10365
+    void testCorrectlyBoundedTypeParameterTypeArgument() {
+        assertScript '''
+            interface I {
+            }
+            class A<T extends Number, Y> implements I {
+                double m(Integer i) {
+                    i.doubleValue()
+                }
+            }
+            class B<T extends I> {
+                public int f
+                double test(A<Float, ? extends T> a) { // "T" is re-purposed
+                    a.m(f) // Cannot call A#m(java.lang.Integer) with arguments [int]
+                }
+            }
+            double result = new B<I>(f:2).test(new A<>())
+            assert result == 2.0d
+        '''
+    }
+
     void testOutOfBoundsByExtendsGenericParameterType() {
         shouldFailWithMessages '''
             class Foo {