# 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`.
