@@ -4,6 +4,7 @@ use std::convert::TryFrom;
44
55use rustc_hir:: Mutability ;
66use rustc_middle:: mir;
7+ use rustc_middle:: mir:: interpret:: { EvalToValTreeResult , GlobalId } ;
78use rustc_middle:: ty:: { self , TyCtxt } ;
89use rustc_span:: { source_map:: DUMMY_SP , symbol:: Symbol } ;
910
@@ -22,7 +23,7 @@ pub use error::*;
2223pub use eval_queries:: * ;
2324pub use fn_queries:: * ;
2425pub 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} ;
2627
2728pub ( crate ) fn const_caller_location (
2829 tcx : TyCtxt < ' _ > ,
@@ -38,6 +39,57 @@ pub(crate) fn const_caller_location(
3839 ConstValue :: Scalar ( Scalar :: from_maybe_pointer ( loc_place. ptr , & tcx) )
3940}
4041
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+
4193/// This function should never fail for validated constants. However, it is also invoked from the
4294/// pretty printer which might attempt to format invalid constants and in that case it might fail.
4395pub ( crate ) fn try_destructure_const < ' tcx > (
@@ -48,7 +100,6 @@ pub(crate) fn try_destructure_const<'tcx>(
48100 trace ! ( "destructure_const: {:?}" , val) ;
49101 let ecx = mk_eval_cx ( tcx, DUMMY_SP , param_env, false ) ;
50102 let op = ecx. const_to_op ( val, None ) ?;
51-
52103 // We go to `usize` as we cannot allocate anything bigger anyway.
53104 let ( field_count, variant, down) = match val. ty ( ) . kind ( ) {
54105 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>(
64115 ty:: Tuple ( substs) => ( substs. len ( ) , None , op) ,
65116 _ => bug ! ( "cannot destructure constant {:?}" , val) ,
66117 } ;
67-
68118 let fields = ( 0 ..field_count)
69119 . map ( |i| {
70120 let field_op = ecx. operand_field ( & down, i) ?;
@@ -73,10 +123,46 @@ pub(crate) fn try_destructure_const<'tcx>(
73123 } )
74124 . collect :: < InterpResult < ' tcx , Vec < _ > > > ( ) ?;
75125 let fields = tcx. arena . alloc_from_iter ( fields) ;
76-
77126 Ok ( mir:: DestructuredConst { variant, fields } )
78127}
79128
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+
80166#[ instrument( skip( tcx) , level = "debug" ) ]
81167pub ( crate ) fn deref_const < ' tcx > (
82168 tcx : TyCtxt < ' tcx > ,
@@ -113,3 +199,39 @@ pub(crate) fn deref_const<'tcx>(
113199
114200 tcx. mk_const ( ty:: ConstS { val : ty:: ConstKind :: Value ( op_to_const ( & ecx, & mplace. into ( ) ) ) , ty } )
115201}
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