Skip to content

Commit ddd6005

Browse files
committed
Handle change to Box<T> structure
Box<T> now users NonNull<T> instead of raw pointer.
1 parent 1443181 commit ddd6005

File tree

1 file changed

+50
-28
lines changed
  • kani-compiler/src/codegen_cprover_gotoc/utils

1 file changed

+50
-28
lines changed

kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,17 @@ impl<'tcx> GotocCtx<'tcx> {
102102
}
103103
}
104104

105+
/// Members traverse path to get to the raw pointer of a box (b.0.pointer.pointer).
106+
const RAW_PTR_FROM_BOX: [&str; 3] = ["0", "pointer", "pointer"];
107+
105108
impl<'tcx> GotocCtx<'tcx> {
106109
/// Dereference a boxed type `std::boxed::Box<T>` to get a `*T`.
107110
///
108111
/// WARNING: This is based on a manual inspection of how boxed types are currently
109112
/// a) implemented by the rust standard library
110113
/// b) codegenned by Kani.
111114
/// If either of those change, this will almost certainly stop working.
112-
pub fn deref_box(&self, e: Expr) -> Expr {
115+
pub fn deref_box(&self, box_expr: Expr) -> Expr {
113116
// Internally, a Boxed type is stored as a chain of structs.
114117
// In particular:
115118
// `Box<T>` is an owning reference to an allocation of type T on the heap.
@@ -137,35 +140,39 @@ impl<'tcx> GotocCtx<'tcx> {
137140
// };
138141
// ```
139142
// And notice that its `.pointer` field is exactly what we want.
140-
self.assert_is_rust_box_like(e.typ());
141-
e.member("0", &self.symbol_table).member("pointer", &self.symbol_table)
143+
self.assert_is_rust_box_like(box_expr.typ());
144+
RAW_PTR_FROM_BOX.iter().fold(box_expr, |expr, name| expr.member(name, &self.symbol_table))
142145
}
143146

144147
/// Box<T> initializer
145-
/// `boxed_type the_box = >>> { .0=nondet(), .1={ ._marker=nondet(), .pointer=boxed_value } } <<<`
148+
///
149+
/// Traverse over the Box representation and only initialize the raw_ptr field. All other
150+
/// members are left uninitialized.
146151
/// `boxed_type` is the type of the resulting expression
147152
pub fn box_value(&self, boxed_value: Expr, boxed_type: Type) -> Expr {
148153
self.assert_is_rust_box_like(&boxed_type);
149-
let unique_ptr_typ = boxed_type.lookup_field_type("0", &self.symbol_table).unwrap();
150-
self.assert_is_rust_unique_pointer_like(&unique_ptr_typ);
151-
let unique_ptr_pointer_typ =
152-
unique_ptr_typ.lookup_field_type("pointer", &self.symbol_table).unwrap();
153-
assert_eq!(&unique_ptr_pointer_typ, boxed_value.typ());
154-
let unique_ptr_val = Expr::struct_expr_with_nondet_fields(
155-
unique_ptr_typ,
156-
btree_string_map![("pointer", boxed_value),],
157-
&self.symbol_table,
158-
);
159-
let boxed_val = Expr::struct_expr_with_nondet_fields(
160-
boxed_type,
161-
btree_string_map![("0", unique_ptr_val),],
162-
&self.symbol_table,
163-
);
164-
boxed_val
154+
tracing::debug!(?boxed_type, ?boxed_value, "box_value");
155+
let mut inner_type = boxed_type;
156+
let type_members = RAW_PTR_FROM_BOX
157+
.iter()
158+
.map(|name| {
159+
let outer_type = inner_type.clone();
160+
inner_type = outer_type.lookup_field_type(name, &self.symbol_table).unwrap();
161+
(*name, outer_type)
162+
})
163+
.collect::<Vec<_>>();
164+
165+
type_members.iter().rev().fold(boxed_value, |value, (name, typ)| {
166+
Expr::struct_expr_with_nondet_fields(
167+
typ.clone(),
168+
btree_string_map![(*name, value),],
169+
&self.symbol_table,
170+
)
171+
})
165172
}
166173

167174
/// Best effort check if the struct represents a rust "std::alloc::Global".
168-
pub fn assert_is_rust_global_alloc_like(&self, t: &Type) {
175+
fn assert_is_rust_global_alloc_like(&self, t: &Type) {
169176
// TODO: A std::alloc::Global appears to be an empty struct, in the cases we've seen.
170177
// Is there something smarter we can do here?
171178
assert!(t.is_struct_like());
@@ -174,7 +181,7 @@ impl<'tcx> GotocCtx<'tcx> {
174181
}
175182

176183
/// Best effort check if the struct represents a rust "std::marker::PhantomData".
177-
pub fn assert_is_rust_phantom_data_like(&self, t: &Type) {
184+
fn assert_is_rust_phantom_data_like(&self, t: &Type) {
178185
// TODO: A std::marker::PhantomData appears to be an empty struct, in the cases we've seen.
179186
// Is there something smarter we can do here?
180187
assert!(t.is_struct_like());
@@ -183,7 +190,7 @@ impl<'tcx> GotocCtx<'tcx> {
183190
}
184191

185192
/// Best effort check if the struct represents a Rust "Box". May return false positives.
186-
pub fn assert_is_rust_box_like(&self, t: &Type) {
193+
fn assert_is_rust_box_like(&self, t: &Type) {
187194
// struct std::boxed::Box<[u8; 8]>::15334369982748499855
188195
// {
189196
// // 1
@@ -204,25 +211,40 @@ impl<'tcx> GotocCtx<'tcx> {
204211
}
205212

206213
/// Checks if the struct represents a Rust "std::ptr::Unique"
207-
pub fn assert_is_rust_unique_pointer_like(&self, t: &Type) {
214+
fn assert_is_rust_unique_pointer_like(&self, t: &Type) {
208215
// struct std::ptr::Unique<[u8; 8]>::14713681870393313245
209216
// {
210217
// // _marker
211218
// struct std::marker::PhantomData<[u8; 8]>::18073278521438838603 _marker;
212219
// // pointer
213-
// struct [u8::16712579856250238426; 8] *pointer;
220+
// NonNull<T> pointer;
214221
// };
215222
assert!(t.is_struct_like());
216223
let components = t.lookup_components(&self.symbol_table).unwrap();
217224
assert_eq!(components.len(), 2);
218225
for c in components {
219226
match c.name().to_string().as_str() {
220227
"_marker" => self.assert_is_rust_phantom_data_like(&c.typ()),
221-
"pointer" => {
222-
assert!(c.typ().is_pointer() || c.typ().is_rust_fat_ptr(&self.symbol_table))
223-
}
228+
"pointer" => self.assert_is_non_null_like(&c.typ()),
224229
_ => panic!("Unexpected component {} in {:?}", c.name(), t),
225230
}
226231
}
227232
}
233+
234+
/// Best effort check if the struct represents a std::ptr::NonNull<T>.
235+
///
236+
/// This assumes the following structure. Any changes to this will break this code.
237+
/// ```
238+
/// pub struct NonNull<T: ?Sized> {
239+
/// pointer: *const T,
240+
/// }
241+
/// ```
242+
fn assert_is_non_null_like(&self, t: &Type) {
243+
assert!(t.is_struct_like());
244+
let components = t.lookup_components(&self.symbol_table).unwrap();
245+
assert_eq!(components.len(), 1);
246+
let component = components.first().unwrap();
247+
assert_eq!(component.name().to_string().as_str(), "pointer");
248+
assert!(component.typ().is_pointer() || component.typ().is_rust_fat_ptr(&self.symbol_table))
249+
}
228250
}

0 commit comments

Comments
 (0)