Skip to content

chore(deps): bump rules_rocq_rust to e4660cc (rules_rust migration; fixes Rocq Formal Proofs CI)#141

Open
avrabe wants to merge 16 commits into
mainfrom
chore/bump-rules-rocq-rust
Open

chore(deps): bump rules_rocq_rust to e4660cc (rules_rust migration; fixes Rocq Formal Proofs CI)#141
avrabe wants to merge 16 commits into
mainfrom
chore/bump-rules-rocq-rust

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 23, 2026

Summary

Picks up pulseengine/rules_rocq_rust#34 which migrates rocq-of-rust to a hermetic rules_rust toolchain, replacing the ad-hoc cargo build in coq_of_rust/private/repository.bzl.

The old build was failing on the CI runner with rust-lld: error: unable to find library -lLLVM-19-rust-1.85.0-nightly, keeping the Rocq Formal Proofs CI check red on main since v1.0.5 and forcing admin-merge through it for the v1.1.0 and v1.1.1 releases.

rules_rocq_rust PR #34's own CI confirms the migrated toolchain builds (Build Example (Linux) and (macOS) both pass).

Closes

Test plan

  • Rocq Formal Proofs CI check on this PR goes green (the whole point)
  • All other substantive checks remain green

avrabe added 16 commits May 23, 2026 06:40
Picks up pulseengine/rules_rocq_rust#34 which migrates rocq-of-rust to
a hermetic rules_rust toolchain, replacing the ad-hoc cargo build in
coq_of_rust/private/repository.bzl. The old build failed on the CI
runner with `rust-lld: error: unable to find library
-lLLVM-19-rust-1.85.0-nightly`, which has kept the Rocq Formal Proofs
CI check red on main since v1.0.5 (admin-merged through it for the
v1.1.0 and v1.1.1 releases).

