@@ -4,6 +4,7 @@ use std::convert::TryFrom;
4
4
5
5
use rustc_hir:: Mutability ;
6
6
use rustc_middle:: mir;
7
+ use rustc_middle:: mir:: interpret:: { EvalToValTreeResult , GlobalId } ;
7
8
use rustc_middle:: ty:: { self , TyCtxt } ;
8
9
use rustc_span:: { source_map:: DUMMY_SP , symbol:: Symbol } ;
9
10
@@ -22,7 +23,7 @@ pub use error::*;
22
23
pub use eval_queries:: * ;
23
24
pub use fn_queries:: * ;
24
25
pub use machine:: * ;
25
- pub ( crate ) use valtrees:: { const_to_valtree , valtree_to_const_value} ;
26
+ pub ( crate ) use valtrees:: { const_to_valtree_inner , valtree_to_const_value} ;
26
27
27
28
pub ( crate ) fn const_caller_location (
28
29
tcx : TyCtxt < ' _ > ,
@@ -38,6 +39,57 @@ pub(crate) fn const_caller_location(
38
39
ConstValue :: Scalar ( Scalar :: from_maybe_pointer ( loc_place. ptr , & tcx) )
39
40
}
40
41
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
+ /// Evaluates a constant and turns it into a type-level constant value.
53
+ pub ( crate ) fn eval_to_valtree < ' tcx > (
54
+ tcx : TyCtxt < ' tcx > ,
55
+ param_env : ty:: ParamEnv < ' tcx > ,
56
+ cid : GlobalId < ' tcx > ,
57
+ ) -> EvalToValTreeResult < ' tcx > {
58
+ let const_alloc = tcx. eval_to_allocation_raw ( param_env. and ( cid) ) ?;
59
+ let ecx = mk_eval_cx (
60
+ tcx, DUMMY_SP , param_env,
61
+ // It is absolutely crucial for soundness that
62
+ // we do not read from static items or other mutable memory.
63
+ false ,
64
+ ) ;
65
+ let place = ecx. raw_const_to_mplace ( const_alloc) . unwrap ( ) ;
66
+ debug ! ( ?place) ;
67
+
68
+ let mut num_nodes = 0 ;
69
+ let valtree_result = const_to_valtree_inner ( & ecx, & place, & mut num_nodes) ;
70
+
71
+ match valtree_result {
72
+ 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
+ }
90
+ }
91
+ }
92
+
41
93
/// This function should never fail for validated constants. However, it is also invoked from the
42
94
/// pretty printer which might attempt to format invalid constants and in that case it might fail.
43
95
pub ( crate ) fn try_destructure_const < ' tcx > (
@@ -48,7 +100,6 @@ pub(crate) fn try_destructure_const<'tcx>(
48
100
trace ! ( "destructure_const: {:?}" , val) ;
49
101
let ecx = mk_eval_cx ( tcx, DUMMY_SP , param_env, false ) ;
50
102
let op = ecx. const_to_op ( val, None ) ?;
51
-
52
103
// We go to `usize` as we cannot allocate anything bigger anyway.
53
104
let ( field_count, variant, down) = match val. ty ( ) . kind ( ) {
54
105
ty:: Array ( _, len) => ( usize:: try_from ( len. eval_usize ( tcx, param_env) ) . unwrap ( ) , None , op) ,
@@ -64,7 +115,6 @@ pub(crate) fn try_destructure_const<'tcx>(
64
115
ty:: Tuple ( substs) => ( substs. len ( ) , None , op) ,
65
116
_ => bug ! ( "cannot destructure constant {:?}" , val) ,
66
117
} ;
67
-
68
118
let fields = ( 0 ..field_count)
69
119
. map ( |i| {
70
120
let field_op = ecx. operand_field ( & down, i) ?;
@@ -73,10 +123,46 @@ pub(crate) fn try_destructure_const<'tcx>(
73
123
} )
74
124
. collect :: < InterpResult < ' tcx , Vec < _ > > > ( ) ?;
75
125
let fields = tcx. arena . alloc_from_iter ( fields) ;
76
-
77
126
Ok ( mir:: DestructuredConst { variant, fields } )
78
127
}
79
128
129
+ #[ instrument( skip( tcx) , level = "debug" ) ]
130
+ pub ( crate ) fn try_destructure_mir_constant < ' tcx > (
131
+ tcx : TyCtxt < ' tcx > ,
132
+ param_env : ty:: ParamEnv < ' tcx > ,
133
+ val : mir:: ConstantKind < ' tcx > ,
134
+ ) -> InterpResult < ' tcx , mir:: DestructuredMirConstant < ' tcx > > {
135
+ trace ! ( "destructure_mir_constant: {:?}" , val) ;
136
+ let ecx = mk_eval_cx ( tcx, DUMMY_SP , param_env, false ) ;
137
+ let op = ecx. mir_const_to_op ( & val, None ) ?;
138
+
139
+ // We go to `usize` as we cannot allocate anything bigger anyway.
140
+ let ( field_count, variant, down) = match val. ty ( ) . kind ( ) {
141
+ ty:: Array ( _, len) => ( len. eval_usize ( tcx, param_env) as usize , None , op) ,
142
+ ty:: Adt ( def, _) if def. variants ( ) . is_empty ( ) => {
143
+ throw_ub ! ( Unreachable )
144
+ }
145
+ ty:: Adt ( def, _) => {
146
+ let variant = ecx. read_discriminant ( & op) . unwrap ( ) . 1 ;
147
+ let down = ecx. operand_downcast ( & op, variant) . unwrap ( ) ;
148
+ ( def. variants ( ) [ variant] . fields . len ( ) , Some ( variant) , down)
149
+ }
150
+ ty:: Tuple ( substs) => ( substs. len ( ) , None , op) ,
151
+ _ => bug ! ( "cannot destructure mir constant {:?}" , val) ,
152
+ } ;
153
+
154
+ let fields_iter = ( 0 ..field_count)
155
+ . map ( |i| {
156
+ let field_op = ecx. operand_field ( & down, i) ?;
157
+ let val = op_to_const ( & ecx, & field_op) ;
158
+ Ok ( mir:: ConstantKind :: Val ( val, field_op. layout . ty ) )
159
+ } )
160
+ . collect :: < InterpResult < ' tcx , Vec < _ > > > ( ) ?;
161
+ let fields = tcx. arena . alloc_from_iter ( fields_iter) ;
162
+
163
+ Ok ( mir:: DestructuredMirConstant { variant, fields } )
164
+ }
165
+
80
166
#[ instrument( skip( tcx) , level = "debug" ) ]
81
167
pub ( crate ) fn deref_const < ' tcx > (
82
168
tcx : TyCtxt < ' tcx > ,
@@ -113,3 +199,39 @@ pub(crate) fn deref_const<'tcx>(
113
199
114
200
tcx. mk_const ( ty:: ConstS { val : ty:: ConstKind :: Value ( op_to_const ( & ecx, & mplace. into ( ) ) ) , ty } )
115
201
}
202
+
203
+ #[ instrument( skip( tcx) , level = "debug" ) ]
204
+ pub ( crate ) fn deref_mir_constant < ' tcx > (
205
+ tcx : TyCtxt < ' tcx > ,
206
+ param_env : ty:: ParamEnv < ' tcx > ,
207
+ val : mir:: ConstantKind < ' tcx > ,
208
+ ) -> mir:: ConstantKind < ' tcx > {
209
+ let ecx = mk_eval_cx ( tcx, DUMMY_SP , param_env, false ) ;
210
+ let op = ecx. mir_const_to_op ( & val, None ) . unwrap ( ) ;
211
+ let mplace = ecx. deref_operand ( & op) . unwrap ( ) ;
212
+ if let Some ( alloc_id) = mplace. ptr . provenance {
213
+ assert_eq ! (
214
+ tcx. get_global_alloc( alloc_id) . unwrap( ) . unwrap_memory( ) . 0.0 . mutability,
215
+ Mutability :: Not ,
216
+ "deref_const cannot be used with mutable allocations as \
217
+ that could allow pattern matching to observe mutable statics",
218
+ ) ;
219
+ }
220
+
221
+ let ty = match mplace. meta {
222
+ MemPlaceMeta :: None => mplace. layout . ty ,
223
+ MemPlaceMeta :: Poison => bug ! ( "poison metadata in `deref_const`: {:#?}" , mplace) ,
224
+ // In case of unsized types, figure out the real type behind.
225
+ MemPlaceMeta :: Meta ( scalar) => match mplace. layout . ty . kind ( ) {
226
+ ty:: Str => bug ! ( "there's no sized equivalent of a `str`" ) ,
227
+ ty:: Slice ( elem_ty) => tcx. mk_array ( * elem_ty, scalar. to_machine_usize ( & tcx) . unwrap ( ) ) ,
228
+ _ => bug ! (
229
+ "type {} should not have metadata, but had {:?}" ,
230
+ mplace. layout. ty,
231
+ mplace. meta
232
+ ) ,
233
+ } ,
234
+ } ;
235
+
236
+ mir:: ConstantKind :: Val ( op_to_const ( & ecx, & mplace. into ( ) ) , ty)
237
+ }
0 commit comments