Skip to content

Commit d7440b9

Browse files
markrtuttleMark R. Tuttle
authored andcommitted
Allow cast of fat pointers to thin pointers (rust-lang#99)
Also: * Correctly cast before dereferencing a thin pointer to a flexible struct * Correct search for trait type in adt (ignore primitive types and pointers) * Correct the choice between thin and fat pointers in codegen_fat_ptr and codegen_ty_ref (and rewrite to use mir pointer metadata) Co-authored-by: Mark R. Tuttle <mrtuttle@amazon.com>
1 parent b5c6524 commit d7440b9

File tree

4 files changed

+105
-74
lines changed

4 files changed

+105
-74
lines changed

Diff for: compiler/rustc_codegen_llvm/src/gotoc/place.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,12 @@ impl<'tcx> GotocCtx<'tcx> {
267267
// should be the ADT itself. So we need the `.dereference()` here.
268268
// Note that this causes problems in `codegen_rvalue_ref()`.
269269
// See the comment there for more details.
270-
inner_goto_expr.member("data", &self.symbol_table).dereference()
270+
inner_goto_expr
271+
.member("data", &self.symbol_table)
272+
// In the case of a vtable fat pointer, this data member is a void pointer,
273+
// so ensure the pointer has the correct type before dereferencing it.
274+
.cast_to(self.codegen_ty(inner_mir_typ).to_pointer())
275+
.dereference()
271276
}
272277
_ => inner_goto_expr.dereference(),
273278
};

Diff for: compiler/rustc_codegen_llvm/src/gotoc/rvalue.rs

+28-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::cbmc::goto_program::{BuiltinFn, Expr, Location, Stmt, Symbol, Type};
44
use super::cbmc::utils::aggr_name;
55
use super::cbmc::MachineModel;
66
use super::metadata::*;
7-
use super::typ::pointee_type;
7+
use super::typ::{is_pointer, pointee_type};
88
use super::utils::{dynamic_fat_ptr, slice_fat_ptr};
99
use crate::btree_string_map;
1010
use rustc_middle::mir::{AggregateKind, BinOp, CastKind, NullOp, Operand, Place, Rvalue, UnOp};
@@ -530,6 +530,19 @@ impl<'tcx> GotocCtx<'tcx> {
530530
)
531531
}
532532

