Quirlv0.5.1
ArchitectureDecisions

Rust 1.97 compatibility and selective adoption

Canonical Quirl project documentation synced from docs/decisions/2026-08-18_192326104_rust-1-97-compatibility-and-selective-adoption.md.

Superseded · 2026-08-18

  • Baseline: Rust 1.97.1, Edition 2024, commit 7c4e9648bd144f75222157fb7c8fe00e37da120f

Context

Quirl pins one compiler because compiler, Cargo, Clippy, Rustdoc, and standard library behavior are part of its safety and reproducibility boundary. Raising that pin does not justify broad API churn: a new capability is adopted only when it makes a resource bound, parser transition, diagnostic, or invariant more visible. The audit covered every Rust, Cargo, and Clippy stable release from 1.89 through 1.97.1 using the official Rust release notes, Cargo changelog, and Clippy changelog.

Release audit

Only changes with a concrete Quirl consequence are recorded. "Validate" means the pinned toolchain and canonical gates exercise the compatibility change; "no action" means neither source nor policy should change.

ReleaseQuirl-relevant findingDisposition
1.89mismatched_lifetime_syntaxes became warn-by-default; File::{lock,try_lock,unlock} and cross-target doctests stabilized; Cargo fixed fix/clippy --fix target selection. Clippy added useful but domain-specific lints without changing Quirl's selected policy.Validate the lifetime lint through warnings-denied Clippy/Rustdoc. File locking is an adopted parallel design for its owning task, but this change does not implement it. No other code.
1.90LLD became the default Linux x86-64 linker, Intel macOS moved to Tier 2, Cargo gained workspace publishing, and Clippy moved uninlined_format_args out of its default style group.Validate Linux linking in CI when available and retain macOS architecture awareness. Quirl has no multi-crate publishing workflow and does not opt into a cosmetic formatting lint.
1.91/1.91.1Pattern-binding drop order was corrected; dangling-local-pointer and integer-to-pointer lints were added; thread stack-size failure became an error instead of a standard-library panic. 1.91.1 fixed cross-crate Wasm imports and illumos File::lock. Clippy's possible_missing_else is already covered by explicit control flow review rather than a new lint group.Validate only. Quirl does not use the affected FFI patterns, and the point-release fixes are inherited by 1.97.1.
1.92Never-type future-compatibility lints became deny-by-default; invalid macro_export arguments became deny-by-default; Linux abort builds regained unwind tables.Validate the deny-by-default lints. Quirl release builds deliberately use panic = "unwind", so the abort-table change requires no policy. No Clippy or Cargo feature materially improves the current gates.
1.93/1.93.1New warnings cover interior mutation of const items and function-to-integer casts; musl moved to 1.2.5. 1.93.1 fixed a rustfmt ICE, a panicking_unwrap false positive, and a WASI file-descriptor leak in toolchain components.Validate warnings, formatting, and supported targets. No source adoption; Quirl already denies unwrap_used/expect_used at its own boundary.
1.94/1.94.1slice::array_windows stabilized; standard macros moved to the prelude; Cargo accepted TOML 1.1 and exposed CARGO_BIN_EXE_* at runtime. 1.94.1 fixed a Clippy ICE and Cargo extraction vulnerabilities.Adopt array_windows::<2> for two-token command-parser transitions. Keep the manifest in the older TOML subset for third-party tooling and use no runtime binary variable. Validate the macro/import and security-fix baseline.
1.95Match-arm if let guards and cfg_select! stabilized; Clippy added configurable allow-lists for unwrap/expect types; Cargo fixed corrupt replacement of an existing larger package archive.Do not adopt if let guards: reviewed parser/state arms already bind their state directly, while moving optional state into a guard would obscure or risk mutation on a failed guard. Do not weaken unwrap/expect or introduce cfg_select! for the small existing platform branches. Validate packaging behavior.
1.96/1.96.1assert_matches!, debug_assert_matches!, and new core::range types stabilized; Cargo fixed alternate-registry vulnerabilities. 1.96.1 fixed a MIR miscompilation, Cargo retry behavior, and libssh2 vulnerabilities.Adopt assert_matches! where parser and lifecycle tests inspect bound payloads and diagnostic fields. Reject debug_assert_matches! in tests because release-mode test evidence must not disappear. Defer new range types until range syntax and migration guidance settle. Validate the point-release fixes through the 1.97.1 pin.
1.97/1.97.1dead_code_pub_in_binary was added allow-by-default; Cargo stabilized build.warnings, made cargo clean reject implausible target directories, and enabled v0 symbol mangling. 1.97.1 fixed an LLVM optimization miscompilation. Clippy added no default-policy change that merits a new exception.Deny dead_code_pub_in_binary workspace-wide. Keep the explicit warnings-denied xtask gates rather than duplicate them in Cargo config. Accept Cargo's safer clean behavior and validate symbol/debugger output. Pin 1.97.1, not 1.97.0.

Decision

Use array_windows::<2> only at command-parser sites where the current token and following token form one transition. The array reference expresses the fixed width without manual index + 1 access; it does not allocate or add a second scan. Focused tests cover a redirect followed by an operator and a non-terminal background marker, including the exact following-token span.

Use std::assert_matches! in parser and process-lifecycle tests when the test must both select a variant and inspect its payload or diagnostic. Keep plain assert_eq! for exact values. Do not use debug_assert_matches! as test evidence because it is disabled when debug assertions are off.

Deny dead_code_pub_in_binary through the workspace lint table, alongside missing_docs = "deny". Cargo compiles library unit tests as executable libtest harnesses, where externally reachable public library APIs can appear unused. Every library root therefore carries one test-only, reasoned allowance; real product, benchmark, and xtask binaries remain at deny. This is stronger and more precise than lowering the workspace lint to warn or weakening public documentation.

Do not adopt match-arm if let guards. The reviewed literal parser and job state machine already expose state with exhaustive tuple/enum patterns and ordinary guards. No candidate improved the failure diagnostic or transition ownership enough to justify a rewrite, and evaluating a mutating optional binding in a failed guard would make state preservation harder to audit.

File locking is a separately adopted, parallel decision because Quirl's persistent-state writers require a coordinated ownership protocol. Its failure model, timeout, and cleanup behavior belong to the task that implements it; this compatibility audit neither adds nor simulates locking.

Failure model and invariants

  • A rejected two-token transition reports the second token's UTF-8 byte span; short input reports the current redirect without indexing past the slice.
  • Parser work remains one bounded scan with no new retained collection.
  • Invalid job transitions still return ShellError with ErrorCode::InvalidArgument, state context, and actionable help.
  • Public library APIs remain documented and externally reachable; only their executable libtest wrappers suppress dead_code_pub_in_binary.
  • Actual executable crates cannot accumulate unused public items.
  • The project never builds release artifacts with Rust 1.97.0; 1.97.1 is required because the official point-release announcement states that an LLVM miscompilation existed since at least Rust 1.87 and that 1.97.0 increased its likelihood. The fix and conservative rustc-side revert make the patch release a safety requirement, not optional maintenance.

Consequences

  • The MSRV remains exactly 1.97.1; adopted syntax and macros need no fallback.
  • The code change is deliberately small and domain-shaped rather than a sweep over every new standard-library API or Clippy lint.
  • Future compiler upgrades repeat this audit and retain point-release security and miscompilation fixes as explicit baseline evidence.

On this page