@@ -101,6 +101,7 @@ impl TypeExt for Type {
101
101
}
102
102
}
103
103
}
104
+
104
105
trait ExprExt {
105
106
fn unit ( symbol_table : & SymbolTable ) -> Self ;
106
107
@@ -354,9 +355,11 @@ impl<'tcx> GotocCtx<'tcx> {
354
355
Type :: unit ( ) . to_typedef ( inner_name)
355
356
}
356
357
357
- /// This will codegen the raw pointer to the trait data.
358
+ /// Codegen the pointer type for a concrete object that implements the trait object.
359
+ /// I.e.: A trait object is a fat pointer which contains a pointer to a concrete object
360
+ /// and a pointer to its vtable. This method returns a type for the first pointer.
358
361
pub fn codegen_trait_data_pointer ( & mut self , typ : ty:: Ty < ' tcx > ) -> Type {
359
- assert ! ( typ . is_trait ( ) ) ;
362
+ assert ! ( self . use_vtable_fat_pointer ( typ ) ) ;
360
363
self . codegen_ty ( typ) . to_pointer ( )
361
364
}
362
365
@@ -440,6 +443,7 @@ impl<'tcx> GotocCtx<'tcx> {
440
443
441
444
vtable_base. append ( & mut flds) ;
442
445
}
446
+ debug ! ( ty=?t, ?vtable_base, "trait_vtable_field_types" ) ;
443
447
vtable_base
444
448
} else {
445
449
unreachable ! ( "Expected to get a dynamic object here" ) ;
@@ -918,24 +922,35 @@ impl<'tcx> GotocCtx<'tcx> {
918
922
919
923
/// Generate code for a trait function declaration.
920
924
///
921
- /// Dynamic function calls first parameter is the fat-pointer representing self.
922
- /// For closures, the type of the first argument is dyn T not &dyn T.
923
- pub fn codegen_dynamic_function_sig ( & mut self , sig : PolyFnSig < ' tcx > ) -> Type {
925
+ /// Dynamic function calls first parameter is self which must be one of the following:
926
+ ///
927
+ /// As of Jul 2022:
928
+ /// P = &Self | &mut Self | Box<Self> | Rc<Self> | Arc<Self>
929
+ /// S = P | Pin<P>
930
+ ///
931
+ /// See <https://doc.rust-lang.org/reference/items/traits.html#object-safety> for more details.
932
+ fn codegen_dynamic_function_sig ( & mut self , sig : PolyFnSig < ' tcx > ) -> Type {
924
933
let sig = self . monomorphize ( sig) ;
925
934
let sig = self . tcx . normalize_erasing_late_bound_regions ( ty:: ParamEnv :: reveal_all ( ) , sig) ;
926
-
927
935
let mut is_first = true ;
928
936
let params = sig
929
937
. inputs ( )
930
938
. iter ( )
931
939
. filter_map ( |arg_type| {
932
940
if is_first {
933
- // This should &dyn T or dyn T (for closures).
934
941
is_first = false ;
935
- let first_ty = pointee_type ( * arg_type) . unwrap_or ( * arg_type) ;
936
942
debug ! ( self_type=?arg_type, fn_signature=?sig, "codegen_dynamic_function_sig" ) ;
937
- assert ! ( first_ty. is_trait( ) , "Expected self type to be a trait" ) ;
938
- Some ( self . codegen_trait_data_pointer ( first_ty) )
943
+ if arg_type. is_ref ( ) {
944
+ // Convert fat pointer to thin pointer to data portion.
945
+ let first_ty = pointee_type ( * arg_type) . unwrap ( ) ;
946
+ Some ( self . codegen_trait_data_pointer ( first_ty) )
947
+ } else if arg_type. is_trait ( ) {
948
+ // Convert dyn T to thin pointer.
949
+ Some ( self . codegen_trait_data_pointer ( * arg_type) )
950
+ } else {
951
+ // Codegen type with thin pointer (E.g.: Box<dyn T> -> Box<data_ptr>).
952
+ Some ( self . codegen_trait_receiver ( * arg_type) )
953
+ }
939
954
} else if self . ignore_var_ty ( * arg_type) {
940
955
debug ! ( "Ignoring type {:?} in function signature" , arg_type) ;
941
956
None
@@ -1367,6 +1382,52 @@ impl<'tcx> GotocCtx<'tcx> {
1367
1382
_ => false ,
1368
1383
}
1369
1384
}
1385
+
1386
+ /// Generate code for a valid object-safe trait receiver type.
1387
+ ///
1388
+ /// Note that all these types only contain the data pointer and ZST fields. Thus, we generate
1389
+ /// the non-ZST branch manually. In some cases, this method is called from inside
1390
+ /// `codegen_ty(arg_ty)` so we don't have information about the final type.
1391
+ fn codegen_trait_receiver ( & mut self , arg_ty : Ty < ' tcx > ) -> Type {
1392
+ // Collect structs that need to be modified
1393
+ // Collect the non-ZST fields until we find a fat pointer.
1394
+ let mut data_path = vec ! [ arg_ty] ;
1395
+ data_path. extend ( self . receiver_data_path ( arg_ty) . map ( |( _, typ) | typ) ) ;
1396
+
1397
+ trace ! ( ?arg_ty, ?data_path, "codegen_trait_receiver" ) ;
1398
+ let orig_pointer_ty = data_path. pop ( ) . unwrap ( ) ;
1399
+ assert ! ( self . is_vtable_fat_pointer( orig_pointer_ty) ) ;
1400
+
1401
+ // Traverse type and replace pointer type.
1402
+ let ptr_type = self . codegen_trait_data_pointer ( pointee_type ( orig_pointer_ty) . unwrap ( ) ) ;
1403
+ data_path. iter ( ) . rev ( ) . fold ( ptr_type, |last_type, curr| {
1404
+ // Codegen the type replacing the non-zst field.
1405
+ let new_name = self . ty_mangled_name ( * curr) . to_string ( ) + "::WithDataPtr" ;
1406
+ if let ty:: Adt ( adt_def, adt_substs) = curr. kind ( ) {
1407
+ let fields = & adt_def. variants ( ) . get ( VariantIdx :: from_u32 ( 0 ) ) . unwrap ( ) . fields ;
1408
+ self . ensure_struct ( new_name, NO_PRETTY_NAME , |ctx, s_name| {
1409
+ let fields_shape = ctx. layout_of ( * curr) . layout . fields ( ) ;
1410
+ let components = fields_shape
1411
+ . index_by_increasing_offset ( )
1412
+ . map ( |idx| {
1413
+ let name = fields[ idx] . name . to_string ( ) . intern ( ) ;
1414
+ let field_ty = fields[ idx] . ty ( ctx. tcx , adt_substs) ;
1415
+ let typ = if !ctx. layout_of ( field_ty) . is_zst ( ) {
1416
+ last_type. clone ( )
1417
+ } else {
1418
+ ctx. codegen_ty ( field_ty)
1419
+ } ;
1420
+ DatatypeComponent :: Field { name, typ }
1421
+ } )
1422
+ . collect ( ) ;
1423
+ trace ! ( ?data_path, ?curr, ?s_name, ?components, "codegen_trait_receiver" ) ;
1424
+ components
1425
+ } )
1426
+ } else {
1427
+ unreachable ! ( "Expected structs only {:?}" , curr) ;
1428
+ }
1429
+ } )
1430
+ }
1370
1431
}
1371
1432
1372
1433
/// Use maps instead of lists to manage mir struct components.
@@ -1412,6 +1473,70 @@ impl<'tcx> GotocCtx<'tcx> {
1412
1473
}
1413
1474
if typ. is_trait ( ) { Some ( typ) } else { None }
1414
1475
}
1476
+
1477
+ /// This function provides an iterator that traverses the data path of a receiver type. I.e.:
1478
+ /// the path that leads to the data pointer.
1479
+ ///
1480
+ /// E.g.: For `Rc<dyn T>` where the Rc definition is:
1481
+ /// ```
1482
+ /// pub struct Rc<T: ?Sized> {
1483
+ /// ptr: NonNull<RcBox<T>>,
1484
+ /// phantom: PhantomData<RcBox<T>>,
1485
+ /// }
1486
+ ///
1487
+ /// pub struct NonNull<T: ?Sized> {
1488
+ /// pointer: *const T,
1489
+ /// }
1490
+ /// ```
1491
+ ///
1492
+ /// The behavior will be:
1493
+ /// ```text
1494
+ /// let it = self.receiver_data_path(rc_typ);
1495
+ /// assert_eq!(it.next(), Some((String::from("ptr"), non_null_typ);
1496
+ /// assert_eq!(it.next(), Some((String::from("pointer"), raw_ptr_typ);
1497
+ /// assert_eq!(it.next(), None);
1498
+ /// ```
1499
+ ///
1500
+ /// Pre-condition: The argument must be a valid receiver for dispatchable trait functions.
1501
+ /// See <https://doc.rust-lang.org/reference/items/traits.html#object-safety> for more details.
1502
+ pub fn receiver_data_path < ' a > (
1503
+ self : & ' a Self ,
1504
+ typ : Ty < ' tcx > ,
1505
+ ) -> impl Iterator < Item = ( String , Ty < ' tcx > ) > + ' a {
1506
+ struct ReceiverIter < ' tcx , ' a > {
1507
+ pub curr : Ty < ' tcx > ,
1508
+ pub ctx : & ' a GotocCtx < ' tcx > ,
1509
+ }
1510
+
1511
+ impl < ' tcx , ' a > Iterator for ReceiverIter < ' tcx , ' a > {
1512
+ type Item = ( String , Ty < ' tcx > ) ;
1513
+
1514
+ fn next ( & mut self ) -> Option < Self :: Item > {
1515
+ if let ty:: Adt ( adt_def, adt_substs) = self . curr . kind ( ) {
1516
+ assert_eq ! (
1517
+ adt_def. variants( ) . len( ) ,
1518
+ 1 ,
1519
+ "Expected a single-variant ADT. Found {:?}" ,
1520
+ self . curr
1521
+ ) ;
1522
+ let ctx = self . ctx ;
1523
+ let fields = & adt_def. variants ( ) . get ( VariantIdx :: from_u32 ( 0 ) ) . unwrap ( ) . fields ;
1524
+ let mut non_zsts = fields
1525
+ . iter ( )
1526
+ . filter ( |field| !ctx. layout_of ( field. ty ( ctx. tcx , adt_substs) ) . is_zst ( ) )
1527
+ . map ( |non_zst| ( non_zst. name . to_string ( ) , non_zst. ty ( ctx. tcx , adt_substs) ) ) ;
1528
+ let ( name, next) = non_zsts. next ( ) . expect ( "Expected one non-zst field." ) ;
1529
+ self . curr = next;
1530
+ assert ! ( non_zsts. next( ) . is_none( ) , "Expected only one non-zst field." ) ;
1531
+ Some ( ( name, self . curr ) )
1532
+ } else {
1533
+ None
1534
+ }
1535
+ }
1536
+ }
1537
+
1538
+ ReceiverIter { ctx : self , curr : typ }
1539
+ }
1415
1540
}
1416
1541
1417
1542
/// The mir type is a mir pointer type.
0 commit comments