]>
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; | |
11 | use rustc_target::abi::{Align, VariantIdx}; | |
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(_)) | |
145 | | ty::Projection(..) | |
146 | | ty::Param(_) | |
147 | | ty::Bound(..) | |
148 | | ty::Placeholder(..) | |
149 | // FIXME(oli-obk): we could look behind opaque types | |
150 | | ty::Opaque(..) | |
151 | | ty::Infer(_) | |
152 | // FIXME(oli-obk): we can probably encode closures just like structs | |
153 | | ty::Closure(..) | |
154 | | ty::Generator(..) | |
923072b8 | 155 | | ty::GeneratorWitness(..) => Err(ValTreeCreationError::NonSupportedType), |
04454e1e FG |
156 | } |
157 | } | |
158 | ||
159 | #[instrument(skip(ecx), level = "debug")] | |
160 | fn create_mplace_from_layout<'tcx>( | |
161 | ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, | |
162 | ty: Ty<'tcx>, | |
163 | ) -> MPlaceTy<'tcx> { | |
164 | let tcx = ecx.tcx; | |
165 | let param_env = ecx.param_env; | |
166 | let layout = tcx.layout_of(param_env.and(ty)).unwrap(); | |
167 | debug!(?layout); | |
168 | ||
169 | ecx.allocate(layout, MemoryKind::Stack).unwrap() | |
170 | } | |
171 | ||
172 | // Walks custom DSTs and gets the type of the unsized field and the number of elements | |
173 | // in the unsized field. | |
174 | fn get_info_on_unsized_field<'tcx>( | |
175 | ty: Ty<'tcx>, | |
176 | valtree: ty::ValTree<'tcx>, | |
177 | tcx: TyCtxt<'tcx>, | |
178 | ) -> (Ty<'tcx>, usize) { | |
179 | let mut last_valtree = valtree; | |
180 | let tail = tcx.struct_tail_with_normalize( | |
181 | ty, | |
182 | |ty| ty, | |
183 | || { | |
184 | let branches = last_valtree.unwrap_branch(); | |
185 | last_valtree = branches[branches.len() - 1]; | |
186 | debug!(?branches, ?last_valtree); | |
187 | }, | |
188 | ); | |
189 | let unsized_inner_ty = match tail.kind() { | |
190 | ty::Slice(t) => *t, | |
191 | ty::Str => tail, | |
192 | _ => bug!("expected Slice or Str"), | |
193 | }; | |
194 | ||
195 | // Have to adjust type for ty::Str | |
196 | let unsized_inner_ty = match unsized_inner_ty.kind() { | |
197 | ty::Str => tcx.mk_ty(ty::Uint(ty::UintTy::U8)), | |
198 | _ => unsized_inner_ty, | |
199 | }; | |
200 | ||
201 | // Get the number of elements in the unsized field | |
202 | let num_elems = last_valtree.unwrap_branch().len(); | |
203 | ||
204 | (unsized_inner_ty, num_elems) | |
205 | } | |
206 | ||
f2b60f7d | 207 | #[instrument(skip(ecx), level = "debug", ret)] |
04454e1e FG |
208 | fn create_pointee_place<'tcx>( |
209 | ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, | |
210 | ty: Ty<'tcx>, | |
211 | valtree: ty::ValTree<'tcx>, | |
212 | ) -> MPlaceTy<'tcx> { | |
213 | let tcx = ecx.tcx.tcx; | |
214 | ||
215 | if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty()) { | |
216 | // We need to create `Allocation`s for custom DSTs | |
217 | ||
218 | let (unsized_inner_ty, num_elems) = get_info_on_unsized_field(ty, valtree, tcx); | |
219 | let unsized_inner_ty = match unsized_inner_ty.kind() { | |
220 | ty::Str => tcx.mk_ty(ty::Uint(ty::UintTy::U8)), | |
221 | _ => unsized_inner_ty, | |
222 | }; | |
223 | let unsized_inner_ty_size = | |
224 | tcx.layout_of(ty::ParamEnv::empty().and(unsized_inner_ty)).unwrap().layout.size(); | |
225 | debug!(?unsized_inner_ty, ?unsized_inner_ty_size, ?num_elems); | |
226 | ||
227 | // for custom DSTs only the last field/element is unsized, but we need to also allocate | |
228 | // space for the other fields/elements | |
229 | let layout = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap(); | |
230 | let size_of_sized_part = layout.layout.size(); | |
231 | ||
232 | // Get the size of the memory behind the DST | |
233 | let dst_size = unsized_inner_ty_size.checked_mul(num_elems as u64, &tcx).unwrap(); | |
234 | ||
923072b8 FG |
235 | let size = size_of_sized_part.checked_add(dst_size, &tcx).unwrap(); |
236 | let align = Align::from_bytes(size.bytes().next_power_of_two()).unwrap(); | |
237 | let ptr = ecx.allocate_ptr(size, align, MemoryKind::Stack).unwrap(); | |
04454e1e FG |
238 | debug!(?ptr); |
239 | ||
f2b60f7d | 240 | MPlaceTy::from_aligned_ptr_with_meta( |
923072b8 FG |
241 | ptr.into(), |
242 | layout, | |
243 | MemPlaceMeta::Meta(Scalar::from_machine_usize(num_elems as u64, &tcx)), | |
f2b60f7d | 244 | ) |
04454e1e FG |
245 | } else { |
246 | create_mplace_from_layout(ecx, ty) | |
247 | } | |
248 | } | |
249 | ||
250 | /// Converts a `ValTree` to a `ConstValue`, which is needed after mir | |
251 | /// construction has finished. | |
923072b8 | 252 | // FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function |
f2b60f7d | 253 | #[instrument(skip(tcx), level = "debug", ret)] |
04454e1e FG |
254 | pub fn valtree_to_const_value<'tcx>( |
255 | tcx: TyCtxt<'tcx>, | |
256 | param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, | |
257 | valtree: ty::ValTree<'tcx>, | |
258 | ) -> ConstValue<'tcx> { | |
259 | // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s | |
260 | // (those for constants with type bool, int, uint, float or char). | |
261 | // For all other types we create an `MPlace` and fill that by walking | |
262 | // the `ValTree` and using `place_projection` and `place_field` to | |
263 | // create inner `MPlace`s which are filled recursively. | |
264 | // FIXME Does this need an example? | |
265 | ||
266 | let (param_env, ty) = param_env_ty.into_parts(); | |
267 | let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); | |
268 | ||
269 | match ty.kind() { | |
270 | ty::FnDef(..) => { | |
271 | assert!(valtree.unwrap_branch().is_empty()); | |
064997fb | 272 | ConstValue::ZeroSized |
04454e1e FG |
273 | } |
274 | ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => match valtree { | |
275 | ty::ValTree::Leaf(scalar_int) => ConstValue::Scalar(Scalar::Int(scalar_int)), | |
276 | ty::ValTree::Branch(_) => bug!( | |
277 | "ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf" | |
278 | ), | |
279 | }, | |
280 | ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => { | |
281 | let mut place = match ty.kind() { | |
282 | ty::Ref(_, inner_ty, _) => { | |
283 | // Need to create a place for the pointee to fill for Refs | |
284 | create_pointee_place(&mut ecx, *inner_ty, valtree) | |
285 | } | |
286 | _ => create_mplace_from_layout(&mut ecx, ty), | |
287 | }; | |
288 | debug!(?place); | |
289 | ||
923072b8 | 290 | valtree_into_mplace(&mut ecx, &mut place, valtree); |
04454e1e FG |
291 | dump_place(&ecx, place.into()); |
292 | intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); | |
293 | ||
f2b60f7d | 294 | match ty.kind() { |
04454e1e FG |
295 | ty::Ref(_, _, _) => { |
296 | let ref_place = place.to_ref(&tcx); | |
297 | let imm = | |
298 | ImmTy::from_immediate(ref_place, tcx.layout_of(param_env_ty).unwrap()); | |
299 | ||
300 | op_to_const(&ecx, &imm.into()) | |
301 | } | |
302 | _ => op_to_const(&ecx, &place.into()), | |
f2b60f7d | 303 | } |
04454e1e FG |
304 | } |
305 | ty::Never | |
306 | | ty::Error(_) | |
307 | | ty::Foreign(..) | |
308 | | ty::Infer(ty::FreshIntTy(_)) | |
309 | | ty::Infer(ty::FreshFloatTy(_)) | |
310 | | ty::Projection(..) | |
311 | | ty::Param(_) | |
312 | | ty::Bound(..) | |
313 | | ty::Placeholder(..) | |
314 | | ty::Opaque(..) | |
315 | | ty::Infer(_) | |
316 | | ty::Closure(..) | |
317 | | ty::Generator(..) | |
318 | | ty::GeneratorWitness(..) | |
319 | | ty::FnPtr(_) | |
320 | | ty::RawPtr(_) | |
321 | | ty::Str | |
322 | | ty::Slice(_) | |
323 | | ty::Dynamic(..) => bug!("no ValTree should have been created for type {:?}", ty.kind()), | |
324 | } | |
325 | } | |
326 | ||
04454e1e | 327 | #[instrument(skip(ecx), level = "debug")] |
923072b8 | 328 | fn valtree_into_mplace<'tcx>( |
04454e1e FG |
329 | ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, |
330 | place: &mut MPlaceTy<'tcx>, | |
331 | valtree: ty::ValTree<'tcx>, | |
332 | ) { | |
333 | // This will match on valtree and write the value(s) corresponding to the ValTree | |
334 | // inside the place recursively. | |
335 | ||
336 | let tcx = ecx.tcx.tcx; | |
337 | let ty = place.layout.ty; | |
338 | ||
339 | match ty.kind() { | |
340 | ty::FnDef(_, _) => { | |
064997fb | 341 | ecx.write_immediate(Immediate::Uninit, &place.into()).unwrap(); |
04454e1e FG |
342 | } |
343 | ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { | |
344 | let scalar_int = valtree.unwrap_leaf(); | |
345 | debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place); | |
f2b60f7d | 346 | ecx.write_immediate(Immediate::Scalar(scalar_int.into()), &place.into()).unwrap(); |
04454e1e FG |
347 | } |
348 | ty::Ref(_, inner_ty, _) => { | |
349 | let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree); | |
350 | debug!(?pointee_place); | |
351 | ||
923072b8 | 352 | valtree_into_mplace(ecx, &mut pointee_place, valtree); |
04454e1e FG |
353 | dump_place(ecx, pointee_place.into()); |
354 | intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap(); | |
355 | ||
356 | let imm = match inner_ty.kind() { | |
357 | ty::Slice(_) | ty::Str => { | |
358 | let len = valtree.unwrap_branch().len(); | |
f2b60f7d | 359 | let len_scalar = Scalar::from_machine_usize(len as u64, &tcx); |
04454e1e FG |
360 | |
361 | Immediate::ScalarPair( | |
f2b60f7d | 362 | Scalar::from_maybe_pointer((*pointee_place).ptr, &tcx), |
04454e1e FG |
363 | len_scalar, |
364 | ) | |
365 | } | |
366 | _ => pointee_place.to_ref(&tcx), | |
367 | }; | |
368 | debug!(?imm); | |
369 | ||
064997fb | 370 | ecx.write_immediate(imm, &place.into()).unwrap(); |
04454e1e FG |
371 | } |
372 | ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => { | |
373 | let branches = valtree.unwrap_branch(); | |
374 | ||
375 | // Need to downcast place for enums | |
376 | let (place_adjusted, branches, variant_idx) = match ty.kind() { | |
377 | ty::Adt(def, _) if def.is_enum() => { | |
378 | // First element of valtree corresponds to variant | |
379 | let scalar_int = branches[0].unwrap_leaf(); | |
380 | let variant_idx = VariantIdx::from_u32(scalar_int.try_to_u32().unwrap()); | |
381 | let variant = def.variant(variant_idx); | |
382 | debug!(?variant); | |
383 | ||
384 | ( | |
385 | place.project_downcast(ecx, variant_idx).unwrap(), | |
386 | &branches[1..], | |
387 | Some(variant_idx), | |
388 | ) | |
389 | } | |
390 | _ => (*place, branches, None), | |
391 | }; | |
392 | debug!(?place_adjusted, ?branches); | |
393 | ||
394 | // Create the places (by indexing into `place`) for the fields and fill | |
395 | // them recursively | |
396 | for (i, inner_valtree) in branches.iter().enumerate() { | |
397 | debug!(?i, ?inner_valtree); | |
398 | ||
399 | let mut place_inner = match ty.kind() { | |
400 | ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(), | |
401 | _ if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty()) | |
402 | && i == branches.len() - 1 => | |
403 | { | |
404 | // Note: For custom DSTs we need to manually process the last unsized field. | |
405 | // We created a `Pointer` for the `Allocation` of the complete sized version of | |
406 | // the Adt in `create_pointee_place` and now we fill that `Allocation` with the | |
407 | // values in the ValTree. For the unsized field we have to additionally add the meta | |
408 | // data. | |
409 | ||
410 | let (unsized_inner_ty, num_elems) = | |
411 | get_info_on_unsized_field(ty, valtree, tcx); | |
412 | debug!(?unsized_inner_ty); | |
413 | ||
414 | let inner_ty = match ty.kind() { | |
415 | ty::Adt(def, substs) => { | |
416 | def.variant(VariantIdx::from_u32(0)).fields[i].ty(tcx, substs) | |
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, |
923072b8 FG |
430 | MemPlaceMeta::Meta(Scalar::from_machine_usize( |
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 | } |