Skip to content

Commit 396feba

Browse files
danielsntedinski
authored andcommitted
Properly handle Fat-ptr to Fat-ptr casts (rust-lang#62)
* Special case fat-ptr to fat-ptr casts * Fix issues in rvalue computation related to ADT fat pointers * Assign statements now always type-check. Remove the special casing for when they didn't
1 parent 3a7abc3 commit 396feba

File tree

5 files changed

+76
-19
lines changed

5 files changed

+76
-19
lines changed

compiler/rustc_codegen_llvm/src/gotoc/cbmc/goto_program/stmt.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -150,22 +150,8 @@ macro_rules! stmt {
150150
impl Stmt {
151151
/// `lhs = rhs;`
152152
pub fn assign(lhs: Expr, rhs: Expr, loc: Location) -> Self {
153-
// Codegen the assignment if types are equal and assert false if not
154-
if lhs.typ() == rhs.typ() {
155-
// TODO: Is there a more suitable notion of type equality? We have seen
156-
// left: `StructTag("tag-&[libc::cmsghdr]")`,
157-
// right: `StructTag("tag-&[std::mem::MaybeUninit<libc::cmsghdr>]")
158-
stmt!(Assign { lhs, rhs }, loc)
159-
} else {
160-
Stmt::assert_false(
161-
&format!(
162-
"Types of lhs and rhs of the assignment are equal. lhs = {:?} rhs = {:?}",
163-
&lhs.typ(),
164-
&rhs.typ()
165-
),
166-
loc.clone(),
167-
)
168-
}
153+
assert_eq!(lhs.typ(), rhs.typ());
154+
stmt!(Assign { lhs, rhs }, loc)
169155
}
170156

171157
/// `__CPROVER_assert(cond, msg);`

compiler/rustc_codegen_llvm/src/gotoc/place.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,20 @@ impl<'tcx> GotocCtx<'tcx> {
253253
inner_goto_expr.member("data", &self.symbol_table)
254254
}
255255
ty::Adt(..) if self.is_unsized(inner_mir_typ) => {
256+
// in cbmc-reg/Strings/os_str_reduced.rs, we see
257+
// ```
258+
// p.projection = [
259+
// Deref,
260+
// Field(
261+
// field[0],
262+
// [u8],
263+
// ),
264+
// ]
265+
// ```
266+
// This implies that the result of a deref on an ADT fat pointer
267+
// should be the ADT itself. So we need the `.dereference()` here.
268+
// Note that this causes problems in `codegen_rvalue_ref()`.
269+
// See the comment there for more details.
256270
inner_goto_expr.member("data", &self.symbol_table).dereference()
257271
}
258272
_ => inner_goto_expr.dereference(),
@@ -340,7 +354,6 @@ impl<'tcx> GotocCtx<'tcx> {
340354
let initial_expr = self.codegen_local(p.local);
341355
let initial_typ = TypeOrVariant::Type(self.local_ty(p.local));
342356
let initial_projection = ProjectedPlace::new(initial_expr, initial_typ, None, None, self);
343-
344357
p.projection
345358
.iter()
346359
.fold(initial_projection, |accum, proj| self.codegen_projection(accum, proj))

compiler/rustc_codegen_llvm/src/gotoc/rvalue.rs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use super::cbmc::utils::aggr_name;
55
use super::cbmc::MachineModel;
66
use super::metadata::*;
77
use super::utils::{dynamic_fat_ptr, slice_fat_ptr};
8+
use crate::btree_string_map;
89
use rustc_ast::ast::Mutability;
910
use rustc_middle::mir::{AggregateKind, BinOp, CastKind, NullOp, Operand, Place, Rvalue, UnOp};
1011
use rustc_middle::ty::adjustment::PointerCast;
@@ -98,8 +99,9 @@ impl<'tcx> GotocCtx<'tcx> {
9899
let pt = self.place_ty(p);
99100
let place = self.codegen_place(p);
100101
debug!("codegen_rvalue_ref||{:?}||{:?}||{:?}||{:?}", p, pt, pt.kind(), place);
102+
101103
match pt.kind() {
102-
ty::Slice(_) | ty::Str | ty::Dynamic(..) => {
104+
_ if self.is_unsized(pt) => {
103105
let fat_ptr = place.fat_ptr_goto_expr.unwrap();
104106
match place.fat_ptr_mir_typ.unwrap().kind() {
105107
ty::Ref(_, to, _) | ty::RawPtr(ty::TypeAndMut { ty: to, .. }) => {
@@ -109,9 +111,23 @@ impl<'tcx> GotocCtx<'tcx> {
109111
// A user defined DST type needs special handling
110112
// https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp
111113
assert!(self.is_unsized(to));
114+
// In cbmc-reg/Strings/os_str_reduced.rs, we have a flexible array
115+
// which needs to be decayed to a pointer.
116+
// In cbmc-reg/Cast/path.rs, we have an ADT, where we need to
117+
// take its address to get the pointer
118+
// See the comment in `codegen_projection`.
119+
let data = if place.goto_expr.typ().is_pointer() {
120+
place.goto_expr
121+
} else if place.goto_expr.typ().is_array_like() {
122+
place.goto_expr.array_to_ptr()
123+
} else {
124+
place.goto_expr.address_of()
125+
};
126+
// TODO: this assumes we have a slice. But it might be dynamic.
112127
slice_fat_ptr(
113128
self.codegen_ty(res_ty),
114-
place.goto_expr.array_to_ptr(),
129+
// place.goto_expr.decay_to_ptr(),
130+
data,
115131
fat_ptr.member("len", &self.symbol_table),
116132
&self.symbol_table,
117133
)
@@ -153,6 +169,7 @@ impl<'tcx> GotocCtx<'tcx> {
153169
// But I'm not sure that its actually correct for all the match arms.
154170
place.goto_expr.address_of()
155171
}
172+
ty::Slice(_) | ty::Str | ty::Dynamic(..) => unreachable!(),
156173
}
157174
}
158175

@@ -477,6 +494,42 @@ impl<'tcx> GotocCtx<'tcx> {
477494
}
478495
}
479496

497+
pub fn codegen_fat_ptr_to_fat_ptr_cast(
498+
&mut self,
499+
src: &Operand<'tcx>,
500+
dst_t: Ty<'tcx>,
501+
) -> Expr {
502+
debug!("codegen_fat_ptr_to_fat_ptr_cast |{:?}| |{:?}|", src, dst_t);
503+
let src_goto_expr = self.codegen_operand(src);
504+
let dst_goto_typ = self.codegen_ty(dst_t);
505+
let dst_data_type =
506+
self.symbol_table.lookup_field_type_in_type(&dst_goto_typ, "data").unwrap();
507+
let dst_data_field = (
508+
"data",
509+
src_goto_expr.clone().member("data", &self.symbol_table).cast_to(dst_data_type.clone()),
510+
);
511+
512+
let dst_metadata_field = if let Some(vtable_typ) =
513+
self.symbol_table.lookup_field_type_in_type(&dst_goto_typ, "vtable")
514+
{
515+
(
516+
"vtable",
517+
src_goto_expr.member("vtable", &self.symbol_table).cast_to(vtable_typ.clone()),
518+
)
519+
} else if let Some(len_typ) =
520+
self.symbol_table.lookup_field_type_in_type(&dst_goto_typ, "len")
521+
{
522+
("len", src_goto_expr.member("len", &self.symbol_table).cast_to(len_typ.clone()))
523+
} else {
524+
unreachable!("fat pointer with neither vtable nor len. {:?} {:?}", src, dst_t);
525+
};
526+
Expr::struct_expr(
527+
dst_goto_typ,
528+
btree_string_map![dst_data_field, dst_metadata_field],
529+
&self.symbol_table,
530+
)
531+
}
532+
480533
fn codegen_misc_cast(&mut self, src: &Operand<'tcx>, dst_t: Ty<'tcx>) -> Expr {
481534
let src_t = self.operand_ty(src);
482535
debug!(
@@ -500,6 +553,11 @@ impl<'tcx> GotocCtx<'tcx> {
500553
return self.codegen_get_discriminant(operand, src_t, dst_t);
501554
}
502555

556+
// Cast between fat pointers
557+
if self.is_ref_of_unsized(src_t) && self.is_ref_of_unsized(dst_t) {
558+
return self.codegen_fat_ptr_to_fat_ptr_cast(src, dst_t);
559+
}
560+
503561
// pointer casting. from a pointer / reference to another pointer / reference
504562
// notice that if fat pointer is involved, it cannot be the destination, which is t.
505563
match dst_t.kind() {

0 commit comments

Comments
 (0)