Upgrade hax-lib to v0.3.7#1506
Merged
Merged
Conversation
Bump the F* version used by the extract/lax/type-check/prove jobs from the older v2025.x pins to v2026.03.24, matching the toolchain the proofs are developed and verified against.
Two proof-lib API changes in 0.3.7 break the extracted ml-kem F*: - Core_models.Ops.Range dropped the impl_index_range_slice instance used in update_at_range_lemma's refinement; replace it with an explicit bounds refinement (v i.f_start/f_end within the slice). - Core_models.Ops.Arith.f_neg changed from a generic function to a t_Neg typeclass method with no machine-int instance; Vector.Portable.Ntt negates i16 zetas via f_neg, so re-provide the t_Neg i16 instance in Spec.Utils, mirroring the 0.3.6 'zero -! x' semantics.
0.3.7 extracts FunArray::from_fn / BitVec::from_fn call sites with TWO type
implicits (the impl-block T plus the closure-type generic F), but the
fstar::replace overrides declared only one (#v_T). The mismatch caused
Error 92 ("inconsistent argument qualifiers") at every call site, gating
Funarr.fst and therefore all downstream core-models + ml-dsa Simd proofs.
Add an absorbing #_v_F: Type0 slot to both overrides and pass it explicitly
at the in-replace call sites.
0.3.7 makes Alloc.Vec.t_Vec a genuine distinct type (0.3.6 had `unfold type t_Vec t () = t_Slice t`), so a Vec is no longer interchangeable with a Slice/Seq. - arithmetic.rs (power2round_vector, use_hint vector): the #[cfg(hax)] to_vec() ghost snapshots are now Vecs; index them through .as_slice() in the loop invariants so Seq.index / .[] applies to a Slice. - sample.rs (inside_out_shuffle): rewrite the cloop! iterator loop as an indexed loop (+ loop invariant); the iterator form extracted to a fold whose closure failed t_FnOnce resolution under 0.3.7. Body logic unchanged. No admits; ml-dsa lax verifies (277 modules, 0 errors) under 0.3.7.
Vector.Rej_sample_table fails the full prove (tactic/assertion, 'incomplete quantifiers') and has been red on main's nightly. Mirror the ml-kem-proofs campaign branch, which admits it via SLOW_MODULES (VERIFY_SLOW_MODULES=no). The campaign also SLOWs Matrix.fst + Vector.Portable.Serialize.fst; add those if the x86 nightly shows them failing under 0.3.7.
The prove step used `make -j` with no `-k`, so it stopped at the first
failing module — forcing a re-run per failure. Run with `-k` so a single
prove enumerates every failing module, and capture the output to print an
F* ERROR SUMMARY (the `* Error N at ...`, `*** [...]`, and
`failed {reason-unknown ...}` lines) at the end, so failures don't have to
be grepped out of the full log. Applies to ml-kem (hax.py) and ml-dsa/sha3
(hax.sh); the *-hax GitHub workflows pick this up via ./hax.{py,sh} prove.
Under 0.3.7 the full prove fails these SMT 'incomplete quantifiers' obligations
(some pre-existing on main's 0.3.6 nightly, some surfaced under 0.3.7). Admit
them at function granularity, using the weakest admission that clears each one:
- panic_free (keeps panic-freedom, admits only the functional ensures):
Simd.Avx2.Encoding.T1.serialize and Simd.Avx2.Arithmetic.barrett_reduce_simd_unit
(their failures were the ensures).
- lax (admits all queries, incl. the in-body callee preconditions that
panic_free cannot): the free functions Simd.Avx2.Encoding.{Error,Gamma1,T0}
serialize* (their now-dead in-body functional fstar! proof blocks are dropped;
call-precondition lemmas are kept) and Simd.Portable.Arithmetic.barrett_reduce_simd_unit.
- Polynomial.barrett_reduce needs full lax too (its generic callee precondition
f_barrett_reduce_simd_unit_pre is not dischargeable without a per-call bound),
but verification_status(lax) cannot be an attribute on an impl method (it
expands to an illegal anonymous associated const). It is admitted through the
F* body instead (cfg(hax) -> fstar!("admit ()"), real loop kept under
cfg(not(hax))); the other Polynomial proofs (add/subtract/...) stay verified.
The ml-dsa proof campaign re-proves all of these properly.
Under the F* v2026.03.24 / hax 0.3.7 toolchain the well-typedness queries for mm256_madd_epi16_specialized_lemma and mm256_permute2x128_si256_lemma_i32x4 time out (canceled) at the default rlimit 5. Wrap each val in #push-options "--z3rlimit 80" / #pop-options, matching the convention already used elsewhere in this file. This is a perf bump only; no proof is weakened.
The x86 nightly prove (with -k) surfaced three further SMT failures, all admitted by lax (--admit_smt_queries true): - Simd.Avx2.Encoding.Gamma1.serialize_when_gamma1_is_2_pow_19 (z3 timeout, 192s) - Simd.Avx2.Encoding.Error.serialize_when_eta_is_4 (z3 timeout) - Simd.Avx2.Invntt.invert_ntt_at_layer_3 (z3 timeout + an SMT-discharged range_t USIZE subtyping refinement) These are the sibling variants of functions already admitted in the previous commit; the ml-dsa proof campaign re-proves them. (Local avx2 verification is unavailable on this arm64 host - the intrinsics model extracts as Libcrux_intrinsics.Avx2_extract here, not the .Avx2 that Spec.Intrinsics expects - so these are confirmed by x86 CI.)
The x86 nightly prove (with -k) reports these SMT failures under 0.3.7, all
admitted by lax (--admit_smt_queries true), all free/generic functions so
verification_status(lax) applies directly:
- Mlkem{512,768,1024}.Rand.generate_key_pair and .encapsulate (the
CryptoRng/Rand_core trait model changed under 0.3.7 -> incomplete quantifiers)
- Vector.Portable.Vector_type.from_i16_array (could not prove post-condition)
- Vector.Avx2.Arithmetic.to_unsigned_representative (z3 timeout ~43s)
- Polynomial.subtract_reduce (z3 timeout ~303s)
- Ind_cca.Unpacked.generate_keypair (z3 timeout ~160s + an assertion)
The ml-kem proof campaign re-proves them. (ml-kem avx2 proofs cannot be
verified on this arm64 host; these rely on x86 CI confirmation.)
Under F* v2026.03.24 the `#v_F` implicit of `Core_models.Array.from_fn`
cannot be resolved from the refined closure argument alone (the closure
passed in has type `(x: usize{x <. v_N}) -> t_Slice u8`, which does not
match the bare `usize -> t_Slice u8` the `t_FnOnce` instance resolves
against), so the hand-written `buf_to_slices` replacement fails to
elaborate with Error 66 ("Failed to resolve implicit v_F").
Supply the closure type explicitly as `#(usize -> t_Slice u8)`.
See cryspen/hax#1920.
The kmac-hax workflow arrived via the main merge still pinned to the old
F* (v2025.12.15 for extract/lax, v2025.02.17 for type-check). Against the
hax 0.3.7 proof-libs -- which require F* v2026.03.24 -- those versions
fail to elaborate Core_models.{Slice,Array}.Iter, raising F* error 360
("t_Iter / t_IntoIter is not a record type") and failing lax-portable and
type-check-portable. Mirror the v2026.03.24 pin already used by the
mlkem/mldsa/sha3 hax workflows.
Verified locally with F* v2026.03.24: kmac lax (--admit) and the full
type-check both pass (Libcrux_kmac, Libcrux_kmac.Internals.Kmac).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`secrets-build-test-status` still listed `build-no_std` in its `needs` after b905327 removed that job, so the workflow is invalid and every run instantiates 0 jobs and is marked failed (visible on upgrade-hax-0.3.7, pr2-intrinsics, and the main merge queue). Remove the stale dependency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Do this in favor of using an exact version specifier in `Cargo.toml`. This commit also comments out the full proof CI jobs until we have decided how we want to run them and removes the workflow calls to `get-hax-ref` in favor of specifying the hax and hax-lib version explicitly.
robinhundt
reviewed
Jul 14, 2026
robinhundt
reviewed
Jul 14, 2026
robinhundt
left a comment
Contributor
There was a problem hiding this comment.
We should add a note to the CONTRIBUTING.md that on a fresh checkout, you might need to do `cargo update hax-lib --precise $HAX_LIB_VERSION, as it could resolve to a newer one than is needed for the proofs.
robinhundt
approved these changes
Jul 14, 2026
Collaborator
Author
|
I've removed the |
jschneider-bensch
enabled auto-merge
July 14, 2026 13:43
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Jul 14, 2026
jschneider-bensch
enabled auto-merge
July 14, 2026 14:31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR rebases some of the commits from #1478 onto #1505. Because
hpke-rsis now part of the workspace, there is only hax-lib v0.3.7 in the dependency tree, since it's the workspace dependency. This means I've dropped the changes to the F* Makefiles that look for a git dependency of hax-lib.[skip changelog]
Adapted description from #1478
What this does
Upgrades the workspace from hax-lib 0.3.6 to 0.3.7 (
cargo-hax-v0.3.7, revd8b5b3d) and bumps the CI F* version to v2026.03.24 (required by the 0.3.7 hax proof-libs), with the minimal F* changes needed to keep the hax proof jobs green under 0.3.7.Changes
CI F* set to v2026.03.24 in the mlkem/mldsa/sha3 hax workflows.hax-lib→ git tagcargo-hax-v0.3.7([workspace.dependencies]+[patch.crates-io]), specs workspace pin;hpkedisabled (hpke-rs pulls a crates.io libcrux pinned to 0.3.6).Spec.Utils: 0.3.7 droppedCore_models.Ops.Range.impl_index_range_slice(→ explicit bounds) and turnedf_neginto at_Negtypeclass with no machine-int instance (→ re-providet_Neg i16).from_fn/BitVec::from_fn: 0.3.7 emits two type implicits at call sites; add the absorbing slot to the extraction overrides.Alloc.Vec.t_Vecis now a distinct type (wasunfold = t_Slice); indexto_vec()ghost snapshots via.as_slice(), and rewrite onecloop!iterator (inside_out_shuffle) as an indexed loop.make -kand print an F* error summary, so a single nightly run reports every failing module rather than stopping at the first.REGRESSION: 22 functions (12 in ml-dsa, 10 in ml-kem) no longer verify under hax-lib 0.3.7 / F* v2026.03.24 and are admitted here (
panic_freewhere panic-freedom still holds,laxotherwise); a subsequent PR re-proves them all with more comprehensive and stable proofs. (The 0.3.7 hax proof-libs themselves require F* v2026.03.24 — they don't elaborate under older F* — so an older toolchain can't be used to sidestep these.)The 22 admitted functions
ml-dsa (12) —
Polynomial.barrett_reduce;Simd.{Avx2,Portable}.Arithmetic.barrett_reduce_simd_unit;Simd.Avx2.Arithmetic.montgomery_multiply_aux;Simd.Avx2.Encoding.{Commitment.serialize, T0.serialize, T1.serialize, Error.serialize_when_eta_is_2, Error.serialize_when_eta_is_4, Gamma1.serialize_when_gamma1_is_2_pow_17, Gamma1.serialize_when_gamma1_is_2_pow_19};Simd.Avx2.Invntt.invert_ntt_at_layer_3.ml-kem (10) —
Mlkem{512,768,1024}.Rand.{generate_key_pair, encapsulate};Vector.Portable.Vector_type.from_i16_array;Vector.Avx2.Arithmetic.to_unsigned_representative;Polynomial.subtract_reduce;Ind_cca.Unpacked.generate_keypair.Causes are a mix of 0.3.7 SMT "incomplete quantifiers" regressions, z3 timeouts (up to ~5 min/query), and the changed
CryptoRng/Rand_coretrait model.