11use rustc_abi:: { BackendRepr , FieldIdx , VariantIdx } ;
2+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
23use rustc_data_structures:: stack:: ensure_sufficient_stack;
34use rustc_middle:: mir:: interpret:: { EvalToValTreeResult , GlobalId , ValTreeCreationError } ;
45use rustc_middle:: traits:: ObligationCause ;
@@ -17,13 +18,15 @@ use crate::interpret::{
1718 intern_const_alloc_recursive,
1819} ;
1920
20- #[ instrument( skip( ecx) , level = "debug" ) ]
21+ #[ instrument( skip( ecx, visited , settled ) , level = "debug" ) ]
2122fn branches < ' tcx > (
2223 ecx : & CompileTimeInterpCx < ' tcx > ,
2324 place : & MPlaceTy < ' tcx > ,
2425 field_count : usize ,
2526 variant : Option < VariantIdx > ,
2627 num_nodes : & mut usize ,
28+ visited : & mut FxHashSet < MPlaceTy < ' tcx > > ,
29+ settled : & mut FxHashMap < MPlaceTy < ' tcx > , EvalToValTreeResult < ' tcx > > ,
2730) -> EvalToValTreeResult < ' tcx > {
2831 let place = match variant {
2932 Some ( variant) => ecx. project_downcast ( place, variant) . unwrap ( ) ,
@@ -45,7 +48,7 @@ fn branches<'tcx>(
4548
4649 for i in 0 ..field_count {
4750 let field = ecx. project_field ( & place, FieldIdx :: from_usize ( i) ) . unwrap ( ) ;
48- let valtree = const_to_valtree_inner ( ecx, & field, num_nodes) ?;
51+ let valtree = const_to_valtree_inner ( ecx, & field, num_nodes, visited , settled ) ?;
4952 branches. push ( ty:: Const :: new_value ( * ecx. tcx , valtree, field. layout . ty ) ) ;
5053 }
5154
@@ -57,39 +60,53 @@ fn branches<'tcx>(
5760 Ok ( ty:: ValTree :: from_branches ( * ecx. tcx , branches) )
5861}
5962
60- #[ instrument( skip( ecx) , level = "debug" ) ]
63+ #[ instrument( skip( ecx, visited , settled ) , level = "debug" ) ]
6164fn slice_branches < ' tcx > (
6265 ecx : & CompileTimeInterpCx < ' tcx > ,
6366 place : & MPlaceTy < ' tcx > ,
6467 num_nodes : & mut usize ,
68+ visited : & mut FxHashSet < MPlaceTy < ' tcx > > ,
69+ settled : & mut FxHashMap < MPlaceTy < ' tcx > , EvalToValTreeResult < ' tcx > > ,
6570) -> EvalToValTreeResult < ' tcx > {
6671 let n = place. len ( ecx) . unwrap_or_else ( |_| panic ! ( "expected to use len of place {place:?}" ) ) ;
6772
6873 let mut elems = Vec :: with_capacity ( n as usize ) ;
6974 for i in 0 ..n {
7075 let place_elem = ecx. project_index ( place, i) . unwrap ( ) ;
71- let valtree = const_to_valtree_inner ( ecx, & place_elem, num_nodes) ?;
76+ let valtree = const_to_valtree_inner ( ecx, & place_elem, num_nodes, visited , settled ) ?;
7277 elems. push ( ty:: Const :: new_value ( * ecx. tcx , valtree, place_elem. layout . ty ) ) ;
7378 }
7479
7580 Ok ( ty:: ValTree :: from_branches ( * ecx. tcx , elems) )
7681}
7782
78- #[ instrument( skip( ecx) , level = "debug" ) ]
83+ #[ instrument( skip( ecx, visited , settled ) , level = "debug" ) ]
7984fn const_to_valtree_inner < ' tcx > (
8085 ecx : & CompileTimeInterpCx < ' tcx > ,
8186 place : & MPlaceTy < ' tcx > ,
8287 num_nodes : & mut usize ,
88+ visited : & mut FxHashSet < MPlaceTy < ' tcx > > ,
89+ settled : & mut FxHashMap < MPlaceTy < ' tcx > , EvalToValTreeResult < ' tcx > > ,
8390) -> EvalToValTreeResult < ' tcx > {
8491 let tcx = * ecx. tcx ;
8592 let ty = place. layout . ty ;
8693 debug ! ( "ty kind: {:?}" , ty. kind( ) ) ;
8794
95+ if let Some ( & result) = settled. get ( place) {
96+ return result;
97+ }
98+
99+ if visited. contains ( place) {
100+ return Err ( ValTreeCreationError :: CyclicConst ) ;
101+ }
102+
88103 if * num_nodes >= VALTREE_MAX_NODES {
89104 return Err ( ValTreeCreationError :: NodesOverflow ) ;
90105 }
91106
92- match ty. kind ( ) {
107+ visited. insert ( place. clone ( ) ) ;
108+
109+ let result = ensure_sufficient_stack ( || match ty. kind ( ) {
93110 ty:: FnDef ( ..) => {
94111 * num_nodes += 1 ;
95112 Ok ( ty:: ValTree :: zst ( tcx) )
@@ -108,7 +125,7 @@ fn const_to_valtree_inner<'tcx>(
108125 // Since the returned valtree does not contain the type or layout, we can just
109126 // switch to the base type.
110127 place. layout = ecx. layout_of ( * base) . unwrap ( ) ;
111- ensure_sufficient_stack ( || const_to_valtree_inner ( ecx, & place, num_nodes) )
128+ const_to_valtree_inner ( ecx, & place, num_nodes, visited , settled )
112129 }
113130
114131 ty:: RawPtr ( _, _) => {
@@ -120,16 +137,16 @@ fn const_to_valtree_inner<'tcx>(
120137 // We could allow wide raw pointers where both sides are integers in the future,
121138 // but for now we reject them.
122139 if matches ! ( val. layout. backend_repr, BackendRepr :: ScalarPair ( ..) ) {
123- return Err ( ValTreeCreationError :: NonSupportedType ( ty) ) ;
140+ Err ( ValTreeCreationError :: NonSupportedType ( ty) )
141+ } else {
142+ let val = val. to_scalar ( ) ;
143+ // We are in the CTFE machine, so ptr-to-int casts will fail.
144+ // This can only be `Ok` if `val` already is an integer.
145+ match val. try_to_scalar_int ( ) {
146+ Ok ( val) => Ok ( ty:: ValTree :: from_scalar_int ( tcx, val) ) ,
147+ Err ( _) => Err ( ValTreeCreationError :: NonSupportedType ( ty) ) ,
148+ }
124149 }
125- let val = val. to_scalar ( ) ;
126- // We are in the CTFE machine, so ptr-to-int casts will fail.
127- // This can only be `Ok` if `val` already is an integer.
128- let Ok ( val) = val. try_to_scalar_int ( ) else {
129- return Err ( ValTreeCreationError :: NonSupportedType ( ty) ) ;
130- } ;
131- // It's just a ScalarInt!
132- Ok ( ty:: ValTree :: from_scalar_int ( tcx, val) )
133150 }
134151
135152 // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
@@ -138,33 +155,39 @@ fn const_to_valtree_inner<'tcx>(
138155
139156 ty:: Ref ( _, _, _) => {
140157 let derefd_place = ecx. deref_pointer ( place) . report_err ( ) ?;
141- const_to_valtree_inner ( ecx, & derefd_place, num_nodes)
158+ const_to_valtree_inner ( ecx, & derefd_place, num_nodes, visited , settled )
142159 }
143160
144- ty:: Str | ty:: Slice ( _) | ty:: Array ( _, _) => slice_branches ( ecx, place, num_nodes) ,
161+ ty:: Str | ty:: Slice ( _) | ty:: Array ( _, _) => {
162+ slice_branches ( ecx, place, num_nodes, visited, settled)
163+ }
145164 // Trait objects are not allowed in type level constants, as we have no concept for
146165 // resolving their backing type, even if we can do that at const eval time. We may
147166 // hypothetically be able to allow `dyn StructuralPartialEq` trait objects in the future,
148167 // but it is unclear if this is useful.
149168 ty:: Dynamic ( ..) => Err ( ValTreeCreationError :: NonSupportedType ( ty) ) ,
150169
151- ty:: Tuple ( elem_tys) => branches ( ecx, place, elem_tys. len ( ) , None , num_nodes) ,
170+ ty:: Tuple ( elem_tys) => {
171+ branches ( ecx, place, elem_tys. len ( ) , None , num_nodes, visited, settled)
172+ }
152173
153174 ty:: Adt ( def, _) => {
154175 if def. is_union ( ) {
155- return Err ( ValTreeCreationError :: NonSupportedType ( ty) ) ;
176+ Err ( ValTreeCreationError :: NonSupportedType ( ty) )
156177 } else if def. variants ( ) . is_empty ( ) {
157178 bug ! ( "uninhabited types should have errored and never gotten converted to valtree" )
179+ } else {
180+ let variant = ecx. read_discriminant ( place) . report_err ( ) ?;
181+ branches (
182+ ecx,
183+ place,
184+ def. variant ( variant) . fields . len ( ) ,
185+ def. is_enum ( ) . then_some ( variant) ,
186+ num_nodes,
187+ visited,
188+ settled,
189+ )
158190 }
159-
160- let variant = ecx. read_discriminant ( place) . report_err ( ) ?;
161- branches (
162- ecx,
163- place,
164- def. variant ( variant) . fields . len ( ) ,
165- def. is_enum ( ) . then_some ( variant) ,
166- num_nodes,
167- )
168191 }
169192
170193 // FIXME(oli-obk): we could look behind opaque types
@@ -186,7 +209,11 @@ fn const_to_valtree_inner<'tcx>(
186209 | ty:: Coroutine ( ..)
187210 | ty:: CoroutineWitness ( ..)
188211 | ty:: UnsafeBinder ( _) => Err ( ValTreeCreationError :: NonSupportedType ( ty) ) ,
189- }
212+ } ) ;
213+
214+ visited. remove ( place) ;
215+ settled. insert ( place. clone ( ) , result) ;
216+ result
190217}
191218
192219/// Valtrees don't store the `MemPlaceMeta` that all dynamically sized values have in the interpreter.
@@ -257,7 +284,9 @@ pub(crate) fn eval_to_valtree<'tcx>(
257284 debug ! ( ?place) ;
258285
259286 let mut num_nodes = 0 ;
260- const_to_valtree_inner ( & ecx, & place, & mut num_nodes)
287+ let mut visited = FxHashSet :: default ( ) ;
288+ let mut settled = FxHashMap :: default ( ) ;
289+ const_to_valtree_inner ( & ecx, & place, & mut num_nodes, & mut visited, & mut settled)
261290}
262291
263292/// Converts a `ValTree` to a `ConstValue`, which is needed after mir
0 commit comments