1use super::*;
3use annotate_snippets::Level;
4use rustc_const_uneval::{AllocTarget, Byte, ConstReader, PtrTarget, ReadError};
5use rustc_middle::ty;
6
7#[tracing::instrument(level = "trace", skip(s))]
8fn scalar_int_to_constant_literal<'tcx, S: UnderOwnerState<'tcx>>(
9 s: &S,
10 x: rustc_middle::ty::ScalarInt,
11 ty: rustc_middle::ty::Ty<'tcx>,
12) -> ConstantLiteral {
13 match ty.kind() {
14 ty::Char => ConstantLiteral::Char(
15 char::try_from(x).expect("scalar_int_to_constant_literal: expected a char"),
16 ),
17 ty::Bool => ConstantLiteral::Bool(
18 x.try_to_bool()
19 .expect("scalar_int_to_constant_literal: expected a bool"),
20 ),
21 ty::Int(kind) => {
22 let v = x.to_int(x.size());
23 ConstantLiteral::Int(ConstantInt::Int(v, kind.sinto(s)))
24 }
25 ty::Uint(kind) => {
26 let v = x.to_uint(x.size());
27 ConstantLiteral::Int(ConstantInt::Uint(v, kind.sinto(s)))
28 }
29 ty::Float(kind) => {
30 let v = x.to_bits_unchecked();
31 bits_and_type_to_float_constant_literal(v, kind.sinto(s))
32 }
33 _ => unreachable!("not a literal type: {ty:?}"),
34 }
35}
36
37fn bits_and_type_to_float_constant_literal(bits: u128, ty: FloatTy) -> ConstantLiteral {
39 use rustc_apfloat::{Float, ieee};
40 let string = match &ty {
41 FloatTy::F16 => ieee::Half::from_bits(bits).to_string(),
42 FloatTy::F32 => ieee::Single::from_bits(bits).to_string(),
43 FloatTy::F64 => ieee::Double::from_bits(bits).to_string(),
44 FloatTy::F128 => ieee::Quad::from_bits(bits).to_string(),
45 };
46 ConstantLiteral::Float(string, bits, ty)
47}
48
49impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ConstantExpr<'tcx>> for ty::Const<'tcx> {
50 #[tracing::instrument(level = "trace", skip(s))]
51 fn sinto(&self, s: &S) -> ConstantExpr<'tcx> {
52 let tcx = s.base().tcx;
53 let span = rustc_span::DUMMY_SP;
54 match self.kind() {
55 ty::ConstKind::Param(p) => {
56 let ty = p.find_const_ty_from_env(s.param_env());
57 let kind = ConstantExprKind::ConstRef { id: p.sinto(s) };
58 ConstantExpr::new(kind, ty.sinto(s))
59 }
60 ty::ConstKind::Alias(_, ucv) => {
61 let def = ucv
62 .kind
63 .opt_def_id()
64 .expect("AliasConstKind with no def id?");
65 let src = ConstSource::TyConst(ucv);
66 if s.base().options.inline_anon_consts
67 && matches!(tcx.def_kind(def), rustc_hir::def::DefKind::AnonConst)
71 && let Some(val) = read_const(s, tcx.def_span(def), src, ReadMode::Structured)
72 {
73 val
74 } else {
75 let item = translate_item_ref(s, def, ucv.args);
76 let kind = ConstantExprKind::NamedGlobal(item);
77 let ty = tcx.type_of(def).instantiate(tcx, ucv.args);
78 let ty = normalize(tcx, s.typing_env(), ty);
79 ConstantExpr::new(kind, ty.sinto(s))
80 }
81 }
82 ty::ConstKind::Value(val) => val.sinto(s),
83 _ => {
84 let msg = format!("unexpected constant kind: {self:?}");
85 s.raise(Level::ERROR, span, msg.clone());
86 let ty = Ty::new(s, TyKind::Error);
87 ConstantExpr::new(ConstantExprKind::Todo(msg), ty)
88 }
89 }
90 }
91}
92
93impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ConstantExpr<'tcx>> for ty::Value<'tcx> {
94 #[tracing::instrument(level = "trace", skip(s))]
95 fn sinto(&self, s: &S) -> ConstantExpr<'tcx> {
96 let span = rustc_span::DUMMY_SP;
97 read_const(s, span, ConstSource::ValTree(*self), ReadMode::Structured).unwrap_or_else(
98 || {
99 ConstantExpr::new(
100 ConstantExprKind::Todo("ConstValTree".into()),
101 self.ty.sinto(s),
102 )
103 },
104 )
105 }
106}
107
108fn const_reader<'tcx, S: UnderOwnerState<'tcx>>(s: &S) -> ConstReader<'tcx> {
110 ConstReader {
111 tcx: s.base().tcx,
112 typing_env: s.typing_env(),
113 config: s.base().options.consts,
114 alloc_types: s.with_global_cache(|cache| cache.alloc_types.clone()),
115 }
116}
117
118impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ItemRef<'tcx>> for GlobalRef {
120 fn sinto(&self, s: &S) -> ItemRef<'tcx> {
121 match *self {
122 GlobalRef::Static(did) => translate_item_ref(s, did, Default::default()),
123 GlobalRef::Alloc(alloc_id) => {
124 let def_id = DefId::make_anon_alloc(s, alloc_id);
125 ItemRef::dummy_without_generics(s, def_id)
126 }
127 }
128 }
129}
130
131impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, FnPtrTarget<'tcx>>
132 for rustc_const_uneval::FnTarget<'tcx>
133{
134 fn sinto(&self, s: &S) -> FnPtrTarget<'tcx> {
135 use rustc_const_uneval::FnTarget;
136 match *self {
137 FnTarget::Instance(instance) => {
138 FnPtrTarget::Fn(translate_item_ref(s, instance.def_id(), instance.args))
139 }
140 FnTarget::ClosureAsFn(def_id, args) => {
141 FnPtrTarget::ClosureAsFn(ClosureTy::sfrom(s, def_id, args))
142 }
143 }
144 }
145}
146
147fn unsizing_metadata<'tcx, S: UnderOwnerState<'tcx>>(
149 s: &S,
150 from: ty::Ty<'tcx>,
151 to: ty::Ty<'tcx>,
152) -> UnsizingMetadata<'tcx> {
153 let from = ty::Ty::new_imm_ptr(s.base().tcx, from);
154 let to = ty::Ty::new_imm_ptr(s.base().tcx, to);
155 let (path, metadata) = compute_unsizing_metadata(s, from, to);
156 assert!(path.is_empty(), "Non-empty unsize path in const ptr");
157 metadata
158}
159
160impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ConstantByteProvenance<'tcx>> for AllocTarget<'tcx> {
162 fn sinto(&self, s: &S) -> ConstantByteProvenance<'tcx> {
163 match *self {
164 AllocTarget::Fn(target) => {
165 let fn_ptr_ty = target.fn_ptr_ty(s.base().tcx, s.typing_env());
166 ConstantByteProvenance::Function(target.sinto(s), fn_ptr_ty.sinto(s))
167 }
168 AllocTarget::Global(global) => ConstantByteProvenance::Global(global.sinto(s)),
169 AllocTarget::TypeId(ty) => ConstantByteProvenance::TypeId(ty.sinto(s)),
170 AllocTarget::VTable(ty, preds) => {
171 let tcx = s.base().tcx;
172 let dyn_ty = ty::Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased);
173 match unsizing_metadata(s, ty, dyn_ty) {
174 UnsizingMetadata::DirectVTable(proof) => ConstantByteProvenance::VTable(proof),
175 metadata => unreachable!("unexpected vtable metadata {metadata:?}"),
176 }
177 }
178 }
179 }
180}
181
182impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ConstantExpr<'tcx>>
183 for rustc_const_uneval::Const<'tcx>
184{
185 fn sinto(&self, s: &S) -> ConstantExpr<'tcx> {
186 use rustc_const_uneval::ConstKind;
187 let ty = match self.ty.kind() {
189 ty::Pat(base_ty, _) => *base_ty,
190 _ => self.ty,
191 };
192 let unsizing_metadata = |unsize: &Option<rustc_const_uneval::Unsize<'tcx>>| {
193 unsize.map(|unsize| unsizing_metadata(s, unsize.from, unsize.to))
194 };
195 let kind = match &self.kind {
196 ConstKind::Scalar(scalar_int) => {
197 ConstantExprKind::Literal(scalar_int_to_constant_literal(s, *scalar_int, ty))
198 }
199 ConstKind::PtrNoProvenance(addr) => {
200 ConstantExprKind::Literal(ConstantLiteral::PtrNoProvenance(*addr))
201 }
202 ConstKind::Str(str) => ConstantExprKind::Literal(ConstantLiteral::Str(str.clone())),
203 ConstKind::ByteStr(bytes) => {
204 ConstantExprKind::Literal(ConstantLiteral::ByteStr(bytes.clone()))
205 }
206 ConstKind::Aggregate { variant, fields } => {
207 let fields = fields.iter().map(|field| field.sinto(s));
208 match ty.kind() {
209 ty::Adt(adt_def, _) => {
210 let variant = variant.unwrap();
211 ConstantExprKind::Adt {
212 kind: get_variant_kind(adt_def, variant, s),
213 fields: fields
214 .zip(&adt_def.variant(variant).fields)
215 .map(|(value, field)| ConstantFieldExpr {
216 field: field.did.sinto(s),
217 value,
218 })
219 .collect(),
220 }
221 }
222 ty::Closure(def_id, _) => {
223 let def_id: DefId = def_id.sinto(s);
224 ConstantExprKind::Adt {
225 kind: VariantKind::Struct,
226 fields: fields
227 .map(|value| ConstantFieldExpr {
228 field: def_id.clone(),
231 value,
232 })
233 .collect(),
234 }
235 }
236 ty::Tuple(_) => ConstantExprKind::Tuple {
237 fields: fields.collect(),
238 },
239 ty::Array(..) | ty::Slice(..) => ConstantExprKind::Array {
240 fields: fields.collect(),
241 },
242 _ => unreachable!("unexpected aggregate type {ty:?}"),
243 }
244 }
245 ConstKind::Repeat { value, .. } => ConstantExprKind::Repeat {
246 value: value.sinto(s),
247 },
248 ConstKind::FnDef { def, args } => {
249 ConstantExprKind::FnDef(translate_item_ref(s, *def, args))
250 }
251 ConstKind::FnPtr(target) => ConstantExprKind::FnPtr(target.sinto(s)),
252 ConstKind::Ptr { target, unsize } => {
253 let metadata = unsizing_metadata(unsize);
254 let (base, projections) = match target {
255 PtrTarget::Global {
256 target,
257 ty,
258 projections,
259 } => {
260 let prov = match *target {
261 AllocTarget::Global(global) => {
264 ConstantByteProvenance::Global(global.sinto(s))
265 }
266 _ => target.sinto(s),
267 };
268 let projections = projections
269 .iter()
270 .map(|(proj, ty)| {
271 use rustc_const_uneval::Projection;
272 let proj = match *proj {
273 Projection::Field(variant, field) => {
274 ConstantProjectionElem::Field(variant, field)
275 }
276 Projection::Index(i) => ConstantProjectionElem::Index(i),
277 Projection::Subslice { from, to } => {
278 ConstantProjectionElem::Subslice { from, to }
279 }
280 Projection::Offset {
281 count,
282 ty: elem_ty,
283 in_bounds,
284 } => ConstantProjectionElem::Offset {
285 count,
286 ty: elem_ty.sinto(s),
287 in_bounds,
288 pointee_is_sized: solve_sized(s, *ty),
289 },
290 };
291 (proj, ty.sinto(s))
292 })
293 .collect();
294 (ConstantPtrBase::Global(prov, ty.sinto(s)), projections)
295 }
296 PtrTarget::Inline(val) => (ConstantPtrBase::Value(val.sinto(s)), vec![]),
297 };
298 ConstantExprKind::Ptr {
299 base,
300 projections,
301 metadata,
302 }
303 }
304 ConstKind::PtrCast { ptr, unsize } => {
305 ConstantExprKind::PtrCast(ptr.sinto(s), unsizing_metadata(unsize))
306 }
307 ConstKind::Memory(bytes) => {
308 let mut last_prov: Option<(AllocTarget, ConstantByteProvenance)> = None;
310 let bytes = bytes.iter().map(|byte| match *byte {
311 Byte::Uninit => ConstantByte::Uninit,
312 Byte::Value(v) => ConstantByte::Value(v),
313 Byte::Ptr(target, offset, i) => {
314 if last_prov.as_ref().is_none_or(|(last, _)| *last != target) {
315 last_prov = Some((target, target.sinto(s)));
316 }
317 let prov = last_prov.as_ref().unwrap().1.clone();
318 ConstantByte::Provenance(prov, offset.bytes(), i)
319 }
320 });
321 ConstantExprKind::Memory(bytes.collect())
322 }
323 ConstKind::TypeId(ty) => ConstantExprKind::TypeId(ty.sinto(s)),
324 ConstKind::Unsupported(msg) => ConstantExprKind::Todo(msg.to_string()),
325 };
326 ConstantExpr::new(kind, self.ty.sinto(s))
327 }
328}
329
330pub fn read_const<'tcx, S: UnderOwnerState<'tcx>>(
332 s: &S,
333 span: rustc_span::Span,
334 src: ConstSource<'tcx>,
335 mode: ReadMode,
336) -> Option<ConstantExpr<'tcx>> {
337 match const_reader(s).read(span, src, mode) {
338 Ok(val) => Some(val.sinto(s)),
339 Err(ReadError::NotEvaluable) => None,
340 Err(err) => {
341 s.raise(
342 Level::WARNING,
343 span,
344 format!("Couldn't convert constant {src:?} back to an expression: {err:?}"),
345 );
346 None
347 }
348 }
349}