1
1
use rustc_middle:: mir:: interpret:: { InterpResult , Pointer } ;
2
2
use rustc_middle:: ty:: layout:: LayoutOf ;
3
- use rustc_middle:: ty:: { self , Ty , TyCtxt , VtblEntry } ;
3
+ use rustc_middle:: ty:: { self , ExistentialPredicateStableCmpExt , Ty , TyCtxt , VtblEntry } ;
4
4
use rustc_target:: abi:: { Align , Size } ;
5
5
use tracing:: trace;
6
6
@@ -11,26 +11,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
11
11
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
12
12
/// objects.
13
13
///
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).
18
18
pub fn get_vtable_ptr (
19
19
& self ,
20
20
ty : Ty < ' tcx > ,
21
- poly_trait_ref : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
21
+ dyn_ty : & ' tcx ty :: List < ty:: PolyExistentialPredicate < ' tcx > > ,
22
22
) -> 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:?})" ) ;
24
24
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 ) ) ;
26
26
27
27
// All vtables must be monomorphic, bail out otherwise.
28
28
ensure_monomorphic_enough ( * self . tcx , ty) ?;
29
- ensure_monomorphic_enough ( * self . tcx , poly_trait_ref ) ?;
29
+ ensure_monomorphic_enough ( * self . tcx , dyn_ty ) ?;
30
30
31
31
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) ;
34
33
let vtable_ptr = self . global_root_pointer ( Pointer :: from ( vtable_symbolic_allocation) ) ?;
35
34
Ok ( vtable_ptr. into ( ) )
36
35
}
@@ -64,17 +63,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
64
63
/// expected trait type.
65
64
pub ( super ) fn check_vtable_for_type (
66
65
& 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 > > ,
69
68
) -> 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
+ }
77
103
}
104
+
78
105
Ok ( ( ) )
79
106
}
80
107
0 commit comments