]>
Commit | Line | Data |
---|---|---|
04454e1e FG |
1 | use super::eval_queries::{mk_eval_cx, op_to_const}; |
2 | use super::machine::CompileTimeEvalContext; | |
923072b8 | 3 | use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; |
04454e1e FG |
4 | use crate::interpret::{ |
5 | intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, | |
f2b60f7d | 6 | MemoryKind, PlaceTy, Scalar, |
04454e1e | 7 | }; |
923072b8 | 8 | use crate::interpret::{MPlaceTy, Value}; |
04454e1e FG |
9 | use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; |
10 | use rustc_span::source_map::DUMMY_SP; | |
353b0b11 | 11 | use rustc_target::abi::{Align, FieldIdx, VariantIdx, FIRST_VARIANT}; |
04454e1e | 12 | |
04454e1e FG |
13 | #[instrument(skip(ecx), level = "debug")] |
14 | fn branches<'tcx>( | |
15 | ecx: &CompileTimeEvalContext<'tcx, 'tcx>, | |
16 | place: &MPlaceTy<'tcx>, | |
17 | n: usize, | |
18 | variant: Option<VariantIdx>, | |
923072b8 FG |
19 | num_nodes: &mut usize, |
20 | ) -> ValTreeCreationResult<'tcx> { | |
04454e1e FG |
21 | let place = match variant { |
22 | Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(), | |
23 | None => *place, | |
24 | }; | |
25 | let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); | |
26 | debug!(?place, ?variant); | |
27 | ||
923072b8 FG |
28 | let mut fields = Vec::with_capacity(n); |
29 | for i in 0..n { | |
04454e1e | 30 | let field = ecx.mplace_field(&place, i).unwrap(); |
923072b8 FG |
31 | let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; |
32 | fields.push(Some(valtree)); | |
33 | } | |
34 | ||
35 | // For enums, we prepend their variant index before the variant's fields so we can figure out | |
04454e1e | 36 | // the variant again when just seeing a valtree. |
923072b8 FG |
37 | let branches = variant |
38 | .into_iter() | |
39 | .chain(fields.into_iter()) | |
40 | .collect::<Option<Vec<_>>>() | |
41 | .expect("should have already checked for errors in ValTree creation"); | |
42 | ||
43 | // Have to account for ZSTs here | |
44 | if branches.len() == 0 { | |
45 | *num_nodes += 1; | |
46 | } | |
47 | ||
48 | Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches))) | |
04454e1e FG |
49 | } |
50 | ||
51 | #[instrument(skip(ecx), level = "debug")] | |
52 | fn slice_branches<'tcx>( | |
53 | ecx: &CompileTimeEvalContext<'tcx, 'tcx>, | |
54 | place: &MPlaceTy<'tcx>, | |
923072b8 FG |
55 | num_nodes: &mut usize, |
56 | ) -> ValTreeCreationResult<'tcx> { | |
04454e1e FG |
57 | let n = place |
58 | .len(&ecx.tcx.tcx) | |
59 | .unwrap_or_else(|_| panic!("expected to use len of place {:?}", place)); | |
923072b8 FG |
60 | |
61 | let mut elems = Vec::with_capacity(n as usize); | |
62 | for i in 0..n { | |
04454e1e | 63 | let place_elem = ecx.mplace_index(place, i).unwrap(); |
923072b8 FG |
64 | let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?; |
65 | elems.push(valtree); | |
66 | } | |
04454e1e | 67 | |
923072b8 | 68 | Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(elems))) |
04454e1e FG |
69 | } |
70 | ||
71 | #[instrument(skip(ecx), level = "debug")] | |
923072b8 | 72 | pub(crate) fn const_to_valtree_inner<'tcx>( |
04454e1e FG |
73 | ecx: &CompileTimeEvalContext<'tcx, 'tcx>, |
74 | place: &MPlaceTy<'tcx>, | |
923072b8 FG |
75 | num_nodes: &mut usize, |
76 | ) -> ValTreeCreationResult<'tcx> { | |
77 | let ty = place.layout.ty; | |
78 | debug!("ty kind: {:?}", ty.kind()); | |
79 | ||
80 | if *num_nodes >= VALTREE_MAX_NODES { | |
81 | return Err(ValTreeCreationError::NodesOverflow); | |
82 | } | |
83 | ||
84 | match ty.kind() { | |
85 | ty::FnDef(..) => { | |
86 | *num_nodes += 1; | |
87 | Ok(ty::ValTree::zst()) | |
88 | } | |
04454e1e | 89 | ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { |
923072b8 FG |
90 | let Ok(val) = ecx.read_immediate(&place.into()) else { |
91 | return Err(ValTreeCreationError::Other); | |
92 | }; | |
f2b60f7d | 93 | let val = val.to_scalar(); |
923072b8 FG |
94 | *num_nodes += 1; |
95 | ||
96 | Ok(ty::ValTree::Leaf(val.assert_int())) | |
04454e1e FG |
97 | } |
98 | ||
99 | // Raw pointers are not allowed in type level constants, as we cannot properly test them for | |
f2b60f7d | 100 | // equality at compile-time (see `ptr_guaranteed_cmp`). |
04454e1e FG |
101 | // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to |
102 | // agree with runtime equality tests. | |
923072b8 | 103 | ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType), |
04454e1e FG |
104 | |
105 | ty::Ref(_, _, _) => { | |
923072b8 FG |
106 | let Ok(derefd_place)= ecx.deref_operand(&place.into()) else { |
107 | return Err(ValTreeCreationError::Other); | |
108 | }; | |
04454e1e FG |
109 | debug!(?derefd_place); |
110 | ||
923072b8 | 111 | const_to_valtree_inner(ecx, &derefd_place, num_nodes) |
04454e1e FG |
112 | } |
113 | ||
114 | ty::Str | ty::Slice(_) | ty::Array(_, _) => { | |
923072b8 | 115 | slice_branches(ecx, place, num_nodes) |
04454e1e FG |
116 | } |
117 | // Trait objects are not allowed in type level constants, as we have no concept for | |
118 | // resolving their backing type, even if we can do that at const eval time. We may | |
119 | // hypothetically be able to allow `dyn StructuralEq` trait objects in the future, | |
120 | // but it is unclear if this is useful. | |
923072b8 | 121 | ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), |
04454e1e | 122 | |
923072b8 FG |
123 | ty::Tuple(elem_tys) => { |
124 | branches(ecx, place, elem_tys.len(), None, num_nodes) | |
125 | } | |
04454e1e FG |
126 | |
127 | ty::Adt(def, _) => { | |
128 | if def.is_union() { | |
923072b8 | 129 | return Err(ValTreeCreationError::NonSupportedType); |
04454e1e FG |
130 | } else if def.variants().is_empty() { |
131 | bug!("uninhabited types should have errored and never gotten converted to valtree") | |
132 | } | |
133 | ||
923072b8 FG |
134 | let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else { |
135 | return Err(ValTreeCreationError::Other); | |
136 | }; | |
137 | branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) | |
04454e1e FG |
138 | } |
139 | ||
140 | ty::Never | |
141 | | ty::Error(_) | |
142 | | ty::Foreign(..) | |
143 | | ty::Infer(ty::FreshIntTy(_)) | |
144 | | ty::Infer(ty::FreshFloatTy(_)) | |
9c376795 FG |
145 | // FIXME(oli-obk): we could look behind opaque types |
146 | | ty::Alias(..) | |
04454e1e FG |
147 | | ty::Param(_) |
148 | | ty::Bound(..) | |
149 | | ty::Placeholder(..) | |
04454e1e FG |
150 | | ty::Infer(_) |
151 | // FIXME(oli-obk): we can probably encode closures just like structs | |
152 | | ty::Closure(..) | |
153 | | ty::Generator(..) | |
9ffffee4 | 154 | | ty::GeneratorWitness(..) |ty::GeneratorWitnessMIR(..)=> Err(ValTreeCreationError::NonSupportedType), |
04454e1e FG |
155 | } |
156 | } | |
157 | ||
158 | #[instrument(skip(ecx), level = "debug")] | |
159 | fn create_mplace_from_layout<'tcx>( | |
160 | ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, | |
161 | ty: Ty<'tcx>, | |
162 | ) -> MPlaceTy<'tcx> { | |
163 | let tcx = ecx.tcx; | |
164 | let param_env = ecx.param_env; | |
165 | let layout = tcx.layout_of(param_env.and(ty)).unwrap(); | |
166 | debug!(?layout); | |
167 | ||
168 | ecx.allocate(layout, MemoryKind::Stack).unwrap() | |
169 | } | |
170 | ||
171 | // Walks custom DSTs and gets the type of the unsized field and the number of elements | |
172 | // in the unsized field. | |
173 | fn get_info_on_unsized_field<'tcx>( | |
174 | ty: Ty<'tcx>, | |
175 | valtree: ty::ValTree<'tcx>, | |
176 | tcx: TyCtxt<'tcx>, | |
177 | ) -> (Ty<'tcx>, usize) { | |
178 | let mut last_valtree = valtree; | |
179 | let tail = tcx.struct_tail_with_normalize( | |
180 | ty, | |
181 | |ty| ty, | |
182 | || { | |
183 | let branches = last_valtree.unwrap_branch(); | |
184 | last_valtree = branches[branches.len() - 1]; | |
185 | debug!(?branches, ?last_valtree); | |
186 | }, | |
187 | ); | |
188 | let unsized_inner_ty = match tail.kind() { | |
189 | ty::Slice(t) => *t, | |
190 | ty::Str => tail, | |
191 | _ => bug!("expected Slice or Str"), | |
192 | }; | |
193 | ||
194 | // Have to adjust type for ty::Str | |
195 | let unsized_inner_ty = match unsized_inner_ty.kind() { | |
9ffffee4 | 196 | ty::Str => tcx.types.u8, |
04454e1e FG |
197 | _ => unsized_inner_ty, |
198 | }; | |
199 | ||
200 | // Get the number of elements in the unsized field | |
201 | let num_elems = last_valtree.unwrap_branch().len(); | |
202 | ||
203 | (unsized_inner_ty, num_elems) | |
204 | } | |
205 | ||
f2b60f7d | 206 | #[instrument(skip(ecx), level = "debug", ret)] |
04454e1e FG |
207 | fn create_pointee_place<'tcx>( |
208 | ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, | |
209 | ty: Ty<'tcx>, | |
210 | valtree: ty::ValTree<'tcx>, | |
211 | ) -> MPlaceTy<'tcx> { | |
212 | let tcx = ecx.tcx.tcx; | |
213 | ||
2b03887a | 214 | if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) { |
04454e1e FG |
215 | // We need to create `Allocation`s for custom DSTs |
216 | ||
217 | let (unsized_inner_ty, num_elems) = get_info_on_unsized_field(ty, valtree, tcx); | |
218 | let unsized_inner_ty = match unsized_inner_ty.kind() { | |
9ffffee4 | 219 | ty::Str => tcx.types.u8, |
04454e1e FG |
220 | _ => unsized_inner_ty, |
221 | }; | |
222 | let unsized_inner_ty_size = | |
223 | tcx.layout_of(ty::ParamEnv::empty().and(unsized_inner_ty)).unwrap().layout.size(); | |
224 | debug!(?unsized_inner_ty, ?unsized_inner_ty_size, ?num_elems); | |
225 | ||
226 | // for custom DSTs only the last field/element is unsized, but we need to also allocate | |
227 | // space for the other fields/elements | |
228 | let layout = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap(); | |
229 | let size_of_sized_part = layout.layout.size(); | |
230 | ||
231 | // Get the size of the memory behind the DST | |
232 | let dst_size = unsized_inner_ty_size.checked_mul(num_elems as u64, &tcx).unwrap(); | |
233 | ||
923072b8 FG |
234 | let size = size_of_sized_part.checked_add(dst_size, &tcx).unwrap(); |
235 | let align = Align::from_bytes(size.bytes().next_power_of_two()).unwrap(); | |
236 | let ptr = ecx.allocate_ptr(size, align, MemoryKind::Stack).unwrap(); | |
04454e1e FG |
237 | debug!(?ptr); |
238 | ||
f2b60f7d | 239 | MPlaceTy::from_aligned_ptr_with_meta( |
923072b8 FG |
240 | ptr.into(), |
241 | layout, | |
9ffffee4 | 242 | MemPlaceMeta::Meta(Scalar::from_target_usize(num_elems as u64, &tcx)), |
f2b60f7d | 243 | ) |
04454e1e FG |
244 | } else { |
245 | create_mplace_from_layout(ecx, ty) | |
246 | } | |
247 | } | |
248 | ||
249 | /// Converts a `ValTree` to a `ConstValue`, which is needed after mir | |
250 | /// construction has finished. | |
923072b8 | 251 | // FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function |
f2b60f7d | 252 | #[instrument(skip(tcx), level = "debug", ret)] |
04454e1e FG |
253 | pub fn valtree_to_const_value<'tcx>( |
254 | tcx: TyCtxt<'tcx>, | |
255 | param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, | |
256 | valtree: ty::ValTree<'tcx>, | |
257 | ) -> ConstValue<'tcx> { | |
258 | // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s | |
259 | // (those for constants with type bool, int, uint, float or char). | |
260 | // For all other types we create an `MPlace` and fill that by walking | |
261 | // the `ValTree` and using `place_projection` and `place_field` to | |
262 | // create inner `MPlace`s which are filled recursively. | |
263 | // FIXME Does this need an example? | |
264 | ||
265 | let (param_env, ty) = param_env_ty.into_parts(); | |
266 | let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); | |
267 | ||
268 | match ty.kind() { | |
269 | ty::FnDef(..) => { | |
270 | assert!(valtree.unwrap_branch().is_empty()); | |
064997fb | 271 | ConstValue::ZeroSized |
04454e1e FG |
272 | } |
273 | ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => match valtree { | |
274 | ty::ValTree::Leaf(scalar_int) => ConstValue::Scalar(Scalar::Int(scalar_int)), | |
275 | ty::ValTree::Branch(_) => bug!( | |
276 | "ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf" | |
277 | ), | |
278 | }, | |
279 | ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => { | |
280 | let mut place = match ty.kind() { | |
281 | ty::Ref(_, inner_ty, _) => { | |
282 | // Need to create a place for the pointee to fill for Refs | |
283 | create_pointee_place(&mut ecx, *inner_ty, valtree) | |
284 | } | |
285 | _ => create_mplace_from_layout(&mut ecx, ty), | |
286 | }; | |
287 | debug!(?place); | |
288 | ||
923072b8 | 289 | valtree_into_mplace(&mut ecx, &mut place, valtree); |
04454e1e FG |
290 | dump_place(&ecx, place.into()); |
291 | intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); | |
292 | ||
f2b60f7d | 293 | match ty.kind() { |
04454e1e FG |
294 | ty::Ref(_, _, _) => { |
295 | let ref_place = place.to_ref(&tcx); | |
296 | let imm = | |
297 | ImmTy::from_immediate(ref_place, tcx.layout_of(param_env_ty).unwrap()); | |
298 | ||
299 | op_to_const(&ecx, &imm.into()) | |
300 | } | |
301 | _ => op_to_const(&ecx, &place.into()), | |
f2b60f7d | 302 | } |
04454e1e FG |
303 | } |
304 | ty::Never | |
305 | | ty::Error(_) | |
306 | | ty::Foreign(..) | |
307 | | ty::Infer(ty::FreshIntTy(_)) | |
308 | | ty::Infer(ty::FreshFloatTy(_)) | |
9c376795 | 309 | | ty::Alias(..) |
04454e1e FG |
310 | | ty::Param(_) |
311 | | ty::Bound(..) | |
312 | | ty::Placeholder(..) | |
04454e1e FG |
313 | | ty::Infer(_) |
314 | | ty::Closure(..) | |
315 | | ty::Generator(..) | |
316 | | ty::GeneratorWitness(..) | |
9ffffee4 | 317 | | ty::GeneratorWitnessMIR(..) |
04454e1e FG |
318 | | ty::FnPtr(_) |
319 | | ty::RawPtr(_) | |
320 | | ty::Str | |
321 | | ty::Slice(_) | |
322 | | ty::Dynamic(..) => bug!("no ValTree should have been created for type {:?}", ty.kind()), | |
323 | } | |
324 | } | |
325 | ||
04454e1e | 326 | #[instrument(skip(ecx), level = "debug")] |
923072b8 | 327 | fn valtree_into_mplace<'tcx>( |
04454e1e FG |
328 | ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, |
329 | place: &mut MPlaceTy<'tcx>, | |
330 | valtree: ty::ValTree<'tcx>, | |
331 | ) { | |
332 | // This will match on valtree and write the value(s) corresponding to the ValTree | |
333 | // inside the place recursively. | |
334 | ||
335 | let tcx = ecx.tcx.tcx; | |
336 | let ty = place.layout.ty; | |
337 | ||
338 | match ty.kind() { | |
339 | ty::FnDef(_, _) => { | |
49aad941 | 340 | // Zero-sized type, nothing to do. |
04454e1e FG |
341 | } |
342 | ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { | |
343 | let scalar_int = valtree.unwrap_leaf(); | |
344 | debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place); | |
f2b60f7d | 345 | ecx.write_immediate(Immediate::Scalar(scalar_int.into()), &place.into()).unwrap(); |
04454e1e FG |
346 | } |
347 | ty::Ref(_, inner_ty, _) => { | |
348 | let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree); | |
349 | debug!(?pointee_place); | |
350 | ||
923072b8 | 351 | valtree_into_mplace(ecx, &mut pointee_place, valtree); |
04454e1e FG |
352 | dump_place(ecx, pointee_place.into()); |
353 | intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap(); | |
354 | ||
355 | let imm = match inner_ty.kind() { | |
356 | ty::Slice(_) | ty::Str => { | |
357 | let len = valtree.unwrap_branch().len(); | |
9ffffee4 | 358 | let len_scalar = Scalar::from_target_usize(len as u64, &tcx); |
04454e1e FG |
359 | |
360 | Immediate::ScalarPair( | |
f2b60f7d | 361 | Scalar::from_maybe_pointer((*pointee_place).ptr, &tcx), |
04454e1e FG |
362 | len_scalar, |
363 | ) | |
364 | } | |
365 | _ => pointee_place.to_ref(&tcx), | |
366 | }; | |
367 | debug!(?imm); | |
368 | ||
064997fb | 369 | ecx.write_immediate(imm, &place.into()).unwrap(); |
04454e1e FG |
370 | } |
371 | ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => { | |
372 | let branches = valtree.unwrap_branch(); | |
373 | ||
374 | // Need to downcast place for enums | |
375 | let (place_adjusted, branches, variant_idx) = match ty.kind() { | |
376 | ty::Adt(def, _) if def.is_enum() => { | |
377 | // First element of valtree corresponds to variant | |
378 | let scalar_int = branches[0].unwrap_leaf(); | |
379 | let variant_idx = VariantIdx::from_u32(scalar_int.try_to_u32().unwrap()); | |
380 | let variant = def.variant(variant_idx); | |
381 | debug!(?variant); | |
382 | ||
383 | ( | |
384 | place.project_downcast(ecx, variant_idx).unwrap(), | |
385 | &branches[1..], | |
386 | Some(variant_idx), | |
387 | ) | |
388 | } | |
389 | _ => (*place, branches, None), | |
390 | }; | |
391 | debug!(?place_adjusted, ?branches); | |
392 | ||
393 | // Create the places (by indexing into `place`) for the fields and fill | |
394 | // them recursively | |
395 | for (i, inner_valtree) in branches.iter().enumerate() { | |
396 | debug!(?i, ?inner_valtree); | |
397 | ||
398 | let mut place_inner = match ty.kind() { | |
399 | ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(), | |
2b03887a | 400 | _ if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) |
04454e1e FG |
401 | && i == branches.len() - 1 => |
402 | { | |
403 | // Note: For custom DSTs we need to manually process the last unsized field. | |
404 | // We created a `Pointer` for the `Allocation` of the complete sized version of | |
405 | // the Adt in `create_pointee_place` and now we fill that `Allocation` with the | |
406 | // values in the ValTree. For the unsized field we have to additionally add the meta | |
407 | // data. | |
408 | ||
409 | let (unsized_inner_ty, num_elems) = | |
410 | get_info_on_unsized_field(ty, valtree, tcx); | |
411 | debug!(?unsized_inner_ty); | |
412 | ||
413 | let inner_ty = match ty.kind() { | |
414 | ty::Adt(def, substs) => { | |
353b0b11 FG |
415 | let i = FieldIdx::from_usize(i); |
416 | def.variant(FIRST_VARIANT).fields[i].ty(tcx, substs) | |
04454e1e FG |
417 | } |
418 | ty::Tuple(inner_tys) => inner_tys[i], | |
419 | _ => bug!("unexpected unsized type {:?}", ty), | |
420 | }; | |
421 | ||
422 | let inner_layout = | |
423 | tcx.layout_of(ty::ParamEnv::empty().and(inner_ty)).unwrap(); | |
424 | debug!(?inner_layout); | |
425 | ||
426 | let offset = place_adjusted.layout.fields.offset(i); | |
427 | place | |
064997fb | 428 | .offset_with_meta( |
04454e1e | 429 | offset, |
9ffffee4 | 430 | MemPlaceMeta::Meta(Scalar::from_target_usize( |
923072b8 FG |
431 | num_elems as u64, |
432 | &tcx, | |
433 | )), | |
04454e1e FG |
434 | inner_layout, |
435 | &tcx, | |
436 | ) | |
437 | .unwrap() | |
438 | } | |
439 | _ => ecx.mplace_field(&place_adjusted, i).unwrap(), | |
440 | }; | |
441 | ||
442 | debug!(?place_inner); | |
923072b8 | 443 | valtree_into_mplace(ecx, &mut place_inner, *inner_valtree); |
04454e1e FG |
444 | dump_place(&ecx, place_inner.into()); |
445 | } | |
446 | ||
447 | debug!("dump of place_adjusted:"); | |
448 | dump_place(ecx, place_adjusted.into()); | |
449 | ||
450 | if let Some(variant_idx) = variant_idx { | |
451 | // don't forget filling the place with the discriminant of the enum | |
064997fb | 452 | ecx.write_discriminant(variant_idx, &place.into()).unwrap(); |
04454e1e FG |
453 | } |
454 | ||
455 | debug!("dump of place after writing discriminant:"); | |
064997fb | 456 | dump_place(ecx, place.into()); |
04454e1e FG |
457 | } |
458 | _ => bug!("shouldn't have created a ValTree for {:?}", ty), | |
459 | } | |
460 | } | |
461 | ||
462 | fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: PlaceTy<'tcx>) { | |
463 | trace!("{:?}", ecx.dump_place(*place)); | |
464 | } |