Skip to content

GROOVY-12166: STC: scope statement-level instanceof narrowing to the … - #2796

Open
paulk-asert wants to merge 1 commit into
apache:masterfrom
paulk-asert:groovy12166
Open

GROOVY-12166: STC: scope statement-level instanceof narrowing to the …#2796
paulk-asert wants to merge 1 commit into
apache:masterfrom
paulk-asert:groovy12166

Conversation

@paulk-asert

Copy link
Copy Markdown
Contributor

…member being visited

Temporary type information from instanceof is keyed by the target variable — for a field access, the shared FieldNode. if/loops/ternaries and expression statements bracket their own frames, but a narrowing recorded at statement level (an explicit return field instanceof Sub, or an assert field instanceof Sub, whose propagation to subsequent statements is intentional) lands in the enclosing frame, which was the frame pushed for the whole class visit. Keyed by the shared FieldNode, it then applied to every member visited afterwards: the checker stamped the narrowed type on unrelated field reads and the static compiler emitted a spurious checkcast, throwing ClassCastException at runtime whenever the field held a different subtype.

Members now push their own frame (methods and constructors, property and field initializers, object initializer blocks), so statement-level narrowing still flows within a member but can never survive into another; narrowing has no meaning past the member boundary. Intra-member flow typing (assert, if-branch) is unchanged.

The checker-side leak predates Groovy 5 but was masked by codegen: StaticTypesTypeChooser resolved types from the declared target first until GROOVY-11375 (5.0.0-alpha-9) gave the expression-stamped inferred type priority, exposing the stale narrowing as a checkcast. That change is correct; the fix belongs here.

…member being visited

Temporary type information from instanceof is keyed by the target
variable — for a field access, the shared FieldNode. if/loops/ternaries
and expression statements bracket their own frames, but a narrowing
recorded at statement level (an explicit `return field instanceof Sub`,
or an `assert field instanceof Sub`, whose propagation to subsequent
statements is intentional) lands in the enclosing frame, which was the
frame pushed for the whole class visit. Keyed by the shared FieldNode,
it then applied to every member visited afterwards: the checker stamped
the narrowed type on unrelated field reads and the static compiler
emitted a spurious checkcast, throwing ClassCastException at runtime
whenever the field held a different subtype.

Members now push their own frame (methods and constructors, property
and field initializers, object initializer blocks), so statement-level
narrowing still flows within a member but can never survive into
another; narrowing has no meaning past the member boundary. Intra-member
flow typing (assert, if-branch) is unchanged.

The checker-side leak predates Groovy 5 but was masked by codegen:
StaticTypesTypeChooser resolved types from the declared target first
until GROOVY-11375 (5.0.0-alpha-9) gave the expression-stamped inferred
type priority, exposing the stale narrowing as a checkcast. That change
is correct; the fix belongs here.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.1211%. Comparing base (142130d) to head (4a6c175).
⚠️ Report is 9 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##               master      #2796        +/-   ##
==================================================
+ Coverage     70.1064%   70.1211%   +0.0147%     
- Complexity      35774      35796        +22     
==================================================
  Files            1561       1562         +1     
  Lines          132373     132408        +35     
  Branches        24332      24345        +13     
==================================================
+ Hits            92802      92846        +44     
+ Misses          31170      31160        -10     
- Partials         8401       8402         +1     
Files with missing lines Coverage Δ
...roovy/transform/stc/StaticTypeCheckingVisitor.java 87.5127% <100.0000%> (+0.0254%) ⬆️

... and 8 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.

@testlens-app

testlens-app Bot commented Aug 17, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 4a6c175
▶️ Tests: 110251 executed
⚪️ Checks: 31/31 completed


Learn more about TestLens at testlens.app.

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.

3 participants