Rule 7.9
Chapter 7, Expressions and statements Test
An expression must be evaluated left to right, as TypeScript and WGSL both evaluate it, and a call that writes (its object, a module variable, a storage binding, an atomic location) inside a larger expression must take effect in that order on every target.
The compiler binds each such call to a temporary ahead of its statement, in source order, and binds ahead of the call an operand evaluated before it that reads what it writes; a call TypeScript evaluates conditionally, in an arm of ?: or the right operand of && or ||, keeps its condition as an if.
A loop condition runs on every iteration, so it may hold such a call only as one side of its comparison.
The rule text and the parts under it are the compiler's own English, as the design document writes them.
Rationale
GLSL ES 3.00 leaves the order of an operator’s operands open, and a scalar ?: lowers to WGSL’s select, which evaluates both arms, so the one order every target keeps is the order the compiler writes out; the passes that fold and repeat expressions then see no call that writes inside one.
Derives from
- Program Order Within an Invocation (“The order of evaluation for operands of an expression is left-to-right in WGSL. For example, foo() + bar() must evaluate foo() before bar().”);
- Function Calls (“Function call argument values are evaluated. The relative order of evaluation is left-to-right.”);
- GLSL ES 3.00 §5.11, which relaxes C++‘s rules and fixes no order for an operator’s operands, §6.1.1 (“All arguments are evaluated at call time, exactly once, in order, from left to right”), and §5.8 (“Expressions on the left of an assignment are evaluated before expressions on the right of the assignment”);
- surface §19 and §26.
How it is verified
Checked by a test. A test, a gate script or a CI workflow names this rule, and the traceability check fails when a file listed below stops naming it.
Where the rule says the compiler enforces it:
src/compiler/ts/sequence.ts (sequenceEffects), pinned by src/compiler/ts/sequence.test.ts, which holds the CPU oracle, the codegen and the debugger to the source order; TS8006 LOOP_BOUND for a loop condition that holds a call that writes anywhere but as one side of its comparison (A while condition runs "rng.next()" on every iteration, and a call that writes can stand there only as one side of the comparison. …); examples/rng-method.shade.ts in the compile gate.
The files that verify it at commit 26de7be8, each at the first line that names the rule:
Explained in
The sections of the surface document that explain this rule, at commit 26de7be8:
Error codes that enforce it
The diagnostic codes the rule names under Enforced by, or whose registry text names the rule:
-
TS8006LOOP_BOUND - A counted
forloop whose exit the compiler cannot prove: the counter is not compared to a bound, or the body writes the bound.
See also
- Rule 7.8 Test
- Rule 8.1 Test
- Design rules
- Error codes
Source
The rule at commit 26de7be8:
docs/language-design.md:591(the design document)reqs/rules/RULE-0709.md(its traceability item)