Wheeler

WIP-0034: Structured uncomputation and clean ancilla scopes

FieldValue
StatusDraft
OwnersWheeler language, compiler, type-system, verifier, quantum, proof, runtime, and tooling maintainers
Created2026-07-20
Updated2026-07-28
AreaLanguage, ownership, uncomputation, ancillas, quantum IR
Depends onWIP-0002, WIP-0005, WIP-0011, WIP-0028, WIP-0031, WIP-0033
SupersedesNone
Superseded byNone

Summary

Wheeler adds a lexical compute, use, and uncompute form and clean ancilla declarations for coherent and unitary code. A paired compute { ... } use { ... } region executes the compute block, executes the use block, then applies the validated inverse or adjoint of the compute block. Values produced for temporary use must remain available to the generated inverse, and every declared ancilla must return to its stated clean basis before the region exits. The compiler records the structure directly instead of relying on hidden runtime history or a mutable stack of prior calls.

Motivation

Many quantum and reversible algorithms follow one pattern:

  1. Derive a temporary value.
  2. Use that value as a control or input.
  3. Reverse the derivation.
  4. Reuse the temporary storage.

Written by hand, the pattern is easy to get wrong:

lookup(index, table, borrow mut value);
phaseIfEqual(value, target);
reverse lookup(index, table, borrow mut value);

The programmer must repeat the call exactly. Generic arguments, implementation choices, classical parameters, ownership, source order, and borrowed resources must all match. A later edit can change the forward call without changing the inverse call.

The compiler also loses the intent that value exists only to support the middle operation. That weakens ancilla scheduling, resource analysis, diagnostics, and source-linked circuit views.

A structural region solves both problems. The programmer states the computation only once. Wheeler owns the inverse call and cleanup proof.

This feature is not ordinary exception cleanup. It cannot run arbitrary user code during scope exit, and it cannot repair a measurement or external effect. It is an exact semantic form over reversible or unitary operations.

Use cases

Lookup, use, and cleanup

unitary void markMatchingRecord(
    borrow QIndex<8> index,
    borrow ClassicalTable<8> table,
    borrow QWord<8> target,
    borrow mut QBit marked
) {
    compute {
        ancilla qvalue<BitInt<8>> value = clean(0);
        lookup(index, table, borrow mut value);
    } use {
        xorIfEqual(
            value,
            target,
            borrow mut marked
        );
    }
}

The compiler applies lookup, executes xorIfEqual, then applies the exact inverse of lookup. value returns to zero and leaves scope.

Arithmetic workspace

A modular multiplication routine may allocate several clean scratch registers inside one lexical ancilla scope. The registers are visible to nested coherent calls but unavailable after the scope closes.

Generic implementation choice

A generic routine receives one statically selected lookup implementation. The compute region records the exact closed callable identity, so generated cleanup uses the same specialization.

Rejection after measurement

Exact uncomputation cannot follow a use block that measures the computed value. Wheeler rejects the region instead of calling measurement “cleanup.”

Nested conjugation

One compute/use region may appear inside another. The compiler preserves lexical nesting and emits inverse operations in the required order.

Goals

Non-goals

Terms and semantic model

Compute block

A compute block is a closed sequence of coherent or unitary operations with a validated inverse or adjoint. It may introduce clean ancillas and temporary bindings whose lexical lifetime extends through the paired use block.

Call its semantic operation C.

Use block

A use block is a closed coherent or unitary region that consumes the computed information without destroying the state needed to apply C^-1 or C†.

Call its semantic operation U here.

Structured uncomputation

The complete region has semantics:

C ; U ; inverse(C)

for a coherently lifted reversible computation, or:

C ; U ; adjoint(C)

for a unitary computation.

The source uses one paired structural form:

compute {
    ...
} use {
    ...
}

WIP-0005 and WIP-0006 own the final token spelling. This proposal owns the paired-region meaning.

Clean basis

A clean basis is one exact known basis value required at both ends of an ancilla lifetime.

Examples include:

