11use rustc_middle:: mir:: interpret:: { InterpResult , Pointer } ;
22use rustc_middle:: ty:: layout:: LayoutOf ;
3- use rustc_middle:: ty:: { self , Ty , TyCtxt , VtblEntry } ;
3+ use rustc_middle:: ty:: { self , ExistentialPredicateStableCmpExt , Ty , TyCtxt , VtblEntry } ;
44use rustc_target:: abi:: { Align , Size } ;
55use tracing:: trace;
66
@@ -11,26 +11,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
1111 /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
1212 /// objects.
1313 ///
14- /// The `trait_ref ` encodes the erased self type. Hence, if we are making an object `Foo<Trait>`
15- /// from a value of type `Foo<T>`, then `trait_ref` would map `T: Trait`. `None` here means that
16- /// this is an auto trait without any methods, so we only need the basic vtable (drop, size ,
17- /// align).
14+ /// The `dyn_ty ` encodes the erased self type. Hence, if we are making an object
15+ /// `Foo<dyn Trait<Assoc = A>> + Send` from a value of type `Foo<T>`, then `dyn_ty`
16+ /// would be `Trait<Assoc = A> + Send`. If this list doesn't have a principal trait ref ,
17+ /// we only need the basic vtable prefix (drop, size, align).
1818 pub fn get_vtable_ptr (
1919 & self ,
2020 ty : Ty < ' tcx > ,
21- poly_trait_ref : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
21+ dyn_ty : & ' tcx ty :: List < ty:: PolyExistentialPredicate < ' tcx > > ,
2222 ) -> InterpResult < ' tcx , Pointer < Option < M :: Provenance > > > {
23- trace ! ( "get_vtable(trait_ref={ :?})" , poly_trait_ref ) ;
23+ trace ! ( "get_vtable(ty={ty :?}, dyn_ty={dyn_ty:?})" ) ;
2424
25- let ( ty, poly_trait_ref ) = self . tcx . erase_regions ( ( ty, poly_trait_ref ) ) ;
25+ let ( ty, dyn_ty ) = self . tcx . erase_regions ( ( ty, dyn_ty ) ) ;
2626
2727 // All vtables must be monomorphic, bail out otherwise.
2828 ensure_monomorphic_enough ( * self . tcx , ty) ?;
29- ensure_monomorphic_enough ( * self . tcx , poly_trait_ref ) ?;
29+ ensure_monomorphic_enough ( * self . tcx , dyn_ty ) ?;
3030
3131 let salt = M :: get_global_alloc_salt ( self , None ) ;
32- let vtable_symbolic_allocation =
33- self . tcx . reserve_and_set_vtable_alloc ( ty, poly_trait_ref, salt) ;
32+ let vtable_symbolic_allocation = self . tcx . reserve_and_set_vtable_alloc ( ty, dyn_ty, salt) ;
3433 let vtable_ptr = self . global_root_pointer ( Pointer :: from ( vtable_symbolic_allocation) ) ?;
3534 Ok ( vtable_ptr. into ( ) )
3635 }
@@ -64,17 +63,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
6463 /// expected trait type.
6564 pub ( super ) fn check_vtable_for_type (
6665 & self ,
67- vtable_trait : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
68- expected_trait : & ' tcx ty:: List < ty:: PolyExistentialPredicate < ' tcx > > ,
66+ vtable_dyn_type : & ' tcx ty :: List < ty:: PolyExistentialPredicate < ' tcx > > ,
67+ expected_dyn_type : & ' tcx ty:: List < ty:: PolyExistentialPredicate < ' tcx > > ,
6968 ) -> InterpResult < ' tcx > {
70- let eq = match ( expected_trait. principal ( ) , vtable_trait) {
71- ( Some ( a) , Some ( b) ) => self . eq_in_param_env ( a, b) ,
72- ( None , None ) => true ,
73- _ => false ,
74- } ;
75- if !eq {
76- throw_ub ! ( InvalidVTableTrait { expected_trait, vtable_trait } ) ;
69+ // We check validity by comparing the lists of predicates for equality. We *could* instead
70+ // check that the dynamic type to which the vtable belongs satisfies all the expected
71+ // predicates, but that would likely be a lot slower and seems unnecessarily permissive.
72+
73+ // FIXME: we are skipping auto traits for now, but might revisit this in the future.
74+ let mut sorted_vtable: Vec < _ > = vtable_dyn_type. without_auto_traits ( ) . collect ( ) ;
75+ let mut sorted_expected: Vec < _ > = expected_dyn_type. without_auto_traits ( ) . collect ( ) ;
76+ // `skip_binder` here is okay because `stable_cmp` doesn't look at binders
77+ sorted_vtable. sort_by ( |a, b| a. skip_binder ( ) . stable_cmp ( * self . tcx , & b. skip_binder ( ) ) ) ;
78+ sorted_vtable. dedup ( ) ;
79+ sorted_expected. sort_by ( |a, b| a. skip_binder ( ) . stable_cmp ( * self . tcx , & b. skip_binder ( ) ) ) ;
80+ sorted_expected. dedup ( ) ;
81+
82+ if sorted_vtable. len ( ) != sorted_expected. len ( ) {
83+ throw_ub ! ( InvalidVTableTrait { vtable_dyn_type, expected_dyn_type } ) ;
84+ }
85+
86+ for ( a_pred, b_pred) in std:: iter:: zip ( sorted_vtable, sorted_expected) {
87+ let is_eq = match ( a_pred. skip_binder ( ) , b_pred. skip_binder ( ) ) {
88+ (
89+ ty:: ExistentialPredicate :: Trait ( a_data) ,
90+ ty:: ExistentialPredicate :: Trait ( b_data) ,
91+ ) => self . eq_in_param_env ( a_pred. rebind ( a_data) , b_pred. rebind ( b_data) ) ,
92+
93+ (
94+ ty:: ExistentialPredicate :: Projection ( a_data) ,
95+ ty:: ExistentialPredicate :: Projection ( b_data) ,
96+ ) => self . eq_in_param_env ( a_pred. rebind ( a_data) , b_pred. rebind ( b_data) ) ,
97+
98+ _ => false ,
99+ } ;
100+ if !is_eq {
101+ throw_ub ! ( InvalidVTableTrait { vtable_dyn_type, expected_dyn_type } ) ;
102+ }
77103 }
104+
78105 Ok ( ( ) )
79106 }
80107
0 commit comments