GROOVY-12166 backport: STC: scope statement-level instanceof narrowing to the … - #2797
Merged
Merged
Conversation
…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 Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## GROOVY_5_1_X #2797 +/- ##
======================================================
+ Coverage 67.2715% 67.2789% +0.0074%
Complexity 29631 29631
======================================================
Files 1383 1383
Lines 117109 117117 +8
Branches 20596 20596
======================================================
+ Hits 78781 78795 +14
+ Misses 31777 31774 -3
+ Partials 6551 6548 -3
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…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 anassert 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.