BitInt<N> value 0
boolean value false
an explicitly named finite basis value

Cleanliness is a proof and ownership fact. It is not a request to erase an unknown value. WIP-0041 uses clean(T) only to choose the payload bits of coherent Slot<T>.Vacant. Restoring an occupied slot to vacancy still requires an admitted inverse or an explicit reset effect.

Ancilla

An ancilla is a compiler-owned affine quantum resource introduced inside a coherent or unitary region with one fixed logical type and initial basis.

Illustrative declarations are:

ancilla qreg<8> scratch = clean;
ancilla qvalue<BitInt<8>> value = clean(0);

An ancilla is part of the semantic region shape. It is not an ordinary heap allocation and does not create a provider object.

Inverse dependency

An inverse dependency is a place, classical parameter, callable identity, type-class evidence value, or structural fact required to apply the generated inverse of the compute block.

The compiler derives the dependency set before checking the use block.

Frame-preserved use

A use operation frame-preserves a computed place when its checked semantic relation leaves that place unchanged, even if quantum ownership requires an exclusive borrow while the operation executes.

This distinction lets a computed value control another operation without becoming classically readable or logically modified.

Ownership and boundaries

The language owns the paired region, its scope, ancilla declarations, and source-level name visibility.

WIP-0028 ownership rules remain authoritative for moves, borrows, disjointness, must-consume values, and use after move. This proposal adds no second aliasing model.

The type checker owns region pairing, scope, callable characteristics, effect exclusion, place overlap, and clean-basis compatibility.

The compiler owns inverse extraction, dependency analysis, frame analysis, ancilla lifetime planning, structural IR emission, and resource bounds.

The verifier owns the exact compute/use/inverse order, callable identities, ancilla initialization and return, disjointness, and prohibited effects.

The proof system owns cleanup, frame, inverse, adjoint, and resource obligations.

The runtime or target owns physical scratch resources. It may reuse physical qubits only when the accepted plan preserves the source clean-lifetime rules.

No host adapter may release, reset, or measure a dirty ancilla and report that exact uncomputation succeeded.

Design

Paired source form

The accepted structural shape is equivalent to:

compute {
    compute-statements
} use {
    use-statements
}

Both blocks are mandatory. They form one lexical scope.

Names declared in the compute block may be visible in the use block when their declaration explicitly permits that lifetime. They are not visible after the paired form.

Ordinary locals declared in the use block follow normal lexical scope and must satisfy all ownership obligations before the use block ends.

Execution order

Forward execution is:

enter paired scope
run compute block forward
run use block forward
run compute block inverse or adjoint
check declared ancillas clean
leave paired scope

The compiler does not reparse source text to construct the inverse. It uses the closed callable and structural identities produced during type checking and specialization.

Adjoint of the paired region

For a unitary paired region:

R = C ; U ; C†

The generated adjoint is:

R† = C ; U† ; C†

The compute block remains in forward order around the adjoint of the use block. The compiler records this structural law instead of flattening the region and rediscovering it from gates.

For a coherent reversible region, the same rule uses the checked language inverse.

Accepted compute statements

The first profile accepts these statements:

It rejects these statements:

Accepted use statements

The use block may perform:

The use block cannot:

Dependency and frame checking

For each compute block, the compiler derives:

ComputeSummary {
    read_places
    written_places
    produced_places
    inverse_dependencies
    introduced_ancillas
    required_entry_clean_states
    required_exit_clean_states
    effects
    callable_and_evidence_ids
    resource_expression
}

For each use block, it derives a frame summary.

The region is accepted only when:

use preserves every inverse dependency
use does not consume any produced place needed by inverse(C)
use writes only disjoint or explicitly frame-compatible places
all introduced owners are consumed or returned by the scope end

Ownership and frame metadata decide simple cases. Harder accepted cases may use WIP-0011 evidence.

A shared source spelling such as borrow does not by itself prove frame preservation for a live quantum value. Quantum operations generally require exclusive ownership while they execute. The callable’s certified frame relation supplies the semantic preservation fact.

