Fix `if let` binding scope in else branches
7fec22301c94093f5e4cd520e0c0fe87c6d9217de6b10818eabc896f351e5a18
`if let` lowering left successful pattern bindings in the variable lookup scope while lowering the else branch. A same-named outer variable was shadowed by a success-only binding there, producing undefined SSA values and runtime traps. Restore the pre-pattern variable scope before lowering the else branch while preserving values needed by the successful path and merge block.
1 parent
d5a7e016
lib/std/lang/lower.rad
+4 -1
| 3777 | 3777 | try lowerPatternMatch(self, &subject, &cond.pattern, &mut thenBlock, "then", elseBlock); |
|
| 3778 | 3778 | ||
| 3779 | 3779 | // Lower then branch. |
|
| 3780 | 3780 | try lowerNode(self, cond.thenBranch); |
|
| 3781 | 3781 | try emitMergeIfUnterminated(self, &mut mergeBlock); |
|
| 3782 | + | // Pattern bindings are visible only in the success branch. Restore the |
|
| 3783 | + | // outer variable scope before lowering `else`, where a same-named outer |
|
| 3784 | + | // variable may be referenced. |
|
| 3785 | + | exitVarScope(self, savedVarsLen); |
|
| 3782 | 3786 | ||
| 3783 | 3787 | // Lower else branch. |
|
| 3784 | 3788 | try switchToAndSeal(self, elseBlock); |
|
| 3785 | 3789 | if let elseBranch = cond.elseBranch { |
|
| 3786 | 3790 | try lowerNode(self, elseBranch); |
| 3788 | 3792 | try emitMergeIfUnterminated(self, &mut mergeBlock); |
|
| 3789 | 3793 | ||
| 3790 | 3794 | if let blk = mergeBlock { |
|
| 3791 | 3795 | try switchToAndSeal(self, blk); |
|
| 3792 | 3796 | } |
|
| 3793 | - | exitVarScope(self, savedVarsLen); |
|
| 3794 | 3797 | } |
|
| 3795 | 3798 | ||
| 3796 | 3799 | /// Emit pattern match branch with optional guard, and bind variables. |
|
| 3797 | 3800 | /// Used by `if-let`, `let-else`, and `while-let` lowering. |
|
| 3798 | 3801 | /// |
test/tests/iflet.shadow.else.rad
added
+18 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | //! An if-let pattern binding only shadows an outer variable in the success |
|
| 3 | + | //! branch; the else branch must still read the outer SSA value. |
|
| 4 | + | ||
| 5 | + | fn choose(value: ?i32) -> i32 { |
|
| 6 | + | let outer: i32 = 7; |
|
| 7 | + | if let outer = value { |
|
| 8 | + | return outer; |
|
| 9 | + | } else { |
|
| 10 | + | return outer; |
|
| 11 | + | } |
|
| 12 | + | } |
|
| 13 | + | ||
| 14 | + | @default fn main() -> i32 { |
|
| 15 | + | assert choose(41) == 41; |
|
| 16 | + | assert choose(nil) == 7; |
|
| 17 | + | return 0; |
|
| 18 | + | } |