533+
pub fn codegen_fat_ptr_to_thin_ptr_cast(
534+
&mut self,
535+
src: &Operand<'tcx>,
536+
dst_t: Ty<'tcx>,
537+
) -> Expr {
538+
debug!("codegen_fat_ptr_to_thin_ptr_cast |{:?}| |{:?}|", src, dst_t);
539+
let src_goto_expr = self.codegen_operand(src);
540+
let dst_goto_typ = self.codegen_ty(dst_t);
541+
// In a vtable fat pointer, the data member is a void pointer,
542+
// so ensure the pointer has the correct type before dereferencing it.
543+
src_goto_expr.member("data", &self.symbol_table).cast_to(dst_goto_typ)
544+
}
545+
533546
fn codegen_misc_cast(&mut self, src: &Operand<'tcx>, dst_t: Ty<'tcx>) -> Expr {
534547
let src_t = self.operand_ty(src);
535548
debug!(
@@ -558,6 +571,10 @@ impl<'tcx> GotocCtx<'tcx> {
558571
return self.codegen_fat_ptr_to_fat_ptr_cast(src, dst_t);
559572
}
560573

574+
if self.is_ref_of_unsized(src_t) && self.is_ref_of_sized(dst_t) {
575+
return self.codegen_fat_ptr_to_thin_ptr_cast(src, dst_t);
576+
}
577+
561578
// pointer casting. from a pointer / reference to another pointer / reference
562579
// notice that if fat pointer is involved, it cannot be the destination, which is t.
563580
match dst_t.kind() {
@@ -949,8 +966,8 @@ impl<'tcx> GotocCtx<'tcx> {
949966
if let Some((concrete_type, trait_type)) =
950967
self.nested_pair_of_concrete_and_trait_types(src_pointee_type, dst_pointee_type)
951968
{
952-
let dst_goto_type = self.codegen_ty(dst_mir_type);
953969
let dst_goto_expr = src_goto_expr.cast_to(Type::void_pointer());
970+
let dst_goto_type = self.codegen_ty(dst_mir_type);
954971
let vtable = self.codegen_vtable(concrete_type, trait_type);
955972
let vtable_expr = vtable.address_of();
956973
Some(dynamic_fat_ptr(dst_goto_type, dst_goto_expr, vtable_expr, &self.symbol_table))
@@ -1020,11 +1037,18 @@ impl<'tcx> GotocCtx<'tcx> {
10201037
/// position a concrete type. This function returns the pair (concrete type,
10211038
/// trait type) that we can use to build the vtable for the concrete type
10221039
/// implementation of the trait type.
1023-
fn nested_pair_of_concrete_and_trait_types(
1040+
pub fn nested_pair_of_concrete_and_trait_types(
10241041
&self,
10251042
src_mir_type: Ty<'tcx>,
10261043
dst_mir_type: Ty<'tcx>,
10271044
) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
1045+
// We are walking an ADT searching for a trait type in this ADT. We can
1046+
// terminate a walk down a path when we hit a primitive type or which we hit
1047+
// a pointer type (that would take us out of this ADT and into another type).
1048+
if dst_mir_type.is_primitive() || is_pointer(dst_mir_type) {
1049+
return None;
1050+
}
1051+
10281052
match (src_mir_type.kind(), dst_mir_type.kind()) {
10291053
(_, ty::Dynamic(..)) => Some((src_mir_type.clone(), dst_mir_type.clone())),
10301054
(ty::Adt(..), ty::Adt(..)) => {
@@ -1067,7 +1091,7 @@ impl<'tcx> GotocCtx<'tcx> {
10671091
// Some((src_mir_type.clone(), dst_mir_type.clone()))
10681092
// }
10691093
_ => panic!(
1070-
"Searching for pairs of concrete and trait types found unexpected types in {:?} and {:?}",
1094+
"Found unexpected types while searching for pairs of concrete and trait types in {:?} and {:?}",
10711095
src_mir_type, dst_mir_type
10721096
),
10731097
}

Diff for: compiler/rustc_codegen_llvm/src/gotoc/typ.rs

+71-60
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ impl<'tcx> GotocCtx<'tcx> {
9696
}
9797
}
9898

99+
/// Is the MIR type a ref of an unsized type (i.e. one represented by a fat pointer?)
100+
pub fn is_ref_of_sized(&self, t: &'tcx TyS<'_>) -> bool {
101+
match t.kind() {
102+
ty::Ref(_, to, _) | ty::RawPtr(ty::TypeAndMut { ty: to, .. }) => !self.is_unsized(to),
103+
_ => false,
104+
}
105+
}
106+
99107
/// Is the MIR type a box of an unsized type (i.e. one represented by a fat pointer?)
100108
pub fn is_box_of_unsized(&self, t: &'tcx TyS<'_>) -> bool {
101109
if t.is_box() {
@@ -519,65 +527,61 @@ impl<'tcx> GotocCtx<'tcx> {
519527
})
520528
}
521529

522-
pub fn codegen_fat_ptr(&mut self, t: Ty<'tcx>) -> Type {
523-
// implement "fat pointers", which are pointers with metadata.
524-
// to my knowledge, there are three kinds of fat pointers:
525-
// 1. references to slices
526-
// 2. references to trait objects.
527-
// 3. references to structs whose last field is a fat pointer
528-
match t.kind() {
529-
ty::Adt(..) if self.is_unsized(t) => {
530-
// https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp
531-
let fat_ptr_name = format!("&{}", self.ty_mangled_name(t));
532-
self.ensure_struct(&fat_ptr_name, |ctx, _| {
533-
vec![
534-
Type::datatype_component("data", ctx.codegen_ty(t).to_pointer()),
535-
Type::datatype_component("len", Type::size_t()),
536-
]
537-
})
538-
}
539-
ty::Slice(e) => {
540-
// a slice &[t] is translated to
541-
// struct {
542-
// t *data;
543-
// usize len;
544-
// }
545-
// c.f. core::ptr::FatPtr<T>
546-
let slice_name = self.ty_mangled_name(t);
547-
self.ensure_struct(&slice_name, |ctx, _| {
548-
vec![
549-
Type::datatype_component("data", ctx.codegen_ty(e).to_pointer()),
550-
Type::datatype_component("len", Type::size_t()),
551-
]
552-
})
553-
}
554-
ty::Str => self.ensure_struct("str", |_, _| {
530+
pub fn codegen_fat_ptr(&mut self, mir_type: Ty<'tcx>) -> Type {
531+
assert!(
532+
!self.use_thin_pointer(mir_type),
533+
"Generating a fat pointer for a type requiring a thin pointer: {:?}",
534+
mir_type.kind()
535+
);
536+
if self.use_slice_fat_pointer(mir_type) {
537+
let pointer_name = match mir_type.kind() {
538+
ty::Slice(..) => self.ty_mangled_name(mir_type),
539+
ty::Str => "str".to_string(),
540+
ty::Adt(..) => format!("&{}", self.ty_mangled_name(mir_type)),
541+
kind => unreachable!("Generating a slice fat pointer to {:?}", kind),
542+
};
543+
let element_type = match mir_type.kind() {
544+
ty::Slice(elt_type) => self.codegen_ty(elt_type),
545+
ty::Str => Type::c_char(),
546+
// For adt, see https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp
547+
ty::Adt(..) => self.codegen_ty(mir_type),
548+
kind => unreachable!("Generating a slice fat pointer to {:?}", kind),
549+
};
550+
self.ensure_struct(&pointer_name, |_, _| {
555551
vec![
556-
Type::datatype_component("data", Type::c_char().to_pointer()),
552+
Type::datatype_component("data", element_type.to_pointer()),
557553
Type::datatype_component("len", Type::size_t()),
558554
]
559-
}),
560-
ty::Dynamic(binder, _region) => {
561-
debug!("type codegen for dynamic with binder {:?} type {:?}", binder, t);
562-
self.codegen_trait_vtable_type(t);
563-
self.codegen_trait_fat_ptr_type(t)
564-
}
565-
566-
_ => unreachable!(),
555+
})
556+
} else if self.use_vtable_fat_pointer(mir_type) {
557+
let (_, trait_type) =
558+
self.nested_pair_of_concrete_and_trait_types(mir_type, mir_type).unwrap();
559+
self.codegen_trait_vtable_type(trait_type);
560+
self.codegen_trait_fat_ptr_type(trait_type)
561+
} else {
562+
unreachable!(
563+
"A pointer is either a thin pointer, slice fat pointer, or vtable fat pointer."
564+
);
567565
}
568566
}
569567

570-
pub fn codegen_ty_ref(&mut self, t: Ty<'tcx>) -> Type {
571-
// Normalize t to remove projection and opaque types
572-
let t = self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t);
568+
pub fn codegen_ty_ref(&mut self, pointee_type: Ty<'tcx>) -> Type {
569+
// Normalize pointee_type to remove projection and opaque types
570+
let pointee_type =
571+
self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), pointee_type);
573572

574-
match t.kind() {
575-
// These types go to fat pointers
576-
ty::Dynamic(..) | ty::Slice(_) | ty::Str => self.codegen_fat_ptr(t),
577-
ty::Adt(..) if self.is_unsized(t) => self.codegen_fat_ptr(t),
573+
if !self.use_thin_pointer(pointee_type) {
574+
return self.codegen_fat_ptr(pointee_type);
575+
}
576+
577+
match pointee_type.kind() {
578+
ty::Dynamic(..) | ty::Slice(_) | ty::Str => {
579+
unreachable!("Should have generated a fat pointer")
580+
}
578581
ty::Projection(_) | ty::Opaque(..) => {
579582
unreachable!("Should have been removed by normalization")
580583
}
584+
581585
// We have a "thin pointer", which is just a pointer
582586
ty::Adt(..)
583587
| ty::Array(..)
@@ -590,22 +594,22 @@ impl<'tcx> GotocCtx<'tcx> {
590594
| ty::RawPtr(_)
591595
| ty::Ref(..)
592596
| ty::Tuple(_)
593-
| ty::Uint(_) => self.codegen_ty(t).to_pointer(),
597+
| ty::Uint(_) => self.codegen_ty(pointee_type).to_pointer(),
594598

595599
// These types were blocking firecracker. Doing the default thing to unblock.
596600
// TODO, determine if this is the right course of action
597-
ty::FnDef(_, _) | ty::Never => self.codegen_ty(t).to_pointer(),
601+
ty::FnDef(_, _) | ty::Never => self.codegen_ty(pointee_type).to_pointer(),
598602

599603
// These types have no regression tests for them.
600604
// For soundess, hold off on generating them till we have test-cases.
601-
ty::Bound(_, _) => todo!("{:?} {:?}", t, t.kind()),
602-
ty::Error(_) => todo!("{:?} {:?}", t, t.kind()),
603-
ty::FnPtr(_) => todo!("{:?} {:?}", t, t.kind()),
604-
ty::Generator(_, _, _) => todo!("{:?} {:?}", t, t.kind()),
605-
ty::GeneratorWitness(_) => todo!("{:?} {:?}", t, t.kind()),
606-
ty::Infer(_) => todo!("{:?} {:?}", t, t.kind()),
607-
ty::Param(_) => todo!("{:?} {:?}", t, t.kind()),
608-
ty::Placeholder(_) => todo!("{:?} {:?}", t, t.kind()),
605+
ty::Bound(_, _) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()),
606+
ty::Error(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()),
607+
ty::FnPtr(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()),
608+
ty::Generator(_, _, _) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()),
609+
ty::GeneratorWitness(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()),
610+
ty::Infer(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()),
611+
ty::Param(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()),
612+
ty::Placeholder(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()),
609613
}
610614
}
611615

@@ -1006,6 +1010,11 @@ impl<'tcx> GotocCtx<'tcx> {
10061010
}
10071011
}
10081012

1013+
/// The mir type is a mir pointer type.
1014+
pub fn is_pointer(mir_type: Ty<'tcx>) -> bool {
1015+
return matches!(mir_type.kind(), ty::Ref(..) | ty::RawPtr(..));
1016+
}
1017+
10091018
/// Extract from a mir pointer type the mir type of the value to which the
10101019
/// pointer points.
10111020
pub fn pointee_type(pointer_type: Ty<'tcx>) -> Option<Ty<'tcx>> {
@@ -1019,7 +1028,9 @@ pub fn pointee_type(pointer_type: Ty<'tcx>) -> Option<Ty<'tcx>> {
10191028
impl<'tcx> GotocCtx<'tcx> {
10201029
/// A pointer to the mir type should be a thin pointer.
10211030
pub fn use_thin_pointer(&self, mir_type: Ty<'tcx>) -> bool {
1022-
return mir_type.ptr_metadata_ty(self.tcx) == self.tcx.types.unit;
1031+
// ptr_metadata_ty is not defined on all types, the projection of an associated type
1032+
return !self.is_unsized(mir_type)
1033+
|| mir_type.ptr_metadata_ty(self.tcx) == self.tcx.types.unit;
10231034
}
10241035
/// A pointer to the mir type should be a slice fat pointer.
10251036
pub fn use_slice_fat_pointer(&self, mir_type: Ty<'tcx>) -> bool {

Diff for: rust-tests/cbmc-reg/SizeAndAlignOfDst/main_fail.rs

-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
// This test still fails with a final coercion error for
77
// DummySubscriber to dyn Subscriber.
88

9-
#![feature(layout_for_ptr)]
109
use std::mem;
1110
use std::sync::Arc;
1211
use std::sync::Mutex;
@@ -30,13 +29,5 @@ impl Subscriber for DummySubscriber {
3029
}
3130

3231
fn main() {
33-
let v = unsafe { mem::size_of_val_raw(&5i32) };
34-
assert!(v == 4);
35-
36-
let x: [u8; 13] = [0; 13];
37-
let y: &[u8] = &x;
38-
let v = unsafe { mem::size_of_val_raw(y) };
39-
assert!(v == 13);
40-
4132
let s: Arc<Mutex<dyn Subscriber>> = Arc::new(Mutex::new(DummySubscriber::new()));
4233
}

0 commit comments

Comments
 (0)