-
Notifications
You must be signed in to change notification settings - Fork 702
Description
The design is vague about how switches work. The spec interpreter does this:
- A switch is an operation with N>=2 operands
- The first operand is an integer of any type.
- The last operand is an expression that yields the same type as the switch that is used by default if no case is matched.
- The other operands are cases.
- Each case has a literal integer and an expression (the syntax allows 0 or many expressions that yield nop or block expressions).
- Each case has an optional fallthru flag that determines whether it will fall through to the following case (or default case). The syntax sugar for a case without an expression implies fallthru.
- If a case is fallthru, then its expression is void type ([] in the spec interpreter).
- If a case isn't fallthru, then its expression is the same type as the switch.
More relevant context about the spec interpreter:
- All operations are expressions: they may yield a value, and so have an associated type
- Break/return/loop operations are considered to yield a value of the bottom type, representing that they never yield a value to their enclosing expression, and may occur as a subexpression where any type is expected.
- Labels are expressions, so the break operation needs an operand of the appropriate type for the label to yield.
This issue is not about whether there should be a C-like concept of statements, which seems to be an open issue. If the spec ends up making switch a statement, this is irrelevant. But if not:
As it's defined in the spec interpreter, there isn't a direct translation to and from C-style switches: the default case must be the equivalent of the final case, and so cannot fall through to other cases. I believe the rationale for this design is to ensure a switch always yields a value of the type expected by its context.
Another way to achieve the same goal would be:
- Give switch an implicit label.
- Add a way to declare a default case, and get rid of the final default expression.
- Make all but the last case the equivalent of "fallthru". That means the last case expression would need to be same type as the switch, but the other case expressions would be any typed.
Instead of each non-fallthru case being an exit from the switch, the exits from this switch would be explicit breaks and the final case. This makes switch equivalent in flexibility to C-style switches, but allows it to still be used as an expression.
For example:
...
(param %key i64)
(local %a f64)
(local %b i32)
(setlocal %a (switch.i64 #switch %key
(case 0)
(case 1 (break #switch (constant.f64 100.0)))
(default (setlocal %b 1))
(case 2 (break #switch (constant.f64 200.0)))
(case 3 (constant.f64 300.0))
))
This would map 0=>100.0, 1=>100.0, 3=>300.0, and any other value =>200.0. Any value other than 0|1|2|3 would also set %b to 1. To implement this with the current spec interpreter's switch, you'd need to duplicate case 2's expression into the default expression.