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.
Alexis Sellier committed ago 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 +
}