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 ,
@@ -40,7 +39,7 @@ pub(crate) fn const_caller_location(
40
39
}
41
40
42
41
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
43
- const VALTREE_MAX_NODES : usize = 1000 ;
42
+ const VALTREE_MAX_NODES : usize = 100000 ;
44
43
45
44
pub ( crate ) enum ValTreeCreationError {
46
45
NodesOverflow ,
@@ -56,6 +55,8 @@ pub(crate) fn eval_to_valtree<'tcx>(
56
55
cid : GlobalId < ' tcx > ,
57
56
) -> EvalToValTreeResult < ' tcx > {
58
57
let const_alloc = tcx. eval_to_allocation_raw ( param_env. and ( cid) ) ?;
58
+
59
+ // FIXME Need to provide a span to `eval_to_valtree`
59
60
let ecx = mk_eval_cx (
60
61
tcx, DUMMY_SP , param_env,
61
62
// It is absolutely crucial for soundness that
@@ -90,40 +91,81 @@ pub(crate) fn eval_to_valtree<'tcx>(
90
91
}
91
92
}
92
93
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 .
94
+ /// Tries to destructure constants of type Array or Adt into the constants
95
+ /// of its fields .
95
96
pub ( crate ) fn try_destructure_const < ' tcx > (
96
97
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 } )
98
+ const_ : ty:: Const < ' tcx > ,
99
+ ) -> Option < ty:: DestructuredConst < ' tcx > > {
100
+ if let ty:: ConstKind :: Value ( valtree) = const_. kind ( ) {
101
+ let branches = match valtree {
102
+ ty:: ValTree :: Branch ( b) => b,
103
+ _ => return None ,
104
+ } ;
105
+
106
+ let ( fields, variant) = match const_. ty ( ) . kind ( ) {
107
+ ty:: Array ( inner_ty, _) | ty:: Slice ( inner_ty) => {
108
+ // construct the consts for the elements of the array/slice
109
+ let field_consts = branches
110
+ . iter ( )
111
+ . map ( |b| {
112
+ tcx. mk_const ( ty:: ConstS { kind : ty:: ConstKind :: Value ( * b) , ty : * inner_ty } )
113
+ } )
114
+ . collect :: < Vec < _ > > ( ) ;
115
+ debug ! ( ?field_consts) ;
116
+
117
+ ( field_consts, None )
118
+ }
119
+ ty:: Adt ( def, _) if def. variants ( ) . is_empty ( ) => bug ! ( "unreachable" ) ,
120
+ ty:: Adt ( def, substs) => {
121
+ let variant_idx = if def. is_enum ( ) {
122
+ VariantIdx :: from_u32 ( branches[ 0 ] . unwrap_leaf ( ) . try_to_u32 ( ) . ok ( ) ?)
123
+ } else {
124
+ VariantIdx :: from_u32 ( 0 )
125
+ } ;
126
+ let fields = & def. variant ( variant_idx) . fields ;
127
+ let mut field_consts = Vec :: with_capacity ( fields. len ( ) ) ;
128
+
129
+ // Note: First element inValTree corresponds to variant of enum
130
+ let mut valtree_idx = if def. is_enum ( ) { 1 } else { 0 } ;
131
+ for field in fields {
132
+ let field_ty = field. ty ( tcx, substs) ;
133
+ let field_valtree = branches[ valtree_idx] ; // first element of branches is variant
134
+ let field_const = tcx. mk_const ( ty:: ConstS {
135
+ kind : ty:: ConstKind :: Value ( field_valtree) ,
136
+ ty : field_ty,
137
+ } ) ;
138
+ field_consts. push ( field_const) ;
139
+ valtree_idx += 1 ;
140
+ }
141
+ debug ! ( ?field_consts) ;
142
+
143
+ ( field_consts, Some ( variant_idx) )
144
+ }
145
+ ty:: Tuple ( elem_tys) => {
146
+ let fields = elem_tys
147
+ . iter ( )
148
+ . enumerate ( )
149
+ . map ( |( i, elem_ty) | {
150
+ let elem_valtree = branches[ i] ;
151
+ tcx. mk_const ( ty:: ConstS {
152
+ kind : ty:: ConstKind :: Value ( elem_valtree) ,
153
+ ty : elem_ty,
154
+ } )
155
+ } )
156
+ . collect :: < Vec < _ > > ( ) ;
157
+
158
+ ( fields, None )
159
+ }
160
+ _ => bug ! ( "cannot destructure constant {:?}" , const_) ,
161
+ } ;
162
+
163
+ let fields = tcx. arena . alloc_from_iter ( fields. into_iter ( ) ) ;
164
+
165
+ Some ( ty:: DestructuredConst { variant, fields } )
166
+ } else {
167
+ None
168
+ }
127
169
}
128
170
129
171
#[ instrument( skip( tcx) , level = "debug" ) ]
@@ -143,8 +185,8 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
143
185
throw_ub ! ( Unreachable )
144
186
}
145
187
ty:: Adt ( def, _) => {
146
- let variant = ecx. read_discriminant ( & op) . unwrap ( ) . 1 ;
147
- let down = ecx. operand_downcast ( & op, variant) . unwrap ( ) ;
188
+ let variant = ecx. read_discriminant ( & op) ? . 1 ;
189
+ let down = ecx. operand_downcast ( & op, variant) ? ;
148
190
( def. variants ( ) [ variant] . fields . len ( ) , Some ( variant) , down)
149
191
}
150
192
ty:: Tuple ( substs) => ( substs. len ( ) , None , op) ,
@@ -163,43 +205,6 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
163
205
Ok ( mir:: DestructuredMirConstant { variant, fields } )
164
206
}
165
207
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
208
#[ instrument( skip( tcx) , level = "debug" ) ]
204
209
pub ( crate ) fn deref_mir_constant < ' tcx > (
205
210
tcx : TyCtxt < ' tcx > ,
@@ -213,14 +218,14 @@ pub(crate) fn deref_mir_constant<'tcx>(
213
218
assert_eq ! (
214
219
tcx. get_global_alloc( alloc_id) . unwrap( ) . unwrap_memory( ) . 0.0 . mutability,
215
220
Mutability :: Not ,
216
- "deref_const cannot be used with mutable allocations as \
221
+ "deref_mir_constant cannot be used with mutable allocations as \
217
222
that could allow pattern matching to observe mutable statics",
218
223
) ;
219
224
}
220
225
221
226
let ty = match mplace. meta {
222
227
MemPlaceMeta :: None => mplace. layout . ty ,
223
- MemPlaceMeta :: Poison => bug ! ( "poison metadata in `deref_const `: {:#?}" , mplace) ,
228
+ MemPlaceMeta :: Poison => bug ! ( "poison metadata in `deref_mir_constant `: {:#?}" , mplace) ,
224
229
// In case of unsized types, figure out the real type behind.
225
230
MemPlaceMeta :: Meta ( scalar) => match mplace. layout . ty . kind ( ) {
226
231
ty:: Str => bug ! ( "there's no sized equivalent of a `str`" ) ,
0 commit comments