-
Notifications
You must be signed in to change notification settings - Fork 288
Description
Today, when we generate match current_block {} expressions, we pick one of the arms of the match to be the _ case in order satisfy match's exhaustiveness requirement. This makes reasoning about current_block control flow difficult because you can't search for the value of a current_block constant to see where it's used.
Instead, we should have an arm that explicitly references the current_block value, and then a _ => unreachable!() arm to satisfy exhaustiveness.
Details
Today, when we emit current_block, we generate code that looks a bit like this:
let mut current_block;
if cond {
current_block = 123456789;
} else {
current_block = 987654321;
}
match current_block {
123456789 => { ... }
_ => { ... }
}If I want to figure out what code each current_block value leads to (in practice setting current_block may happen very far away from where we match on it), I can search the code for 123456789 and see both where it's set and where it's used. But the other label 987654321 doesn't show up in the match because it's instead covered by the _ case.
We do this to satisfy exhaustiveness: current_block is just an integer, so if we did
match current_block {
123456789 => { ... }
987654321 => { ... }
}The compiler would complain that our match isn't exhaustive. Since we control all the possible values that current_block will have, it's valid for us to arbitrarily pick one of these arms and make it the _ case. However, as noted above, this harms readability and makes it harder to reason about how current_block is being used.
Instead we should do this
match current_block {
123456789 => { ... }
987654321 => { ... }
_ => unreachable!(),
}This would ensure that you can always see all the places where a current_block value is used, which would make reasoning about current_block control flow easier.