Skip to content

Limitations

Limitations

This page is generated from a test, not written by hand.

Every row below is measured by running the construct through the real pipeline – parse, lower, LLVM build – and recording whether it was accepted. The table lives in compiler/tests/capability_matrix_test.rs, and three tests keep this page honest:

  • the_capability_matrix_matches_reality fails if a row’s recorded outcome differs from what the compiler does today.
  • every_refused_construct_names_a_reason fails if a refusal stops explaining itself.
  • the_published_limitations_document_matches_the_matrix fails if this page drifts from the measured table.

So a backend changing behaviour without updating this page breaks CI.

Why this page exists

Seven defects shipped in which a backend produced output that claimed more than it delivered, and every one exited 0:

  1. QIR emitted a reversible block with the adjoint discarded.
  2. WGSL linearity checking did not recurse into blocks.
  3. WGSL wrote // Unsupported expr into a shader and reported success.
  4. return inside a branch was hoisted, so guard(5) returned -1 instead of 7.
  5. The Cranelift backend returned a hardcoded 42 for every program.
  6. WGSL emitted a callee after its caller; the resulting module did not parse.
  7. A statement-position reversible { ... } dropped the block and generated no inverse.

Documentation cannot prevent that class of bug – it drifts. What prevents it is a test that compares belief against behaviour, which is what the capability matrix is.

The rule

A construct is either correct or refused. There is no third state.

“Emits something close” is not an acceptable outcome for a backend that cannot lower the construct, and neither is a placeholder value, a shader comment, or an exit status of 0.

LLVM backend

ConstructStatusNote

| integer arithmetic | emits | | | float arithmetic | emits | | | if as an expression | emits | | | if without else | emits | | | while | emits | | | counted for | emits | lowers onto the while CFG | | break | emits | | | continue | emits | | | return inside if | emits | lowered in place, not hoisted | | function calls | emits | one LLVM symbol per Naso function | | numeric casts | emits | width-preserving, signedness-tracked | | scalar parameters | emits | | | tensor parameters | emits | caller-supplied pointer plus a length | | 2-D tensors | emits | row-major | | quantum parameters | emits | Qubit and QRegister both map to the pointer ABI | | symbolic loop bounds | emits | affine schedule bands | | reversible { ... } | refused | no inverse is generated; used to drop the block and report success | | nested tensors | refused | one ptr per tensor has a single stride | | tensor with a zero extent | refused | no elements to index | | quantity used as a type | refused | Many is a quantity; write [1] Qubit | | widthless int parameter | refused | no exact ABI slot without a width | | break in an affine forall band | refused | an affine band has no data-dependent runtime exit | | expression-position reversible | refused | | | adding two booleans | emits | KNOWN GAP, pre-existing and verified at HEAD: bool lowers to i1 and unify_types does not separate arithmetic on bool from arithmetic on integers, so this compiles rather than being refused |

Other targets

These are not covered by the matrix above, which probes the LLVM pipeline. They are honest about their current state:

TargetState
LLVMThe working backend. Executes natively; verified by running generated IR.
QIRStructural emission only. A validated circuit text, not QPU execution.
WGSLGenerates shaders, validated with naga. No GPU execution has been performed: there is no /dev/dri, no Vulkan ICD, and no browser WebGPU in the build environment.
CraneliftNot implemented. naso build --target cranelift refuses with an explicit diagnostic rather than returning a result.

QIR and WGSL backends

These refuse a different set of constructs than LLVM, and the refusals are correct for them: QIR emits one void quantum operation and has no scalar arithmetic at all, so refusing a + b is not a worse LLVM. Recording these in the same table as the LLVM rows would imply otherwise, so they are separate.

ConstructQIRWGSL straight-lineWGSL computeNote
empty functionemitsemitsrefuseda compute kernel with no tensor parameter has nothing to bind
scalar float multiplyrefusedemitsrefusedQIR has no scalar arithmetic; it emits one void quantum operation
scalar integer addrefusedemitsrefused
function callrefusedemitsrefused
tensor kernel entry pointemitsrefusedemitsthe only shape the compute path accepts is a tensor-parameter kernel
if in straight-line WGSLrefusedrefusedrefusedcontrol flow has no place in a straight-line shader
tensor subscript in straight-line WGSLrefusedrefusedrefuseda tensor type has no WGSL equivalent in that position
assignmentrefusedemitsrefusedassignment to a Var is not expressible in QIR

QIR output is structural. A validated circuit text is not QPU execution.

WGSL is validated with naga, which is not GPU execution. There is no /dev/dri, no Vulkan ICD, and no browser WebGPU in the build environment, so no generated shader has ever been run. naga proves the module parses and its types line up; it proves nothing about what the shader computes.

A compute kernel is chosen by name and must own tensor parameters. The compute WGSL path takes the kernel name as an argument, and refuses a kernel with no tensor parameter because the ABI is defined in terms of those bindings.

Verification claims

Stated precisely, because the distinction matters:

  • WGSL is checked with naga. naga validation is not GPU execution.
  • QIR emission is structural. It is not quantum execution.
  • Float proof obligations are reported as warnings, not discharged mathematical-real proofs.
  • Pointer ABI guards validate the supplied lengths. They cannot prove non-nullness, memory safety, lifetime, provenance, or allocation shape.

Not implemented

Kept deliberately, and refused rather than approximated:

  • reversible { ... } uncomputation. compiler/src/lowering/reversible_lowering.rs contains an inverse generator that nothing calls; it is unwired and unverified, and its measurement path fabricates a qubit operand.
  • break / continue inside affine forall bands, which have no data-dependent runtime exit.
  • Cranelift, entirely.