11// Not in interpret to make sure we do not use private implementation details
22
3- use std:: convert:: TryFrom ;
4-
53use rustc_hir:: Mutability ;
64use rustc_middle:: mir;
75use rustc_middle:: mir:: interpret:: { EvalToValTreeResult , GlobalId } ;
86use rustc_middle:: ty:: { self , TyCtxt } ;
97use rustc_span:: { source_map:: DUMMY_SP , symbol:: Symbol } ;
8+ use rustc_target:: abi:: VariantIdx ;
109
1110use crate :: interpret:: {
1211 intern_const_alloc_recursive, ConstValue , InternKind , InterpCx , InterpResult , MemPlaceMeta ,
@@ -25,6 +24,12 @@ pub use fn_queries::*;
2524pub use machine:: * ;
2625pub ( crate ) use valtrees:: { const_to_valtree_inner, valtree_to_const_value} ;
2726
27+ pub ( crate ) enum ValTreeCreationError {
28+ NonSupportedType ,
29+ Other ,
30+ }
31+ pub ( crate ) type ValTreeCreationResult < ' tcx > = Result < ty:: ValTree < ' tcx > , ValTreeCreationError > ;
32+
2833pub ( crate ) fn const_caller_location (
2934 tcx : TyCtxt < ' _ > ,
3035 ( file, line, col) : ( Symbol , u32 , u32 ) ,
@@ -39,23 +44,15 @@ pub(crate) fn const_caller_location(
3944 ConstValue :: Scalar ( Scalar :: from_maybe_pointer ( loc_place. ptr , & tcx) )
4045}
4146
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-
5247/// Evaluates a constant and turns it into a type-level constant value.
5348pub ( crate ) fn eval_to_valtree < ' tcx > (
5449 tcx : TyCtxt < ' tcx > ,
5550 param_env : ty:: ParamEnv < ' tcx > ,
5651 cid : GlobalId < ' tcx > ,
5752) -> EvalToValTreeResult < ' tcx > {
5853 let const_alloc = tcx. eval_to_allocation_raw ( param_env. and ( cid) ) ?;
54+
55+ // FIXME Need to provide a span to `eval_to_valtree`
5956 let ecx = mk_eval_cx (
6057 tcx, DUMMY_SP , param_env,
6158 // It is absolutely crucial for soundness that
@@ -65,65 +62,89 @@ pub(crate) fn eval_to_valtree<'tcx>(
6562 let place = ecx. raw_const_to_mplace ( const_alloc) . unwrap ( ) ;
6663 debug ! ( ?place) ;
6764
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) ;
7066
7167 match valtree_result {
7268 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 ) ,
9070 }
9171}
9272
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 .
9575pub ( crate ) fn try_destructure_const < ' tcx > (
9676 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+ }
127148}
128149
129150#[ instrument( skip( tcx) , level = "debug" ) ]
@@ -143,8 +164,8 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
143164 throw_ub ! ( Unreachable )
144165 }
145166 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) ? ;
148169 ( def. variants ( ) [ variant] . fields . len ( ) , Some ( variant) , down)
149170 }
150171 ty:: Tuple ( substs) => ( substs. len ( ) , None , op) ,
@@ -163,43 +184,6 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
163184 Ok ( mir:: DestructuredMirConstant { variant, fields } )
164185}
165186
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-
203187#[ instrument( skip( tcx) , level = "debug" ) ]
204188pub ( crate ) fn deref_mir_constant < ' tcx > (
205189 tcx : TyCtxt < ' tcx > ,
@@ -211,16 +195,16 @@ pub(crate) fn deref_mir_constant<'tcx>(
211195 let mplace = ecx. deref_operand ( & op) . unwrap ( ) ;
212196 if let Some ( alloc_id) = mplace. ptr . provenance {
213197 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,
215199 Mutability :: Not ,
216- "deref_const cannot be used with mutable allocations as \
200+ "deref_mir_constant cannot be used with mutable allocations as \
217201 that could allow pattern matching to observe mutable statics",
218202 ) ;
219203 }
220204
221205 let ty = match mplace. meta {
222206 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) ,
224208 // In case of unsized types, figure out the real type behind.
225209 MemPlaceMeta :: Meta ( scalar) => match mplace. layout . ty . kind ( ) {
226210 ty:: Str => bug ! ( "there's no sized equivalent of a `str`" ) ,
0 commit comments