Skip to content

Commit 2fedcb2

Browse files
authored
Merge pull request #2086 from cryspen/core-models-various-extensions
Core models extensions.
2 parents 7744f21 + 1553e8e commit 2fedcb2

40 files changed

Lines changed: 4178 additions & 1081 deletions

hax-lib/core-models/COVERAGE.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Some platform and runtime modules are not targeted and taken out of the count. S
88

99
## `core`
1010

11-
**Targeted coverage: 683/3888 items (18%) across 35 modules — 21 have at least a partial model.**
11+
**Targeted coverage: 706/3888 items (18%) across 35 modules — 21 have at least a partial model.**
1212

1313
| module | covered | total | coverage |
1414
|---|--:|--:|---|
@@ -30,13 +30,13 @@ Some platform and runtime modules are not targeted and taken out of the count. S
3030
| `f64` | 0 | 107 | 0% |
3131
| `fmt` | 9 | 111 | 8% |
3232
| `from` | 0 | 1 | 0% |
33-
| `hash` | 3 | 29 | 10% |
33+
| `hash` | 5 | 29 | 17% |
3434
| `hint` | 2 | 15 | 13% |
3535
| `index` | 0 | 2 | 0% |
3636
| `iter` | 27 | 170 | 16% |
3737
| `marker` | 6 | 35 | 17% |
3838
| `mem` | 19 | 80 | 24% |
39-
| `num` | 417 | 1835 | 23% |
39+
| `num` | 435 | 1835 | 24% |
4040
| `ops` | 84 | 151 | 56% |
4141
| `option` | 26 | 57 | 46% |
4242
| `panicking` | 2 | 31 | 6% |
@@ -45,18 +45,18 @@ Some platform and runtime modules are not targeted and taken out of the count. S
4545
| `ptr` | 0 | 191 | 0% |
4646
| `range` | 0 | 19 | 0% |
4747
| `result` | 30 | 42 | 71% |
48-
| `slice` | 8 | 205 | 4% |
48+
| `slice` | 11 | 205 | 5% |
4949
| `str` | 6 | 160 | 4% |
50-
| **subtotal** | **683** | **3888** | **18%** |
50+
| **subtotal** | **706** | **3888** | **18%** |
5151

52-
<details><summary>Non-targeted modules: 20 modules, 0/14842 items</summary>
52+
<details><summary>Non-targeted modules: 20 modules, 1/14842 items</summary>
5353

5454
`alloc`, `arch`, `async_iter`, `autodiff`, `bstr`, `contracts`, `ffi`, `future`, `intrinsics`, `io`, `net`, `os`, `panic`, `pat`, `random`, `simd`, `sync`, `task`, `time`, `unsafe_binder`
5555
</details>
5656

5757
## `alloc`
5858

59-
**Targeted coverage: 44/608 items (7%) across 11 modules — 7 have at least a partial model.**
59+
**Targeted coverage: 45/608 items (7%) across 11 modules — 7 have at least a partial model.**
6060

6161
| module | covered | total | coverage |
6262
|---|--:|--:|---|
@@ -70,8 +70,8 @@ Some platform and runtime modules are not targeted and taken out of the count. S
7070
| `slice` | 0 | 6 | 0% |
7171
| `str` | 0 | 1 | 0% |
7272
| `string` | 5 | 63 | 8% |
73-
| `vec` | 19 | 80 | 24% |
74-
| **subtotal** | **44** | **608** | **7%** |
73+
| `vec` | 20 | 80 | 25% |
74+
| **subtotal** | **45** | **608** | **7%** |
7575

7676
<details><summary>Non-targeted modules: 4 modules, 0/106 items</summary>
7777

hax-lib/core-models/alloc/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,13 @@ pub mod vec {
469469
}
470470
}
471471