Ancilla declaration

An ancilla declaration names:

For example:

ancilla qvalue<BitInt<8>> value = clean(0);

The initializer must be a compile-time known admitted basis value. It cannot read runtime measurement, host state, or another unknown coherent value.

A raw register declaration:

ancilla qreg<8> scratch = clean;

means all eight logical qubits begin and end in the canonical zero state.

The source does not select physical qubits. The target plan may assign and reuse physical resources after checking lifetimes.

Ancilla lifetime

An ancilla declared in a compute block remains live through the use block and generated uncompute. It becomes unavailable immediately after the compiler verifies its clean exit.

An ancilla declared in a nested scope cannot escape through:

Clean-state proof

The compiler may establish clean exit through:

Testing a few basis states is not enough to release the ancilla in verified code.

Nested regions

Paired regions nest lexically.

compute {
    outerCompute();
} use {
    compute {
        innerCompute();
    } use {
        body();
    }
}

Forward order is:

outerCompute
innerCompute
body
inverse innerCompute
inverse outerCompute

The verifier records the nesting directly.

Relationship to reverse blocks

A reverse { ... } block runs the inverse of calls listed in the block in reverse lexical order. It is explicit inverse execution.

A compute/use region instead declares a conjugation-shaped lifetime and asks the compiler to generate the cleanup portion.

The two forms are not aliases. reverse may appear where the programmer wants inverse execution as the primary action. compute/use is for temporary information that must be cleaned after another operation uses it.

Compiler scheduling

The source semantics places the generated inverse immediately after the use block.

A verified optimizer may move all or part of cleanup within the same enclosing coherent or unitary region only when it preserves:

Any movement appears in a derived transformation report under WIP-0037. It does not change the canonical source region.

No nonlexical receipt in the first profile

This proposal does not add:

ComputeReceipt receipt = compute operation();
...
uncompute(receipt);

A nonlexical receipt would need suspension, storage, branch, aliasing, and must-consume rules. The lexical form covers the common case and gives the compiler a simpler trusted boundary.

A later proposal may eventually add affine receipts after the lexical model is implemented and measured.

Reversibility and history

The generated cleanup uses a language inverse or unitary adjoint. It does not consume WIP-0001 step records.

The compute block may not depend on bounded logged history. A computation that needs logged overwrite is not coherently uncomputable through this form.

Ancilla release is valid only after exact clean-state evidence. Releasing memory that held a classical clean value and returning a quantum ancilla to a clean basis are different operations, even when both free implementation resources.

Measurement-assisted cleanup, reset, branch discard, compensation, replay, and retry are nonunitary workflow actions. They require separate source forms and effect rows.

A trap inside an admitted coherent or unitary region indicates malformed verified IR or a failed implementation invariant. The paired form is not a hidden exception handler. It does not promise to execute the inverse after arbitrary runtime failure.

Concurrency and determinism

Pairing, inverse generation, dependency analysis, ancilla identities, and source diagnostics are deterministic for one closed specialization.

The compiler may analyze independent nested regions in parallel. It reduces those summaries by canonical routine and source identity.

Operations on disjoint quantum resources may be scheduled in parallel by a target. The scheduling plan must still respect compute/use/inverse dependencies and ancilla lifetime overlap.

Physical reuse may differ across targets. The logical ancilla high-water mark and clean-lifetime graph remain stable for the same semantic region and policy.

This proposal introduces no shared mutable classical state or cross-task ancilla borrowing.

Quantum and proof implications

For each paired region, Wheeler creates the core obligation:

R = C ; U ; inverse(C)

or:

R = C ; U ; adjoint(C)

It also checks:

all compute inputs required by inverse(C) are preserved by U
all introduced ancillas finish in their declared clean basis
all non-ancilla frame locations satisfy the declared postcondition
R contains no measurement, reset, target, or host effect

A callable used in the use block may carry a frame theorem such as:

preserves(control_value)

That theorem allows coherent use of a computed value without preventing later uncomputation.

