Extensions#15
Conversation
4ea35ec to
0dd2fb8
Compare
abentkamp
left a comment
There was a problem hiding this comment.
The solution for Vec looks great! I have a couple of comments.
| pub trait Allocator { | ||
| fn dummy(); | ||
| } |
There was a problem hiding this comment.
Why the dummy function? Can we document why it's here?
| 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 | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| 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"] |
There was a problem hiding this comment.
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>"] |
| 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) | ||
| } |
There was a problem hiding this comment.
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.
| -- `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 |
| /-! ## 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`. |
There was a problem hiding this comment.
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.
Various extensions needed for a proof project. The main decision was to switch the
Vecmodel to take no allocator, this goes together with a change in aeneas (merged to our core-models option in cryspen/aeneas/dev)