472+
/// See [`std::vec::Vec::default`]: an empty `Vec`.
473+
impl<T> Default for Vec<T> {
474+
fn default() -> Vec<T> {
475+
Vec::new()
476+
}
477+
}
478+
472479
#[hax_lib::attributes]
473480
impl<T> Vec<T> {
474481
pub fn len(&self) -> usize {
@@ -524,6 +531,13 @@ pub mod vec {
524531
seq_concat(&mut self.0, &mut other.0);
525532
other.0 = seq_empty()
526533
}
534+
/// See [`std::vec::Vec::split_off`]: truncate `self` to `[0, at)` and
535+
/// return the tail `[at, len)` as a new `Vec`.
536+
#[hax_lib::requires(at <= self.len())]
537+
pub fn split_off(&mut self, at: usize) -> Vec<T> {
538+
let l = seq_len(&self.0);
539+
Vec(seq_drain(&mut self.0, at, l))
540+
}
527541
#[hax_lib::opaque]
528542
pub fn drain<R /* : RangeBounds<usize> */>(
529543
&mut self,
@@ -702,6 +716,13 @@ pub mod vec {
702716
seq_concat(&mut self.0, &mut other.0);
703717
other.0 = seq_empty()
704718
}
719+
/// See [`std::vec::Vec::split_off`]: truncate `self` to `[0, at)` and
720+
/// return the tail `[at, len)` as a new `Vec`.
721+
#[hax_lib::requires(at <= self.len())]
722+
pub fn split_off(&mut self, at: usize) -> Vec<T, A> {
723+
let l = seq_len(&self.0);
724+
Vec(seq_drain(&mut self.0, at, l), PhantomData)
725+
}
705726
#[hax_lib::opaque]
706727
pub fn drain<R /* : RangeBounds<usize> */>(&mut self, _range: R) -> drain::Drain<T, A> {
707728
let l = seq_len(&self.0);

hax-lib/core-models/alloc/src/vec/tests.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ proptest! {
101101
}
102102
}
103103

104+
#[test]
105+
fn test_split_off(v in prop::collection::vec(any::<u8>(), 0..50), at in 0usize..50) {
106+
if at <= v.len() {
107+
let mut model = v.inject();
108+
let mut std_v = v.clone();
109+
let model_tail = model.split_off(at);
110+
let std_tail = std_v.split_off(at);
111+
prop_assert_eq!(model, std_v.inject());
112+
prop_assert_eq!(model_tail, std_tail.inject());
113+
}
114+
}
115+
104116
#[test]
105117
fn test_append(v1 in prop::collection::vec(any::<u8>(), 0..50), v2 in prop::collection::vec(any::<u8>(), 0..50)) {
106118
let mut model1 = v1.inject();
@@ -156,6 +168,16 @@ fn test_with_capacity() {
156168
assert_eq!(model, std_v.inject());
157169
}
158170

171+
// Only the default variant models `Default`: adding the impl to the F* variant
172+
// would shift F*'s positional impl-block numbering and rename every `Vec` method.
173+
#[cfg(not(hax_backend_fstar))]
174+
#[test]
175+
fn test_default() {
176+
let model: super::Vec<u8> = Default::default();
177+
let std_v: std::vec::Vec<u8> = Default::default();
178+
assert_eq!(model, std_v.inject());
179+
}
180+
159181
// ----- Clone / PartialEq / IntoIterator -------
160182
//
161183
// The F* variant of `vec` models none of these three, so the tests below are

hax-lib/core-models/core-models/src/core/clone.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl<T> Clone for T {
2323
}
2424
}
2525

26-
macro_rules! clone_impl_for_int {
26+
macro_rules! clone_impl_for_copy {
2727
($($t:ty),*) => {
2828
$(
2929
impl crate::clone::Clone for $t {
@@ -36,7 +36,8 @@ macro_rules! clone_impl_for_int {
3636
}
3737

3838
#[cfg(not(hax_backend_fstar))]
39-
clone_impl_for_int!(
39+
clone_impl_for_copy!(
40+
core::primitive::bool,
4041
core::primitive::u8,
4142
core::primitive::u16,
4243
core::primitive::u32,
@@ -54,13 +55,25 @@ clone_impl_for_int!(
5455
#[cfg(test)]
5556
mod tests {
5657
use crate::testing::Inject;
58+
use pastey::paste;
5759
use proptest::prelude::*;
5860

59-
proptest! {
60-
#[test]
61-
fn test_clone(x in any::<u8>()) {
62-
let model = x.inject();
63-
prop_assert_eq!(model.clone(), model);
64-
}
61+
// For every `Copy` type with a `Clone` impl, check the model's `Clone`
62+
// agrees with std's on a random value.
63+
macro_rules! clone_tests {
64+
($($t:ident),*) => {
65+
paste! { $(
66+
proptest! {
67+
#[test]
68+
fn [<test_clone_ $t>](x in any::<$t>()) {
69+
prop_assert_eq!(crate::clone::Clone::clone(x.inject()), x.clone().inject());
70+
}
71+
}
72+
)* }
73+
};
6574
}
75+
76+
clone_tests!(
77+
bool, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
78+
);
6679
}

hax-lib/core-models/core-models/src/core/convert.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ trait Into<T> {
1919

2020
/// See [`std::convert::From`]
2121
#[hax_lib::attributes]
22-
trait From<T> {
22+
pub trait From<T> {
2323
/// See [`std::convert::From::from`]
2424
#[hax_lib::requires(true)]
2525
fn from(x: T) -> Self;
@@ -80,14 +80,16 @@ impl<T> From<T> for T {
8080

8181
/// See [`std::convert::AsRef`]
8282
#[hax_lib::attributes]
83-
trait AsRef<T> {
83+
pub trait AsRef<T: ?Sized> {
8484
/// See [`std::convert::AsRef::as_ref`]
8585
#[hax_lib::requires(true)]
86-
fn as_ref(self) -> T;
86+
fn as_ref(&self) -> &T;
8787
}
8888

89-
impl<T> AsRef<T> for T {
90-
fn as_ref(self) -> T {
89+
// The blanket `AsRef<T> for T` can't cover the unsized `[T]`, so we provide the
90+
// concrete slice impl.
91+
impl<T> AsRef<[T]> for [T] {
92+
fn as_ref(&self) -> &[T] {
9193
self
9294
}
9395
}
@@ -249,9 +251,14 @@ mod tests {
249251
prop_assert_eq!(super::Into::<u8>::into(x.inject()), x);
250252
}
251253

254+
// Model's `AsRef<[u8]>` vs std's, both projecting a slice to itself.
252255
#[test]
253-
fn test_as_ref_identity(x in any::<u8>()) {
254-
prop_assert_eq!(super::AsRef::as_ref(x.inject()), x);
256+
fn test_as_ref_slice_identity(v in prop::collection::vec(any::<u8>(), 0..=8)) {
257+
let s: &[u8] = &v[..];
258+
prop_assert_eq!(
259+
super::AsRef::<[u8]>::as_ref(s),
260+
core::convert::AsRef::<[u8]>::as_ref(s)
261+
);
255262
}
256263
}
257264

hax-lib/core-models/core-models/src/core/fmt.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,35 @@ impl<T> Debug for T {
4444
}
4545
}
4646

47+
// No blanket `Display` (unlike `Debug` above); spell out the integer impls,
48+
// stubbed to `Ok(())` like the rest of this module.
49+
macro_rules! impl_display_for_int {
50+
($($t:ty),*) => {
51+
$(
52+
impl Display for $t {
53+
fn fmt(&self, f: &mut Formatter) -> Result {
54+
Result::Ok(())
55+
}
56+
}
57+
)*
58+
};
59+
}
60+
61+
impl_display_for_int!(
62+
core::primitive::u8,
63+
core::primitive::u16,
64+
core::primitive::u32,
65+
core::primitive::u64,
66+
core::primitive::u128,
67+
core::primitive::usize,
68+
core::primitive::i8,
69+
core::primitive::i16,
70+
core::primitive::i32,
71+
core::primitive::i64,
72+
core::primitive::i128,
73+
core::primitive::isize
74+
);
75+
4776
impl<'a> Arguments<'a> {}
4877
impl<'a> Arguments<'a> {}
4978
impl<'a> Arguments<'a> {}
Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,49 @@
11
/// See [`std::hash::Hasher`]
2-
pub trait Hasher {}
2+
pub trait Hasher {
3+
/// See [`std::hash::Hasher::finish`]
4+
fn finish(&self) -> u64;
5+
/// See [`std::hash::Hasher::write`]
6+
fn write(&mut self, bytes: &[u8]);
7+
}
38

49
/// See [`std::hash::Hash`]
510
#[hax_lib::attributes]
611
pub trait Hash {
7-
/// See [`std::hash::Hash::hash`]
12+
/// See [`std::hash::Hash::hash`]. As elsewhere in the model, the hasher is
13+
/// threaded by value (`h: H` in, `H` out) rather than by `&mut`.
814
#[hax_lib::requires(true)]
915
fn hash<H: Hasher>(&self, h: H) -> H;
1016
}
1117

12-
// Temporary
13-
impl<T> Hash for T {
14-
fn hash<H: Hasher>(&self, h: H) -> H {
15-
crate::panicking::internal::panic()
16-
}
18+
// The integer `Hash` impls std keeps in `core::hash::impls`.
19+
//
20+
// DEVIATION(std): std feeds `to_ne_bytes()`; the abstract `Hasher` makes the
21+
// exact bytes unobservable, so we feed a single cast byte.
22+
macro_rules! impl_hash_for_int {
23+
($($t:ty),*) => {
24+
$(
25+
#[hax_lib::attributes]
26+
impl Hash for $t {
27+
fn hash<H: Hasher>(&self, mut h: H) -> H {
28+
h.write(&[*self as u8]);
29+
h
30+
}
31+
}
32+
)*
33+
};
1734
}
35+
36+
impl_hash_for_int!(
37+
core::primitive::u8,
38+
core::primitive::u16,
39+
core::primitive::u32,
40+
core::primitive::u64,
41+
core::primitive::u128,
42+
core::primitive::usize,
43+
core::primitive::i8,
44+
core::primitive::i16,
45+
core::primitive::i32,
46+
core::primitive::i64,
47+
core::primitive::i128,
48+
core::primitive::isize
49+
);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// See [`core::intrinsics::unreachable`]. UB in Rust; modeled as an unreachable
2+
/// panic, with `requires(false)` so callers must prove it is never hit.
3+
#[hax_lib::opaque]
4+
#[hax_lib::requires(false)]
5+
pub fn unreachable() -> ! {
6+
panic!()
7+
}

0 commit comments

Comments
 (0)