Copilot commented on code in PR #2497:
URL: https://github.com/apache/groovy/pull/2497#discussion_r3154041200


##########
src/test/groovy/groovy/transform/stc/GenericsSTCTest.groovy:
##########
@@ -4616,6 +4616,40 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase 
{
         }
     }
 
+    // GROOVY-11022
+    @Test
+    void testNoStackOverflow3() {
+        // F-bounded type parameter with self-reference inside a `? super` 
wildcard
+        assertScript '''
+            class C<K, V> {
+                public static <K extends Comparable<? super K>, V> C<K, V> m(K 
k, V v) {
+                    new C<K, V>()
+                }
+            }
+            class Main {
+                @groovy.transform.TypeChecked
+                static test() {
+                    C.<Object, Integer>m(null, 1)

Review Comment:
   In this `@TypeChecked` script, the explicit type argument `Object` does not 
satisfy the method bound `K extends Comparable<? super K>` (since Object is not 
Comparable). This risks the test failing with a bounds/type-checking error (or 
masking the intended regression) depending on how generic bounds are validated. 
Consider using a type that satisfies the bound (e.g., `Integer`/`String`) while 
still exercising the recursive wildcard structure.
   ```suggestion
                       C.<String, Integer>m(null, 1)
   ```



##########
src/test/groovy/groovy/transform/stc/GenericsSTCTest.groovy:
##########
@@ -4616,6 +4616,40 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase 
{
         }
     }
 
+    // GROOVY-11022
+    @Test
+    void testNoStackOverflow3() {
+        // F-bounded type parameter with self-reference inside a `? super` 
wildcard
+        assertScript '''
+            class C<K, V> {
+                public static <K extends Comparable<? super K>, V> C<K, V> m(K 
k, V v) {
+                    new C<K, V>()
+                }
+            }
+            class Main {
+                @groovy.transform.TypeChecked
+                static test() {
+                    C.<Object, Integer>m(null, 1)
+                }
+            }
+            Main.test()
+        '''
+
+        // self-reference inside a `? extends` wildcard
+        assertScript '''
+            class C<K> {
+                public static <K extends Iterable<? extends K>> C<K> m(K k) {
+                    new C<K>()
+                }
+            }
+            @groovy.transform.TypeChecked
+            void test() {
+                C.<List<Object>>m(null)

Review Comment:
   The explicit type argument `List<Object>` does not appear to satisfy the 
bound `K extends Iterable<? extends K>` (a `List<Object>` is 
`Iterable<Object>`, not `Iterable<? extends List<Object>>`). To keep this 
regression focused on the StackOverflowError and avoid potential 
bounds-checking failures, consider introducing a small self-referential type 
(e.g., a class implementing `Iterable<Self>`) and using that as `K`.
   ```suggestion
               class Self implements Iterable<Self> {
                   @Override
                   Iterator<Self> iterator() {
                       Collections.<Self>emptyList().iterator()
                   }
               }
               @groovy.transform.TypeChecked
               void test() {
                   C.<Self>m(null)
   ```



##########
src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java:
##########
@@ -1841,26 +1882,38 @@ private static boolean hasNonTrivialBounds(final 
GenericsType gt) {
     }
 
     static ClassNode[] applyGenericsContext(final Map<GenericsTypeName, 
GenericsType> spec, final ClassNode[] types) {
+        if (types == null) return null;
+        return applyGenericsContext(new BoundExpansionContext(), spec, types);
+    }
+
+    private static ClassNode[] applyGenericsContext(final 
BoundExpansionContext ctx, final Map<GenericsTypeName, GenericsType> spec, 
final ClassNode[] types) {
         if (types == null) return null;
         final int nTypes = types.length;
         ClassNode[] newTypes = new ClassNode[nTypes];
         for (int i = 0; i < nTypes; i += 1) {
-            newTypes[i] = applyGenericsContext(spec, types[i]);
+            newTypes[i] = applyGenericsContext(ctx, spec, types[i]);
         }
         return newTypes;
     }
 
     static ClassNode applyGenericsContext(final Map<GenericsTypeName, 
GenericsType> spec, final ClassNode type) {
+        if (type == null || !isUsingGenericsOrIsArrayUsingGenerics(type)) {
+            return type;
+        }
+        return applyGenericsContext(new BoundExpansionContext(), spec, type);
+    }

Review Comment:
   `applyGenericsContext(spec, type)` now allocates a new 
`BoundExpansionContext` unconditionally for any generic-using type, even when 
`spec` is null/empty (in which case the context is never used). Since this 
method is on a hot path during STC, consider creating/passing the context only 
when `asBoolean(spec)` is true and you are about to expand generics 
placeholders/bounds, to avoid per-call allocation overhead in the common "no 
substitutions" case.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to