Wintermute 0.3.5 — plain-value type switch
Generalize the type switch from the otp.Receive() operand (0.3.4) to any value
operand: switch V := X.(type) now lowers to an Erlang case X of {tag, Field…} -> body; … end, the value-branching counterpart to the receive dispatch.
- Struct-typed cases only, reusing the shared clause builder (tagged tuple
{tag, Field…}, per-clause bound-field scope). A newemitTypeSwitchentry
point dispatches on the operand:otp.Receive()→receive, any other value →
case X of. - Default required for the value form: a value matching no case falls through
in Go (ordinary control flow), which a total Erlangcasecannot express, so a
default-less value switch is rejected rather than silently mis-transpiled (the
default becomes the trailing_ ->). The receive form keeps default optional (a
selective receive blocks on a non-match — it never falls through). - Operand and alias are uppercase Erlang variables (
V := M.(type),M any); a
lowercase operand is rejected, never silently emitted as an atom. V.Fieldaccess resolves to the bound field variable; a bare aliasVis
rejected (whole-value passing deferred).terminates()distinguishes the two forms: a value type switch terminates only
with a default (a valuecasefalls through in Go without one), so a
default-carrying one may be a bare-ifthen-branch; a receive terminates without
a default (it blocks, never falling through).- Runnable rung:
testdata/typeswitch/classify.gotranspiles, compiles with
erlc, and RUNS —{ping, 1}and{pong, 2}each fire their clause.
Core is a behaviour-preserving refactor: the 0.3.4 receive clause loop was
extracted into emitTypeSwitchClauses, now shared by both the receive and the
value wrapper.
Deferred to 0.3.6+ (all error loudly): non-struct cases (→ guards), multi-type
cases, whole-alias V, tagless switch, init statement.