1
1
// Not in interpret to make sure we do not use private implementation details
2
2
3
- use std:: convert:: TryFrom ;
4
-
5
3
use rustc_hir:: Mutability ;
6
4
use rustc_middle:: mir;
7
5
use rustc_middle:: mir:: interpret:: { EvalToValTreeResult , GlobalId } ;
8
6
use rustc_middle:: ty:: { self , TyCtxt } ;
9
7
use rustc_span:: { source_map:: DUMMY_SP , symbol:: Symbol } ;
8
+ use rustc_target:: abi:: VariantIdx ;
10
9
11
10
use crate :: interpret:: {
12
11
intern_const_alloc_recursive, ConstValue , InternKind , InterpCx , InterpResult , MemPlaceMeta ,
@@ -25,6 +24,12 @@ pub use fn_queries::*;
25
24
pub use machine:: * ;
26
25
pub ( crate ) use valtrees:: { const_to_valtree_inner, valtree_to_const_value} ;
27
26
27
+ pub ( crate ) enum ValTreeCreationError {
28
+ NonSupportedType ,
29
+ Other ,
30
+ }
31
+ pub ( crate ) type ValTreeCreationResult < ' tcx > = Result < ty:: ValTree < ' tcx > , ValTreeCreationError > ;
32
+
28
33
pub ( crate ) fn const_caller_location (
29
34
tcx : TyCtxt < ' _ > ,
30
35
( file, line, col) : ( Symbol , u32 , u32 ) ,
@@ -39,23 +44,15 @@ pub(crate) fn const_caller_location(
39
44
ConstValue :: Scalar ( Scalar :: from_maybe_pointer ( loc_place. ptr , & tcx) )
40
45
}
41
46
42
- // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
43
- const VALTREE_MAX_NODES : usize = 1000 ;
44
-
45
- pub ( crate ) enum ValTreeCreationError {
46
- NodesOverflow ,
47
- NonSupportedType ,
48
- Other ,
49
- }
50
- pub ( crate ) type ValTreeCreationResult < ' tcx > = Result < ty:: ValTree < ' tcx > , ValTreeCreationError > ;
51
-
52
47
/// Evaluates a constant and turns it into a type-level constant value.
53
48
pub ( crate ) fn eval_to_valtree < ' tcx > (
54
49
tcx : TyCtxt < ' tcx > ,
55
50
param_env : ty:: ParamEnv < ' tcx > ,
56
51
cid : GlobalId < ' tcx > ,
57
52
) -> EvalToValTreeResult < ' tcx > {
58
53
let const_alloc = tcx. eval_to_allocation_raw ( param_env. and ( cid) ) ?;
54
+
55
+ // FIXME Need to provide a span to `eval_to_valtree`
59
56
let ecx = mk_eval_cx (
60
57
tcx, DUMMY_SP , param_env,
61
58
// It is absolutely crucial for soundness that
@@ -65,65 +62,89 @@ pub(crate) fn eval_to_valtree<'tcx>(
65
62
let place = ecx. raw_const_to_mplace ( const_alloc) . unwrap ( ) ;
66
63
debug ! ( ?place) ;
67
64
68
- let mut num_nodes = 0 ;
69
- let valtree_result = const_to_valtree_inner ( & ecx, & place, & mut num_nodes) ;
65
+ let valtree_result = const_to_valtree_inner ( & ecx, & place) ;
70
66
71
67
match valtree_result {
72
68
Ok ( valtree) => Ok ( Some ( valtree) ) ,
73
- Err ( err) => {
74
- let did = cid. instance . def_id ( ) ;
75
- let s = cid. display ( tcx) ;
76
- match err {
77
- ValTreeCreationError :: NodesOverflow => {
78
- let msg = format ! ( "maximum number of nodes exceeded in constant {}" , & s) ;
79
- let mut diag = match tcx. hir ( ) . span_if_local ( did) {
80
- Some ( span) => tcx. sess . struct_span_err ( span, & msg) ,
81
- None => tcx. sess . struct_err ( & msg) ,
82
- } ;
83
- diag. emit ( ) ;
84
-
85
- Ok ( None )
86
- }
87
- ValTreeCreationError :: NonSupportedType | ValTreeCreationError :: Other => Ok ( None ) ,
88
- }
89
- }
69
+ Err ( _) => Ok ( None ) ,
90
70
}
91
71
}
92
72
93
- /// This function should never fail for validated constants. However, it is also invoked from the
94
- /// pretty printer which might attempt to format invalid constants and in that case it might fail .
73
+ /// Tries to destructure constants of type Array or Adt into the constants
74
+ /// of its fields .
95
75
pub ( crate ) fn try_destructure_const < ' tcx > (
96
76
tcx : TyCtxt < ' tcx > ,
97
- param_env : ty:: ParamEnv < ' tcx > ,
98
- val : ty:: Const < ' tcx > ,
99
- ) -> InterpResult < ' tcx , mir:: DestructuredConst < ' tcx > > {
100
- trace ! ( "destructure_const: {:?}" , val) ;
101
- let ecx = mk_eval_cx ( tcx, DUMMY_SP , param_env, false ) ;
102
- let op = ecx. const_to_op ( val, None ) ?;
103
- // We go to `usize` as we cannot allocate anything bigger anyway.
104
- let ( field_count, variant, down) = match val. ty ( ) . kind ( ) {
105
- ty:: Array ( _, len) => ( usize:: try_from ( len. eval_usize ( tcx, param_env) ) . unwrap ( ) , None , op) ,
106
- // Checks if we have any variants, to avoid downcasting to a non-existing variant (when
107
- // there are no variants `read_discriminant` successfully returns a non-existing variant
108
- // index).
109
- ty:: Adt ( def, _) if def. variants ( ) . is_empty ( ) => throw_ub ! ( Unreachable ) ,
110
- ty:: Adt ( def, _) => {
111
- let variant = ecx. read_discriminant ( & op) ?. 1 ;
112
- let down = ecx. operand_downcast ( & op, variant) ?;
113
- ( def. variant ( variant) . fields . len ( ) , Some ( variant) , down)
114
- }
115
- ty:: Tuple ( substs) => ( substs. len ( ) , None , op) ,
116
- _ => bug ! ( "cannot destructure constant {:?}" , val) ,
117
- } ;
118
- let fields = ( 0 ..field_count)
119
- . map ( |i| {
120
- let field_op = ecx. operand_field ( & down, i) ?;
121
- let val = op_to_const ( & ecx, & field_op) ;
122
- Ok ( ty:: Const :: from_value ( tcx, val, field_op. layout . ty ) )
123
- } )
124
- . collect :: < InterpResult < ' tcx , Vec < _ > > > ( ) ?;
125
- let fields = tcx. arena . alloc_from_iter ( fields) ;
126
- Ok ( mir:: DestructuredConst { variant, fields } )
77
+ const_ : ty:: Const < ' tcx > ,
78
+ ) -> Option < mir:: DestructuredConst < ' tcx > > {
79
+ if let ty:: ConstKind :: Value ( valtree) = const_. val ( ) {
80
+ let branches = match valtree {
81
+ ty:: ValTree :: Branch ( b) => b,
82
+ _ => return None ,
83
+ } ;
84
+
85
+ let ( fields, variant) = match const_. ty ( ) . kind ( ) {
86
+ ty:: Array ( inner_ty, _) | ty:: Slice ( inner_ty) => {
87
+ // construct the consts for the elements of the array/slice
88
+ let field_consts = branches
89
+ . iter ( )
90
+ . map ( |b| {
91
+ tcx. mk_const ( ty:: ConstS { kind : ty:: ConstKind :: Value ( * b) , ty : * inner_ty } )
92
+ } )
93
+ . collect :: < Vec < _ > > ( ) ;
94
+ debug ! ( ?field_consts) ;
95
+
96
+ ( field_consts, None )
97
+ }
98
+ ty:: Adt ( def, _) if def. variants ( ) . is_empty ( ) => bug ! ( "unreachable" ) ,
99
+ ty:: Adt ( def, substs) => {
100
+ let variant_idx = if def. is_enum ( ) {
101
+ VariantIdx :: from_u32 ( branches[ 0 ] . unwrap_leaf ( ) . try_to_u32 ( ) . ok ( ) ?)
102
+ } else {
103
+ VariantIdx :: from_u32 ( 0 )
104
+ } ;
105
+ let fields = & def. variant ( variant_idx) . fields ;
106
+ let mut field_consts = Vec :: with_capacity ( fields. len ( ) ) ;
107
+
108
+ // Note: First element inValTree corresponds to variant of enum
109
+ let mut valtree_idx = if def. is_enum ( ) { 1 } else { 0 } ;
110
+ for field in fields {
111
+ let field_ty = field. ty ( tcx, substs) ;
112
+ let field_valtree = branches[ valtree_idx] ; // first element of branches is variant
113
+ let field_const = tcx. mk_const ( ty:: ConstS {
114
+ kind : ty:: ConstKind :: Value ( field_valtree) ,
115
+ ty : field_ty,
116
+ } ) ;
117
+ field_consts. push ( field_const) ;
118
+ valtree_idx += 1 ;
119
+ }
120
+ debug ! ( ?field_consts) ;
121
+
122
+ ( field_consts, Some ( variant_idx) )
123
+ }
124
+ ty:: Tuple ( elem_tys) => {
125
+ let fields = elem_tys
126
+ . iter ( )
127
+ . enumerate ( )
128
+ . map ( |( i, elem_ty) | {
129
+ let elem_valtree = branches[ i] ;
130
+ tcx. mk_const ( ty:: ConstS {
131
+ kind : ty:: ConstKind :: Value ( elem_valtree) ,
132
+ ty : elem_ty,
133
+ } )
134
+ } )
135
+ . collect :: < Vec < _ > > ( ) ;
136
+
137
+ ( fields, None )
138
+ }
139
+ _ => bug ! ( "cannot destructure constant {:?}" , const_) ,
140
+ } ;
141
+
142
+ let fields = tcx. arena . alloc_from_iter ( fields. into_iter ( ) ) ;
143
+
144
+ Some ( mir:: DestructuredConst { variant, fields } )
145
+ } else {
146
+ None
147
+ }
127
148
}
128
149
129
150
#[ instrument( skip( tcx) , level = "debug" ) ]
@@ -143,8 +164,8 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
143
164
throw_ub ! ( Unreachable )
144
165
}
145
166
ty:: Adt ( def, _) => {
146
- let variant = ecx. read_discriminant ( & op) . unwrap ( ) . 1 ;
147
- let down = ecx. operand_downcast ( & op, variant) . unwrap ( ) ;
167
+ let variant = ecx. read_discriminant ( & op) ? . 1 ;
168
+ let down = ecx. operand_downcast ( & op, variant) ? ;
148
169
( def. variants ( ) [ variant] . fields . len ( ) , Some ( variant) , down)
149
170
}
150
171
ty:: Tuple ( substs) => ( substs. len ( ) , None , op) ,
@@ -163,43 +184,6 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
163
184
Ok ( mir:: DestructuredMirConstant { variant, fields } )
164
185
}
165
186
166
- #[ instrument( skip( tcx) , level = "debug" ) ]
167
- pub ( crate ) fn deref_const < ' tcx > (
168
- tcx : TyCtxt < ' tcx > ,
169
- param_env : ty:: ParamEnv < ' tcx > ,
170
- val : ty:: Const < ' tcx > ,
171
- ) -> ty:: Const < ' tcx > {
172
- trace ! ( "deref_const: {:?}" , val) ;
173
- let ecx = mk_eval_cx ( tcx, DUMMY_SP , param_env, false ) ;
174
- let op = ecx. const_to_op ( val, None ) . unwrap ( ) ;
175
- let mplace = ecx. deref_operand ( & op) . unwrap ( ) ;
176
- if let Some ( alloc_id) = mplace. ptr . provenance {
177
- assert_eq ! (
178
- tcx. get_global_alloc( alloc_id) . unwrap( ) . unwrap_memory( ) . inner( ) . mutability,
179
- Mutability :: Not ,
180
- "deref_const cannot be used with mutable allocations as \
181
- that could allow pattern matching to observe mutable statics",
182
- ) ;
183
- }
184
-
185
- let ty = match mplace. meta {
186
- MemPlaceMeta :: None => mplace. layout . ty ,
187
- MemPlaceMeta :: Poison => bug ! ( "poison metadata in `deref_const`: {:#?}" , mplace) ,
188
- // In case of unsized types, figure out the real type behind.
189
- MemPlaceMeta :: Meta ( scalar) => match mplace. layout . ty . kind ( ) {
190
- ty:: Str => bug ! ( "there's no sized equivalent of a `str`" ) ,
191
- ty:: Slice ( elem_ty) => tcx. mk_array ( * elem_ty, scalar. to_machine_usize ( & tcx) . unwrap ( ) ) ,
192
- _ => bug ! (
193
- "type {} should not have metadata, but had {:?}" ,
194
- mplace. layout. ty,
195
- mplace. meta
196
- ) ,
197
- } ,
198
- } ;
199
-
200
- tcx. mk_const ( ty:: ConstS { kind : ty:: ConstKind :: Value ( op_to_const ( & ecx, & mplace. into ( ) ) ) , ty } )
201
- }
202
-
203
187
#[ instrument( skip( tcx) , level = "debug" ) ]
204
188
pub ( crate ) fn deref_mir_constant < ' tcx > (
205
189
tcx : TyCtxt < ' tcx > ,
@@ -211,16 +195,16 @@ pub(crate) fn deref_mir_constant<'tcx>(
211
195
let mplace = ecx. deref_operand ( & op) . unwrap ( ) ;
212
196
if let Some ( alloc_id) = mplace. ptr . provenance {
213
197
assert_eq ! (
214
- tcx. get_global_alloc( alloc_id) . unwrap( ) . unwrap_memory( ) . 0.0 . mutability,
198
+ tcx. get_global_alloc( alloc_id) . unwrap( ) . unwrap_memory( ) . 0 . 0 . mutability,
215
199
Mutability :: Not ,
216
- "deref_const cannot be used with mutable allocations as \
200
+ "deref_mir_constant cannot be used with mutable allocations as \
217
201
that could allow pattern matching to observe mutable statics",
218
202
) ;
219
203
}
220
204
221
205
let ty = match mplace. meta {
222
206
MemPlaceMeta :: None => mplace. layout . ty ,
223
- MemPlaceMeta :: Poison => bug ! ( "poison metadata in `deref_const `: {:#?}" , mplace) ,
207
+ MemPlaceMeta :: Poison => bug ! ( "poison metadata in `deref_mir_constant `: {:#?}" , mplace) ,
224
208
// In case of unsized types, figure out the real type behind.
225
209
MemPlaceMeta :: Meta ( scalar) => match mplace. layout . ty . kind ( ) {
226
210
ty:: Str => bug ! ( "there's no sized equivalent of a `str`" ) ,
0 commit comments