# OpenQASM 3.1: what the reader does with each construct The reader (`wai_quantum::qasm`) is the one grammar every surface takes: the job service, the command line, both MCP servers, the Studio, the desktop app, the edge component and the in-browser IDE. This page lists every construct of OpenQASM 3.1, and the OpenQASM 2 forms in common use, with what the reader does with it, what the bit-exact path does with it, and the diagnostic code each gives (`qasm-diagnostics.md` has the table of codes). What the reader does (the **Status** column): - **supported**: read as the language defines it. - **lowered**: read and lowered exactly to the engine's gates and steps. - **carried**: kept in the program as written (timing, outputs); it changes nothing a method computes, and the result says so where it matters. - **preserved**: kept verbatim for export, never executed. - **refused**: the program is not read: the refusal names the line, says what to write instead, and ends with its code. A program read is one program for every method. Its classical values are worked out on reading in the reader's own arithmetic, the same bits on every platform: integers, bits and sized angles exactly, floats as IEEE doubles rounded to their declared width, the built-in functions by the crate's own functions. Its gates' angles are doubles. A method then runs it at the accuracy it states (the methods resource gives each one's class): `exact` bit-exact, `statevector` in double precision, and so on. What the bit-exact path does (the **Bit-exact** column) is the method `exact`, `qasm::exact_circuit`, `qasm::dyadic_circuit` (the in-browser IDE) and `quantum_toolchain::from_qasm`. It takes the program the reader gives, under its own restriction, refusing with a code what it cannot hold exactly and never rounding it: - **same**: it runs what the reader read, to the bit. - **dyadic**: it runs it when every angle is a dyadic multiple of 2π (`2πm/2^k`, `k ≤ 32`: `pi/4`, `3*pi/8`, an `angle[n]` with `n ≤ 32`), and refuses any other angle (Q500); the double-precision methods take it. - **Q502**: a program that measures a qubit and then acts on it, resets one after its first gate, or acts on measured bits; `statevector` and `stabilizer` run it. - It holds at most 255 qubits (Q501). The conformance corpus (`wai/quantum-conformance/qasm3/`) holds the specification's example programs, a program for each construct below, and programs written as the common toolkits export them, each with what the reader must do with it (outcome probabilities among them); CI runs it natively, under wasm32-wasip2 and in the browser build. ## One reader There is one front end. Text becomes tokens (`qasm::lex`), the tokens statements (`qasm::parse`), and the statements one program (`qasm::elab`): its gates, which bit each measurement writes, its steps where it branches, and what it carries (outputs, timing, calibrations). Every entry point runs those three stages: `read` and `read_with` (every surface), `read_whole` (a gate under many controls held as one op), `read_callouts` (the hybrid profile's `extern` calls as steps), `tangents` (each gate's derivative by the program's parameters), and `parse`, `parse_with` and `parse_lines`, which give the same reading with a string for its refusal. Each method lowers that one program: `Program::matrices` and `sv_ops` for the methods in double precision, `local_matrices` for `mps`, `rotations` for `pauli`, `clifford` and `clifford_t` for `stabilizer` and `stabilizer_rank`, and `dyadic` and `dyadic_native` for the bit-exact method. The bit-exact path is a lowering of the reader's program, not a second reader. A job that names no method is planned: `auto` tries `exact` among the first for counts and expectation values, and a program it refuses (an angle not dyadic, Q500; a measurement in the middle, Q502) goes to the next method, the plan saying why and the result naming the method and its accuracy class. A job that names `exact` for such a program is refused with that code, never run another way. ## Program structure | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `OPENQASM 3.1;`, `OPENQASM 3;`, `OPENQASM 2.0;`, or none | supported; another version is read as OpenQASM 3 | Q010 | same | | `include "stdgates.inc"`, `include "qelib1.inc"` | supported: the libraries are built in | Q012 | same | | `include` of any other file | warned: its definitions are not read | Q011 | same | | Comments `//` and `/* */` | supported; an unclosed block comment is refused | Q003 | same | | Identifiers, Unicode letters included (`θ`, `π`) | supported | | same | | Statements and blocks `{ }`, scopes | supported; qubits, gates, subroutines and externs are declared at the top level | Q105 | same | | Nesting: statements in blocks and bodies, parentheses, operators, brackets, calls, casts and types | supported to 64 levels together in the text (a statement, each enclosing statement, and each level of its expressions' trees count one). A run of operators of one precedence (`a + b - c`, `c[0] && c[1] && …`) is one level, and an `else if` chain one level, at any length: both are read and run in loops, left to right. Deeper is refused, never read by a recursion that could run out of stack; an array has at most 7 dimensions | Q006 | same | | Nesting in all, through the gates and subroutines a program calls | supported to 256 levels (each statement, expression and call the reader runs counts one), and calls 32 deep; deeper is refused. Both hold: a recursive subroutine whose body nests more than about seven levels reaches 256 before 32 calls | Q006, Q209 | same | | What a program holds on reading: its classical values, gates, steps, measurements and timing | supported to 64 MiB for one value (an array or a bit register, each element counted at its width) and 512 MiB in all; more is refused before it is made. Making or copying a value counts toward the reader's 20 000 000 steps at one step for every 128 bytes, and an element read from an array copies the element alone | Q130, Q312 | same | | `pragma …`, `#pragma …`, annotations `@keyword …` | warned: kept, not applied; `@wai.max_iterations N` bounds the `while` after it | Q013 | same | ## Literals | Construct | Status | Codes | Bit-exact | |---|---|---|---| | Integers: decimal, `0x`, `0o`, `0b`, with `_` | supported | Q002 | same | | Floats, exponents, `.5` | supported | Q002 | dyadic | | Imaginary `2.5im` | supported | | same | | `true`, `false` | supported | | same | | Bit strings `"0101"` (the first character the highest bit) | supported | Q110 | same | | Durations `ns`, `us`, `µs`, `ms`, `s`, `dt` | supported | Q406 | same | | `pi` `π`, `tau` `τ`, `euler` `ℯ` | supported; not redeclarable | Q106 | dyadic | ## Declarations and types | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `qubit q;`, `qubit[n] q;`, `qreg q[n];` | supported (at most 65 536 qubits) | Q101, Q105, Q111 | same, at most 255 qubits (Q501) | | Physical qubits `$0`, `$1` | supported, in a program that declares none | Q124 | same | | `bit c;`, `bit[n] c;`, `creg c[n];` | supported: written by measurement; a value known on reading (`bit[2] f = "11";`) is held until a measurement writes the bit, and a bit that still holds 1 at the end with no measurement writing it is refused, since the results carry what measurement writes | Q319 | same | | A bit declared inside a block or a subroutine | lowered: a bit of its own, which the steps read and the results do not show | Q322 | Q502 where the steps read it | | `int`, `int[n]`, `uint`, `uint[n]` (n ≤ 64) | supported; range-checked: arithmetic that leaves the type's range is refused, never wrapped; `~` and `<<` on a `uint[n]` act on its n bits, a bit shifted past the highest gone | Q112, Q122 | same | | `float`, `float[16]`, `float[32]`, `float[64]` | supported, rounded to the width as defined; `float[128]` refused | Q121, Q122 | dyadic | | `angle`, `angle[n]` (n ≤ 64) | supported: `angle[n]` holds n bits of a turn; a float cast to it rounds to the nearest, a tie to the even one; `+`, `-`, `*` and `/` by an integer wrap as the language defines; an `angle[n]` divided by an `angle[n]` is the `uint[n]` quotient of their bits | Q121 | same for `n ≤ 32`; a finer angle dyadic | | `complex`, `complex[float[n]]` | supported; a complex gate parameter is refused | Q110 | same | | `bool` | supported | | same | | `duration` | supported: SI units and `dt`, not mixed | Q406 | same | | `stretch` | carried by name, or as the expression that holds it, for a scheduler | Q403 | same | | `array[T, d1, d2, …]`, nested values, `sizeof` | supported | Q107, Q110 | same | | `const` | supported: known on reading; not assignable | Q127 | same | | `input int n;`, `input uint n;` | supported: given when the program is read (a generator) | Q114 | same | | `input float theta;`, `input angle phi;` | a parameter: a job's `params` binds it as a `const` at exactly the double given before the program is read (left without a value, the job is refused with Q114 and J050); the reader alone gives it no value | Q115 | dyadic, once bound | | a parameter followed into a gradient | supported through arithmetic, the built-in functions, loops and gate definitions into gates' arguments; refused under a modifier, in a classical variable, a condition, an index, a size, a loop's range, a subroutine, or a type that rounds it | Q129 | not used | | `output` | carried into the result: a bit register's places in each outcome, or a value | Q600, Q601 | same | | `let a = q[0:2] ++ r;` | supported; a `let` that reuses an enclosing scope's name is refused | Q101 | same | | `extern f(…) -> T;` | supported as a declaration; a call is refused (the hybrid profile runs it, extensions/quantum-wasmq.md) | Q125 | same | ## Indexing | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `q[i]`, negative `q[-1]` | supported | Q107 | same | | Slices `q[a:b]`, `q[a:s:b]`, `q[a:]`, `q[:]`, both ends included | supported | Q107, Q313 | same | | Index sets `q[{0, 2, 5}]` | supported | Q107 | same | | Multi-dimensional `m[i, j]`, `m[i][j]`, array slices and slice assignment | supported | Q107, Q110 | same | | An integer's bits `k[i]` | supported | | same | ## Expressions | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `+ - * / % **`, unary `-`, with the specification's precedence (`-2**2` is −4, `**` right to left) | supported; integer division truncates toward zero; integer overflow refused | Q112, Q113 | same | | Integer division with a remainder used as a real (`1/2` in a gate) | refused: OpenQASM 3 truncates it, OpenQASM 2 does not; in a program that says `OPENQASM 2` it is 0.5 | Q118 | same | | `& \| ~ << >>` | supported on integers and bits, on a `uint[n]` its n bits (`~` of a `uint[4]` 3 is 12); on angles refused | Q110 | same | | `^` | exclusive or in an OpenQASM 3 program; a power in OpenQASM 2; between integers with no version stated it is refused, between reals read as a power with a warning | Q119, Q120 | same | | `== != < <= > >=`, `&& \|\| !` | supported | | same | | Casts `int[8](x)`, `float(x)`, `angle[n](b)`, `bit[n](i)`, `bool(x)` | supported (a cast to `int` truncates; bits to a signed integer of their width read as two's complement) | Q123 | same | | `sin cos tan arcsin arccos arctan exp log ln sqrt ceiling floor mod pow popcount rotl rotr real imag sizeof` | supported, computed with the crate's own functions, the same bits on every platform | Q104 | dyadic where they give an angle | | `durationof({ … })` | refused: it needs gate durations the program does not give | Q405 | same | | Arithmetic on measured bits at run time (`int k = c;`) | refused: the steps branch on bits and hold no other run-time value | Q306 | same | | A value that depends on a measurement where one is needed on reading | refused | Q116 | same | | `++` between arrays outside a `let` | refused | Q004 | same | ## Gates | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `U(θ, φ, λ)`, `gphase(θ)` | supported as the specification defines them: `U` is `e^{iθ/2}` times OpenQASM 2's matrix, a phase a control sees (`ctrl @ U(θ, φ, λ)` is `cu(θ, φ, λ, θ/2)`); a global phase is dropped, and under controls is a phase on the controls. In a program that says `OPENQASM 2`, `U` is OpenQASM 2's (`u3`) | Q206 | dyadic | | The standard library: `p x y z h s sdg t tdg sx rx ry rz cx cy cz cp crx cry crz ch swap ccx cswap cu CX phase cphase id u1 u2 u3`, and `sxdg iswap rxx ryy rzz ccz cnot toffoli fredkin u cu1 cu3` | supported, each checked against its definition in `U`, `gphase` and `CX`, global phase included (`cu(θ, φ, λ, γ)` is controlled `e^{iγ}` times OpenQASM 2's `U`; `u3` and `u2` are OpenQASM 2's `U`, `e^{−i(θ+φ+λ)/2} U(θ, φ, λ)`; `CX` is `cx`, as the specification names it) | Q012 | dyadic | | `gate name(params) qubits { … }` | lowered: inlined at each call | Q200, Q212 | dyadic | | A `gate` under a name the reader has built in (`gate h a { … }`, `gate iswap a, b { … }`) | lowered: the program's own definition is the gate it runs, noted, unless it is the built-in gate exactly, global phase included (up to a global phase in a program that says `OPENQASM 2`), when the built-in gate stands in its place; `U` and `gphase` are the language's and cannot be defined | Q213, Q106 | dyadic | | `opaque` gates | refused at a call: no matrix | Q126 | same | | A call on whole registers (broadcast) | supported | Q108 | same | | A gate's duration `x[20ns] q;` | carried in the timing | Q407 | same | | Wrong parameters or qubits, a qubit named twice, an unknown gate | refused | Q103, Q104, Q109 | same | | A gate body that indexes its qubit argument (`a[0]`) | read: the argument is one qubit, and `a[0]` is that qubit (the specification forbids the form) | | same | ## Modifiers | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `ctrl @`, `ctrl(n) @`, `negctrl @`, `negctrl(n) @` on any gate, user gates included | lowered exactly, the global phases its gates carry made phases on the controls (a negative control is a control between two `x`); at most 16 added controls in standard gates. A job on `statevector` holds a gate whose standard gates would double as one operation, under as many as 255, and lowers it only where a method needs standard gates (`qasm::read_whole`); `exact` does so only when asked (`"options": {"controls": "whole"}`), since its bits are those of the gates it applies. Those gates are `x`, `y`, `z` and `h` under three or more controls and `s`, `sdg`, `t`, `tdg`, `p`, `rx`, `ry`, `rz`, `u`, `sx` and `sxdg` under two or more, each lowered to the method's gates under its controls (`sx` as `h s h`, `ry` as `sdg h rz h s`), and not `p(pi)`, which is `z`. A job with an `intent` is held to 16: an intent is checked in standard gates, while a verdict with none reads the program as it ran | Q201, Q207, Q208 | dyadic; a controlled gate held whole under all its controls | | `inv @` on any gate | lowered exactly: each gate's inverse, in reverse order, `gphase(a)` as `gphase(−a)` | Q202 | dyadic | | `pow(k) @`, `k` a whole number | lowered exactly: angles scaled or the gate repeated; negative `k` repeats the inverse | Q203 | dyadic | | `pow(r) @`, `r` fractional | lowered where an exact principal root exists (`pow(1/2) @ x` is `sx`; rotations by less than 2π; phases; controlled forms; one such gate written in `U` with global phases that cancel, as the libraries write `x` and `rx`); refused otherwise (`u`, `cu`, user gates of several gates) | Q204, Q205 | dyadic | | A modifier on a subroutine, or nested as deep as 32 | refused | Q211, Q209 | same | | A chain of modifiers (`inv @ inv @ …`) | read at any length: a list, not a nesting | | same | ## Measurement and reset | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `c = measure q;`, `c[0] = measure q[0];`, `bit c = measure q;` | supported | Q321 | same, at the end | | `measure q -> c;` | supported | Q321 | same, at the end | | `measure q;` | supported where no bit register is declared, and refused in a program that calls externs | Q320, Q324 | same, at the end | | Mid-circuit measurement, measurement again, a gate after it | lowered: the program runs as steps (counts by shot splitting, exact expectation values) | | Q502 | | `reset q;` | supported: before a qubit's first gate it changes nothing; after it, the program runs as steps | | same before the first gate; Q502 after | | `barrier`, `barrier q;` | carried in the timing | Q404 | same | ## Classical control | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `if`/`else` known on reading | decided on reading | Q300 | same | | `if`/`else` on measured bits: `c[0]`, `c == 3`, `c >= 2`, `int[2](c) != 0`, with `&&`, `\|\|`, `!` | lowered: steps that branch forward | Q301, Q302, Q304, Q305, Q307 | Q502 | | A bit register of several bits as a condition (`if (c)`) | refused: compare it with a number | Q303 | same | | `for` over a range (both ends included), a set or an array | lowered: unrolled | Q308, Q313, Q312 | same | | `while` known on reading | lowered: unrolled until it fails (at most 1 000 000 times) | Q309, Q312 | same | | `while` on measured bits | lowered under `@wai.max_iterations N`, with a warning that runs still meeting the condition stop there; refused with no bound | Q310, Q311 | Q502 | | `switch` / `case` / `default` | decided on reading, or a chain of conditions on measured bits | Q317 | same, or Q502 on measured bits | | `break`, `continue` | supported; under a condition on measured bits, jumps forward | Q316, Q314 | same, or Q502 on measured bits | | `end` | supported | Q316 | same | ## Subroutines | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `def f(T x, qubit q, qubit[n] r) -> T { … }` | lowered: inlined, classical arguments by value, qubits by reference | Q318, Q104 | same | | `return v;`, `return measure q;` | supported; a returned measurement writes the caller's bit | Q315 | same, or Q502 | | `readonly` / `mutable` array arguments, `#dim` | supported as references: a `mutable` one writes back to the array it was given, a slice or row of one included; a `readonly` one is not written | Q131, Q127 | same | | Bits declared in a subroutine | lowered: bits of its own, fed forward | Q322 | Q502 where the steps read them | | A subroutine called as a gate (`f(1) q, r;`) | supported | | same | | Recursion | refused past 32 calls deep | Q209 | same | ## Timing | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `delay[d] q;` | carried | Q401 | same | | `box`, `box [d]` | carried, around the gates it holds | Q402 | same | | `stretch` | carried | Q403 | same | | Durations in a result | no method applies them; a result of a program that states durations says so (`timing.applied: false`) | Q400 | same | ## Calibration (OpenPulse) | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `defcalgrammar "openpulse";` | preserved; in a pulse run, read (another grammar is refused) | Q452, Q454 | same | | `cal { … }` | preserved, never run by the circuit methods; in a pulse run, read as OpenPulse and run in order | Q451, Q453 | same | | `defcal …` | preserved, never run by the circuit methods; the gate's matrix comes from its definition or the library. In a pulse run, read as OpenPulse and run at each call on its physical qubits | Q450, Q126, Q453, Q459 | same | | Ports, frames, waveforms (`constant`, `gaussian`, `sech`, `gaussian_square`, `drag`, `sine`, explicit samples) | in a pulse run, lowered to the schedule | Q453, Q455 to Q458 | not used | | `play`, `delay`, `shift_phase`, `set_phase`, `shift_frequency`, `set_frequency`, `capture_v0` to `capture_v4`, `barrier` | in a pulse run, lowered to the schedule on frames | Q453, Q457, Q458, Q461 | not used | | Plays on a measurement port | in a pulse run, scheduled and timed; the readout is not simulated | Q462 | not used | A pulse run is the job method `pulse`; [the pulse page](pulse.md) gives the model, the bound and the options. ## OpenQASM 2 | Construct | Status | Codes | Bit-exact | |---|---|---|---| | `U(θ, φ, λ)` and `CX` with no include | supported: OpenQASM 2's `U`, and `cx` | | dyadic | | `qreg`, `creg`, `measure q -> c;`, `if (c == n) …;` on a whole register | supported | Q321 | same, or Q502 | | qelib1.inc's gates | supported as built in: those the table of standard gates names, and `u0` (an idle), `csx`, `c3x`, `c3sqrtx` and `c4x` (`sx` and `x` under that many controls) and `rccx` and `rc3x` (the relative-phase Toffoli gates, by qelib1.inc's definitions, each `u2(0,π)` there a Hadamard); a program's own definition of one is the one that runs | Q012 | dyadic | | `opaque` | supported as a declaration; a call is refused | Q126 | same | | `1/2` as a real, `^` as a power | supported | | dyadic | ## What was read wrong, and is no longer A program read to something other than what the specification says, with no word of it, is the worst thing a reader can do: a method would run it and seal the wrong answer in a receipt. These were found on 2026-11-10 by holding the reader to the specification's text construct by construct, and each was fixed with a test that failed first (`qasm::spec_tests`, which names each). | Construct | Read before | Read now | |---|---|---| | `ctrl @ U(θ, φ, λ)` (and `negctrl @`, and under `inv @` or `pow @`) | controlled OpenQASM 2 `U`, missing the `e^{iθ/2}` OpenQASM 3's `U` carries: a phase `θ/2` short on the control | `cu(θ, φ, λ, θ/2)`, as the specification defines `U` | | `ctrl @ u3(θ, φ, λ)`, `ctrl @ u2(φ, λ)` | controlled OpenQASM 2 `U`, with no phase | controlled `e^{−i(θ+φ+λ)/2} U(θ, φ, λ)`, as standard_library.rst defines them | | A program's own gate written in `U` and `gphase`, under a control (stdgates.inc's text for `x`, `h`, `rx`, `ry`, copied into a program) | wrong by the phase of each `U`: stdgates.inc's own `x` written out, under `ctrl @`, was `cx` followed by `sdg` on the control (overlap 0.71) | the library's gate, phase included | | A `gate` under a name the reader has built in (`gate h a { x a; }` in a program with no include; `gate iswap`, `gate sxdg` in an exported program) | the definition dropped and the built-in gate run instead | the program's own gate, noted with Q213, unless it is the built-in gate exactly | | A `mutable` array argument given a slice or a row (`f(aa[1:3])`, the specification's own example) | the subroutine's writes dropped | written back to the slice or row | | A write to a `readonly` array argument | read, the write kept inside the subroutine | refused (Q131) | | `angle[n] / angle[n]` | the ratio of the angles as a float (`(5π/4) / (3π/8)` was 3.33…) | the `uint[n]` quotient of their bits (3), as classical.rst defines it | | A float cast to `angle[n]` exactly halfway between two angles | rounded away from zero | rounded to the even one, as types.rst defines it | | `~` and `<<` on a `uint[n]` (`uint[4] u = 3; ~u == 12`, `uint[8] b = 143; (b << 1) == 30`) | worked out at any width: `~u` was −4 and `b << 1` 286, so both conditions were false | its n bits, as classical.rst defines the bitwise operators on `uint` | The conformance case `lang/ctrl_user.qasm` held `ctrl @ U(0.1, 0.2, 0.3)` to `cu(0.1, 0.2, 0.3, 0)`, the first of these; its reference now reads `cu(0.1, 0.2, 0.3, 0.05)`. ## What is refused, and why Every construct the specification defines is read, or refused with a code that says why; none is dropped. What is refused is refused because running it would need something the program does not give, or would mean holding a value the engine's steps do not hold: - **Values known only at run time** (Q306, Q116): the steps branch on measured bits and hold no other run-time value, so arithmetic on measured values, and a measured value used as a gate's angle, an index or a size, are refused. Conditions on measured bits, `switch` on them and bounded `while` loops on them run. - **A `while` on measured bits with no bound** (Q310): it would not be known to end; `@wai.max_iterations N` bounds it, with a warning (Q311). - **`durationof`** (Q405): it needs gate durations, which a program does not give a simulator. - **A call of an `extern`** (Q125): its body is outside the program; the hybrid profile (extensions/quantum-wasmq.md) runs such calls. - **`opaque` gates and gates with only a calibration** (Q126): their matrix is not known. - **Integer arithmetic that overflows** (Q112): refused rather than wrapped, so an overflow is seen. - **A bit that holds 1 at the end with no measurement writing it** (Q319): the results carry what measurement writes. - **A fractional power with no exact gate here** (Q205), **`1/2` where a real is wanted in a program that does not say its version** (Q118), and **`^` between integers with no version stated** (Q119): the reader does not guess between two readings. - **Widths past what the reader holds** (Q122): `float[128]`, integers and angles wider than 64 bits. - **Bitwise operations on angles, `++` between arrays outside a `let`, and a bit register of several bits as a condition** (Q110, Q004, Q303).