Skip to content

Commit 5c29c49

Browse files
committed
tests: Re-activate some equivalence tests, and document better the cause for the ones that still cannot be activated.
1 parent c3a7794 commit 5c29c49

6 files changed

Lines changed: 214 additions & 61 deletions

File tree

tests/rust_lean_equiv_test/source/src/alloc/slice.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,5 +124,8 @@ pub fn test_slice_to_vec_then_push() -> bool {
124124

125125
// ----- closure-using methods (excluded) --------------------------------------
126126

127-
// TODO(closure-extraction): `[T]::sort_by_key`, `[T]::sort_by`, and other
128-
// closure-taking slice methods are skipped until Fn/FnMut extraction lands.
127+
// TODO(slice-method-missing): `[T]::sort_by_key`, `[T]::sort_by`, and other
128+
// closure-taking slice methods are skipped — Aeneas handles closures
129+
// (see core::array::from_fn) but these methods are not in the model:
130+
// `alloc_models::slice::_::sort_by` is in `ALLOC_CHARON_EXCLUDES` and the
131+
// generic slice impl is not provided either.

tests/rust_lean_equiv_test/source/src/alloc/vec.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
//! - Indexing `v[i]` is in `ALLOC_CHARON_EXCLUDES` (see top-level `Makefile`),
88
//! so we verify per-element contents by sequential `pop()`s instead of
99
//! `v[i] == x`.
10-
//! - Several methods (`remove`, `clear`, `swap_remove`, `truncate`,
11-
//! `resize`, `drain`) are `#[hax_lib::opaque]` in the model, so the Lean
12-
//! side cannot observe their effect. Those are commented out.
1310
1411
use rust_lean_test_macro::rust_lean_test;
1512

@@ -254,21 +251,17 @@ pub fn test_vec_from_elem_zero_len() -> bool {
254251
// TODO(vec-index-excluded): Vec::from_iter is in ALLOC_CHARON_EXCLUDES.
255252

256253
// ----- remove ----------------------------------------------------------------
257-
//
258-
// `Vec::remove` is `#[hax_lib::opaque]` in the model, but Aeneas still
259-
// extracts the underlying `seq_remove` body, so the Lean side mirrors
260-
// the real removal semantics.
261254

262255
// TODO(vec-extraction-arity-mismatch: the Lean model marks `Vec.is_empty`/`as_slice`/`pop`/... with implicit `{A}`, but Aeneas extracts the call applying the allocator explicitly. Until that mismatch is fixed in the model, `Vec::*` tests fail.)
263-
/*
264-
#[rust_lean_test]
256+
257+
/* #[rust_lean_test]
265258
pub fn test_vec_remove_only_element() -> bool {
266259
let mut v: Vec<u8> = Vec::new();
267260
v.push(7);
268261
let x = v.remove(0);
269262
x == 7 && v.is_empty()
270-
}
271-
*/
263+
} */
264+
272265

273266
// ----- swap_remove -----------------------------------------------------------
274267

@@ -311,11 +304,14 @@ pub fn test_vec_swap_remove_back() -> bool {
311304

312305
// ----- drain (iterator) ------------------------------------------------------
313306

314-
// TODO(closure-extraction): Vec::drain returns an iterator we don't have
307+
// TODO(vec-iter-extraction): Vec::drain returns an iterator we don't have
315308
// a stable way to drive in extracted Lean yet.
316309

317310
// ----- closure-using methods (excluded) --------------------------------------
318311

319-
// TODO(closure-extraction): Vec::retain takes a closure; revisit when we
320-
// exercise Fn/FnMut.
321-
// TODO(closure-extraction): Vec::iter / Vec::into_iter use iterator traits.
312+
// TODO(closure-extraction + vec-extraction-arity-mismatch): Vec::retain takes
313+
// a closure. Aeneas can extract the closure (see core::array::from_fn) but
314+
// every Vec method first trips on the arity-mismatch issue above. Revisit
315+
// once Vec tests can compile in the first place.
316+
// TODO(vec-iter-extraction): Vec::iter / Vec::into_iter use iterator traits
317+
// whose Lean models we don't have yet.

tests/rust_lean_equiv_test/source/src/core/array.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,27 +61,26 @@ pub fn test_eq_all_zeros() -> bool {
6161
a == b
6262
}
6363

64+
#[rust_lean_test]
65+
pub fn test_from_fn_identity() -> bool {
66+
let a: [u8; 4] = core::array::from_fn(|i| i as u8);
67+
a == [0, 1, 2, 3]
68+
}
69+
6470
// ----- map / from_fn / each_ref ---------------------------------------------
6571

66-
// TODO(closure-extraction): `[T; N]::map` takes a closure; Aeneas emits an
67-
// opaque def for the closure and the `Fn` instance has to live in the type
68-
// universe. Revisit when closure extraction lands.
69-
// pub fn test_map_add_one() -> bool {
70-
// let a: [u8; 4] = [1, 2, 3, 4];
71-
// a.map(|x| x + 1) == [2, 3, 4, 5]
72-
// }
72+
// TODO: `[T; N]::map` is not defined yet
73+
/* pub fn test_map_add_one() -> bool {
74+
let a: [u8; 4] = [1, 2, 3, 4];
75+
a.map(|x| x + 1) == [2, 3, 4, 5]
76+
} */
7377

74-
// TODO(closure-extraction): `core::array::from_fn` takes a closure.
75-
// pub fn test_from_fn_identity() -> bool {
76-
// let a: [u8; 4] = core::array::from_fn(|i| i as u8);
77-
// a == [0, 1, 2, 3]
78-
// }
7978

8079
// TODO(each-ref-refs): `each_ref` returns `[&T; N]`; arrays of references
8180
// through Aeneas extraction are tricky. Revisit when references-in-arrays
8281
// modelling lands.
83-
// pub fn test_each_ref_first() -> bool {
84-
// let a: [u8; 4] = [1, 2, 3, 4];
85-
// let refs: [&u8; 4] = a.each_ref();
86-
// *refs[0] == 1
87-
// }
82+
/* pub fn test_each_ref_first() -> bool {
83+
let a: [u8; 4] = [1, 2, 3, 4];
84+
let refs: [&u8; 4] = a.each_ref();
85+
*refs[0] == 1
86+
} */

tests/rust_lean_equiv_test/source/src/core/iter.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,26 @@ pub fn test_range_count_offset() -> bool {
3737

3838
// ----- skipped: closure-based iterator combinators --------------------------
3939

40-
// TODO(closure-extraction): `Iterator::fold` takes a closure; Aeneas emits
41-
// an opaque def for the closure and the `Fn` instance has to live in the
42-
// type universe.
40+
// TODO(slice-iter-extraction): closure-based iterator combinators like
41+
// `fold` extract through Aeneas, but they bottom out in `slice::Iter::fold`
42+
// (and friends) which are missing from the extracted Lean. Tried
43+
// `[1u32,2,3,4].iter().fold(0u32, |acc, &x| acc + x)` — Aeneas emits the
44+
// closure fine, then Lean fails with
45+
// `Unknown constant CoreModels.core.slice.Slice.iter`
46+
// `Unknown constant CoreModels.core.slice.iter.Iter.Insts...fold`.
47+
// Revisit once slice iterators are modelled.
4348
// pub fn test_fold_sum() -> bool {
4449
// let v = [1u32, 2, 3, 4];
4550
// v.iter().fold(0u32, |acc, &x| acc + x) == 10
4651
// }
4752

48-
// TODO(closure-extraction): `Iterator::map` / `Iterator::filter` /
53+
// TODO(slice-iter-extraction): `Iterator::map` / `Iterator::filter` /
4954
// `Iterator::any` / `Iterator::all` / `Iterator::find` /
5055
// `Iterator::find_map` / `Iterator::position` / `Iterator::for_each` /
51-
// `Iterator::reduce` all take closures.
56+
// `Iterator::reduce` all take closures. Aeneas handles the closure side
57+
// (see `core::array::test_from_fn_identity`), but on slice iterators they
58+
// bottom out in `slice::Iter::*` which is missing from the model — same
59+
// blocker as `fold` above.
5260
// pub fn test_map_collect_sum() -> bool {
5361
// let v = [1u32, 2, 3];
5462
// v.iter().map(|x| x + 1).fold(0u32, |a, b| a + b) == (2 + 3 + 4)
@@ -66,7 +74,10 @@ pub fn test_range_count_offset() -> bool {
6674
// [10u32, 20, 30].iter().position(|x| *x == 20) == Some(1)
6775
// }
6876

69-
// TODO(closure-extraction): `core::iter::from_fn` takes a closure.
77+
// `core::iter::from_fn` builds an iterator from a closure. Aeneas extracts
78+
// the closure, but the resulting `iter::FromFn` adapter is not modelled, so
79+
// any observation (`count`, `fold`, etc.) hits an unknown-constant error in
80+
// Lean. Revisit once `iter::FromFn` is added to the model.
7081
// pub fn test_from_fn() -> bool { ... }
7182

7283
// TODO(slice-iter-extraction): `[T; N]::iter()` / `[T]::iter()` produce a

tests/rust_lean_equiv_test/source/src/core/option.rs

Lines changed: 141 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,37 @@ pub fn test_is_none_some_max() -> bool {
5555

5656
// ----- is_some_and -----------------------------------------------------------
5757

58-
// TODO(closure-extraction): is_some_and takes a closure; revisit when we exercise FnOnce.
58+
#[rust_lean_test]
59+
pub fn test_is_some_and_some_true() -> bool {
60+
Some(4u8).is_some_and(|x| x > 0) == true
61+
}
62+
63+
#[rust_lean_test]
64+
pub fn test_is_some_and_some_false() -> bool {
65+
Some(0u8).is_some_and(|x| x > 0) == false
66+
}
67+
68+
#[rust_lean_test]
69+
pub fn test_is_some_and_none() -> bool {
70+
none_u8().is_some_and(|x| x > 0) == false
71+
}
5972

6073
// ----- is_none_or ------------------------------------------------------------
6174

62-
// TODO(closure-extraction): is_none_or takes a closure; revisit when we exercise FnOnce.
75+
#[rust_lean_test]
76+
pub fn test_is_none_or_some_true() -> bool {
77+
Some(4u8).is_none_or(|x| x > 0) == true
78+
}
79+
80+
#[rust_lean_test]
81+
pub fn test_is_none_or_some_false() -> bool {
82+
Some(0u8).is_none_or(|x| x > 0) == false
83+
}
84+
85+
#[rust_lean_test]
86+
pub fn test_is_none_or_none() -> bool {
87+
none_u8().is_none_or(|x| x > 0) == true
88+
}
6389

6490
// ----- as_ref ----------------------------------------------------------------
6591

@@ -154,7 +180,20 @@ pub fn test_unwrap_or_none_default_max() -> bool {
154180

155181
// ----- unwrap_or_else --------------------------------------------------------
156182

157-
// TODO(closure-extraction): unwrap_or_else takes a closure; revisit when we exercise FnOnce.
183+
#[rust_lean_test]
184+
pub fn test_unwrap_or_else_some_zero() -> bool {
185+
Some(0u8).unwrap_or_else(|| 42) == 0
186+
}
187+
188+
#[rust_lean_test]
189+
pub fn test_unwrap_or_else_some_max() -> bool {
190+
Some(u8::MAX).unwrap_or_else(|| 0) == u8::MAX
191+
}
192+
193+
#[rust_lean_test]
194+
pub fn test_unwrap_or_else_none() -> bool {
195+
none_u8().unwrap_or_else(|| 7) == 7
196+
}
158197

159198
// ----- unwrap_or_default -----------------------------------------------------
160199

@@ -184,19 +223,60 @@ pub fn test_unwrap_or_default_none() -> bool {
184223

185224
// ----- map -------------------------------------------------------------------
186225

187-
// TODO(closure-extraction): map takes a closure; revisit when we exercise FnOnce.
226+
// TODO(closure-extraction-map): `Option::map` returns `Option<U>`; observing the
227+
// result requires either `Option::PartialEq` (blocked by option-eq-extraction)
228+
// or destructuring through `match`, but the latter goes back through
229+
// `Option::PartialEq` once we compare the inner value. Revisit alongside
230+
// option-eq-extraction.
231+
232+
#[rust_lean_test]
233+
pub fn test_map_some_via_unwrap_or() -> bool {
234+
Some(3u8).map(|x| x + 1).unwrap_or(0) == 4
235+
}
236+
237+
#[rust_lean_test]
238+
pub fn test_map_none_via_unwrap_or() -> bool {
239+
none_u8().map(|x| x + 1).unwrap_or(99) == 99
240+
}
241+
242+
#[rust_lean_test]
243+
pub fn test_map_some_is_some() -> bool {
244+
Some(3u8).map(|x| x + 1).is_some()
245+
}
246+
247+
#[rust_lean_test]
248+
pub fn test_map_none_is_none() -> bool {
249+
none_u8().map(|x| x + 1).is_none()
250+
}
188251

189252
// ----- map_or ----------------------------------------------------------------
190253

191-
// TODO(closure-extraction): map_or takes a closure; revisit when we exercise FnOnce.
254+
#[rust_lean_test]
255+
pub fn test_map_or_some_zero() -> bool {
256+
Some(3u8).map_or(0u8, |x| x + 1) == 4
257+
}
258+
259+
#[rust_lean_test]
260+
pub fn test_map_or_some_max() -> bool {
261+
Some(0u8).map_or(99u8, |x| x + 1) == 1
262+
}
263+
264+
#[rust_lean_test]
265+
pub fn test_map_or_none() -> bool {
266+
none_u8().map_or(99u8, |x| x + 1) == 99
267+
}
192268

193269
// ----- map_or_else -----------------------------------------------------------
194270

195-
// TODO(closure-extraction): map_or_else takes two closures; revisit when we exercise FnOnce.
271+
// TODO(closure-extraction-order): `Option::map_or_else` takes (default: D, f: F)
272+
// in source order but the model lists the trait instances in (F, D) order, so
273+
// Aeneas emits them swapped and the Lean elaboration fails for the `None`
274+
// branch. Revisit once the closure-instance ordering is fixed.
196275

197276
// ----- map_or_default --------------------------------------------------------
198277

199-
// TODO(closure-extraction): map_or_default takes a closure; revisit when we exercise FnOnce.
278+
// TODO(map_or_default-unstable): `Option::map_or_default` is gated behind
279+
// `result_option_map_or_default` on stable. Revisit once it stabilises.
200280

201281
// ----- ok_or -----------------------------------------------------------------
202282

@@ -220,9 +300,40 @@ pub fn test_ok_or_none_err_max() -> bool {
220300
matches!(none_u8().ok_or(u8::MAX), Err(v) if v == u8::MAX)
221301
}
222302

303+
// ----- ok_or_else ------------------------------------------------------------
304+
305+
#[rust_lean_test]
306+
pub fn test_ok_or_else_some() -> bool {
307+
matches!(Some(7u8).ok_or_else(|| 99u8), Ok(7))
308+
}
309+
310+
#[rust_lean_test]
311+
pub fn test_ok_or_else_none() -> bool {
312+
matches!(none_u8().ok_or_else(|| 42u8), Err(42))
313+
}
314+
315+
// ----- and_then --------------------------------------------------------------
316+
317+
#[rust_lean_test]
318+
pub fn test_and_then_some_to_some() -> bool {
319+
Some(3u8).and_then(|x| Some(x + 1)).unwrap_or(0) == 4
320+
}
321+
322+
#[rust_lean_test]
323+
pub fn test_and_then_some_to_none() -> bool {
324+
Some(3u8).and_then(|_| none_u8()).is_none()
325+
}
326+
327+
#[rust_lean_test]
328+
pub fn test_and_then_none() -> bool {
329+
none_u8().and_then(|x| Some(x + 1)).is_none()
330+
}
331+
223332
// ----- filter ----------------------------------------------------------------
224333

225-
// TODO(closure-extraction): filter takes a closure; revisit when we exercise FnOnce.
334+
// TODO(closure-by-ref-extraction): `Option::filter` takes `FnOnce(&T) -> bool`;
335+
// Aeneas trips on the borrow with "Region ids should not be visited directly".
336+
// Revisit once closures whose parameter is a reference extract.
226337

227338
// ----- or --------------------------------------------------------------------
228339

@@ -257,7 +368,25 @@ pub fn test_or_none_none() -> bool {
257368

258369
// ----- or_else ---------------------------------------------------------------
259370

260-
// TODO(closure-extraction): or_else takes a closure; revisit when we exercise FnOnce.
371+
#[rust_lean_test]
372+
pub fn test_or_else_some_kept() -> bool {
373+
Some(0u8).or_else(|| Some(99u8)).unwrap_or(7) == 0
374+
}
375+
376+
#[rust_lean_test]
377+
pub fn test_or_else_some_max_kept() -> bool {
378+
Some(u8::MAX).or_else(|| none_u8()).unwrap_or(0) == u8::MAX
379+
}
380+
381+
#[rust_lean_test]
382+
pub fn test_or_else_none_to_some() -> bool {
383+
none_u8().or_else(|| Some(42u8)).unwrap_or(0) == 42
384+
}
385+
386+
#[rust_lean_test]
387+
pub fn test_or_else_none_to_none() -> bool {
388+
none_u8().or_else(|| none_u8()).is_none()
389+
}
261390

262391
// ----- xor -------------------------------------------------------------------
263392

@@ -337,7 +466,9 @@ pub fn test_zip_none_none() -> bool {
337466

338467
// ----- inspect ---------------------------------------------------------------
339468

340-
// TODO(closure-extraction): inspect takes a closure; revisit when we exercise FnOnce.
469+
// TODO(closure-by-ref-extraction): `Option::inspect` takes `FnOnce(&T)`;
470+
// Aeneas trips on the borrow with "Region ids should not be visited directly".
471+
// Revisit once closures whose parameter is a reference extract.
341472

342473
// ----- flatten ---------------------------------------------------------------
343474

0 commit comments

Comments
 (0)