Skip to content

Extensions#15

Open
maximebuyse wants to merge 6 commits into
mainfrom
spqr-extensions
Open

Extensions#15
maximebuyse wants to merge 6 commits into
mainfrom
spqr-extensions

Conversation

@maximebuyse

@maximebuyse maximebuyse commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Various extensions needed for a proof project. The main decision was to switch the Vec model to take no allocator, this goes together with a change in aeneas (merged to our core-models option in cryspen/aeneas/dev)

@maximebuyse maximebuyse marked this pull request as ready for review June 8, 2026 09:48
@maximebuyse maximebuyse requested a review from abentkamp June 8, 2026 09:48

@abentkamp abentkamp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The solution for Vec looks great! I have a couple of comments.

Comment thread alloc/src/lib.rs
Comment on lines +12 to +14
pub trait Allocator {
fn dummy();
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the dummy function? Can we document why it's here?

Comment on lines +256 to +268
fn eq(&self, other: &[U]) -> bool {
if self.len() != other.len() {
false
} else {
let mut res = true;
for i in 0..self.len() {
if !self[i].eq(&other[i]) {
res = false;
}
}
res
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should short-circuit when a mismatch is found, right? Later comparisons might panic, which would cause a behavior that's different from the official implementation.

Comment on lines +51 to +56
The `@[rust_fun "…"]` attribute binds the Rust path of the provided method to
this body, so the downstream name-map lands here. The body just builds the
`Map` adapter; iteration then runs through `Map`'s own `Iterator` instance.
`F` is the closure, `T` the item, `O` its output (the `FnMut` instance is
irrelevant to the model, hence `_`-prefixed). -/
@[rust_fun "alloc::vec::into_iter::{core::iter::traits::iterator::Iterator<alloc::vec::into_iter::IntoIter<@T, @A>, @T>}::map"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the @[rust_fun] attribute necessary? Isn't that needed only for Aeneas's core setup?

super-instance (the `iteratorIteratorInst` field was dropped), so there is no
`next` to drive a fold here. Refine if downstream reasoning depends on the
contents of a `VecDeque::from_iter` result. -/
@[rust_trait_impl "core::iter::traits::collect::FromIterator<alloc::collections::vec_deque::VecDeque<@T, alloc::alloc::Global>, @T>"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here.

Comment on lines +72 to +87
NOTE: this is a *stub* — `from_iter` returns an empty deque. We cannot model
the real collect: core-models' `IntoIterator` carries no `Iterator`
super-instance (the `iteratorIteratorInst` field was dropped), so there is no
`next` to drive a fold here. Refine if downstream reasoning depends on the
contents of a `VecDeque::from_iter` result. -/
@[rust_trait_impl "core::iter::traits::collect::FromIterator<alloc::collections::vec_deque::VecDeque<@T, alloc::alloc::Global>, @T>"]
def collections.vec_deque.VecDequeTGlobal.Insts.CoreIterTraitsCollectFromIterator
(T : Type) :
core.iter.traits.collect.FromIterator
(collections.vec_deque.VecDeque T alloc.Global) T := {
from_iter := fun {U Item IntoIter}
(_IntoIteratorInst : core.iter.traits.collect.IntoIterator U Item IntoIter)
(_iter : U) => do
let s ← rust_primitives.sequence.seq_empty T
Aeneas.Std.Result.ok (s, core.marker.PhantomData.mk)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use Lean's opaque command instead of a def?

opaque collections.vec_deque.VecDequeTGlobal.Insts.CoreIterTraitsCollectFromIterator.from_iter
  (T : Type) : {T_1 Clause0_Item Clause0_IntoIter : Type} →
  core.iter.traits.collect.IntoIterator T_1 Clause0_Item Clause0_IntoIter →
  T_1 → Aeneas.Std.Result (VecDeque T alloc.Global)

def collections.vec_deque.VecDequeTGlobal.Insts.CoreIterTraitsCollectFromIterator
  (T : Type) :
  core.iter.traits.collect.FromIterator
    (collections.vec_deque.VecDeque T alloc.Global) T := {
  from_iter := collections.vec_deque.VecDequeTGlobal.Insts.CoreIterTraitsCollectFromIterator.from_iter T
}

That will make the constant available without giving a definition, similar to an axiom. But in contrast to an axiom, it will not introduce extra logical assumptions (using the fact that Aeneas.Std.Result is inhabited).

That way we could avoid defining a model that is not accurate.

Comment on lines +114 to +119
-- `vec.Vec.new` / `vec.Vec.with_capacity` are now extracted directly into
-- `CoreModels.Alloc` (and `vec.VecTGlobal` no longer exists, since `vec.Vec`
-- dropped its allocator type parameter), so these manual re-exports are
-- obsolete:
-- def vec.Vec.new := @vec.VecTGlobal.new
-- def vec.Vec.with_capacity := @vec.VecTGlobal.with_capacity

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this comment?

Comment on lines +22 to +33
/-! ## PhantomData — replaces Aeneas's reducible alias

Aeneas extracts `core::marker::PhantomData` as the reducible alias
`def marker.PhantomData (T) := T` and builds phantom values with `()` (Charon
models it as a ZST). That alias is unusable here: where `PhantomData A` is the
second component of a pair (e.g. `vec.drain.Drain T A := Seq T × PhantomData A`),
Lean unfolds it and loses `A` during unification.

So we model it as a *non-reducible* `structure` instead, which keeps `A`
syntactically present. `patch_lean.py`'s `rewrite_phantom_data` rewires the
Aeneas output onto this carrier: it comments out the generated alias and
rewrites the `()` constructor sites to `core.marker.PhantomData.mk`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this explanation is wrong. The issue is not unfolding during unification.

The issue is that when A is abstract (or any type other than alloc.Global or Unit), the value () does not have the right type because () is of type Unit and not of type PhantomData A or A.

A simpler solution might be to define

def marker.PhantomData (A : Type) := Unit

and to remove the () -> core.marker.PhantomData.mk replacement.

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.

2 participants