Proof certificates bind the closed compute and use bodies, selected generic and type-class evidence, source region identity, resource profile, and compiler profile.

A hardware experiment may report whether measured outputs match expectations. It cannot prove that an unmeasured ancilla was clean on every execution.

Bytecode, persistence, and compatibility

Canonical .wbc records paired-region metadata in quantum or coherent body IR.

The semantic form contains:

ComputeUseRegion {
    region_id
    compute_body
    use_body
    inverse_or_adjoint_body
    dependency_summary
    ancilla_descriptors
    frame_evidence
    resource_bound_id
    certificate_ids
    source_map
}

The inverse or adjoint identity is explicit. A loader never reconstructs it from source order or a method name.

Ancilla descriptors contain logical type, width, clean entry and exit basis, origin, and lexical lifetime. They contain no physical coordinates.

Existing artifacts without this required feature remain valid. Loaders that do not understand the paired-region feature reject an artifact that requires it before execution.

The feature does not create persisted live quantum values. Hybrid runs persist only ordinary continuation state and observations under WIP-0004.

Safety, limits, and failures

Limits cover:

The first stable diagnostic families should include:

WUNC001 compute block has no exact inverse or adjoint
WUNC002 prohibited effect in compute block
WUNC003 prohibited effect in use block
WUNC004 use block changes an inverse dependency
WUNC005 computed value is moved, measured, reset, or released
WUNC006 ancilla is not clean at scope exit
WUNC007 ancilla escapes its lexical scope
WUNC008 compute and generated inverse select different specialization evidence
WUNC009 overlapping quantum places in paired region
WUNC010 paired-region resource limit exceeded
WUNC011 unsupported trap or dynamic dispatch remains
WUNC012 missing frame or cleanup evidence

A failed check emits no partial paired region, generated inverse body, proof certificate, or target plan.

Migration and deletion

  1. Add paired-region nodes to typed source and semantic IR.
  2. Add compute-block inverse and adjoint extraction over closed callables.
  3. Add inverse-dependency and frame summaries.
  4. Add clean ancilla declarations over qreg and accepted qvalue<T> types.
  5. Add lexical lifetime and escape checking.
  6. Add semantic simulator and verifier support.
  7. Add cleanup, frame, adjoint, and resource proof obligations.
  8. Add parser, Tree-sitter, formatter, documentation, disassembly, and diagnostics.
  9. Migrate one QROM-like fixture and one arithmetic-workspace fixture.
  10. Delete duplicated manual forward/reverse call pairs and temporary cleanup annotations replaced by the accepted form.

Progress

Testing and acceptance

Alternatives

Require explicit forward and inverse calls

Rejected as the only form. It duplicates source and hides the temporary lifetime from the compiler.

Use a mutable call stack like an object-level uncompute() method

Rejected. Cleanup identity would depend on runtime object history and call order. It would also interact poorly with aliasing, generic specialization, nested calls, and compiler scheduling.

Add general defer

Rejected. General scope-exit code would mix exact uncomputation with traps, I/O, cancellation, and cleanup effects. Wheeler needs a narrower semantic form.

Treat reset as acceptable cleanup

Rejected. Reset is an explicit nonunitary effect. It cannot satisfy an exact clean-ancilla theorem.

Make ancillas normal droppable values

Rejected. Automatic drop cannot prove that unknown quantum state returned to the required basis.

Add nonlexical compute receipts first

Rejected. They add ownership and suspension problems before the lexical contract is proven useful.

Infer compute/use patterns from optimized IR

Rejected. Source intent, diagnostics, proof identity, and resource lifetime should not depend on a pattern-matching optimization.

Open questions

Integration with reversible concurrency

Task exclusion

Task, shared atomic, synchronization, and blocking operations reject from compute and use regions. A TaskScope is not an ancilla scope. Schedule observations and TaskScheduleWitness values cannot masquerade as clean quantum workspace.

A finite WIP-0015 model may use bounded task-model workspace only when every model transition and witness uncomputes under this WIP.

References