compiler: Initialize pattern binding destinations

f04dd59a4402f0b117c0778333921a74a3727eabeb1d9c37fd4128689ddc2bc6
Alexis Sellier committed ago 1 parent 37ceac34
lib/std/lang/lower.rad +16 -17
3926 3926
3927 3927
/// Lower an `if let` statement.
3928 3928
unsafe fn lowerIfLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, cond: ast::IfLet) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3929 3929
    let savedVarsLen = enterVarScope(&self.vars);
3930 3930
    let subject = try lowerMatchSubject(self, cond.pattern.scrutinee);
3931 -
    let mut thenBlock: BlockId = undefined;
3931 +
    let mut thenBlock: ?BlockId = nil;
3932 3932
    if cond.pattern.guard == nil {
3933 3933
        set thenBlock = try createBlock(self, "then");
3934 3934
    }
3935 3935
    let elseBlock = try createBlock(self, "else");
3936 3936
    let mut mergeBlock: ?BlockId = nil;
3937 3937
3938 3938
    // Pattern match: jump to @then on success, @else on failure.
3939 -
    try lowerPatternMatch(self, &subject, &cond.pattern, &mut thenBlock, "then", elseBlock);
3939 +
    try lowerPatternMatch(self, &subject, &cond.pattern, thenBlock, "then", elseBlock);
3940 3940
3941 3941
    // Lower then branch.
3942 3942
    try lowerNode(self, cond.thenBranch);
3943 3943
    try emitMergeIfUnterminated(self, &mut mergeBlock);
3944 3944
    // Pattern bindings are visible only in the success branch. Restore the
3965 3965
/// to ensure block indices are in RPO.
3966 3966
unsafe fn lowerPatternMatch 'arena 'phase 'function (
3967 3967
    self: &mut FnLowerer 'arena 'phase 'function,
3968 3968
    subject: &MatchSubject,
3969 3969
    pat: &ast::PatternMatch,
3970 -
    successBlock: &mut BlockId,
3970 +
    initialSuccess: ?BlockId,
3971 3971
    successLabel: *[u8],
3972 3972
    failBlock: BlockId
3973 3973
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3974 3974
    // If guard present, pattern match jumps to @guard, then guard evaluation
3975 3975
    // jumps to `successBlock` or `failBlock`. Otherwise, jump directly to
3976 3976
    // `successBlock`.
3977 -
    let mut targetBlock: BlockId = undefined;
3977 +
    let mut guardBlock: ?BlockId = nil;
3978 3978
    if pat.guard <> nil {
3979 -
        set targetBlock = try createBlock(self, "guard");
3980 -
        set *successBlock = try createBlock(self, successLabel);
3981 -
    } else {
3982 -
        set targetBlock = *successBlock;
3979 +
        set guardBlock = try createBlock(self, "guard");
3983 3980
    }
3981 +
    let successBlock = initialSuccess else try createBlock(self, successLabel);
3982 +
    let targetBlock = guardBlock else successBlock;
3984 3983
    match pat.kind {
3985 3984
        case ast::PatternKind::Case => {
3986 3985
            let patterns = [pat.pattern];
3987 3986
            // Jump to `targetBlock` if the pattern matches, `failBlock` otherwise.
3988 3987
            try emitPatternMatches(self, subject, &patterns[..], targetBlock, failBlock);
4000 3999
            try bindMatchVariable(self, subject, pat.pattern, pat.mutable);
4001 4000
        }
4002 4001
    }
4003 4002
    // Handle guard: on success jump to `successBlock`, on failure jump to `failBlock`.
4004 4003
    if let g = pat.guard {
4005 -
        try emitCondBranch(self, g, *successBlock, failBlock);
4006 -
        try switchToAndSeal(self, *successBlock);
4004 +
        try emitCondBranch(self, g, successBlock, failBlock);
4005 +
        try switchToAndSeal(self, successBlock);
4007 4006
    } else if *currentBlock(self) <> *targetBlock {
4008 4007
        // Nested tests changed the current block. Create a new success block
4009 4008
        // after the nest blocks to maintain RPO ordering, and jump to it.
4010 -
        set *successBlock = try createBlock(self, successLabel);
4009 +
        let nestedSuccess = try createBlock(self, successLabel);
4011 4010
4012 -
        try emitJmp(self, *successBlock);
4013 -
        try switchToAndSeal(self, *successBlock);
4011 +
        try emitJmp(self, nestedSuccess);
4012 +
        try switchToAndSeal(self, nestedSuccess);
4014 4013
    }
4015 4014
}
4016 4015
4017 4016
/// Lower a `let-else` statement.
4018 4017
unsafe fn lowerLetElse 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, letElse: ast::LetElse) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4019 4018
    let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee);
4020 -
    let mut successBlock: BlockId = undefined;
4019 +
    let mut successBlock: ?BlockId = nil;
4021 4020
    if letElse.pattern.guard == nil {
4022 4021
        set successBlock = try createBlock(self, "success");
4023 4022
    }