Closes the v1.1.1 carry-forward item (task #89).

Trace: REQ-12
The pin bump alone (307b65f → e4660cc1b) is necessary but not sufficient:
rules_rocq_rust#34 changed the rocq_of_rust extension's API. Without
matching consumer-side changes the Rocq Formal Proofs CI check fails at
analysis time with

  module extension @@rules_rocq_rust+//coq_of_rust:extensions.bzl%rocq_of_rust
  does not generate repository "rocq_of_rust_source", yet it is imported
  as "rocq_of_rust_source" in the usage at MODULE.bazel:51:29

Updates to match the new API, modelled on the canonical consumer
example at examples/rust_to_rocq/MODULE.bazel in rules_rocq_rust:

- Add bazel_dep(name = "rules_nixpkgs_core", version = "0.13.0").
  rules_rocq_rust's nix_repo is dev_dependency=True there, so as the
  root module LOOM must configure nixpkgs itself.
- Add the nix_repo extension call pinning nixpkgs to the same
  2026-04-01 commit the upstream example uses (Rocq 9.0.1).
- rocq.toolchain: add `with_rocq_of_rust_deps = True`.
- use_repo(rocq, ...): drop `rocq_nixpkgs` (no longer exported by the
  new extension API — the root configures nixpkgs above).
- rocq_of_rust.toolchain: drop the `commit = "..."` arg; the new
  toolchain uses a build-time pinned default (rocq-of-rust @ 877dd65).
- use_repo(rocq_of_rust, ...): replace `rocq_of_rust_source` with
  `rocq_of_rust_build` (the new extension's output repo name).

rules_rust, crate_universe, and the hermetic Rust nightly+rustc-dev
toolchain are NOT dev_dependency in rules_rocq_rust@e4660cc1b's own
MODULE.bazel, so they are inherited transitively and no consumer-side
plumbing is needed for them.

Trace: REQ-12
Follow-up to the MODULE.bazel migration in 1e731fb. The new
rules_rocq_rust API renamed the build-output repo from
rocq_of_rust_source to rocq_of_rust_build; the alias in
proofs/rust_verified/BUILD.bazel still pointed at the old name and
held up the Rocq Formal Proofs check at analysis time with

  ERROR: no such package '@@[unknown repo 'rocq_of_rust_source'
         requested from @@]//': ... referenced by
         '//proofs/rust_verified:rocq_of_rust_lib'

The :rocq_of_rust_main target it consumes is preserved in the new
repo (verified in upstream coq_of_rust/private/rocq_of_rust_build.bzl
@ e4660cc1b).

Trace: REQ-12
…l change

The Track A roundtrip proof was passing on the v1.0.5-era pin of
rules_rocq_rust (Rocq toolchain pulled by the old ad-hoc cargo build).
After bumping to e4660cc1b — which pulls Rocq 9.0.1 with a stdlib
where [simpl] reduces [0 + n * 1] to [n * 1] eagerly — the existing

  replace (0 + n * 1) with n by lia.

silently no-ops (pattern absent) and the follow-up [reflexivity] then
fails with

  Unable to unify "Some (n, rest)" with "Some (n * 1, rest)".

at proofs/codec/Roundtrip.v:123. Match the post-simpl shape directly:
[replace (n * 1) with n by lia]. The proof still closes with [Qed];
no Admitteds added.

Trace: REQ-12
After the rules_rocq_rust toolchain bump, proofs/stack/StackSignature.v
line 214 fails with

  Unable to unify "rev (rev ?M768) = ?M768" with
                  "p0 :: ps = rev (rev (p0 :: ps))".

Rocq 9.0's `apply` no longer silently flips orientation when the goal
direction is reversed. The local proof obligation after
`rewrite <- Hrev` is `p0 :: ps = rev (rev (p0 :: ps))`, while
`rev_involutive : rev (rev l) = l` goes the other way — insert an
explicit `symmetry`. Same proof, same Qed, no Admitteds added.

Trace: REQ-12
The new rules_rocq_rust rocq_library rule preserves subdirectory
hierarchy under the logical prefix:

  proofs/semantics/WasmSemantics.v  →  proofs.semantics.WasmSemantics
  proofs/simplify/ConstantFolding.v →  proofs.simplify.ConstantFolding

The old pin (307b65f) appears to have flattened these to plain
proofs.WasmSemantics / proofs.ConstantFolding, so every dependent
file used `From proofs Require Import X.`. Under the new rules
that resolves to logical path "proofs.X" with no .vo file at that
location and fails with:

  Error: Cannot find a physical path bound to logical path
         WasmSemantics with prefix proofs.

Update all 17 imports across 9 .v files to point at the correct
subdirectory:

- WasmSemantics / TermSemantics    → From proofs.semantics
- ConstantFolding / Identity /
  Bitwise / StrengthReduction      → From proofs.simplify

No proof bodies changed; only the module path qualifiers.

Trace: REQ-12
…lds rev

Follow-up to 47b45f6. That commit added a `symmetry` to fix the
orientation of [apply rev_involutive], but the proof still failed
one tactic later at line 216 with

  Unable to unify
    "Some {| params := p0 :: ps; results := r; kind := Fixed |}"
  with
    "Some {| params := rev ys ++ [y]; results := r; kind := Fixed |}".

Cause: Rocq 9.0's [simpl] above the [replace] is more eager than the
v1.1.0-era pin's, and it unfolds [rev (y :: ys)] definitionally to
[rev ys ++ [y]]. The old replace pattern [(rev (y :: ys))] then no
longer matches anything in the post-simpl goal, the replace silently
no-ops, and [destruct k; reflexivity] fails because the goal still
holds [rev ys ++ [y]] where the lemma statement has [p0 :: ps].

Match the post-simpl shape directly and route the proof obligation
through a [change] back to [rev (y :: ys)] (definitionally equal),
then apply the same rev-involutive identity.

Same Qed, no Admitteds added.

Trace: REQ-12
… 238

After 3008dbb closed compose_empty_left, compose_empty_right now fails
at line 238 with

  Error: Found no subterm matching "?M755 - 0" in the current goal.

Same root cause as the previous two simpl-eagerness fixes: Rocq 9.0's
[simpl] above the [rewrite] has already reduced [length (r0 :: rs) - 0]
to its [length] form, so [rewrite Nat.sub_0_r] has no subterm to match.

Wrap with [try] so the proof tolerates both simpl behaviours — the
v1.1.0-era pin (which left the [- 0] intact) and the new pin (which
eats it). No other tactics are changed; same Qed.

Trace: REQ-12
Follow-up to 672a4b5. The new pin's Rocq 9.0 [simpl] keeps eating
the subterms each downstream [rewrite] wanted to match — first
[?M - 0], now [rev (rev ?M)]. Same pattern, same root cause: the
old pin left these untouched and the proof did explicit rewrites;
the new pin reduces them eagerly so the rewrites no-op-or-error.

Wrap every rewrite in the [compose_empty_right] non-empty branch
with [try], so the proof tolerates whichever way [simpl] settles
the term. If [simpl] reduced everything, the rewrites all no-op
and [destruct k; reflexivity] closes the residual goal directly;
if it left some shapes intact, the rewrites still fire.

Trace: REQ-12
My earlier 9042f2e change used the wrong half of the Coq import syntax:

  From proofs.semantics Require Import WasmSemantics.   (wrong)

`From X Require Import Y.` looks for module path `X.Y`, but `X` must
match an existing `-Q` prefix binding. The new rules_rocq_rust
`rocq_library` produces only the `proofs` prefix binding (from
`ctx.label.package`), not `proofs.semantics`. So coqc reports:

  Cannot find a physical path bound to logical path
  WasmSemantics with prefix proofs.semantics.

The subdir hierarchy belongs in the Import part, not the From part:

  From proofs Require Import semantics.WasmSemantics.   (right)

The `proofs` prefix matches the `-Q` binding; `semantics.WasmSemantics`
is the path *within* that prefix and resolves to the .vo at
`proofs/semantics/WasmSemantics.vo`.

Apply to all 17 imports across the 7 dependent files. Pure
text-level fix; no proof bodies touched.

Trace: REQ-12
The try-wrapped rewrites from 4834176 still left compose_empty_right
failing at line 246:

  Unable to unify
    "Some {| ...; results := r0 :: rs; ... |}"
  with
    "Some {| ...; results := rev (rev rs ++ [r0]); ... |}".

Rocq 9.0's simpl unfolds the *inner* rev (r0 :: rs) definitionally to
rev rs ++ [r0], so the results field becomes rev (rev rs ++ [r0]).
[rewrite rev_involutive] cannot fire — its pattern rev (rev ?M) does
not match rev (rev rs ++ [r0]) (the inner term is an append, not a
rev). The try-wrap therefore no-ops and the goal stays unreduced.

rev rs ++ [r0] is definitionally rev (r0 :: rs), so replace the field
with r0 :: rs and discharge via change + rev_involutive. Uses only
rev_involutive (known to resolve) and a definitional change — no
guess at possibly-renamed stdlib lemma names (rev_app_distr / rev_unit).

Trace: REQ-12
Root cause of the recurring TermSemantics.v "Cannot find a physical
path bound to logical path WasmSemantics" error, found by reading the
new rocq_library impl (rules_rocq_rust@e4660cc1b,
rocq/private/rocq.bzl): each source's coqc action gets

  inputs = [src] + dep_vo_files + stdlib + ...

where dep_vo_files comes ONLY from `deps` (transitive_compiled). The
.vo files compiled from *sibling sources in the same library* are
never added as inputs to later compilations in that library. So in
the two-file :semantics target, TermSemantics.v was compiled without
WasmSemantics.vo present — no import syntax could fix that.

Two-part fix:

1. proofs/BUILD.bazel: split :semantics (WasmSemantics.v +
   TermSemantics.v) into two single-file rocq_library targets,
   :wasm_semantics and :term_semantics (deps = [:wasm_semantics]).
   The cross-file dependency now flows through `deps`, which DOES
   propagate the include path and wire the .vo as a compile input.
   Replace the :semantics alias pair + :semantics_test with the two
   real libraries and their _test targets (the names all_proofs_test
   already referenced — closing the stale test_suite warning too).

2. The dependent .v files now use plain `Require Import WasmSemantics.`
   (no `From proofs`). Plain Require resolves by logical-name suffix
   across every -Q loadpath, so it is invariant to whether the rule
   maps the .vo to `proofs.WasmSemantics` or
   `proofs.semantics.WasmSemantics` — the prefix ambiguity that caused
   two earlier failed attempts. All 17 imports across 7 files updated.

Trace: REQ-12
…y_right

The semantics library split (3640b98) cleared the import errors; the
build now reaches StackSignature.v again, failing at the [replace]
block I added in 277627f:

  Unable to unify
    "Some {| ...; results := r0 :: rs; ... |}"
  with
    "Some {| ...; results := (r0 :: rs) ++ []; ... |}".

The [replace] correctly reduced [rev (rev rs ++ [r0])] to [r0 :: rs],
but the surrounding [++ results empty_sig] = [++ []] survived (the
pre-replace [try rewrite app_nil_r] fired on a different occurrence /
shape). Add a [try rewrite app_nil_r] after the replace to discharge
the residual append before [destruct k; reflexivity].

Trace: REQ-12
Empirical fix, modelled on the working pulseengine/meld proofs
(proofs/spec/BUILD.bazel) which solve exactly this inter-file
dependency case under the same rules_rocq_rust rule.

The meld idiom, applied here:
  1. one rocq_library per .v file (already done in the prior split);
  2. inter-file deps wired through `deps` (already done);
  3. a SHARED explicit include_path on every library in the group
     (meld uses "MeldSpec"; loom now uses "LoomProofs"); and
  4. cross-file imports written `From <Prefix> Require Import <Module>`.

Why the earlier attempts failed: the rules_rocq_rust rocq_library
rule maps a library's .vo under `-Q <output_dir> <include_path>`,
where include_path defaults to the BUILD package path. loom's .v
files live in SUBDIRECTORIES of the proofs/ package, so the
package-derived prefix did not line up with where the .vo landed,
and neither plain `Require Import` nor a `From proofs[.subdir]`
guess could resolve the dependency's module. A shared explicit
include_path gives every library a stable logical prefix independent
of Bazel's per-target output-dir layout — which is precisely why meld
sets it on every spec library.

Changes:
- proofs/BUILD.bazel: include_path = "LoomProofs" on all 8 libraries
  in the inter-dependent group (wasm_semantics, term_semantics, the
  five simplify libs, correctness). The standalone glob libraries
  (stack/codec/isle — no cross-proof deps) are unchanged.
- 17 cross-proof imports across 7 .v files rewritten to
  `From LoomProofs Require Import <Module>`.

stack/codec/isle proofs are independent single-file groups with no
inter-file deps, so they keep the plain glob layout.

Trace: REQ-12
Two mechanical Rocq-9.0 fixes in proofs/codec/Roundtrip.v, both
verified locally via `bazel build //proofs:codec_proofs` (now PASSES)
using the nix-hermetic Rocq 9.0.1 toolchain:

- functypes_roundtrip_n (was line 551): Rocq 9.0's `simpl` unfolds the
  `decode_functype` Definition itself, leaving no `decode_functype (_)`
  subterm for `rewrite functype_roundtrip`. Use
  `cbn [length encode_functypes decode_functypes_n]` so the structural
  fixpoints unfold but `decode_functype` stays folded for the rewrite.
- roundtrip_identity (was line 646): `rewrite <- !app_assoc` (one-or-
  more) errors because 9.0's `simpl` already right-associates the
  section appends. Changed to `<- ?app_assoc` (zero-or-more): a no-op
  when simpl already normalized, still correct otherwise.

Verified locally: codec_proofs, stack_proofs, isle_proofs all build.
TermSemantics.v is a separate, deeper issue (see PR discussion) — not
touched here.

Trace: REQ-12
…141)

Locally-verified foundation for fixing simplify_preserves_semantics
(per the Option-A well-typedness decision). Both new pieces compile
clean under the nix-hermetic Rocq 9.0 toolchain (verified: the build
error remains only at the pre-existing theorem below them):

- HasType : Term -> ValueType : a 42-rule static typing relation
  mirroring wasm. i64 comparisons produce TI32 even on TI64 operands;
  TDrop/TNop produce no value and so have no rule (they always TRFail).
- canonical_forms : HasType t ty -> a well-typed term evaluates to a
  value of its type (never TRFail), by induction on the derivation.

NOT yet done (theorem rework deferred — see PR discussion): the
identity arms (x+0=x, …) need a SECOND invariant beyond well-typedness
— a wrappedness invariant — because a TI32Const may hold an unwrapped
Z, making eval(x+0)=VI32(wrap32 v) <> VI32 v. Reworking the theorem
also changes its signature (adds the HasType hypothesis), which ripples
into the callers in Correctness.v and Identity.v. The old theorem is
left intact (still failing at its TI32Add case) rather than Admitted,
because its current statement `forall t, term_equiv t (simplify t)` is
FALSE and must not be admitted.

stack/codec/isle proof targets remain green (committed in d609a31).

Trace: REQ-12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant