1use super::*;
3use rustc_const_eval::const_eval;
4use rustc_const_eval::interpret::{FnVal, InterpResult, interp_ok};
5use rustc_middle::mir::interpret;
6use rustc_middle::{mir, ty};
7
8impl ConstantLiteral {
9 fn byte_str(bytes: Vec<u8>) -> Self {
13 match String::from_utf8(bytes.clone()) {
14 Ok(s) => Self::Str(s),
15 Err(_) => Self::ByteStr(bytes),
16 }
17 }
18}
19
20#[tracing::instrument(level = "trace", skip(s))]
21pub(crate) fn scalar_int_to_constant_literal<'tcx, S: UnderOwnerState<'tcx>>(
22 s: &S,
23 x: rustc_middle::ty::ScalarInt,
24 ty: rustc_middle::ty::Ty<'tcx>,
25) -> ConstantLiteral {
26 match ty.kind() {
27 ty::Char => ConstantLiteral::Char(
28 char::try_from(x).s_expect(s, "scalar_int_to_constant_literal: expected a char"),
29 ),
30 ty::Bool => ConstantLiteral::Bool(
31 x.try_to_bool()
32 .s_expect(s, "scalar_int_to_constant_literal: expected a bool"),
33 ),
34 ty::Int(kind) => {
35 let v = x.to_int(x.size());
36 ConstantLiteral::Int(ConstantInt::Int(v, kind.sinto(s)))
37 }
38 ty::Uint(kind) => {
39 let v = x.to_uint(x.size());
40 ConstantLiteral::Int(ConstantInt::Uint(v, kind.sinto(s)))
41 }
42 ty::Float(kind) => {
43 let v = x.to_bits_unchecked();
44 bits_and_type_to_float_constant_literal(v, kind.sinto(s))
45 }
46 ty::Pat(inner, _) => scalar_int_to_constant_literal(s, x, *inner),
47 _ => {
48 let ty_sinto: Ty = ty.sinto(s);
49 supposely_unreachable_fatal!(
50 s,
51 "scalar_int_to_constant_literal_ExpectedLiteralType";
52 { ty, ty_sinto, x }
53 )
54 }
55 }
56}
57
58fn bits_and_type_to_float_constant_literal(bits: u128, ty: FloatTy) -> ConstantLiteral {
60 use rustc_apfloat::{Float, ieee};
61 let string = match &ty {
62 FloatTy::F16 => ieee::Half::from_bits(bits).to_string(),
63 FloatTy::F32 => ieee::Single::from_bits(bits).to_string(),
64 FloatTy::F64 => ieee::Double::from_bits(bits).to_string(),
65 FloatTy::F128 => ieee::Quad::from_bits(bits).to_string(),
66 };
67 ConstantLiteral::Float(string, ty)
68}
69
70impl ConstantExprKind {
71 pub fn decorate(self, ty: Ty, _span: Span) -> Decorated<Self> {
72 Decorated {
73 contents: Box::new(self),
74 ty,
75 }
76 }
77}
78
79pub(crate) fn is_anon_const(
86 did: rustc_span::def_id::DefId,
87 tcx: rustc_middle::ty::TyCtxt<'_>,
88) -> bool {
89 matches!(tcx.def_kind(did), rustc_hir::def::DefKind::AnonConst)
90}
91
92pub fn eval_ty_constant<'tcx, S: UnderOwnerState<'tcx>>(
94 s: &S,
95 uv: rustc_middle::ty::AliasConst<'tcx>,
96) -> Option<ty::Const<'tcx>> {
97 use ty::TypeVisitableExt;
98 let tcx = s.base().tcx;
99 let typing_env = s.typing_env();
100 if uv.has_non_region_param() {
101 return None;
102 }
103 let def = uv.kind.opt_def_id().unwrap();
104 let span = tcx.def_span(def);
105 let erased_uv = tcx.erase_and_anonymize_regions(uv);
106 let val = tcx
107 .const_eval_resolve_for_typeck(typing_env, erased_uv, span)
108 .ok()?
109 .ok()?;
110 let ty = tcx.type_of(def).instantiate(tcx, uv.args);
111 let ty = normalize(tcx, typing_env, ty);
112 Some(ty::Const::new_value(tcx, val, ty))
113}
114
115impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ConstantExpr> for ty::Const<'tcx> {
116 #[tracing::instrument(level = "trace", skip(s))]
117 fn sinto(&self, s: &S) -> ConstantExpr {
118 let tcx = s.base().tcx;
119 let span = rustc_span::DUMMY_SP;
120 match self.kind() {
121 ty::ConstKind::Param(p) => {
122 let ty = p.find_const_ty_from_env(s.param_env());
123 let kind = ConstantExprKind::ConstRef { id: p.sinto(s) };
124 kind.decorate(ty.sinto(s), span.sinto(s))
125 }
126 ty::ConstKind::Infer(..) => {
127 fatal!(s[span], "ty::ConstKind::Infer node? {:#?}", self)
128 }
129
130 ty::ConstKind::Alias(_, ucv) => {
131 let def = ucv
132 .kind
133 .opt_def_id()
134 .expect("AliasConstKind with no def id?");
135 if s.base().options.inline_anon_consts
136 && is_anon_const(def, tcx)
137 && let Some(val) = eval_ty_constant(s, ucv)
138 {
139 val.sinto(s)
140 } else {
141 use rustc_middle::query::QueryKey;
142 let span = tcx
143 .def_ident_span(def)
144 .unwrap_or_else(|| def.default_span(tcx));
145 let item = translate_item_ref(s, def, ucv.args);
146 let kind = ConstantExprKind::NamedGlobal(item);
147 let ty = tcx.type_of(def).instantiate(tcx, ucv.args);
148 let ty = normalize(tcx, s.typing_env(), ty);
149 kind.decorate(ty.sinto(s), span.sinto(s))
150 }
151 }
152
153 ty::ConstKind::Value(val) => valtree_to_constant_expr(s, val.valtree, val.ty, span),
154 ty::ConstKind::Error(_) => fatal!(s[span], "ty::ConstKind::Error"),
155 ty::ConstKind::Expr(e) => fatal!(s[span], "ty::ConstKind::Expr {:#?}", e),
156
157 ty::ConstKind::Bound(i, bound) => {
158 supposely_unreachable_fatal!(s[span], "ty::ConstKind::Bound"; {i, bound})
159 }
160 _ => fatal!(s[span], "unexpected case"),
161 }
162 }
163}
164
165impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ConstantExpr> for ty::Value<'tcx> {
166 #[tracing::instrument(level = "trace", skip(s))]
167 fn sinto(&self, s: &S) -> ConstantExpr {
168 valtree_to_constant_expr(s, self.valtree, self.ty, rustc_span::DUMMY_SP)
169 }
170}
171
172#[tracing::instrument(level = "trace", skip(s))]
173pub(crate) fn valtree_to_constant_expr<'tcx, S: UnderOwnerState<'tcx>>(
174 s: &S,
175 valtree: rustc_middle::ty::ValTree<'tcx>,
176 ty: rustc_middle::ty::Ty<'tcx>,
177 span: rustc_span::Span,
178) -> ConstantExpr {
179 let ty = normalize(s.base().tcx, s.typing_env(), ty::Unnormalized::new_wip(ty));
180
181 let kind = match (&*valtree, ty.kind()) {
182 (_, ty::Ref(_, inner_ty, _)) => {
183 ConstantExprKind::Borrow(valtree_to_constant_expr(s, valtree, *inner_ty, span))
184 }
185 (ty::ValTreeKind::Branch(valtrees), ty::Str) => {
186 let bytes = valtrees
187 .iter()
188 .map(|x| match x.try_to_leaf() {
189 Some(leaf) => leaf.to_u8(),
190 None => fatal!(
191 s[span],
192 "Expected a flat list of leaves while translating \
193 a str literal, got a arbitrary valtree."
194 ),
195 })
196 .collect();
197 ConstantExprKind::Literal(ConstantLiteral::byte_str(bytes))
198 }
199 (ty::ValTreeKind::Branch(fields), ty::Array(..) | ty::Slice(..) | ty::Tuple(..)) => {
200 let fields = fields.iter().map(|field| field.sinto(s)).collect();
201 match ty.kind() {
202 ty::Array(..) | ty::Slice(..) => ConstantExprKind::Array { fields },
203 ty::Tuple(_) => ConstantExprKind::Tuple { fields },
204 _ => unreachable!(),
205 }
206 }
207 (ty::ValTreeKind::Branch(_), ty::Adt(def, _)) => {
208 let contents: rustc_middle::ty::DestructuredAdtConst =
209 ty::Value { valtree, ty }.destructure_adt_const();
210
211 let fields = contents.fields.iter().copied();
212 let variant_idx = contents.variant;
213 let variant_def = &def.variant(variant_idx);
214
215 ConstantExprKind::Adt {
216 kind: get_variant_kind(def, variant_idx, s),
217 fields: fields
218 .into_iter()
219 .zip(&variant_def.fields)
220 .map(|(value, field)| ConstantFieldExpr {
221 field: field.did.sinto(s),
222 value: value.sinto(s),
223 })
224 .collect(),
225 }
226 }
227 (ty::ValTreeKind::Leaf(x), ty::RawPtr(_, _)) => {
228 let raw_address = x.to_bits_unchecked();
229 ConstantExprKind::Literal(ConstantLiteral::PtrNoProvenance(raw_address))
230 }
231 (ty::ValTreeKind::Leaf(x), _) => {
232 ConstantExprKind::Literal(scalar_int_to_constant_literal(s, *x, ty))
233 }
234 _ => supposely_unreachable_fatal!(
235 s[span], "valtree_to_expr";
236 {valtree, ty}
237 ),
238 };
239 kind.decorate(ty.sinto(s), span.sinto(s))
240}
241
242fn alloc_provenance<'tcx, S: UnderOwnerState<'tcx>>(
244 s: &S,
245 alloc_id: interpret::AllocId,
246) -> ConstantByteProvenance {
247 use interpret::GlobalAlloc::*;
248 let tcx = s.base().tcx;
249 match tcx.global_alloc(alloc_id) {
250 Function { instance } => ConstantByteProvenance::Function(translate_item_ref(
251 s,
252 instance.def_id(),
253 instance.args,
254 )),
255 Static(did)
258 if let rustc_hir::def::DefKind::Static { nested: false, .. } = tcx.def_kind(did) =>
259 {
260 ConstantByteProvenance::Global(translate_item_ref(s, did, Default::default()))
261 }
262 _ => ConstantByteProvenance::Unknown,
265 }
266}
267
268fn op_to_raw_bytes<'tcx, S: UnderOwnerState<'tcx>>(
271 s: &S,
272 ecx: &const_eval::CompileTimeInterpCx<'tcx>,
273 op: &rustc_const_eval::interpret::OpTy<'tcx>,
274) -> InterpResult<'tcx, Vec<ConstantByte>> {
275 op.as_mplace_or_imm().either(
276 |mplace| mplace_to_raw_bytes(s, ecx, &mplace),
277 |imm| interp_ok(imm_to_raw_bytes(s, &imm)),
278 )
279}
280
281fn imm_to_raw_bytes<'tcx, S: UnderOwnerState<'tcx>>(
283 s: &S,
284 imm: &rustc_const_eval::interpret::ImmTy<'tcx>,
285) -> Vec<ConstantByte> {
286 use rustc_abi::Size;
287 use rustc_const_eval::interpret::Immediate;
288 let mut bytes = vec![ConstantByte::Uninit; imm.layout.size.bytes_usize()];
289 let mut write = |offset: Size, scalar: interpret::Scalar| {
290 match scalar {
291 interpret::Scalar::Int(int) => {
292 let mut scalar = vec![0; int.size().bytes_usize()];
293 let endian = s.base().tcx.data_layout.endian;
294 interpret::write_target_uint(endian, &mut scalar, int.to_bits(int.size())).unwrap();
295 for (i, b) in scalar.into_iter().enumerate() {
296 bytes[offset.bytes_usize() + i] = ConstantByte::Value(b);
297 }
298 }
299 interpret::Scalar::Ptr(ptr, size) => {
300 let prov = alloc_provenance(s, ptr.provenance.alloc_id());
301 for i in 0..size {
302 bytes[offset.bytes_usize() + i as usize] =
303 ConstantByte::Provenance(prov.clone(), i);
304 }
305 }
306 };
307 };
308 match **imm {
309 Immediate::Uninit => {}
310 Immediate::Scalar(a) => write(Size::ZERO, a),
311 Immediate::ScalarPair(a, b) => {
312 let rustc_abi::BackendRepr::ScalarPair { b_offset, .. } = imm.layout.backend_repr
313 else {
314 unreachable!()
315 };
316 write(Size::ZERO, a);
317 write(b_offset, b);
318 }
319 }
320 bytes
321}
322
323fn mplace_to_raw_bytes<'tcx, S: UnderOwnerState<'tcx>>(
325 s: &S,
326 ecx: &const_eval::CompileTimeInterpCx<'tcx>,
327 mplace: &rustc_const_eval::interpret::MPlaceTy<'tcx>,
328) -> InterpResult<'tcx, Vec<ConstantByte>> {
329 use rustc_abi::Size;
330 let size = mplace.layout.size;
331 if size.bytes() == 0 {
332 return interp_ok(vec![]);
333 }
334 let (alloc_id, offset, _) = ecx.ptr_get_alloc_id(mplace.ptr(), size.bytes() as i64)?;
335 let alloc = ecx.get_alloc_raw(alloc_id)?;
336 let range = interpret::alloc_range(offset, size);
337 let raw_bytes = alloc.get_bytes_unchecked(range);
338 let mut bytes: Vec<ConstantByte> = (0..size.bytes())
339 .map(
340 |i| match alloc.init_mask().get(offset + Size::from_bytes(i)) {
341 true => ConstantByte::Value(raw_bytes[i as usize]),
342 false => ConstantByte::Uninit,
343 },
344 )
345 .collect();
346
347 for (prov_range, prov) in alloc.provenance().get_range(range, ecx) {
348 let prov = alloc_provenance(s, prov.alloc_id());
349 for i in 0..prov_range.size.bytes() {
350 let pos = prov_range.start + Size::from_bytes(i);
351 if range.start <= pos && pos < range.end() {
352 bytes[(pos - range.start).bytes_usize()] =
353 ConstantByte::Provenance(prov.clone(), i as u8);
354 }
355 }
356 }
357 interp_ok(bytes)
358}
359
360fn op_to_const<'tcx, S: UnderOwnerState<'tcx>>(
363 s: &S,
364 span: rustc_span::Span,
365 ecx: &const_eval::CompileTimeInterpCx<'tcx>,
366 op: rustc_const_eval::interpret::OpTy<'tcx>,
367) -> InterpResult<'tcx, ConstantExpr> {
368 use rustc_const_eval::interpret::Projectable;
369 let tcx = s.base().tcx;
372 let ty = op.layout.ty;
373 let read_fields = |of: rustc_const_eval::interpret::OpTy<'tcx>, field_count| {
375 (0..field_count).map(move |i| {
376 let field_op = ecx.project_field(&of, rustc_abi::FieldIdx::from_usize(i))?;
377 op_to_const(s, span, ecx, field_op)
378 })
379 };
380 let kind = match ty.kind() {
381 _ if let Some(place) = op.as_mplace_or_imm().left()
384 && let ptr = place.ptr()
385 && let Some((alloc_id, _, _)) = ecx.ptr_get_alloc_id(ptr, 0).discard_err()
386 && let interpret::GlobalAlloc::Static(did) = tcx.global_alloc(alloc_id)
387 && let rustc_hir::def::DefKind::Static { nested: false, .. } = tcx.def_kind(did) =>
388 {
389 let item = translate_item_ref(s, did, ty::GenericArgsRef::default());
390 ConstantExprKind::NamedGlobal(item)
391 }
392 ty::Char | ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Float(_) => {
393 let scalar = ecx.read_scalar(&op)?;
394 let scalar_int = scalar.try_to_scalar_int().unwrap();
395 let lit = scalar_int_to_constant_literal(s, scalar_int, ty);
396 ConstantExprKind::Literal(lit)
397 }
398 ty::Adt(adt_def, ..) if adt_def.is_union() => {
399 ConstantExprKind::Memory(op_to_raw_bytes(s, ecx, &op)?)
400 }
401 ty::Adt(adt_def, ..) => {
402 let variant = ecx.read_discriminant(&op)?;
403 let op = if adt_def.is_enum() {
404 ecx.project_downcast(&op, variant)?
405 } else {
406 op
407 };
408 let field_count = adt_def.variants()[variant].fields.len();
409 let fields = read_fields(op, field_count)
410 .zip(&adt_def.variant(variant).fields)
411 .map(|(value, field)| {
412 interp_ok(ConstantFieldExpr {
413 field: field.did.sinto(s),
414 value: value?,
415 })
416 })
417 .collect::<InterpResult<Vec<_>>>()?;
418 ConstantExprKind::Adt {
419 kind: get_variant_kind(adt_def, variant, s),
420 fields,
421 }
422 }
423 ty::Closure(def_id, args) => {
424 let def_id: DefId = def_id.sinto(s);
426 let field_count = args.as_closure().upvar_tys().len();
427 let fields = read_fields(op, field_count)
428 .map(|value| {
429 interp_ok(ConstantFieldExpr {
430 field: def_id.clone(),
433 value: value?,
434 })
435 })
436 .collect::<InterpResult<Vec<_>>>()?;
437 ConstantExprKind::Adt {
438 kind: VariantKind::Struct,
439 fields,
440 }
441 }
442 ty::Tuple(args) => {
443 let fields = read_fields(op, args.len()).collect::<InterpResult<Vec<_>>>()?;
444 ConstantExprKind::Tuple { fields }
445 }
446 ty::Array(..) | ty::Slice(..) => {
447 let len = op.len(ecx)?;
448 let fields = (0..len)
449 .map(|i| {
450 let op = ecx.project_index(&op, i)?;
451 op_to_const(s, span, ecx, op)
452 })
453 .collect::<InterpResult<Vec<_>>>()?;
454 ConstantExprKind::Array { fields }
455 }
456 ty::Str => {
457 let str = ecx.read_str(&op.assert_mem_place())?;
458 ConstantExprKind::Literal(ConstantLiteral::Str(str.to_owned()))
459 }
460 ty::FnDef(def_id, args) => {
461 let args = args.no_bound_vars().expect("bound variables in FnDef");
462 let item = translate_item_ref(s, *def_id, args);
463 ConstantExprKind::FnDef(item)
464 }
465 ty::FnPtr(..) => {
466 let fn_ptr = ecx.read_pointer(&op)?;
467 let FnVal::Instance(instance) = ecx.get_ptr_fn(fn_ptr)?;
468 let def_id = instance.def_id();
469 let generics = instance.args;
470 let fun = translate_item_ref(s, def_id, generics);
471 ConstantExprKind::FnPtr(fun)
472 }
473 ty::RawPtr(..) | ty::Ref(..) => {
474 let place_dangling = ecx.deref_pointer(&op).discard_err();
476 let place = place_dangling
477 .filter(|place| ecx.ptr_get_alloc_id(place.ptr(), 0).discard_err().is_some());
478 if let Some(op) = place {
479 let val = op_to_const(s, span, ecx, op.into())?;
481 match ty.kind() {
482 ty::Ref(..) => ConstantExprKind::Borrow(val),
483 ty::RawPtr(.., mutability) => ConstantExprKind::RawBorrow {
484 arg: val,
485 mutability: mutability.sinto(s),
486 },
487 _ => unreachable!(),
488 }
489 } else {
490 let scalar = ecx.read_scalar(&op)?;
492 let scalar_int = scalar.try_to_scalar_int().unwrap();
493 let v = scalar_int.to_uint(scalar_int.size());
494 let lit = ConstantLiteral::PtrNoProvenance(v);
495 ConstantExprKind::Literal(lit)
496 }
497 }
498 ty::Pat(..) => {
499 let op = ecx.project_field(&op, FieldIdx::from_u16(0))?;
500 *op_to_const(s, span, ecx, op)?.contents
501 }
502 ty::Dynamic(..)
503 | ty::Foreign(..)
504 | ty::UnsafeBinder(..)
505 | ty::CoroutineClosure(..)
506 | ty::Coroutine(..)
507 | ty::CoroutineWitness(..) => ConstantExprKind::Todo("Unhandled constant type".into()),
508 ty::Alias(..) | ty::Param(..) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => {
509 fatal!(s[span], "Encountered evaluated constant of non-monomorphic type"; {op})
510 }
511 ty::Never | ty::Error(..) => {
512 fatal!(s[span], "Encountered evaluated constant of invalid type"; {ty})
513 }
514 };
515 let val = kind.decorate(ty.sinto(s), span.sinto(s));
516 interp_ok(val)
517}
518
519pub fn const_value_to_constant_expr<'tcx, S: UnderOwnerState<'tcx>>(
520 s: &S,
521 ty: rustc_middle::ty::Ty<'tcx>,
522 val: mir::ConstValue,
523 span: rustc_span::Span,
524) -> InterpResult<'tcx, ConstantExpr> {
525 let tcx = s.base().tcx;
526 let typing_env = s.typing_env();
527 let (ecx, op) =
528 const_eval::mk_eval_cx_for_const_val(tcx.at(span), typing_env, val, ty).unwrap();
529 op_to_const(s, span, &ecx, op)
530}