# Dynamic Semantics - Branch Selection Decision Status: Accepted (Implemented) Cycle: Initial branch-selection closure pass ## 1. Context PBS v1 needs a closed runtime contract for `if` and `switch` before the dynamic semantics spec can be completed. The remaining questions were: - whether conditions and selectors are evaluated once, - whether non-selected branches evaluate at all, - which selector categories are admitted for `switch`, - and whether branch selection itself may trap. ## 2. Decision PBS v1 adopts the following branch-selection rules: 1. `if` evaluates its condition exactly once. 2. `switch` evaluates its selector exactly once. 3. Only the selected branch or arm executes. 4. Non-selected branches and arms perform no evaluation. 5. `if` and `switch` are trap-free by themselves; only selected subexpressions may trap. 6. `switch` is limited to statically discriminable selector categories. 7. `switch` does not accept `result`, `error`, structs, string objects, or heap-backed types as selectors in v1. ## 3. `if` For: ```text if cond { then_block } else { else_block } ``` the runtime behaves as follows: 1. evaluate `cond` exactly once, 2. if it yields `true`, execute only `then_block`, 3. if it yields `false`, execute only `else_block`. The non-selected branch is not evaluated, even partially. Any trap associated with `if` arises only from: - evaluating `cond`, - or executing the selected branch. ## 4. `switch` ### 4.1 Selector evaluation For: ```text switch selector { ... } ``` the runtime: 1. evaluates `selector` exactly once, 2. determines the matching arm using the canonical matching rule for the selector category, 3. executes exactly one selected arm. No non-selected arm is evaluated. ### 4.2 Admitted selector categories In v1, `switch` is restricted to selector categories with static, deterministic matching behavior. These include: - literal-comparable scalar values, - enum values, - `str` values with canonical static identity, - and other compile-time-constant-compatible selector categories only if explicitly admitted elsewhere. `switch` does not accept: - `result` values, - `error` values, - structs, - string object/reference types distinct from canonical `str`, - or heap-backed selector categories. Dynamic or structural matching belongs to a future `match`-style construct rather than to `switch`. ### 4.3 Matching rule `switch` arm selection is exact and deterministic for the admitted selector type. - Enum selectors match by canonical enum-case identity. - Scalar selectors match by the exact equality rule already defined for that scalar category. - `str` selectors match by canonical static string identity rather than by heap-object comparison. `switch` does not perform structural matching, guard evaluation, or user-defined equality dispatch. ## 5. Invariants - `if` and `switch` never evaluate more than one branch or arm. - Branch selection is deterministic for the same selector value. - `switch` remains a static discriminant-selection construct, not a result/error-processing construct. - Error processing with logic belongs to `handle`, not to `switch`. ## 6. Explicit Non-Decisions This decision record does not yet close: - the final future design of `match`, - whether additional scalar-like selector categories will be admitted later, - the complete memory/cost wording associated with selector evaluation. ## 7. Spec Impact This decision should feed at least: - `docs/pbs/specs/9. Dynamic Semantics Specification.md` - `docs/pbs/specs/4. Static Semantics Specification.md` - `docs/pbs/specs/3. Core Syntax Specification.md` ## 8. Validation Notes The intended split is: - `if` and `switch` perform static, deterministic branch selection, - `handle` processes modeled `result` errors, - future dynamic or structural branching belongs to `match`, not to `switch`.