Skip to content

GROOVY-11022: StackOverflowError when having parameterized function w…#2496

Closed
paulk-asert wants to merge 1 commit into
apache:masterfrom
paulk-asert:groovy-11022-surgical
Closed

GROOVY-11022: StackOverflowError when having parameterized function w…#2496
paulk-asert wants to merge 1 commit into
apache:masterfrom
paulk-asert:groovy-11022-surgical

Conversation

@paulk-asert
Copy link
Copy Markdown
Contributor

@paulk-asert paulk-asert commented Apr 28, 2026

…ith recursive bounds

This is the fix recommended for GROOVY_5_0_X

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 28, 2026

Codecov Report

❌ Patch coverage is 90.90909% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 67.1396%. Comparing base (5a41b8f) to head (fad65c6).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
...roovy/transform/stc/StaticTypeCheckingSupport.java 90.9091% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##               master      #2496        +/-   ##
==================================================
+ Coverage     67.1374%   67.1396%   +0.0021%     
- Complexity      31622      31627         +5     
==================================================
  Files            1451       1451                
  Lines          122565     122573         +8     
  Branches        22007      22009         +2     
==================================================
+ Hits            82287      82295         +8     
+ Misses          33199      33197         -2     
- Partials         7079       7081         +2     
Files with missing lines Coverage Δ
...roovy/transform/stc/StaticTypeCheckingSupport.java 81.5790% <90.9091%> (+0.0419%) ⬆️

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes GROOVY-11022 (StackOverflowError during static type checking) by adding a recursion guard when expanding generic placeholder bounds, and adds a regression test covering F-bounded type parameters with self-references inside wildcard bounds.

Changes:

  • Add a per-thread “currently expanding bounds” stack to prevent infinite recursion in applyGenericsContext for F-bounded generics with wildcard self-references.
  • Add STC regression tests for ? super / ? extends self-referential bounds patterns.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/test/groovy/groovy/transform/stc/GenericsSTCTest.groovy Adds regression coverage for the StackOverflowError scenario involving recursive bounds inside wildcards.
src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java Introduces a recursion guard during generics bound expansion to avoid infinite recursion.

Comment thread src/test/groovy/groovy/transform/stc/GenericsSTCTest.groovy
@paulk-asert paulk-asert force-pushed the groovy-11022-surgical branch from b841622 to fad65c6 Compare April 28, 2026 12:20
}

private static final ThreadLocal<Deque<GenericsTypeName>> EXPANDING_BOUNDS =
ThreadLocal.withInitial(ArrayDeque::new);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am very critical to using a ThreadLocal. At the very least there should be a description of why this has to be ThreadLocal

newGT.setPlaceholder(true);
return newGT;
} finally {
expanding.pop();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we push to expanding and then pop from expanding. That means we remove the entry we just added, even in the case of line 1806, with the return. Why is it then not always empty again? Ah, because of the recursive call applyGenericsContext(spec, gt.getLowerBound()). But why does it have to be a ThreadLocal then? I would prefere going for a breaking change here nstead of using ThreadLocal

@paulk-asert
Copy link
Copy Markdown
Contributor Author

Abandoned, instead the slightly larger solution for master will be back-ported.

@paulk-asert paulk-asert closed this May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants