Skip to content

Commit a96e119

Browse files
celinvaltedinski
authored andcommitted
Remove restriction to double fat pointer projection (rust-lang#1065)
We were restricting projections to only go through a max of one fat pointer dereference. This is not always the case, however, each projection should only care about the outer most fat pointer. Added tests to ensure this works as expected. It looks like size_of_val is returning the wrong values for the projection of traits. I'm adding fixme test cases for those here and I'll create a new issue for them.
1 parent 6b87bc7 commit a96e119

File tree

6 files changed

+255
-3
lines changed

6 files changed

+255
-3
lines changed

src/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,19 +318,16 @@ impl<'tcx> GotocCtx<'tcx> {
318318

319319
let inner_mir_typ_and_mut = base_type.builtin_deref(true).unwrap();
320320
let fat_ptr_mir_typ = if self.is_box_of_unsized(base_type) {
321-
assert!(before.fat_ptr_mir_typ.is_none());
322321
// If we have a box, its fat pointer typ is a pointer to the boxes inner type.
323322
Some(self.tcx.mk_ptr(inner_mir_typ_and_mut))
324323
} else if self.is_ref_of_unsized(base_type) {
325-
assert!(before.fat_ptr_mir_typ.is_none());
326324
Some(before.mir_typ_or_variant.expect_type())
327325
} else {
328326
before.fat_ptr_mir_typ
329327
};
330328

331329
let fat_ptr_goto_expr =
332330
if self.is_box_of_unsized(base_type) || self.is_ref_of_unsized(base_type) {
333-
assert!(before.fat_ptr_goto_expr.is_none());
334331
Some(inner_goto_expr.clone())
335332
} else {
336333
before.fat_ptr_goto_expr
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: --unwind 3
4+
5+
//! This test case checks the behavior of size_of_val for traits.
6+
use std::mem::size_of_val;
7+
8+
trait T {}
9+
10+
struct A {}
11+
12+
impl T for A {}
13+
14+
#[cfg_attr(kani, kani::proof)]
15+
fn check_size_simple() {
16+
let a = A {};
17+
let t: &dyn T = &a;
18+
assert_eq!(size_of_val(t), 0);
19+
assert_eq!(size_of_val(&t), 16);
20+
}
21+
22+
trait Wrapper<T: ?Sized> {
23+
fn inner(&self) -> &T;
24+
}
25+
26+
struct Concrete<'a, T: ?Sized> {
27+
inner: &'a T,
28+
}
29+
30+
impl<T: ?Sized> Wrapper<T> for Concrete<'_, T> {
31+
fn inner(&self) -> &T {
32+
self.inner
33+
}
34+
}
35+
36+
#[cfg_attr(kani, kani::proof)]
37+
fn check_size_inner() {
38+
let val = 10u8;
39+
let conc_wrapper: Concrete<u8> = Concrete { inner: &val };
40+
let trait_wrapper = &conc_wrapper as &dyn Wrapper<u8>;
41+
42+
assert_eq!(size_of_val(conc_wrapper.inner()), 1); // This is the size of val.
43+
assert_eq!(size_of_val(&conc_wrapper), 8); // This is the size of Concrete.
44+
assert_eq!(size_of_val(trait_wrapper), 8); // This is also the size of Concrete.
45+
assert_eq!(size_of_val(&trait_wrapper), 16); // This is the size of the fat pointer.
46+
}
47+
48+
// This can be run with rustc for comparison.
49+
fn main() {
50+
check_size_simple();
51+
check_size_inner();
52+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: --unwind 3
4+
5+
//! This test case checks the usage of dyn Trait<dyn Trait>.
6+
use std::mem::size_of_val;
7+
8+
trait Wrapper<T: ?Sized> {
9+
fn inner(&self) -> &T;
10+
}
11+
12+
struct Concrete<'a, T: ?Sized> {
13+
inner: &'a T,
14+
}
15+
16+
impl<T: ?Sized> Wrapper<T> for Concrete<'_, T> {
17+
fn inner(&self) -> &T {
18+
self.inner
19+
}
20+
}
21+
22+
#[cfg_attr(kani, kani::proof)]
23+
fn check_val() {
24+
let val = 20;
25+
let inner: Concrete<u8> = Concrete { inner: &val };
26+
let trait_inner: &dyn Wrapper<u8> = &inner;
27+
let original: Concrete<dyn Wrapper<u8>> = Concrete { inner: trait_inner };
28+
let wrapper = &original as &dyn Wrapper<dyn Wrapper<u8>>;
29+
assert_eq!(*wrapper.inner().inner(), val);
30+
}
31+
32+
#[cfg_attr(kani, kani::proof)]
33+
fn check_size() {
34+
let val = 10u8;
35+
let inner: Concrete<u8> = Concrete { inner: &val };
36+
let trait_inner: &dyn Wrapper<u8> = &inner;
37+
let original: Concrete<dyn Wrapper<u8>> = Concrete { inner: trait_inner };
38+
let wrapper = &original as &dyn Wrapper<dyn Wrapper<u8>>;
39+
40+
assert_eq!(size_of_val(wrapper), 16);
41+
assert_eq!(size_of_val(&wrapper.inner()), 16);
42+
assert_eq!(size_of_val(wrapper.inner()), 8);
43+
}
44+
45+
// For easy comparison, this allow us to run with rustc.
46+
fn main() {
47+
check_val();
48+
check_size();
49+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: --unwind 3
4+
5+
//! This test case checks the usage of dyn Trait<[u8]>.
6+
use std::mem::size_of_val;
7+
8+
trait Wrapper<T: ?Sized> {
9+
fn inner(&self) -> &T;
10+
}
11+
12+
struct Concrete<'a, T: ?Sized> {
13+
inner: &'a T,
14+
}
15+
16+
impl<T: ?Sized> Wrapper<T> for Concrete<'_, T> {
17+
fn inner(&self) -> &T {
18+
self.inner
19+
}
20+
}
21+
22+
#[cfg_attr(kani, kani::proof)]
23+
fn check_size() {
24+
let original: Concrete<[u8]> = Concrete { inner: &[1u8, 2u8] };
25+
let wrapper = &original as &dyn Wrapper<[u8]>;
26+
let mut sum = 0u8;
27+
for next in wrapper.inner() {
28+
sum += next;
29+
}
30+
assert_eq!(sum, 3);
31+
}
32+
33+
#[cfg_attr(kani, kani::proof)]
34+
fn check_iterator() {
35+
let original: Concrete<[u8]> = Concrete { inner: &[1u8, 2u8] };
36+
let wrapper = &original as &dyn Wrapper<[u8]>;
37+
assert_eq!(size_of_val(wrapper), 16);
38+
assert_eq!(size_of_val(&wrapper.inner()), 16);
39+
assert_eq!(size_of_val(wrapper.inner()), 2);
40+
assert_eq!(wrapper.inner().len(), 2);
41+
}
42+
43+
// Leave this here so it's easy to run with rustc.
44+
fn main() {
45+
check_iterator();
46+
check_size();
47+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// kani-flags: --unwind 3
5+
6+
//! Check that nested fat pointers work. This used to trigger an issue.
7+
//! The projection should only keep track of the inner most dereferenced
8+
//! element.
9+
//!
10+
//! See: https://github.com/model-checking/kani/issues/378
11+
12+
trait Trait {
13+
fn id(&self) -> u8;
14+
}
15+
16+
struct Concrete {
17+
pub id: u8,
18+
}
19+
20+
impl Trait for Concrete {
21+
fn id(&self) -> u8 {
22+
self.id
23+
}
24+
}
25+
26+
#[kani::proof]
27+
fn check_slice_boxed() {
28+
let boxed_t: &[Box<dyn Trait>] = &[Box::new(Concrete { id: 0 }), Box::new(Concrete { id: 1 })];
29+
30+
assert_eq!(boxed_t[0].id(), 0);
31+
assert_eq!(boxed_t[1].id(), 1);
32+
}
33+
34+
#[kani::proof]
35+
#[kani::unwind(3)]
36+
fn check_slice_boxed_iterator() {
37+
let boxed_t: &[Box<dyn Trait>] = &[Box::new(Concrete { id: 0 }), Box::new(Concrete { id: 1 })];
38+
39+
// Check iterator
40+
let mut sum = 0;
41+
let mut count = 0;
42+
for obj in boxed_t {
43+
sum += obj.id();
44+
count += 1;
45+
}
46+
assert_eq!(sum, 1);
47+
assert_eq!(count, 2);
48+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: --unwind 5
4+
5+
//! This test case checks the usage of slices of slices (&[&[T]]).
6+
use std::mem::size_of_val;
7+
8+
/// Structure with a raw string (i.e.: [char]).
9+
struct MyStr {
10+
header: u16,
11+
data: str,
12+
}
13+
14+
impl MyStr {
15+
/// This creates a MyStr from a byte slice.
16+
fn new(original: &mut String) -> &mut Self {
17+
let buf = original.get_mut(..).unwrap();
18+
assert!(size_of_val(buf) > 2, "This requires at least 2 bytes");
19+
let unsized_len = buf.len() - 2;
20+
let ptr = std::ptr::slice_from_raw_parts_mut(buf.as_mut_ptr(), unsized_len);
21+
unsafe { &mut *(ptr as *mut Self) }
22+
}
23+
}
24+
25+
#[kani::proof]
26+
fn sanity_check_my_str() {
27+
let mut buf = String::from("123456");
28+
let my_str = MyStr::new(&mut buf);
29+
my_str.header = 0;
30+
31+
assert_eq!(size_of_val(my_str), 6);
32+
assert_eq!(my_str.data.len(), 4);
33+
assert_eq!(my_str.data.chars().nth(0), Some('3'));
34+
assert_eq!(my_str.data.chars().nth(3), Some('6'));
35+
}
36+
37+
#[kani::proof]
38+
fn check_slice_my_str() {
39+
let mut buf_0 = String::from("000");
40+
let mut buf_1 = String::from("001");
41+
let my_slice = &[MyStr::new(&mut buf_0), MyStr::new(&mut buf_1)];
42+
assert_eq!(my_slice.len(), 2);
43+
44+
assert_eq!(my_slice[0].data.len(), 1);
45+
assert_eq!(my_slice[1].data.len(), 1);
46+
47+
assert_eq!(my_slice[0].data.chars().nth(0), Some('0'));
48+
assert_eq!(my_slice[1].data.chars().nth(0), Some('1'));
49+
}
50+
51+
#[kani::proof]
52+
fn check_size_of_val() {
53+
let mut buf_0 = String::from("000");
54+
let mut buf_1 = String::from("001");
55+
let my_slice = &[MyStr::new(&mut buf_0), MyStr::new(&mut buf_1)];
56+
assert_eq!(size_of_val(my_slice), 32); // Slice of 2 fat pointers.
57+
assert_eq!(size_of_val(my_slice[0]), 4); // Size of a fat pointer.
58+
assert_eq!(size_of_val(&my_slice[0].data), 1); // Size of str.
59+
}

0 commit comments

Comments
 (0)