4024 4023
    // The else branch executes when the pattern fails to match.
4025 4024
    let elseBlock = try createBlock(self, "else");
4027 4026
    // Evaluate the pattern and jump to @success or @else.
4028 4027
    try lowerPatternMatch(
4029 4028
        self,
4030 4029
        &subject,
4031 4030
        &letElse.pattern,
4032 -
        &mut successBlock,
4031 +
        successBlock,
4033 4032
        "success",
4034 4033
        elseBlock,
4035 4034
    );
4036 4035
    let mut bindingVar: ?Var = nil;
4037 4036
    if let case ast::PatternKind::Binding = letElse.pattern.kind {
4059 4058
unsafe fn lowerWhileLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, w: ast::WhileLet) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4060 4059
    let savedVarsLen = enterVarScope(&self.vars);
4061 4060
    // Create control flow blocks: loop header, body (created lazily when
4062 4061
    // there's a guard), and exit.
4063 4062
    let whileBlock = try createBlock(self, "while");
4064 -
    let mut bodyBlock: BlockId = undefined;
4063 +
    let mut bodyBlock: ?BlockId = nil;
4065 4064
    if w.pattern.guard == nil {
4066 4065
        set bodyBlock = try createBlock(self, "body");
4067 4066
    }
4068 4067
    let endBlock = try createBlock(self, "merge");
4069 4068
4071 4070
    enterLoop(self, endBlock, whileBlock);
4072 4071
    try switchAndJumpTo(self, whileBlock);
4073 4072
    let subject = try lowerMatchSubject(self, w.pattern.scrutinee);
4074 4073
4075 4074
    // Evaluate pattern and jump to loop body or loop end.
4076 -
    try lowerPatternMatch(self, &subject, &w.pattern, &mut bodyBlock, "body", endBlock);
4075 +
    try lowerPatternMatch(self, &subject, &w.pattern, bodyBlock, "body", endBlock);
4077 4076
4078 4077
    // Lower loop body, jump back to loop header, and exit loop context.
4079 4078
    try lowerBlock(self, w.body);
4080 4079
    try emitJmpAndSeal(self, whileBlock);
4081 4080
test/tests/pattern.guard.flow.rad added +68 -0
1 +
//! returns: 0
2 +
3 +
/// Count guard evaluations.
4 +
fn guard(calls: &mut u32, accepted: bool) -> bool {
5 +
    set *calls += 1;
6 +
    return accepted;
7 +
}
8 +
9 +
/// Check guarded conditional binding.
10 +
fn conditional(value: u32, accepted: bool, calls: &mut u32) -> u32 {
11 +
    if let case 1 = value; guard(calls, accepted) {
12 +
        return 10;
13 +
    } else {
14 +
        return 20;
15 +
    }
16 +
}
17 +
18 +
/// Check guarded binding with a diverging fallback.
19 +
fn binding(value: u32, accepted: bool, calls: &mut u32) -> u32 {
20 +
    let case 1 = value if guard(calls, accepted) else { return 20; };
21 +
    return 10;
22 +
}
23 +
24 +
/// Check guard failure and pattern failure at loop exit.
25 +
fn looping(value: u32, limit: u32, calls: &mut u32) -> u32 {
26 +
    let mut steps: u32 = 0;
27 +
    while let case 1 = value; guard(calls, steps < limit) {
28 +
        set steps += 1;
29 +
    }
30 +
    return steps;
31 +
}
32 +
33 +
/// Check preallocated success blocks without guards.
34 +
fn unguarded(value: u32) -> u32 {
35 +
    let case 1 = value else { return 0; };
36 +
    let mut current = value;
37 +
    let mut result: u32 = 0;
38 +
    while let case 1 = current {
39 +
        if let case 1 = current {
40 +
            set result += 1;
41 +
        }
42 +
        set current = 0;
43 +
    }
44 +
    return result;
45 +
}
46 +
47 +
/// Exercise both pattern outcomes and guard outcomes for every binding form.
48 +
@default fn main() -> u32 {
49 +
    for value in 0..3 {
50 +
        assert unguarded(value) == (1 if value == 1 else 0);
51 +
        for flag in 0..2 {
52 +
            let accepted = flag == 1;
53 +
            let expected: u32 = 10 if value == 1 and accepted else 20;
54 +
            let mut calls: u32 = 0;
55 +
            assert conditional(value, accepted, &mut calls) == expected;
56 +
            assert calls == (1 if value == 1 else 0);
57 +
            set calls = 0;
58 +
            assert binding(value, accepted, &mut calls) == expected;
59 +
            assert calls == (1 if value == 1 else 0);
60 +
        }
61 +
        for limit in 0..4 {
62 +
            let mut calls: u32 = 0;
63 +
            assert looping(value, limit, &mut calls) == (limit if value == 1 else 0);
64 +
            assert calls == (limit + 1 if value == 1 else 0);
65 +
        }
66 +
    }
67 +
    return 0;
68 +
}