diff --git a/Justfile b/Justfile index 62355e00..96a01117 100644 --- a/Justfile +++ b/Justfile @@ -183,7 +183,10 @@ ci-rust: setup-python test/verify/qaoa.py \ test/verify/shor.py \ samples/research/compiler_experiment_log_smoke.py \ - samples/research/algorithm_correctness_narrative_smoke.py \ + samples/research/algorithm_correctness_narrative_smoke.py + do + python3 "$script" + done # RAP Table I dump (#111, --release --include-ignored). Local convenience: # the same enforced n42 dump test `just ci-rust` runs (#111 Phase-2b). The # OOM concern that kept this out of ci-rust pre-#297 is resolved — #297's diff --git a/backend/src/target.rs b/backend/src/target.rs index 757a33b7..990a4ff4 100644 --- a/backend/src/target.rs +++ b/backend/src/target.rs @@ -104,7 +104,11 @@ impl ConnectivityGraph { // Floyd-Warshall relaxation that `try_from_edges` performs for sparse // topologies. let dist: Vec> = (0..num_qubits) - .map(|i| (0..num_qubits).map(|j| if i == j { 0 } else { 1 }).collect()) + .map(|i| { + (0..num_qubits) + .map(|j| if i == j { 0 } else { 1 }) + .collect() + }) .collect(); ConnectivityGraph { num_qubits, diff --git a/backend/tests/target.rs b/backend/tests/target.rs index b31451e0..389ee6d9 100644 --- a/backend/tests/target.rs +++ b/backend/tests/target.rs @@ -206,8 +206,8 @@ fn trivial_native_gates() -> Vec { #[test] fn fixed_target_new_derives_num_qubits_from_topology() { - let topology = ConnectivityGraph::try_from_edges(4, vec![(0, 1), (1, 2), (2, 3)]) - .expect("valid topology"); + let topology = + ConnectivityGraph::try_from_edges(4, vec![(0, 1), (1, 2), (2, 3)]).expect("valid topology"); let target = FixedTarget::new( topology, trivial_native_gates(), @@ -223,8 +223,8 @@ fn fixed_target_new_derives_num_qubits_from_topology() { #[test] fn fixed_target_try_new_rejects_inconsistent_dimensions() { // Topology has 4 qubits but the declared count is 3. - let topology = ConnectivityGraph::try_from_edges(4, vec![(0, 1), (1, 2), (2, 3)]) - .expect("valid topology"); + let topology = + ConnectivityGraph::try_from_edges(4, vec![(0, 1), (1, 2), (2, 3)]).expect("valid topology"); let err = FixedTarget::try_new( 3, topology, @@ -243,8 +243,8 @@ fn fixed_target_try_new_rejects_inconsistent_dimensions() { #[test] fn fixed_target_try_new_accepts_agreeing_dimensions() { - let topology = ConnectivityGraph::try_from_edges(3, vec![(0, 1), (1, 2)]) - .expect("valid topology"); + let topology = + ConnectivityGraph::try_from_edges(3, vec![(0, 1), (1, 2)]).expect("valid topology"); let target = FixedTarget::try_new( 3, topology, @@ -371,7 +371,10 @@ fn json_round_trips_through_descriptor() { assert_eq!(reloaded.id, target.id); assert_eq!(reloaded_fixed.num_qubits, target_fixed.num_qubits); - assert_eq!(reloaded_fixed.topology.edges(), target_fixed.topology.edges()); + assert_eq!( + reloaded_fixed.topology.edges(), + target_fixed.topology.edges() + ); assert_eq!( reloaded_fixed.noise.single_qubit_fidelity, target_fixed.noise.single_qubit_fidelity @@ -513,14 +516,12 @@ fn all_to_all_1000_qubits_constructs_quickly() { let graph = ConnectivityGraph::all_to_all(1000); let elapsed = start.elapsed(); - assert_eq!(graph.num_qubits, 1000); + assert_eq!(graph.num_qubits(), 1000); assert_eq!(graph.dist(0, 0), 0); assert_eq!(graph.dist(0, 999), 1); assert_eq!(graph.dist(999, 0), 1); assert_eq!(graph.dist(500, 501), 1); assert_eq!(graph.dist(501, 500), 1); - assert_eq!(graph.dist.len(), 1000); - assert_eq!(graph.dist[0].len(), 1000); assert!( elapsed.as_secs() < 1, diff --git a/docs/reviews/idiomatic-rust-re-review-2026-08-04.md b/docs/reviews/idiomatic-rust-re-review-2026-08-04.md new file mode 100644 index 00000000..4bdecd4b --- /dev/null +++ b/docs/reviews/idiomatic-rust-re-review-2026-08-04.md @@ -0,0 +1,186 @@ +# Re-review: Idiomatic Rust code quality (2026-08-04) + +**Prior review:** session `019fce3b-…` (2026-08-04, "Prepare audit issue cleanup"), +using the `rust-skills` skill. That audit scored the repo **3/10 for 5× scale +readiness**, filed **27 issues (#389–#415)**, and changed no files. + +**This review:** re-assesses the same axes against the current `main` +(`13c60e9`), after all 27 issues were closed. Every prior finding was +re-derived from source this session; gate commands were re-run locally. + +--- + +## Verdict + +**Substantial, real improvement on every substantive axis — but the quality +baseline is red again.** The stop-ship correctness and safety findings that +made the prior audit say "do not scale 5×" are fixed at the source level and +verified. However, the most recent batch of fix-PRs (#455–#460) landed without +running the canonical `just test-ci` gate, so fmt, clippy, rustdoc, and the +backend tests all fail on a clean checkout. This is the *same failure mode* +the prior audit flagged in #389: fix-PRs landing while the baseline is +untrustworthy, so regressions are invisible. + +### Current scores + +| Area | Prior | Current | Assessment | +|---|---:|---:|---| +| Correctness | 4/10 | **8/10** | All five reproduced panics/fail-open paths are closed and verified | +| Rust safety | 4/10 | **8/10** | Workspace `unsafe_code = "deny"`; FFI centralized in `ffi.rs` with `SAFETY:` everywhere | +| Tests | 7/10 (red) | **7/10 (red)** | 3,767 pass in the unblocked crates; backend test crate does not compile | +| Architecture | 5/10 | **7/10** | Invariant-bearing fields sealed; canonical AST visitor; feature seams aligned | +| Flux | 2/10 | **6/10** | #404–#414 closed; load-bearing contracts added (not independently re-verified this session — needs nightly) | +| 5× scale readiness | 3/10 | **6/10** | Trust gaps closed; blocked mainly by the red baseline, not design | + +--- + +## Gate status (observed on `main` @ `13c60e9`) + +| Gate | Prior | Current | Evidence | +|---|---|---|---| +| `cargo fmt --all -- --check` | RED (14 files) | **RED (~30 files)** | Drift across backend, frontend, mlir_bridge, quonlint, quon_na, flux_verify from #455–#460 | +| `cargo clippy --workspace --exclude flux_verify --all-targets -- -D warnings` | RED (2 errors) | **RED (1 error)** | `quon_core/src/depth.rs:261` `needless_borrow` (`&b"_"[..]`) from #398 | +| `just test-fast` / nextest | RED (5 snapshots, 331 unrun) | **PARTIAL** | frontend/quon_core/quon_qec/zx: 3,767 pass, 0 fail. **backend tests do not compile** | +| `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --exclude flux_verify --no-deps` | RED (37 warnings) | **RED** | `mlir_bridge` fails: public docs link private `PassContext`/`with_context`/`emit_error` (#459) | +| `npx @taskless/cli@latest check` | GREEN despite 556 warnings | **GREEN, 0 findings** | #390 converted warnings to a real failing gate | +| `cargo build -p mlir_bridge --tests` | RED (removed import) | **GREEN** | Fuzz targets import `DepthExpr` from `quon_core` (#410) | +| `just test-ci` (canonical) | RED | **RED** | Fails at fmt; would also fail clippy, rustdoc, backend tests | + +**The backend test regression is a cross-PR integration defect:** `backend/tests/target.rs:522` +(the `all_to_all_1000_qubits_constructs_quickly` test from #408) accesses +`graph.dist` as a *field*, but #394 sealed `ConnectivityGraph` fields private. +Three `E0616` errors block the entire `backend` test crate. #408 and #394 +both closed green in isolation but never agreed with each other. + +--- + +## Stop-ship findings — re-verification + +Every prior stop-ship finding was re-checked against current source. + +### 1. Classical arithmetic panics (#409) — FIXED +`frontend/src/elaborate.rs`: `eval_classical` and `eval_binop` now use +`checked_add/sub/mul/div`, with dedicated `ElabError::{Overflow, DivByZero, +NegativeExponent}` variants. `eval_classical` is documented "Total for the +supported fragment". A `mod arithmetic_totality_tests` test module locks the +behavior. `eval_classical(1/0)` returns `Err(DivByZero)`, not a panic. + +### 2. MLIR passes fail open (#391) — FIXED +`native_gate_decomp.rs` now defines `DecompError` and threads a +`&mut Diagnostics<'c>` accumulator through `decompose_block`/`decompose_module`; +`run_on_module` returns `Diagnostics`. The `eprintln!`-and-continue pattern is +gone. The pass functions no longer return `()` — failure is communicated. + +### 3. Phase-polynomial 128-qubit panic (#392) — FIXED +`phase_polynomial.rs` represents parities as a dynamic `Parity` bitset +(`Vec`), explicitly "well beyond the 128-qubit limit of a fixed `u128`". +`extract(129, &[])` no longer shifts overflow. + +### 4. QEC workload deserialization bypass (#396) — FIXED +`quon_qec/src/workload.rs`: `QecWorkload` now holds private `blocks`/`ops` +fields and deserializes via `#[serde(try_from = "QecWorkloadRaw")]`, which +replays the `WorkloadBuilder` ordering validation. `WorkloadBlock` has a +custom `Deserialize` that derives `code_family`. The post-measure memory round +the prior audit constructed is now rejected at the type boundary. + +### 5. OpenQASM silent semantic loss (#405) — FIXED +`quonc/src/qasm.rs`: `measure`, `reset`, `gate`/`opaque` definitions, and +classical control flow are all rejected with line-tagged `QasmError::Unsupported`. +Multi-parameter gates (`u2`/`u3`, parameterized entanglers) are rejected with +`QasmError::TooManyParams` *before* graph construction. No operation is +silently dropped. Tests cover each rejection path. + +### 6. "Exact" state prep heuristic substitution (#397) — FIXED +`quon_na/src/qec_schedule.rs`: `StatePrepMode::Exact` now invokes a real z3 +`schedule_exact` solver (behind the `solver` feature); the report is labelled +`Exact` only when `SolverOutcome::Proven`, else `Heuristic`. **Fail-closed +without the feature:** a non-solver build returns the typed +`NaPipelineError::ExactStatePrepRequiresSolver` — never a silent fallback. + +### 7. Unsafe boundary does not exist (#415) — FIXED +`Cargo.toml` now sets `[workspace.lints.rust] unsafe_code = "deny"` and +`undocumented_unsafe_blocks = "warn"`. `mlir_bridge/src/ffi.rs` is the single +`#![allow(unsafe_code)]` module, with a `// SAFETY:` comment on every block. +The raw `*mut` sinks were removed from `TypeChecker` (#393, closed). The pass +modules no longer call `mlir-sys` directly. + +### 8. `test.qubit` unverified allocation (#401) — FIXED +`frontend/src/lower.rs::alloc_qubit` now builds a verified +`quantum.dynamic.alloc` op (dialect verifier runs inside the builder), with a +doc note that it replaces the unregistered `test.qubit`. A `BuildError` is +returned on failure. + +--- + +## Architecture & maintainability — what improved + +- **Invariant-bearing fields sealed (#394, #402).** `ConnectivityGraph`, + `FixedTarget`, interaction graphs, and reports no longer expose mutable + invariant-bearing fields publicly. (The backend test regression is the + flip side: a consumer test wasn't migrated — see gates.) +- **Canonical AST visitor (#399).** `frontend/src/visitor.rs` provides one + traversal interface, reducing the duplicated exhaustive walkers across + analysis/formatter/linter/LSP. +- **Feature seams aligned (#407).** `quon_na`'s MLIR dependency is no longer + default; `frontend` default no longer pulls the heavy MLIR/Z3 stack for + tooling consumers. +- **Dead scaffolds removed (#395).** The orphaned `compaction/types.rs`, + no-op ZX stubs, and crate-wide `#![allow(dead_code)]` are gone (one residual + `is_captured` dead-code rustdoc warning remains in `mlir_bridge`). +- **All-to-all is O(N²) (#408).** Analytic distance matrix; no Floyd-Warshall + for the fully-connected case. (Test compiles against a private field — see + gates.) +- **DepthExpr canonical ordering (#398), elaboration borrowing (#400).** + Both closed; the former introduced the one current clippy error. + +--- + +## What is still red / regressions introduced by the fixes + +These are the actionable items for the next baseline pass: + +1. **fmt drift (~30 files).** `cargo fmt --all` fixes it; the fix-PRs simply + weren't formatted. +2. **clippy: `quon_core/src/depth.rs:261`** — `(&b"_"[..]).cmp(b.as_bytes())` + should be `b"_"[..].cmp(...)`. One-line fix. +3. **backend tests don't compile** — `backend/tests/target.rs:522-523` + accesses private `graph.dist`; switch to the `graph.dist(i, j)` accessor + (or a public len method). Cross-PR regression from #408 vs #394. +4. **rustdoc `-D warnings` fails on `mlir_bridge`** — the `ffi.rs` module + doc links to private `PassContext`, `with_context`, `emit_error`. Either + make those `pub(crate)`-documented or reword the links. This contradicts + #406's "CI-enforced warning-free" claim; the gate exists in `ci-rust` + (Justfile:152) but #459 broke it. + +**Root cause is process, not code:** the 27 fix-PRs were correct individually +but the last several merged without a green `just test-ci`. The prior audit's +#389 existed for exactly this reason. Until a PR cannot merge without a green +`ci-rust` (fmt + clippy + rustdoc + build + tests), this will recur. + +--- + +## Not independently verified this session + +- **Flux (#404, #411–#414).** The prior audit found `quon_qec` Flux failing + with 7 proof errors and `backend` Flux broken via the dependency. All four + issues are closed and the commits are on `main` (#456–#458), but running + `cargo flux` requires the nightly Flux toolchain and was not re-run here. + The contracts are present in source (`flux_verify`, `quon_qec/family.rs` + `#[flux]` attrs, `qldpc.rs`). Treat the Flux score as "closed, contracts + present, not re-proven" rather than verified green. +- **Snapshot tests (quon_na).** The prior audit had 5 stale snapshots. They + were not re-run this session because the backend test crate blocks first; + the quon_na snapshot crate was not exercised in isolation. + +--- + +## Recommended order of work + +1. `cargo fmt --all` — restore formatting. +2. Fix the one clippy error in `quon_core/src/depth.rs:261`. +3. Fix `backend/tests/target.rs:522` to use the `dist` accessor. +4. Fix `mlir_bridge/src/ffi.rs` rustdoc private-item links. +5. Run `just test-ci` to green. Re-run `cargo flux` on the nightly toolchain + to confirm #404–#414. +6. Add branch protection so `ci-rust` must be green to merge — this is the + single highest-leverage change to prevent recurrence. diff --git a/flux_verify/src/lib.rs b/flux_verify/src/lib.rs index 8bd8e88a..131c5e23 100644 --- a/flux_verify/src/lib.rs +++ b/flux_verify/src/lib.rs @@ -73,7 +73,6 @@ mod smoke { // the Flux spec makes it a contract violation at verified call sites. } - #[test] fn quon_core_linearity_kernels_match_issue6() { assert!(is_linear_use_count(1)); diff --git a/frontend/src/elaborate.rs b/frontend/src/elaborate.rs index 037fd579..90c61578 100644 --- a/frontend/src/elaborate.rs +++ b/frontend/src/elaborate.rs @@ -191,10 +191,10 @@ pub fn eval_classical( } } Expr::Neg(inner) => match eval_classical(inner, env, fuel)? { - Value::Int(n) => Ok(Value::Int( - n.checked_neg() - .ok_or(ElabError::Overflow { op: "negate", span: expr.1 })?, - )), + Value::Int(n) => Ok(Value::Int(n.checked_neg().ok_or(ElabError::Overflow { + op: "negate", + span: expr.1, + })?)), Value::Float(f) => Ok(Value::Float(-f)), _ => Err(ElabError::NotClassical { name: "negation of a non-numeric value", @@ -283,21 +283,30 @@ pub fn eval_classical( fn eval_binop(op: BinOp, a: &Value, b: &Value, span: SimpleSpan) -> Result { if let (&Value::Int(x), &Value::Int(y)) = (a, b) { let result = match op { - BinOp::Add => x.checked_add(y).ok_or(ElabError::Overflow { op: "add", span })?, - BinOp::Sub => x.checked_sub(y).ok_or(ElabError::Overflow { op: "subtract", span })?, - BinOp::Mul => x.checked_mul(y).ok_or(ElabError::Overflow { op: "multiply", span })?, + BinOp::Add => x + .checked_add(y) + .ok_or(ElabError::Overflow { op: "add", span })?, + BinOp::Sub => x.checked_sub(y).ok_or(ElabError::Overflow { + op: "subtract", + span, + })?, + BinOp::Mul => x.checked_mul(y).ok_or(ElabError::Overflow { + op: "multiply", + span, + })?, BinOp::Div => { if y == 0 { return Err(ElabError::DivByZero { span }); } - x.checked_div(y).ok_or(ElabError::Overflow { op: "divide", span })? + x.checked_div(y) + .ok_or(ElabError::Overflow { op: "divide", span })? } BinOp::Pow => { if y < 0 { return Err(ElabError::NegativeExponent { exp: y, span }); } - let exp = u32::try_from(y) - .map_err(|_| ElabError::Overflow { op: "power", span })?; + let exp = + u32::try_from(y).map_err(|_| ElabError::Overflow { op: "power", span })?; x.checked_pow(exp) .ok_or(ElabError::Overflow { op: "power", span })? } @@ -531,7 +540,13 @@ pub fn elaborate_circuit_body( if let Expr::Controlled(inner) = &gate.0 { let (control, target) = tuple2(&qubits)?; return decompose_controlled( - inner, &control, &target, classical_env, ctx, fuel, span, + inner, + &control, + &target, + classical_env, + ctx, + fuel, + span, ); } let gate = subst_classical_vars(gate, classical_env)?; @@ -1221,7 +1236,10 @@ fn elaborate_named_callee( && ctx.parametric.contains_key(name) { return Ok(Some(elaborate_circuit_body( - inner, classical_env, ctx, fuel, + inner, + classical_env, + ctx, + fuel, )?)); } } @@ -1268,17 +1286,13 @@ fn decompose_controlled( // fully-unrolled gate tree — the same partial-evaluation pass a bare // call site runs (issue #374). if let Some(elaborated) = elaborate_named_callee(inner, classical_env, ctx, fuel)? { - return decompose_controlled( - &elaborated, control, target, classical_env, ctx, fuel, span, - ); + return decompose_controlled(&elaborated, control, target, classical_env, ctx, fuel, span); } let fail = |construct: &'static str| ElabError::unsupported(construct, inner.1); match &inner.0 { Expr::Compose(lhs, rhs) => { - let left = - decompose_controlled(lhs, control, target, classical_env, ctx, fuel, span)?; - let right = - decompose_controlled(rhs, control, target, classical_env, ctx, fuel, span)?; + let left = decompose_controlled(lhs, control, target, classical_env, ctx, fuel, span)?; + let right = decompose_controlled(rhs, control, target, classical_env, ctx, fuel, span)?; Ok(compose_nonempty(left, right, span)) } Expr::CircuitBlock(stmts) => { @@ -1300,8 +1314,7 @@ fn decompose_controlled( let mut composed = empty_circuit(span); for i in 0..k { let t = shift_qubit_targets(target, i); - let step = - decompose_controlled(body, control, &t, classical_env, ctx, fuel, span)?; + let step = decompose_controlled(body, control, &t, classical_env, ctx, fuel, span)?; composed = compose_nonempty(composed, step, span); } Ok(composed) @@ -1316,8 +1329,7 @@ fn decompose_controlled( let mut offset = 0i64; for elem in elems { let t = shift_qubit_targets(target, offset); - let step = - decompose_controlled(elem, control, &t, classical_env, ctx, fuel, span)?; + let step = decompose_controlled(elem, control, &t, classical_env, ctx, fuel, span)?; let w = max_qubit_index(&step).map(|m| m as i64 + 1).unwrap_or(1); offset += w; composed = compose_nonempty(composed, step, span); @@ -1369,7 +1381,7 @@ fn decompose_controlled( _ => { return Err(fail( "controlled() of an unrecognized gate in a named circuit body", - )) + )); } }; // Reject multi-qubit body gates: the controlled decomposition only @@ -1645,7 +1657,13 @@ mod controlled_tests { let ctx = empty_ctx(); let mut fuel = 10_000u32; decompose_controlled( - &inner, &lit_int(0), &lit_int(1), &HashMap::new(), &ctx, &mut fuel, no_span(), + &inner, + &lit_int(0), + &lit_int(1), + &HashMap::new(), + &ctx, + &mut fuel, + no_span(), ) } @@ -1865,7 +1883,12 @@ mod controlled_tests { // `trotter_step(theta)` body: `circuit { Rz(theta) @0 }`. let step_body = ( Expr::CircuitBlock(vec![( - Stmt::Expr(rotation_gate_app("Rz", &var("theta"), &lit_int(0), no_span())), + Stmt::Expr(rotation_gate_app( + "Rz", + &var("theta"), + &lit_int(0), + no_span(), + )), no_span(), )]), no_span(), @@ -1955,7 +1978,10 @@ mod controlled_tests { .iter() .filter(|(g, _)| matches!(&g.0, Expr::Var(n) if n == "CNOT")) .count(); - assert_eq!(cnots, 4, "expected four CNOTs (two per controlled step): {decomposed:?}"); + assert_eq!( + cnots, 4, + "expected four CNOTs (two per controlled step): {decomposed:?}" + ); // Every gate targets either the control (qubit 0) or target (qubit 1) // — the controlled realization never touches another wire. for (_, qubits) in &gates { @@ -1963,7 +1989,10 @@ mod controlled_tests { Expr::Int(n) => vec![*n], Expr::Tuple(items) => items .iter() - .filter_map(|q| match q.0 { Expr::Int(n) => Some(n), _ => None }) + .filter_map(|q| match q.0 { + Expr::Int(n) => Some(n), + _ => None, + }) .collect(), _ => vec![], }; @@ -2084,13 +2113,31 @@ mod arithmetic_totality_tests { assert_eq!(eval(&int(n)).unwrap(), Value::Int(n), "literal {n}"); } // Sanity: ordinary in-range arithmetic still works. - assert_eq!(eval(&binop(BinOp::Add, 1, 2, no_span())).unwrap(), Value::Int(3)); - assert_eq!(eval(&binop(BinOp::Sub, 10, 4, no_span())).unwrap(), Value::Int(6)); - assert_eq!(eval(&binop(BinOp::Mul, 6, 7, no_span())).unwrap(), Value::Int(42)); - assert_eq!(eval(&binop(BinOp::Div, 20, 5, no_span())).unwrap(), Value::Int(4)); - assert_eq!(eval(&binop(BinOp::Pow, 2, 10, no_span())).unwrap(), Value::Int(1024)); + assert_eq!( + eval(&binop(BinOp::Add, 1, 2, no_span())).unwrap(), + Value::Int(3) + ); + assert_eq!( + eval(&binop(BinOp::Sub, 10, 4, no_span())).unwrap(), + Value::Int(6) + ); + assert_eq!( + eval(&binop(BinOp::Mul, 6, 7, no_span())).unwrap(), + Value::Int(42) + ); + assert_eq!( + eval(&binop(BinOp::Div, 20, 5, no_span())).unwrap(), + Value::Int(4) + ); + assert_eq!( + eval(&binop(BinOp::Pow, 2, 10, no_span())).unwrap(), + Value::Int(1024) + ); // 0^0 is defined as 1 by checked_pow (matches i64::pow). - assert_eq!(eval(&binop(BinOp::Pow, 0, 0, no_span())).unwrap(), Value::Int(1)); + assert_eq!( + eval(&binop(BinOp::Pow, 0, 0, no_span())).unwrap(), + Value::Int(1) + ); // 1^(large u32 exponent) stays 1, no overflow. assert_eq!( eval(&binop(BinOp::Pow, 1, u32::MAX as i64, no_span())).unwrap(), @@ -2100,7 +2147,7 @@ mod arithmetic_totality_tests { // contract) even for a base of 1 — the exponent must be a valid power count. assert!(matches!( eval(&binop(BinOp::Pow, 1, i64::MAX, no_span())), - Err(ElabError::Overflow { op: "power", .. }) + Err(ElabError::Overflow { op: "power", .. }) )); } @@ -2123,26 +2170,26 @@ mod arithmetic_totality_tests { } proptest! { - #![proptest_config(ProptestConfig::with_cases(1024))] - /// Arbitrary (op, i64, i64) pairs must never panic the elaborator: - /// every combination returns either Ok or a span-aware ElabError. - #[test] - fn prop_arith_never_panics(op in 0u8..5, x in any::(), y in any::()) { - let kind = match op { - 0 => BinOp::Add, - 1 => BinOp::Sub, - 2 => BinOp::Mul, - 3 => BinOp::Div, - _ => BinOp::Pow, - }; - let expr = binop(kind, x, y, SimpleSpan::from(0..0)); - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| eval(&expr))); - match result { - Ok(Ok(_)) | Ok(Err(_)) => (), - Err(_) => panic!("arithmetic panicked for ({:?}, {}, {})", kind, x, y), + #![proptest_config(ProptestConfig::with_cases(1024))] + /// Arbitrary (op, i64, i64) pairs must never panic the elaborator: + /// every combination returns either Ok or a span-aware ElabError. + #[test] + fn prop_arith_never_panics(op in 0u8..5, x in any::(), y in any::()) { + let kind = match op { + 0 => BinOp::Add, + 1 => BinOp::Sub, + 2 => BinOp::Mul, + 3 => BinOp::Div, + _ => BinOp::Pow, + }; + let expr = binop(kind, x, y, SimpleSpan::from(0..0)); + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| eval(&expr))); + match result { + Ok(Ok(_)) | Ok(Err(_)) => (), + Err(_) => panic!("arithmetic panicked for ({:?}, {}, {})", kind, x, y), + } } - } -} + } } #[cfg(test)] diff --git a/frontend/src/lexer.rs b/frontend/src/lexer.rs index da8e62f3..6b5630a1 100644 --- a/frontend/src/lexer.rs +++ b/frontend/src/lexer.rs @@ -141,7 +141,10 @@ impl std::fmt::Display for Token { /// On failure returns one `(message, span)` per lexical error — never panics. pub fn lex(src: &str) -> Result>, Vec>> { if let Some(start) = c_style_comment_outside_comments(src) { - return Err(vec![(C_STYLE_COMMENT_MSG.to_owned(), (start..start + 2).into())]); + return Err(vec![( + C_STYLE_COMMENT_MSG.to_owned(), + (start..start + 2).into(), + )]); } lexer().parse(src).into_result().map_err(|errs| { errs.into_iter() diff --git a/frontend/src/lower.rs b/frontend/src/lower.rs index 592be693..7d5c1986 100644 --- a/frontend/src/lower.rs +++ b/frontend/src/lower.rs @@ -655,12 +655,12 @@ impl<'c> LoweringCtx<'c> { let x_op = qc::gate( self.context, "X", - 1, // depth_contribution (Clifford) + 1, // depth_contribution (Clifford) true, // clifford &wires, self.location, )?; - return Ok(self.append_dynamic_op(x_op, 1)?); + return self.append_dynamic_op(x_op, 1); } } // `init_plus()` — allocate a fresh qubit and apply H @@ -673,12 +673,12 @@ impl<'c> LoweringCtx<'c> { let h_op = qc::gate( self.context, "H", - 1, // depth_contribution (Clifford) + 1, // depth_contribution (Clifford) true, // clifford &wires, self.location, )?; - return Ok(self.append_dynamic_op(h_op, 1)?); + return self.append_dynamic_op(h_op, 1); } } // `measure(q)` — consume one qubit, produce one bit. diff --git a/frontend/src/visitor.rs b/frontend/src/visitor.rs index 5973bb51..62b185ac 100644 --- a/frontend/src/visitor.rs +++ b/frontend/src/visitor.rs @@ -137,11 +137,7 @@ pub fn walk_type_param(v: &mut V, tp: &TypeParam) { pub fn walk_expr(v: &mut V, expr: &Sp) { if matches!(v.visit_expr_pre(expr), Traversal::Recurse) { match &expr.0 { - Expr::Int(_) - | Expr::Float(_) - | Expr::Bool(_) - | Expr::Unit - | Expr::Var(_) => {} + Expr::Int(_) | Expr::Float(_) | Expr::Bool(_) | Expr::Unit | Expr::Var(_) => {} Expr::Lam { params, body } => { for (pat, ty) in params { diff --git a/frontend/tests/lexer.rs b/frontend/tests/lexer.rs index e0be6e43..5bb21edf 100644 --- a/frontend/tests/lexer.rs +++ b/frontend/tests/lexer.rs @@ -191,7 +191,6 @@ fn unknown_char_is_span_accurate_error_not_panic() { assert_eq!(span.start, 2); } - #[test] fn c_style_comment_is_lex_error_recommending_dash() { // `//` is not a Quon operator; the common C-style comment mistake must @@ -203,10 +202,7 @@ fn c_style_comment_is_lex_error_recommending_dash() { msg.contains("//"), "message should name the unsupported spelling: {msg:?}" ); - assert!( - msg.contains("--"), - "message should recommend `--`: {msg:?}" - ); + assert!(msg.contains("--"), "message should recommend `--`: {msg:?}"); // The span covers both slashes, starting at the first one (byte offset 2). assert_eq!(span.start, 2); assert_eq!(span.end - span.start, 2); @@ -216,7 +212,10 @@ fn c_style_comment_is_lex_error_recommending_dash() { fn single_slash_still_lexes() { // `/` is the division operator and must still tokenize after the `//` guard. use Token::*; - assert_eq!(toks("a / b"), vec![Ident("a".into()), Slash, Ident("b".into())]); + assert_eq!( + toks("a / b"), + vec![Ident("a".into()), Slash, Ident("b".into())] + ); } #[test] @@ -224,13 +223,12 @@ fn c_style_comment_not_flagged_inside_real_comments() { // `//` appearing inside a `--` line comment or `{- -}` block comment is // part of the comment text, not source — it must not be flagged. use Token::*; - assert_eq!(toks("a -- https://example.com\nb"), vec![ - Ident("a".into()), - Newline, - Ident("b".into()), - ]); - assert_eq!(toks("a {- // ignored -} b"), vec![ - Ident("a".into()), - Ident("b".into()), - ]); + assert_eq!( + toks("a -- https://example.com\nb"), + vec![Ident("a".into()), Newline, Ident("b".into()),] + ); + assert_eq!( + toks("a {- // ignored -} b"), + vec![Ident("a".into()), Ident("b".into()),] + ); } diff --git a/frontend/tests/lower.rs b/frontend/tests/lower.rs index daa1375b..a441f4a0 100644 --- a/frontend/tests/lower.rs +++ b/frontend/tests/lower.rs @@ -344,7 +344,10 @@ fn main(): Q = run { } "#; let text = lower_text(src); - assert!(text.contains("quantum.dynamic.alloc"), "missing allocation: {text}"); + assert!( + text.contains("quantum.dynamic.alloc"), + "missing allocation: {text}" + ); assert!( text.contains(r#"gate_name = "H""#), "missing H prep gate: {text}" @@ -366,7 +369,10 @@ fn main(): Q = run { } "#; let text = lower_text(src); - assert!(text.contains("quantum.dynamic.alloc"), "missing allocation: {text}"); + assert!( + text.contains("quantum.dynamic.alloc"), + "missing allocation: {text}" + ); assert!( text.contains(r#"gate_name = "X""#), "missing X prep gate: {text}" @@ -420,7 +426,10 @@ fn main(): Q = run { } "#; let text = lower_text(src); - assert!(text.contains("quantum.dynamic.alloc"), "missing allocation: {text}"); + assert!( + text.contains("quantum.dynamic.alloc"), + "missing allocation: {text}" + ); assert!( !text.contains(r#"gate_name = "H""#), "qubit() must not emit H: {text}" diff --git a/frontend/tests/lsp_diagnostics.rs b/frontend/tests/lsp_diagnostics.rs index 0017677a..0d860f64 100644 --- a/frontend/tests/lsp_diagnostics.rs +++ b/frontend/tests/lsp_diagnostics.rs @@ -69,7 +69,6 @@ fn unterminated_block_comment_has_code() { assert_code(src, "quon.lex.unterminated-comment"); } - #[test] fn c_style_comment_has_code() { // `//` is the common C-style comment mistake; it must surface as a stable @@ -79,7 +78,11 @@ fn c_style_comment_has_code() { let d = first_with_code(src, "quon.lex.unsupported-comment"); let slashes = src.find("//").unwrap(); assert_eq!(d.span.start, slashes); - assert!(d.message.contains("--"), "message should recommend `--`: {}", d.message); + assert!( + d.message.contains("--"), + "message should recommend `--`: {}", + d.message + ); } #[test] diff --git a/frontend/tests/parser.rs b/frontend/tests/parser.rs index e6dff7e3..cc8ac5e9 100644 --- a/frontend/tests/parser.rs +++ b/frontend/tests/parser.rs @@ -328,7 +328,6 @@ fn nat_only_alias_params_still_parse() { } } - #[test] fn valid_comments_parse() { // Line and block comments are part of the lex grammar; a program using both @@ -351,5 +350,8 @@ fn c_style_comment_fails_at_lex_with_recommendation() { let src = "fn f(): Int = 1 // oops"; let err = lex(src).expect_err("expected `//` to be a lex error"); let (msg, _) = &err[0]; - assert!(msg.contains("//") && msg.contains("--"), "lex message: {msg:?}"); + assert!( + msg.contains("//") && msg.contains("--"), + "lex message: {msg:?}" + ); } diff --git a/frontend/tests/visitor.rs b/frontend/tests/visitor.rs index 001b45c7..4fea5944 100644 --- a/frontend/tests/visitor.rs +++ b/frontend/tests/visitor.rs @@ -231,7 +231,11 @@ fn canonical_visitor_visits_every_node_kind_with_balanced_pre_post() { assert!(rec.seen_pat >= 1, "pats: {}", rec.seen_pat); assert!(rec.seen_type >= 3, "types: {}", rec.seen_type); assert!(rec.seen_nat_expr >= 2, "nat exprs: {}", rec.seen_nat_expr); - assert!(rec.seen_type_param >= 1, "type params: {}", rec.seen_type_param); + assert!( + rec.seen_type_param >= 1, + "type params: {}", + rec.seen_type_param + ); // All pre/post stacks drained: nesting is balanced. assert!(rec.expr_stack.is_empty(), "unbalanced expr pre/post"); @@ -264,10 +268,7 @@ fn assert_pat_balanced(ev: &VecDeque) { } fn assert_nat_expr_balanced(ev: &VecDeque) { - let pre = ev - .iter() - .filter(|e| matches!(e, Event::NatExprPre)) - .count(); + let pre = ev.iter().filter(|e| matches!(e, Event::NatExprPre)).count(); let post = ev .iter() .filter(|e| matches!(e, Event::NatExprPost)) diff --git a/mlir_bridge/fuzz/Cargo.toml b/mlir_bridge/fuzz/Cargo.toml index 84c82d1a..0dac769d 100644 --- a/mlir_bridge/fuzz/Cargo.toml +++ b/mlir_bridge/fuzz/Cargo.toml @@ -15,6 +15,15 @@ cargo-fuzz = true [workspace] members = ["."] +# Same _FORTIFY_SOURCE / -O0 fix as the main workspace Cargo.toml: Nix's gcc +# injects -D_FORTIFY_SOURCE, and glibc emits a fatal -Werror warning at -O0, +# which tblgen's -Werror turns fatal. +[profile.dev.build-override] +opt-level = 1 + +[profile.release.build-override] +opt-level = 1 + [dependencies] libfuzzer-sys = "0.4" arbitrary = { version = "1", features = ["derive"] } diff --git a/mlir_bridge/src/diagnostics.rs b/mlir_bridge/src/diagnostics.rs index 37b17669..e6ff0274 100644 --- a/mlir_bridge/src/diagnostics.rs +++ b/mlir_bridge/src/diagnostics.rs @@ -10,7 +10,7 @@ //! typed [`Result`] into it with [`Diagnostics::report`]. None of that code //! touches the FFI boundary. //! * [`Diagnostics::emit`] flushes the accumulator to MLIR via the safe -//! [`crate::ffi::emit_error`] wrapper. The `unsafe` `mlirEmitError` call +//! `crate::ffi::emit_error` wrapper. The `unsafe` `mlirEmitError` call //! lives solely in [`crate::ffi`], alongside all other unsafe MLIR FFI in //! the crate. //! @@ -53,7 +53,7 @@ impl<'c> Diagnostic<'c> { /// /// The message is sanitized of interior NUL bytes so the `CString` /// conversion is infallible. The actual FFI call is delegated to the safe - /// [`crate::ffi::emit_error`] wrapper — this module contains no `unsafe` code. + /// `crate::ffi::emit_error` wrapper — this module contains no `unsafe` code. fn emit(&self) { let sanitized: String = self .message diff --git a/mlir_bridge/src/ffi.rs b/mlir_bridge/src/ffi.rs index 5cf4a814..28cfb134 100644 --- a/mlir_bridge/src/ffi.rs +++ b/mlir_bridge/src/ffi.rs @@ -9,8 +9,8 @@ //! * Error emission (`mlirEmitError`) — called by [`crate::diagnostics`]. //! * Raw operation mutation (`mlirOperationSetAttributeByName`, //! `mlirOperationSetOperand`). -//! * External-pass context lifetime erasure ([`PassContext`] + -//! [`with_context`]). +//! * External-pass context lifetime erasure (`PassContext` + +//! `with_context`). //! //! Every `unsafe` block below carries a `SAFETY` comment tied to the upstream //! FFI contract or the MLIR pass-framework lifetime guarantee. @@ -131,11 +131,6 @@ impl PassContext { self.raw = Some(unsafe { context.to_ref().to_raw() }); } - /// Whether a context has been captured. - pub(crate) fn is_captured(&self) -> bool { - self.raw.is_some() - } - /// Returns the raw `MlirContext` handle, or `None` if [`capture`](Self::capture) /// was never called. /// diff --git a/mlir_bridge/src/fixed_physical.rs b/mlir_bridge/src/fixed_physical.rs index 99e4328c..a22d6a3c 100644 --- a/mlir_bridge/src/fixed_physical.rs +++ b/mlir_bridge/src/fixed_physical.rs @@ -108,11 +108,7 @@ fn corrupt_block<'c, 'a>( } } -fn corrupt_op<'c, 'a>( - op: OperationRef<'c, 'a>, - name: &str, - attr: &melior::ir::Attribute<'c>, -) { +fn corrupt_op<'c, 'a>(op: OperationRef<'c, 'a>, name: &str, attr: &melior::ir::Attribute<'c>) { // Only ops that already have a phys_qubit attr are touched — overwriting // a non-existent attr would *add* one, which is not the test's intent. if op.attribute(quantum_dynamic::attr::PHYS_QUBIT).is_ok() { diff --git a/mlir_bridge/src/lib.rs b/mlir_bridge/src/lib.rs index eddf6b8e..5d521f81 100644 --- a/mlir_bridge/src/lib.rs +++ b/mlir_bridge/src/lib.rs @@ -2,10 +2,10 @@ pub mod circ_extract; pub mod diagnostics; -pub mod ffi; pub mod dialect; pub mod dynamic_walk; pub mod emit; +pub mod ffi; pub mod fixed_physical; pub mod metrics; pub mod passes; diff --git a/mlir_bridge/src/passes/classical_region_fusion.rs b/mlir_bridge/src/passes/classical_region_fusion.rs index 63bff212..b8f9fb89 100644 --- a/mlir_bridge/src/passes/classical_region_fusion.rs +++ b/mlir_bridge/src/passes/classical_region_fusion.rs @@ -30,9 +30,9 @@ use melior::pass::{ExternalPass, Pass, RunExternalPass, create_external}; use melior::{Context, ContextRef, IrRewriter}; use thiserror::Error; -use crate::ffi::PassContext; use crate::diagnostics::Diagnostics; use crate::dialect::{quantum_circ, quantum_dynamic}; +use crate::ffi::PassContext; use crate::passes::qubit_wiring::{self, WireTracker}; #[derive(Debug, Error)] @@ -671,17 +671,13 @@ fn fuse_module<'c, 'a>( } /// Runs classical region fusion on `module`, returning any error diagnostics. -pub fn run_on_module<'c>( - context: &'c Context, - module: &melior::ir::Module<'c>, -) -> Diagnostics<'c> { +pub fn run_on_module<'c>(context: &'c Context, module: &melior::ir::Module<'c>) -> Diagnostics<'c> { let mut diagnostics = Diagnostics::new(); fuse_module(context, module.as_operation(), &mut diagnostics); diagnostics.emit(); diagnostics } - #[repr(align(8))] struct PassId; @@ -694,7 +690,9 @@ struct ClassicalRegionFusion { impl ClassicalRegionFusion { fn new() -> Self { - Self { context: PassContext::new() } + Self { + context: PassContext::new(), + } } } diff --git a/mlir_bridge/src/passes/clifford_t_opt.rs b/mlir_bridge/src/passes/clifford_t_opt.rs index c2aa204c..7a7a3e2e 100644 --- a/mlir_bridge/src/passes/clifford_t_opt.rs +++ b/mlir_bridge/src/passes/clifford_t_opt.rs @@ -39,8 +39,8 @@ use melior::pass::{ExternalPass, Pass, RunExternalPass, create_external}; use melior::{Context, ContextRef, IrRewriter}; use quon_core::DepthExpr; -use crate::ffi::{self, PassContext}; use crate::dialect::quantum_circ::{self, attr}; +use crate::ffi::{self, PassContext}; use crate::passes::{phase_polynomial, stabilizer_tableau}; // --------------------------------------------------------------------------- @@ -188,7 +188,13 @@ fn rebuild_block<'c, 'a>( // Build new gates, inserting before the return op. let location = return_op.location(); let mut wires: Vec> = (0..n_qubits) - .map(|i| Value::from(block.argument(i).unwrap_or_else(|_| unreachable!("block {i} has argument {i}")))) + .map(|i| { + Value::from( + block + .argument(i) + .unwrap_or_else(|_| unreachable!("block {i} has argument {i}")), + ) + }) .collect(); for (gate_name, targets) in new_gates { @@ -200,7 +206,11 @@ fn rebuild_block<'c, 'a>( .unwrap_or_else(|_| unreachable!("rebuilt gate builds")), ); for (i, &target) in targets.iter().enumerate() { - wires[target] = Value::from(new_op.result(i).unwrap_or_else(|_| unreachable!("rebuilt result {i}"))); + wires[target] = Value::from( + new_op + .result(i) + .unwrap_or_else(|_| unreachable!("rebuilt result {i}")), + ); } } @@ -309,7 +319,9 @@ struct CliffordTOpt { impl CliffordTOpt { fn new() -> Self { - Self { context: PassContext::new() } + Self { + context: PassContext::new(), + } } } diff --git a/mlir_bridge/src/passes/compiler_uncomputation.rs b/mlir_bridge/src/passes/compiler_uncomputation.rs index 893c9d75..c29a86d2 100644 --- a/mlir_bridge/src/passes/compiler_uncomputation.rs +++ b/mlir_bridge/src/passes/compiler_uncomputation.rs @@ -195,7 +195,9 @@ struct CompilerUncomputation { impl CompilerUncomputation { fn new() -> Self { - Self { context: PassContext::new() } + Self { + context: PassContext::new(), + } } } diff --git a/mlir_bridge/src/passes/depth_scheduling.rs b/mlir_bridge/src/passes/depth_scheduling.rs index 0b8a4506..7d0d92f6 100644 --- a/mlir_bridge/src/passes/depth_scheduling.rs +++ b/mlir_bridge/src/passes/depth_scheduling.rs @@ -16,9 +16,9 @@ use melior::ir::{BlockLike, OperationRef, RegionLike}; use melior::pass::{ExternalPass, Pass, RunExternalPass, create_external}; use melior::{Context, ContextRef}; -use crate::ffi::{self, PassContext}; use crate::dialect::quantum_circ; use crate::dynamic_walk::{self, DynamicVisitor}; +use crate::ffi::{self, PassContext}; const GATE_TIME_US: f64 = 0.1; diff --git a/mlir_bridge/src/passes/gate_cancellation.rs b/mlir_bridge/src/passes/gate_cancellation.rs index f6eb5609..3a6a4c50 100644 --- a/mlir_bridge/src/passes/gate_cancellation.rs +++ b/mlir_bridge/src/passes/gate_cancellation.rs @@ -13,11 +13,11 @@ use melior::pass::{ExternalPass, Pass, RunExternalPass, create_external}; use melior::{Context, ContextRef, IrRewriter}; use quon_core::DepthExpr; -use crate::ffi::{self, PassContext}; use crate::dialect::{ quantum_circ::{self, attr}, quantum_dynamic, }; +use crate::ffi::{self, PassContext}; #[derive(Clone, Copy)] struct GateRef<'c, 'a> { @@ -278,7 +278,9 @@ struct GateCancellation { impl GateCancellation { fn new() -> Self { - Self { context: PassContext::new() } + Self { + context: PassContext::new(), + } } } diff --git a/mlir_bridge/src/passes/measurement_deferral.rs b/mlir_bridge/src/passes/measurement_deferral.rs index 5796ac55..5ded7ae0 100644 --- a/mlir_bridge/src/passes/measurement_deferral.rs +++ b/mlir_bridge/src/passes/measurement_deferral.rs @@ -409,10 +409,7 @@ fn defer_module<'c, 'a>( } /// Runs measurement deferral on `module`, returning any error diagnostics. -pub fn run_on_module<'c>( - context: &'c Context, - module: &melior::ir::Module<'c>, -) -> Diagnostics<'c> { +pub fn run_on_module<'c>(context: &'c Context, module: &melior::ir::Module<'c>) -> Diagnostics<'c> { let mut diagnostics = Diagnostics::new(); defer_module(context, module.as_operation(), &mut diagnostics); diagnostics.emit(); @@ -431,7 +428,9 @@ struct MeasurementDeferral { impl MeasurementDeferral { fn new() -> Self { - Self { context: PassContext::new() } + Self { + context: PassContext::new(), + } } } diff --git a/mlir_bridge/src/passes/native_gate_decomp.rs b/mlir_bridge/src/passes/native_gate_decomp.rs index 2c5e6770..5b6ff381 100644 --- a/mlir_bridge/src/passes/native_gate_decomp.rs +++ b/mlir_bridge/src/passes/native_gate_decomp.rs @@ -17,9 +17,9 @@ use melior::{Context, ContextRef, IrRewriter}; use thiserror::Error; use crate::diagnostics::Diagnostics; -use crate::ffi::{self, PassContext}; use crate::dialect::quantum_circ::{self, attr}; use crate::dialect::quantum_dynamic; +use crate::ffi::{self, PassContext}; #[derive(Debug, Error)] pub enum DecompError { diff --git a/mlir_bridge/src/passes/phase_polynomial.rs b/mlir_bridge/src/passes/phase_polynomial.rs index e423ac81..524e1d6f 100644 --- a/mlir_bridge/src/passes/phase_polynomial.rs +++ b/mlir_bridge/src/passes/phase_polynomial.rs @@ -62,7 +62,7 @@ pub struct Parity { impl Parity { /// Number of `u64` words needed to address `n` qubits (`ceil(n / 64)`). const fn width(n: usize) -> usize { - (n + 63) / 64 + n.div_ceil(64) } /// The zero parity (no bits set). diff --git a/mlir_bridge/src/passes/rotation_merging.rs b/mlir_bridge/src/passes/rotation_merging.rs index aaa8aca4..162d2ab9 100644 --- a/mlir_bridge/src/passes/rotation_merging.rs +++ b/mlir_bridge/src/passes/rotation_merging.rs @@ -11,11 +11,11 @@ use melior::pass::{ExternalPass, Pass, RunExternalPass, create_external}; use melior::{Context, ContextRef, IrRewriter}; use quon_core::DepthExpr; -use crate::ffi::{self, PassContext}; use crate::dialect::{ quantum_circ::{self, attr}, quantum_dynamic, }; +use crate::ffi::{self, PassContext}; #[derive(Clone, Copy)] struct GateRef<'c, 'a> { @@ -184,7 +184,11 @@ fn merge_pair<'c, 'a>( ) .unwrap_or_else(|_| unreachable!("merged rotation builds")), ); - let merged_out = Value::from(merged.result(0).unwrap_or_else(|_| unreachable!("merged result"))); + let merged_out = Value::from( + merged + .result(0) + .unwrap_or_else(|_| unreachable!("merged result")), + ); for output in outputs { base.replace_all_uses_with(output, merged_out); } @@ -352,7 +356,9 @@ struct RotationMerging { impl RotationMerging { fn new() -> Self { - Self { context: PassContext::new() } + Self { + context: PassContext::new(), + } } } diff --git a/mlir_bridge/src/passes/sabre_routing.rs b/mlir_bridge/src/passes/sabre_routing.rs index bfcc8107..6a1a09e7 100644 --- a/mlir_bridge/src/passes/sabre_routing.rs +++ b/mlir_bridge/src/passes/sabre_routing.rs @@ -16,8 +16,8 @@ use melior::{Context, ContextRef}; use thiserror::Error; use crate::diagnostics::Diagnostics; -use crate::ffi::{self, PassContext}; use crate::dialect::{quantum_circ, quantum_dynamic}; +use crate::ffi::{self, PassContext}; use crate::passes::qubit_wiring::{self, WireTracker}; fn set_i32_attr<'c>(context: &'c Context, op: OperationRef<'c, '_>, key: &str, value: i32) { @@ -685,7 +685,14 @@ fn route_module<'c, 'a>( // just each named `quantum.circ.func`). let mut top_level_state = RouteState::new(target.num_qubits); top_level_state.tracker.seed_block_args(&body); - route_block(context, target, cost, body, &mut top_level_state, diagnostics); + route_block( + context, + target, + cost, + body, + &mut top_level_state, + diagnostics, + ); // Each named `quantum.circ.func` is an independent circuit (its own qubit // register), so it gets a fresh `RouteState`. Post-inlining these are dead @@ -718,7 +725,13 @@ pub fn run_on_module<'c>( ) -> Diagnostics<'c> { let mut diagnostics = Diagnostics::new(); if let Some(target) = target.fixed_target() { - route_module(context, target, cost, module.as_operation(), &mut diagnostics); + route_module( + context, + target, + cost, + module.as_operation(), + &mut diagnostics, + ); } diagnostics.emit(); diagnostics diff --git a/mlir_bridge/src/passes/zx_simplification.rs b/mlir_bridge/src/passes/zx_simplification.rs index 76381a83..aa615302 100644 --- a/mlir_bridge/src/passes/zx_simplification.rs +++ b/mlir_bridge/src/passes/zx_simplification.rs @@ -11,9 +11,9 @@ use melior::pass::{ExternalPass, Pass, RunExternalPass, create_external}; use melior::{Context, ContextRef}; use zx::{GateRef, circuit_to_zx, simplify, zx_to_circuit}; -use crate::ffi::PassContext; use crate::circ_extract; use crate::dialect::{quantum_circ, quantum_dynamic}; +use crate::ffi::PassContext; fn op_name<'c: 'a, 'a, O: OperationLike<'c, 'a>>(operation: &O) -> String { operation @@ -147,7 +147,9 @@ struct ZxSimplification { impl ZxSimplification { fn new() -> Self { - Self { context: PassContext::new() } + Self { + context: PassContext::new(), + } } } diff --git a/mlir_bridge/tests/dynamic.rs b/mlir_bridge/tests/dynamic.rs index 6175c2ed..dfe6754f 100644 --- a/mlir_bridge/tests/dynamic.rs +++ b/mlir_bridge/tests/dynamic.rs @@ -533,14 +533,19 @@ fn alloc_verifier_rejections() { let op = generic_op(&context, qd::op::ALLOC, &[], &[], &[], vec![], location); assert!(matches!( qd::verify(&op), - Err(qd::VerifyError::Arity { - role: "result", - .. - }) + Err(qd::VerifyError::Arity { role: "result", .. }) )); // Operands present — allocation takes no operands. - let op = generic_op(&context, qd::op::ALLOC, &[q], &[qubit], &[], vec![], location); + let op = generic_op( + &context, + qd::op::ALLOC, + &[q], + &[qubit], + &[], + vec![], + location, + ); assert!(matches!( qd::verify(&op), Err(qd::VerifyError::Arity { @@ -553,10 +558,7 @@ fn alloc_verifier_rejections() { let op = generic_op(&context, qd::op::ALLOC, &[], &[bit], &[], vec![], location); assert!(matches!( qd::verify(&op), - Err(qd::VerifyError::WrongValueType { - role: "result", - .. - }) + Err(qd::VerifyError::WrongValueType { role: "result", .. }) )); // A well-formed single-qubit allocation verifies. diff --git a/mlir_bridge/tests/measurement_deferral.rs b/mlir_bridge/tests/measurement_deferral.rs index 61541153..8442081f 100644 --- a/mlir_bridge/tests/measurement_deferral.rs +++ b/mlir_bridge/tests/measurement_deferral.rs @@ -452,7 +452,9 @@ fn fails_closed_on_measure_missing_qubit() { "pass should fail closed on a measure missing its qubit" ); assert!( - messages.iter().any(|m| m.contains("missing measured qubit")), + messages + .iter() + .any(|m| m.contains("missing measured qubit")), "expected the error routed through Diagnostics, got: {messages:?}" ); } diff --git a/mlir_bridge/tests/native_gate_decomp.rs b/mlir_bridge/tests/native_gate_decomp.rs index 0f8eb8f2..e6bda693 100644 --- a/mlir_bridge/tests/native_gate_decomp.rs +++ b/mlir_bridge/tests/native_gate_decomp.rs @@ -158,9 +158,8 @@ fn fails_closed_on_undecomposable_gate() { let qubit = qc::qubit_type(&context); let block = Block::new(&[(qubit, location)]); let q = Value::from(block.argument(0).unwrap()); - let gate = block.append_operation( - qc::gate(&context, "NOPENOPE", 1, true, &[q], location).unwrap(), - ); + let gate = + block.append_operation(qc::gate(&context, "NOPENOPE", 1, true, &[q], location).unwrap()); let r = Value::from(gate.result(0).unwrap()); block.append_operation(qc::r#return(&[r], location).unwrap()); let region = Region::new(); diff --git a/mlir_bridge/tests/support/mod.rs b/mlir_bridge/tests/support/mod.rs index a569c5b6..5a5cf819 100644 --- a/mlir_bridge/tests/support/mod.rs +++ b/mlir_bridge/tests/support/mod.rs @@ -5,7 +5,6 @@ use std::cell::RefCell; use std::rc::Rc; -use melior::pass::{Pass, PassManager}; use melior::Context; use melior::ir::attribute::{BoolAttribute, FloatAttribute, IntegerAttribute, StringAttribute}; use melior::ir::operation::OperationBuilder; @@ -15,6 +14,7 @@ use melior::ir::{ Attribute, Block, BlockLike, Identifier, Location, Module, Operation, Region, RegionLike, Type, Value, }; +use melior::pass::{Pass, PassManager}; use mlir_bridge::dialect::quantum_circ as qc; use mlir_bridge::dialect::quantum_dynamic as qd; diff --git a/quon_core/src/depth.rs b/quon_core/src/depth.rs index 636cc4d2..5117bd4d 100644 --- a/quon_core/src/depth.rs +++ b/quon_core/src/depth.rs @@ -258,7 +258,7 @@ impl DepthExpr { // A `Var` whose name starts with `_` versus `Hole` (`_`): compare // the full name against the single underscore. (DepthExpr::Var(a), DepthExpr::Hole) => a.as_bytes().cmp(&b"_"[..]), - (DepthExpr::Hole, DepthExpr::Var(b)) => (&b"_"[..]).cmp(b.as_bytes()), + (DepthExpr::Hole, DepthExpr::Var(b)) => b"_"[..].cmp(b.as_bytes()), _ => unreachable!("cmp_atom_full on atoms with differing first bytes"), } } @@ -408,14 +408,6 @@ fn leading_decimal_byte(n: u64) -> u8 { /// access. This helper carries no flux spec, so trusting it skips no /// verification. Remove once flux-infer can infer loop invariants here. #[cfg_attr(feature = "flux", flux_rs::trusted)] -/// `#[trusted]` under Flux: the decrement-then-index digit-extraction loop -/// needs a loop invariant Flux cannot synthesize (`i >= 1` follows from `m > 0` -/// implying at most 20 digits, but Flux has no refined spec for the division- -/// driven iteration count). The body is panic-free by construction (a `u64` has -/// <= 20 decimal digits, so `i` never underflows and `buf[i]` stays in `0..20`) -/// and carries no Flux spec, so trusting it skips no verification. See ADR-0027 -/// for the `#[trusted]` convention. -#[cfg_attr(feature = "flux", trusted)] fn write_decimal(n: u64, buf: &mut [u8; 20]) -> usize { if n == 0 { buf[19] = b'0'; @@ -838,7 +830,8 @@ mod tests { let by_ord = a.cmp(b); let by_sexpr = a.to_sexpr().cmp(&b.to_sexpr()); assert_eq!( - by_ord, by_sexpr, + by_ord, + by_sexpr, "Ord disagrees with sexpr string cmp:\n a = {}\n b = {}\n ord = {:?}\n sexpr = {:?}", a.to_sexpr(), b.to_sexpr(), @@ -866,10 +859,7 @@ mod tests { let other = (0..100u64) .map(|i| var(&format!("w{i}"))) .fold(nat(0), |acc, v| acc.seq(v)); - let expr = DepthExpr::repeat( - DepthExpr::Max(Box::new(expr), Box::new(other)), - nat(3), - ); + let expr = DepthExpr::repeat(DepthExpr::Max(Box::new(expr), Box::new(other)), nat(3)); let once = expr.normalize(); // Idempotent: a second pass is a no-op (also exercises the sort path again). diff --git a/quon_lsp/src/analysis.rs b/quon_lsp/src/analysis.rs index 7185f22e..1776aa6f 100644 --- a/quon_lsp/src/analysis.rs +++ b/quon_lsp/src/analysis.rs @@ -91,8 +91,12 @@ impl AnalysisScheduler { let (lsp_diags, analysis) = match tokio::task::spawn_blocking(move || { let result = frontend::analyze(&text_for_task); let line_index = LineIndex::new(&text_for_task); - let mut diags = - analysis_to_lsp_diags(&text_for_task, &result, &line_index, &uri_for_analysis); + let mut diags = analysis_to_lsp_diags( + &text_for_task, + &result, + &line_index, + &uri_for_analysis, + ); if result.diagnostics.is_empty() { let lint_path = std::path::Path::new(uri_for_analysis.path()); @@ -144,10 +148,9 @@ impl AnalysisScheduler { } } }); - guard.pending.insert( - uri_for_pending, - PendingTask { handle, generation }, - ); + guard + .pending + .insert(uri_for_pending, PendingTask { handle, generation }); } /// Cancel any pending analysis for `uri` and drop its task handle. @@ -208,10 +211,7 @@ mod tests { fn scheduler(debounce: Duration) -> AnalysisSchedulerView { let (service, _socket) = LspService::new(move |c| QuonLanguageServer::with_debounce(c, debounce)); - AnalysisSchedulerView { - _socket, - service, - } + AnalysisSchedulerView { _socket, service } } struct AnalysisSchedulerView { @@ -251,7 +251,11 @@ mod tests { let view = scheduler(Duration::from_millis(1)); let sched = view.sched(); sched.request_analysis(uri("a")); - assert_eq!(sched.pending_count(), 1, "task pending immediately after request"); + assert_eq!( + sched.pending_count(), + 1, + "task pending immediately after request" + ); assert_settles(sched, 0, Duration::from_secs(2)).await; } @@ -262,7 +266,11 @@ mod tests { for i in 0..8 { sched.request_analysis(uri(&format!("doc{i}"))); } - assert_eq!(sched.pending_count(), 8, "one pending task per unique document"); + assert_eq!( + sched.pending_count(), + 8, + "one pending task per unique document" + ); assert_settles(sched, 0, Duration::from_secs(2)).await; } diff --git a/quon_lsp/tests/support/lsp_client.rs b/quon_lsp/tests/support/lsp_client.rs index 9e435e73..68114c6c 100644 --- a/quon_lsp/tests/support/lsp_client.rs +++ b/quon_lsp/tests/support/lsp_client.rs @@ -24,12 +24,12 @@ pub struct LspClient { } impl LspClient { -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn spawn() -> Self { Self::spawn_with_env(&[]) } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn spawn_with_env(extra_env: &[(&str, &str)]) -> Self { let mut cmd = Command::new(env!("CARGO_BIN_EXE_quon_lsp")); cmd.stdin(Stdio::piped()) @@ -58,7 +58,7 @@ impl LspClient { } } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn send_request(&mut self, method: &str, params: Option) { let id = self.next_id; self.next_id += 1; @@ -73,20 +73,20 @@ impl LspClient { self.pending_response_id = Some(id); } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn recv_response(&mut self) -> Value { let id = self.pending_response_id.expect("no pending request"); self.pending_response_id = None; self.wait_response(id) } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn send_request_with_response(&mut self, method: &str, params: Option) -> Value { self.send_request(method, params); self.recv_response() } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn send_notification(&mut self, method: &str, params: Value) { let msg = json!({ "jsonrpc": "2.0", @@ -96,7 +96,7 @@ impl LspClient { write_message(&mut self.stdin, &msg); } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn wait_notification(&self, method: &str, timeout: Duration) -> Option { let deadline = std::time::Instant::now() + timeout; while std::time::Instant::now() < deadline { @@ -109,7 +109,7 @@ impl LspClient { None } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn wait_publish_diagnostics(&self, uri: &str, timeout: Duration) -> Option { let deadline = std::time::Instant::now() + timeout; while std::time::Instant::now() < deadline { @@ -125,7 +125,7 @@ impl LspClient { None } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper pub fn shutdown_and_exit(mut self) { self.send_request("shutdown", None); let _ = self.recv_response(); @@ -135,7 +135,7 @@ impl LspClient { let _ = self.child.wait(); } -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper fn wait_response(&self, id: i64) -> Value { let deadline = std::time::Instant::now() + Duration::from_secs(10); while std::time::Instant::now() < deadline { @@ -153,7 +153,7 @@ impl LspClient { } impl Drop for LspClient { -#[allow(dead_code)] // shared test helper — not every integration test uses every helper + #[allow(dead_code)] // shared test helper — not every integration test uses every helper fn drop(&mut self) { if !self.graceful_shutdown { let _ = self.child.kill(); diff --git a/quon_na/src/entangling_schedule.rs b/quon_na/src/entangling_schedule.rs index 6b7826b1..105ef947 100644 --- a/quon_na/src/entangling_schedule.rs +++ b/quon_na/src/entangling_schedule.rs @@ -444,7 +444,10 @@ fn color_edge( let mut fan: Vec = vec![v]; let mut in_fan: BTreeSet = BTreeSet::from([v]); loop { - let tip = fan.last().copied().unwrap_or_else(|| unreachable!("fan non-empty")); + let tip = fan + .last() + .copied() + .unwrap_or_else(|| unreachable!("fan non-empty")); let free_at_tip = free_color(&color_to_edge[tip]); let mut next = None; for (&w, &eidx) in &adj[u] { @@ -466,7 +469,10 @@ fn color_edge( } let c = free_color(&color_to_edge[u]); - let tip = fan.last().copied().unwrap_or_else(|| unreachable!("fan non-empty")); + let tip = fan + .last() + .copied() + .unwrap_or_else(|| unreachable!("fan non-empty")); let d = free_color(&color_to_edge[tip]); // Always invert the cd-path at u. Because c is free at u, any non-empty diff --git a/quon_na/src/movement/bank.rs b/quon_na/src/movement/bank.rs index f4ad3122..08c58898 100644 --- a/quon_na/src/movement/bank.rs +++ b/quon_na/src/movement/bank.rs @@ -229,4 +229,3 @@ pub(crate) fn free_interaction_pairs( .filter(|p| !occ_site.contains_key(&p.left) && !occ_site.contains_key(&p.right)) .collect() } - diff --git a/quon_na/src/qec_schedule.rs b/quon_na/src/qec_schedule.rs index 96ce7233..af015e94 100644 --- a/quon_na/src/qec_schedule.rs +++ b/quon_na/src/qec_schedule.rs @@ -20,6 +20,8 @@ use crate::compaction::{ compact_schedule, feed_forward_dependencies, infer_atom_dependencies, }; use crate::entangling_schedule::schedule_entangling_layers; +#[cfg(feature = "solver")] +use crate::exact::state_prep::{CzGate, ExactStatePrepParams, schedule_exact}; use crate::graph::{ AtomVertexId, DEFAULT_GAMMA, Interaction, InteractionEdge, InteractionGraph, InteractionId, InteractionSegment, LogicalQubitId, SegmentKind, @@ -29,13 +31,9 @@ use crate::pipeline::{ NaPipelineError, NaScheduleArtifacts, NaScheduleOptions, validate_speed_model, }; use crate::plan::{QecStageAccumulator, plan_backend}; -#[cfg(feature = "solver")] -use crate::exact::state_prep::{ - CzGate, ExactStatePrepParams, schedule_exact, -}; +use crate::qec::code_blocks_from_expanded; #[cfg(feature = "solver")] use crate::report::ScheduleOptimality; -use crate::qec::code_blocks_from_expanded; use crate::report::{attach_qec_error_budget, build_resource_report}; use crate::schedule::{LocalGateKind, MeasurementBasis, NeutralAtomAction, ScheduleLayer}; use crate::schedule_entry::{GraphScheduleRequest, schedule_from_graph}; @@ -411,21 +409,16 @@ fn schedule_cnot_phase( // the schedule `Heuristic`. The non-solver build rejects `Exact` // up-front in `schedule_expanded`. #[cfg(feature = "solver")] - let (phase_graph, phase_layers, state_prep_outcome) = if opts.state_prep - == crate::pipeline::StatePrepMode::Exact - { - let gates = cnots_to_cz_gates(cnots); - let result = schedule_exact(&gates, ExactStatePrepParams::default()) - .map_err(NaPipelineError::ExactStatePrepFailed)?; - (req.graph, result.layers, Some(result.outcome)) - } else { - let scheduled = schedule_entangling_layers(req, max_pairs)?; - ( - scheduled.request.graph, - scheduled.request.layers, - None, - ) - }; + let (phase_graph, phase_layers, state_prep_outcome) = + if opts.state_prep == crate::pipeline::StatePrepMode::Exact { + let gates = cnots_to_cz_gates(cnots); + let result = schedule_exact(&gates, ExactStatePrepParams::default()) + .map_err(NaPipelineError::ExactStatePrepFailed)?; + (req.graph, result.layers, Some(result.outcome)) + } else { + let scheduled = schedule_entangling_layers(req, max_pairs)?; + (scheduled.request.graph, scheduled.request.layers, None) + }; #[cfg(not(feature = "solver"))] let (phase_graph, phase_layers) = { let scheduled = schedule_entangling_layers(req, max_pairs)?; @@ -1490,15 +1483,42 @@ mod tests { use crate::pipeline::StatePrepMode; let cnots: Vec = vec![ - PhysicalCnot { control: PhysicalAtomId(0), target: PhysicalAtomId(1) }, - PhysicalCnot { control: PhysicalAtomId(0), target: PhysicalAtomId(2) }, - PhysicalCnot { control: PhysicalAtomId(0), target: PhysicalAtomId(3) }, - PhysicalCnot { control: PhysicalAtomId(1), target: PhysicalAtomId(4) }, - PhysicalCnot { control: PhysicalAtomId(1), target: PhysicalAtomId(5) }, - PhysicalCnot { control: PhysicalAtomId(2), target: PhysicalAtomId(4) }, - PhysicalCnot { control: PhysicalAtomId(2), target: PhysicalAtomId(6) }, - PhysicalCnot { control: PhysicalAtomId(3), target: PhysicalAtomId(5) }, - PhysicalCnot { control: PhysicalAtomId(3), target: PhysicalAtomId(6) }, + PhysicalCnot { + control: PhysicalAtomId(0), + target: PhysicalAtomId(1), + }, + PhysicalCnot { + control: PhysicalAtomId(0), + target: PhysicalAtomId(2), + }, + PhysicalCnot { + control: PhysicalAtomId(0), + target: PhysicalAtomId(3), + }, + PhysicalCnot { + control: PhysicalAtomId(1), + target: PhysicalAtomId(4), + }, + PhysicalCnot { + control: PhysicalAtomId(1), + target: PhysicalAtomId(5), + }, + PhysicalCnot { + control: PhysicalAtomId(2), + target: PhysicalAtomId(4), + }, + PhysicalCnot { + control: PhysicalAtomId(2), + target: PhysicalAtomId(6), + }, + PhysicalCnot { + control: PhysicalAtomId(3), + target: PhysicalAtomId(5), + }, + PhysicalCnot { + control: PhysicalAtomId(3), + target: PhysicalAtomId(6), + }, ]; let all_atoms: Vec = (0..7).map(PhysicalAtomId).collect(); let na = load_na(); @@ -1543,7 +1563,10 @@ mod tests { .count() }) .sum(); - assert_eq!(entangle2, 9, "all 9 Steane CZ pairs must appear as Entangle2"); + assert_eq!( + entangle2, 9, + "all 9 Steane CZ pairs must appear as Entangle2" + ); // Schedule verification: no atom in two gates of the same entangle // layer — the exact colouring guarantee (movement-compatible). diff --git a/quon_na/tests/interaction_graph.rs b/quon_na/tests/interaction_graph.rs index 249b9165..82bea638 100644 --- a/quon_na/tests/interaction_graph.rs +++ b/quon_na/tests/interaction_graph.rs @@ -212,7 +212,7 @@ fn cubic_generator_is_3_regular() { /// A valid triangle graph serialized to a JSON value — the base for mutations. fn valid_graph_value() -> serde_json::Value { - serde_json::to_value(&triangle_graph()).expect("serialize valid graph") + serde_json::to_value(triangle_graph()).expect("serialize valid graph") } /// Assert that deserializing a mutated graph fails with a `GraphError` diff --git a/quon_qec/src/lattice_surgery.rs b/quon_qec/src/lattice_surgery.rs index 0acb2b98..00f80b57 100644 --- a/quon_qec/src/lattice_surgery.rs +++ b/quon_qec/src/lattice_surgery.rs @@ -348,9 +348,7 @@ pub(crate) fn right_column_data(block: &ExpandedBlock) -> Result Result, ExpandError> { @@ -423,11 +421,7 @@ pub(crate) fn rough_merge_round( // (all three slices have length `n`) rather than projecting an index // refinement through the loop body — the closure-projection weakness of // ADR-0027 does not affect `Iterator::zip`. - for ((&l, &r), &s) in left_col - .iter() - .zip(right_col.iter()) - .zip(&seam.atoms) - { + for ((&l, &r), &s) in left_col.iter().zip(right_col.iter()).zip(&seam.atoms) { entangling.push(PhysicalCnot { control: l, target: s, @@ -488,7 +482,11 @@ pub(crate) fn smooth_merge_round( let mut x_cnots = Vec::with_capacity(2 * n); // Iterator-zip iteration so Flux proves bounds through the shared length // rather than projecting an index refinement through the loop body. - for (&s, (&a, &b)) in seam.atoms.iter().zip(above_row.iter().zip(below_row.iter())) { + for (&s, (&a, &b)) in seam + .atoms + .iter() + .zip(above_row.iter().zip(below_row.iter())) + { local_mid.push(RoundLocalOp::H { atom: s }); x_cnots.push(PhysicalCnot { control: s, @@ -613,7 +611,10 @@ mod tests { let block = surface_block(3); let col = right_column_data(&block).expect("d3"); // Row-major: right column = indices 2, 5, 8 - assert_eq!(col, vec![PhysicalAtomId(2), PhysicalAtomId(5), PhysicalAtomId(8)]); + assert_eq!( + col, + vec![PhysicalAtomId(2), PhysicalAtomId(5), PhysicalAtomId(8)] + ); } #[test] @@ -659,7 +660,10 @@ mod tests { let block = surface_block(3); let row = bottom_row_data(&block).expect("d3"); // Last 3 of 9: indices 6, 7, 8 - assert_eq!(row, vec![PhysicalAtomId(6), PhysicalAtomId(7), PhysicalAtomId(8)]); + assert_eq!( + row, + vec![PhysicalAtomId(6), PhysicalAtomId(7), PhysicalAtomId(8)] + ); } #[test] @@ -686,16 +690,17 @@ mod tests { let left = vec![PhysicalAtomId(0), PhysicalAtomId(1), PhysicalAtomId(2)]; let right = vec![PhysicalAtomId(3), PhysicalAtomId(4)]; // only 2, not 3 let seam = seam_with_n(3); - let err = rough_merge_round( - &left, - &right, - &seam, - LogicalQubitId(0), - LogicalQubitId(1), - ) - .unwrap_err(); + let err = rough_merge_round(&left, &right, &seam, LogicalQubitId(0), LogicalQubitId(1)) + .unwrap_err(); assert!( - matches!(err, ExpandError::SeamLengthMismatch { left: 3, right: 2, seam: 3 }), + matches!( + err, + ExpandError::SeamLengthMismatch { + left: 3, + right: 2, + seam: 3 + } + ), "got {err:?}" ); } @@ -707,7 +712,11 @@ mod tests { let seam = seam_with_n(3); assert!(matches!( rough_merge_round(&left, &right, &seam, LogicalQubitId(0), LogicalQubitId(1)), - Err(ExpandError::SeamLengthMismatch { left: 1, right: 3, seam: 3 }) + Err(ExpandError::SeamLengthMismatch { + left: 1, + right: 3, + seam: 3 + }) )); } @@ -716,14 +725,8 @@ mod tests { let left = vec![PhysicalAtomId(0), PhysicalAtomId(1), PhysicalAtomId(2)]; let right = vec![PhysicalAtomId(3), PhysicalAtomId(4), PhysicalAtomId(5)]; let seam = seam_with_n(3); - let round = rough_merge_round( - &left, - &right, - &seam, - LogicalQubitId(0), - LogicalQubitId(1), - ) - .expect("d3 rough merge"); + let round = rough_merge_round(&left, &right, &seam, LogicalQubitId(0), LogicalQubitId(1)) + .expect("d3 rough merge"); assert_eq!(round.kind, RoundKind::Merge(MergeBoundary::Rough)); // 3 pairs × 2 CNOTs each = 6 @@ -747,14 +750,8 @@ mod tests { let left: Vec<_> = (0..5).map(PhysicalAtomId).collect(); let right: Vec<_> = (10..15).map(PhysicalAtomId).collect(); let seam = seam_with_n(5); - let round = rough_merge_round( - &left, - &right, - &seam, - LogicalQubitId(0), - LogicalQubitId(1), - ) - .expect("d5 rough merge"); + let round = rough_merge_round(&left, &right, &seam, LogicalQubitId(0), LogicalQubitId(1)) + .expect("d5 rough merge"); assert_eq!(round.entangling.len(), 10); // 5 pairs × 2 assert_eq!(round.z_cnot_count, 10); @@ -783,16 +780,17 @@ mod tests { let above = vec![PhysicalAtomId(0), PhysicalAtomId(1), PhysicalAtomId(2)]; let below = vec![PhysicalAtomId(3), PhysicalAtomId(4)]; // only 2 let seam = seam_with_n(3); - let err = smooth_merge_round( - &above, - &below, - &seam, - LogicalQubitId(0), - LogicalQubitId(1), - ) - .unwrap_err(); + let err = smooth_merge_round(&above, &below, &seam, LogicalQubitId(0), LogicalQubitId(1)) + .unwrap_err(); assert!( - matches!(err, ExpandError::SeamLengthMismatch { left: 3, right: 2, seam: 3 }), + matches!( + err, + ExpandError::SeamLengthMismatch { + left: 3, + right: 2, + seam: 3 + } + ), "got {err:?}" ); } @@ -804,7 +802,11 @@ mod tests { let seam = seam_with_n(3); assert!(matches!( smooth_merge_round(&above, &below, &seam, LogicalQubitId(0), LogicalQubitId(1)), - Err(ExpandError::SeamLengthMismatch { left: 2, right: 3, seam: 3 }) + Err(ExpandError::SeamLengthMismatch { + left: 2, + right: 3, + seam: 3 + }) )); } @@ -813,14 +815,8 @@ mod tests { let above = vec![PhysicalAtomId(0), PhysicalAtomId(1), PhysicalAtomId(2)]; let below = vec![PhysicalAtomId(3), PhysicalAtomId(4), PhysicalAtomId(5)]; let seam = seam_with_n(3); - let round = smooth_merge_round( - &above, - &below, - &seam, - LogicalQubitId(0), - LogicalQubitId(1), - ) - .expect("d3 smooth merge"); + let round = smooth_merge_round(&above, &below, &seam, LogicalQubitId(0), LogicalQubitId(1)) + .expect("d3 smooth merge"); assert_eq!(round.kind, RoundKind::Merge(MergeBoundary::Smooth)); assert_eq!(round.z_cnot_count, 0); @@ -847,14 +843,8 @@ mod tests { let above: Vec<_> = (0..5).map(PhysicalAtomId).collect(); let below: Vec<_> = (10..15).map(PhysicalAtomId).collect(); let seam = seam_with_n(5); - let round = smooth_merge_round( - &above, - &below, - &seam, - LogicalQubitId(0), - LogicalQubitId(1), - ) - .expect("d5 smooth merge"); + let round = smooth_merge_round(&above, &below, &seam, LogicalQubitId(0), LogicalQubitId(1)) + .expect("d5 smooth merge"); assert_eq!(round.entangling.len(), 10); // 5 pairs × 2 assert_eq!(round.local_mid.len(), 5); diff --git a/quon_qec/src/workload.rs b/quon_qec/src/workload.rs index 7ee7e1cf..a0a3fb74 100644 --- a/quon_qec/src/workload.rs +++ b/quon_qec/src/workload.rs @@ -224,9 +224,7 @@ impl TryFrom for QecWorkload { basis, logical_id, } => builder.construct(*family, *distance, *basis, *logical_id)?, - WorkloadOp::MemoryRound { logical_id } => { - builder.memory_round(*logical_id)? - } + WorkloadOp::MemoryRound { logical_id } => builder.memory_round(*logical_id)?, WorkloadOp::MeasureLogical { logical_id, basis } => { builder.measure_logical(*logical_id, *basis)? } @@ -882,8 +880,10 @@ mod tests { b.construct(SourceFamily::Surface, 3, LogicalBasis::X, LogicalQubitId(1)) .unwrap(); b.logical_cx(LogicalQubitId(0), LogicalQubitId(1)).unwrap(); - b.measure_logical(LogicalQubitId(0), LogicalBasis::Z).unwrap(); - b.measure_logical(LogicalQubitId(1), LogicalBasis::X).unwrap(); + b.measure_logical(LogicalQubitId(0), LogicalBasis::Z) + .unwrap(); + b.measure_logical(LogicalQubitId(1), LogicalBasis::X) + .unwrap(); let original = b.finish(); let json = serde_json::to_string(&original).expect("serialize"); @@ -895,12 +895,16 @@ mod tests { fn deserialization_rejects_use_after_measure() { let mut value = valid_repetition_json(); // Insert a memory_round after the measure op. - value["ops"].as_array_mut().unwrap().push(serde_json::json!({ - "op": "memory_round", "logical_id": 0 - })); + value["ops"] + .as_array_mut() + .unwrap() + .push(serde_json::json!({ + "op": "memory_round", "logical_id": 0 + })); let err = serde_json::from_value::(value).expect_err("use-after-measure"); assert!( - err.to_string().contains("use of logical qubit 0 after it was measured"), + err.to_string() + .contains("use of logical qubit 0 after it was measured"), "{err}" ); } @@ -908,12 +912,16 @@ mod tests { #[test] fn deserialization_rejects_double_measure() { let mut value = valid_repetition_json(); - value["ops"].as_array_mut().unwrap().push(serde_json::json!({ - "op": "measure_logical", "logical_id": 0, "basis": "z" - })); + value["ops"] + .as_array_mut() + .unwrap() + .push(serde_json::json!({ + "op": "measure_logical", "logical_id": 0, "basis": "z" + })); let err = serde_json::from_value::(value).expect_err("double measure"); assert!( - err.to_string().contains("logical qubit 0 was already measured"), + err.to_string() + .contains("logical qubit 0 was already measured"), "{err}" ); } @@ -926,7 +934,10 @@ mod tests { "ops": [{ "op": "memory_round", "logical_id": 0 }] }); let err = serde_json::from_value::(value).expect_err("use-before-construct"); - assert!(err.to_string().contains("unknown logical qubit id 0"), "{err}"); + assert!( + err.to_string().contains("unknown logical qubit id 0"), + "{err}" + ); } #[test] @@ -937,7 +948,8 @@ mod tests { })); let err = serde_json::from_value::(value).expect_err("duplicate construct"); assert!( - err.to_string().contains("duplicate construct for logical qubit 0"), + err.to_string() + .contains("duplicate construct for logical qubit 0"), "{err}" ); } @@ -954,9 +966,11 @@ mod tests { { "op": "logical_t", "logical_id": 0 } ] }); - let err = serde_json::from_value::(value).expect_err("logical_t on repetition"); + let err = + serde_json::from_value::(value).expect_err("logical_t on repetition"); assert!( - err.to_string().contains("`logical_t` requires surface-code blocks"), + err.to_string() + .contains("`logical_t` requires surface-code blocks"), "{err}" ); } @@ -976,9 +990,11 @@ mod tests { { "op": "logical_cx", "control": 0, "target": 1 } ] }); - let err = serde_json::from_value::(value).expect_err("logical_cx on repetition"); + let err = + serde_json::from_value::(value).expect_err("logical_cx on repetition"); assert!( - err.to_string().contains("logical_cx requires surface-code blocks"), + err.to_string() + .contains("logical_cx requires surface-code blocks"), "{err}" ); } @@ -997,7 +1013,8 @@ mod tests { }); let err = serde_json::from_value::(value).expect_err("inconsistent blocks"); assert!( - err.to_string().contains("block metadata inconsistent with construct ops"), + err.to_string() + .contains("block metadata inconsistent with construct ops"), "{err}" ); } diff --git a/quonc/src/main.rs b/quonc/src/main.rs index 8cfe26a8..1fff3e1f 100644 --- a/quonc/src/main.rs +++ b/quonc/src/main.rs @@ -1452,7 +1452,10 @@ fn render_target_summary(target: &BackendTarget) -> String { match &target.kind { TargetKind::Fixed(fixed) => { out.push_str(&format!("num_qubits: {}\n", fixed.num_qubits)); - out.push_str(&format!("topology_edges: {}\n", fixed.topology.edges().len())); + out.push_str(&format!( + "topology_edges: {}\n", + fixed.topology.edges().len() + )); out.push_str(&format!( "native_gates: {}\n", fixed diff --git a/quonc/src/qasm.rs b/quonc/src/qasm.rs index 15291105..c1a0a5d8 100644 --- a/quonc/src/qasm.rs +++ b/quonc/src/qasm.rs @@ -1267,7 +1267,12 @@ mod tests { assert_eq!(program.gates[0].params.len(), 3); let err = build_interaction_graph(&program).unwrap_err(); match err { - QasmError::TooManyParams { line, gate, got, max } => { + QasmError::TooManyParams { + line, + gate, + got, + max, + } => { assert_eq!(line, 2); assert_eq!(gate, "u3"); assert_eq!(got, 3); @@ -1281,7 +1286,12 @@ mod tests { fn u2_two_params_is_rejected_before_graph() { let err = parse_to_graph("qreg q[1];\nu2(pi/2, 0) q[0];\n").unwrap_err(); match err { - QasmError::TooManyParams { line, gate, got, max } => { + QasmError::TooManyParams { + line, + gate, + got, + max, + } => { assert_eq!(line, 2); assert_eq!(gate, "u2"); assert_eq!(got, 2); @@ -1298,7 +1308,12 @@ mod tests { // lose its angle entirely, so reject it. let err = parse_to_graph("qreg q[2];\ncrz(0.5) q[0],q[1];\n").unwrap_err(); match err { - QasmError::TooManyParams { line, gate, got, max } => { + QasmError::TooManyParams { + line, + gate, + got, + max, + } => { assert_eq!(line, 2); assert_eq!(gate, "crz"); assert_eq!(got, 1); @@ -1337,9 +1352,15 @@ ry(1.0) q[2]; // Two ≥2-qubit gates → two interactions, names and operands preserved. assert_eq!(graph.interactions.len(), 2); assert_eq!(graph.interactions[0].gate_name, "cz"); - assert_eq!(graph.interactions[0].qubits, [LogicalQubitId(0), LogicalQubitId(1)]); + assert_eq!( + graph.interactions[0].qubits, + [LogicalQubitId(0), LogicalQubitId(1)] + ); assert_eq!(graph.interactions[1].gate_name, "cx"); - assert_eq!(graph.interactions[1].qubits, [LogicalQubitId(1), LogicalQubitId(2)]); + assert_eq!( + graph.interactions[1].qubits, + [LogicalQubitId(1), LogicalQubitId(2)] + ); // Three 1-qubit gates → three local extracts with names + angles kept. assert_eq!(local.len(), 3); diff --git a/quonfmt/src/config.rs b/quonfmt/src/config.rs index 766f13a3..6b7443a1 100644 --- a/quonfmt/src/config.rs +++ b/quonfmt/src/config.rs @@ -17,4 +17,3 @@ impl Default for StyleConfig { } } } - diff --git a/quonlint/src/context.rs b/quonlint/src/context.rs index 595d1a46..f5d419a4 100644 --- a/quonlint/src/context.rs +++ b/quonlint/src/context.rs @@ -196,7 +196,9 @@ impl<'a, 'v> LintWalker<'a, 'v> { /// rule callback. `child` is private to this module; `LintWalker` lives in /// the same module so it can reach it. fn callback(&mut self, expr: &Sp) { - let ctx = self.base.child(self.current_in_circuit(), self.borrow_depth); + let ctx = self + .base + .child(self.current_in_circuit(), self.borrow_depth); (self.visit)(&ctx, expr); } } diff --git a/test/lit/lit.cfg.py b/test/lit/lit.cfg.py index 18f45f4a..192a1dd7 100644 --- a/test/lit/lit.cfg.py +++ b/test/lit/lit.cfg.py @@ -34,3 +34,4 @@ config.substitutions.append(("%sabre-route", "sabre_route")) config.substitutions.append(("%depth-schedule", "depth_schedule")) config.substitutions.append(("%FileCheck", "FileCheck")) +config.substitutions.append(("%clifford-t-opt", "clifford_t_opt")) diff --git a/website/src/content/docs/reference/compiler.md b/website/src/content/docs/reference/compiler.md index f4d44d7d..a6f2ac52 100644 --- a/website/src/content/docs/reference/compiler.md +++ b/website/src/content/docs/reference/compiler.md @@ -180,13 +180,12 @@ cargo run -p quonc -- \ --print-target ``` -See the [quonc CLI reference](../quonc/) for command examples, the [diagnostic catalog](../diagnostics/) for error-specific guidance, and the See the [quonc CLI reference](./quonc/) for command examples and the [maturation path](/guides/roadmap/) for the production-hardening direction. ## Where to go next -- **[quonc CLI](../quonc/)** — the companion reference page: every flag and +- **[quonc CLI](./quonc/)** — the companion reference page: every flag and option the compiler accepts. - **[Language guide: Introduction](/language/introduction/)** `(concept — Language guide)` — step back when a contract references a source-language diff --git a/website/src/content/docs/reference/quonc.md b/website/src/content/docs/reference/quonc.md index fd2d5ca3..3a493a32 100644 --- a/website/src/content/docs/reference/quonc.md +++ b/website/src/content/docs/reference/quonc.md @@ -388,7 +388,7 @@ exit codes, read the ## Where to go next -- **[Compiler pipeline](../compiler/)** — the companion reference page: the +- **[Compiler pipeline](./compiler/)** — the companion reference page: the per-stage contract this CLI drives. - **[Language guide: Introduction](/language/introduction/)** `(concept — Language guide)` — step back when a flag's behavior depends on a @@ -401,4 +401,4 @@ exit codes, read the For error-specific guidance — minimal reproducers, explanations, and supported repairs for every compiler diagnostic — see the -[diagnostic catalog](../diagnostics/). +[diagnostic catalog](./diagnostics/).