hax_frontend_exporter/constant_utils/
uneval.rs

1//! Reconstruct structured expressions from rustc's various constant representations.
2use super::*;
3use rustc_const_eval::interpret::{InterpResult, interp_ok};
4use rustc_middle::mir::interpret;
5use rustc_middle::{mir, ty};
6
7impl ConstantLiteral {
8    /// Rustc always represents string constants as `&[u8]`, but this
9    /// is not nice to consume. This associated function interpret
10    /// bytes as an unicode string, and as a byte string otherwise.
11    fn byte_str(bytes: Vec<u8>) -> Self {
12        match String::from_utf8(bytes.clone()) {
13            Ok(s) => Self::Str(s),
14            Err(_) => Self::ByteStr(bytes),
15        }
16    }
17}
18
19#[tracing::instrument(level = "trace", skip(s))]
20pub(crate) fn scalar_int_to_constant_literal<'tcx, S: UnderOwnerState<'tcx>>(
21    s: &S,
22    x: rustc_middle::ty::ScalarInt,
23    ty: rustc_middle::ty::Ty<'tcx>,
24) -> ConstantLiteral {
25    match ty.kind() {
26        ty::Char => ConstantLiteral::Char(
27            char::try_from(x).s_expect(s, "scalar_int_to_constant_literal: expected a char"),
28        ),
29        ty::Bool => ConstantLiteral::Bool(
30            x.try_to_bool()
31                .s_expect(s, "scalar_int_to_constant_literal: expected a bool"),
32        ),
33        ty::Int(kind) => {
34            let v = x.to_int(x.size());
35            ConstantLiteral::Int(ConstantInt::Int(v, kind.sinto(s)))
36        }
37        ty::Uint(kind) => {
38            let v = x.to_uint(x.size());
39            ConstantLiteral::Int(ConstantInt::Uint(v, kind.sinto(s)))
40        }
41        ty::Float(kind) => {
42            let v = x.to_bits_unchecked();
43            bits_and_type_to_float_constant_literal(v, kind.sinto(s))
44        }
45        _ => {
46            let ty_sinto: Ty = ty.sinto(s);
47            supposely_unreachable_fatal!(
48                s,
49                "scalar_int_to_constant_literal_ExpectedLiteralType";
50                { ty, ty_sinto, x }
51            )
52        }
53    }
54}
55
56/// Converts a bit-representation of a float of type `ty` to a constant literal
57fn bits_and_type_to_float_constant_literal(bits: u128, ty: FloatTy) -> ConstantLiteral {
58    use rustc_apfloat::{Float, ieee};
59    let string = match &ty {
60        FloatTy::F16 => ieee::Half::from_bits(bits).to_string(),
61        FloatTy::F32 => ieee::Single::from_bits(bits).to_string(),
62        FloatTy::F64 => ieee::Double::from_bits(bits).to_string(),
63        FloatTy::F128 => ieee::Quad::from_bits(bits).to_string(),
64    };
65    ConstantLiteral::Float(string, ty)
66}
67
68impl ConstantExprKind {
69    pub fn decorate(self, ty: Ty, span: Span) -> Decorated<Self> {
70        Decorated {
71            contents: Box::new(self),
72            hir_id: None,
73            attributes: vec![],
74            ty,
75            span,
76        }
77    }
78}
79
80/// Whether a `DefId` is a `AnonConst`. An anonymous constant is
81/// generated by Rustc, hoisting every constat bits from items as
82/// separate top-level items. This AnonConst mechanism is internal to
83/// Rustc; we don't want to reflect that, instead we prefer inlining
84/// those. `is_anon_const` is used to detect such AnonConst so that we
85/// can evaluate and inline them.
86pub(crate) fn is_anon_const(
87    did: rustc_span::def_id::DefId,
88    tcx: rustc_middle::ty::TyCtxt<'_>,
89) -> bool {
90    matches!(
91        tcx.def_kind(did),
92        rustc_hir::def::DefKind::AnonConst | rustc_hir::def::DefKind::InlineConst
93    )
94}
95
96/// Attempts to translate a `ty::UnevaluatedConst` into a constant expression. This handles cases
97/// of references to top-level or associated constants. Returns `None` if the input was not a named
98/// constant.
99pub fn translate_constant_reference<'tcx>(
100    s: &impl UnderOwnerState<'tcx>,
101    span: rustc_span::Span,
102    ucv: rustc_middle::ty::UnevaluatedConst<'tcx>,
103) -> Option<ConstantExpr> {
104    let tcx = s.base().tcx;
105    if s.base().options.inline_anon_consts && is_anon_const(ucv.def, tcx) {
106        return None;
107    }
108    let typing_env = s.typing_env();
109    let ty = s.base().tcx.type_of(ucv.def).instantiate(tcx, ucv.args);
110    let ty = tcx
111        .try_normalize_erasing_regions(typing_env, ty)
112        .unwrap_or(ty);
113    let kind = if let Some(assoc) = s.base().tcx.opt_associated_item(ucv.def)
114        && matches!(
115            assoc.container,
116            ty::AssocContainer::Trait | ty::AssocContainer::TraitImpl(..)
117        ) {
118        // This is an associated constant in a trait.
119        let name = assoc.name().to_string();
120        let impl_expr = self_clause_for_item(s, ucv.def, ucv.args).unwrap();
121        ConstantExprKind::TraitConst { impl_expr, name }
122    } else {
123        let item = translate_item_ref(s, ucv.def, ucv.args);
124        ConstantExprKind::GlobalName(item)
125    };
126    let cv = kind.decorate(ty.sinto(s), span.sinto(s));
127    Some(cv)
128}
129
130/// Evaluate a `ty::Const`.
131pub fn eval_ty_constant<'tcx, S: UnderOwnerState<'tcx>>(
132    s: &S,
133    uv: rustc_middle::ty::UnevaluatedConst<'tcx>,
134) -> Option<ty::Const<'tcx>> {
135    use ty::TypeVisitableExt;
136    let tcx = s.base().tcx;
137    let typing_env = s.typing_env();
138    if uv.has_non_region_param() {
139        return None;
140    }
141    let span = tcx.def_span(uv.def);
142    let erased_uv = tcx.erase_and_anonymize_regions(uv);
143    let val = tcx
144        .const_eval_resolve_for_typeck(typing_env, erased_uv, span)
145        .ok()?
146        .ok()?;
147    let ty = tcx.type_of(uv.def).instantiate(tcx, uv.args);
148    Some(ty::Const::new_value(tcx, val, ty))
149}
150
151/// Evaluate a `mir::Const`.
152pub fn eval_mir_constant<'tcx, S: UnderOwnerState<'tcx>>(
153    s: &S,
154    c: mir::Const<'tcx>,
155) -> Option<mir::Const<'tcx>> {
156    let evaluated = c
157        .eval(s.base().tcx, s.typing_env(), rustc_span::DUMMY_SP)
158        .ok()?;
159    let evaluated = mir::Const::Val(evaluated, c.ty());
160    (evaluated != c).then_some(evaluated)
161}
162
163impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ConstantExpr> for ty::Const<'tcx> {
164    #[tracing::instrument(level = "trace", skip(s))]
165    fn sinto(&self, s: &S) -> ConstantExpr {
166        use rustc_middle::query::Key;
167        let span = self.default_span(s.base().tcx);
168        match self.kind() {
169            ty::ConstKind::Param(p) => {
170                let ty = p.find_const_ty_from_env(s.param_env());
171                let kind = ConstantExprKind::ConstRef { id: p.sinto(s) };
172                kind.decorate(ty.sinto(s), span.sinto(s))
173            }
174            ty::ConstKind::Infer(..) => {
175                fatal!(s[span], "ty::ConstKind::Infer node? {:#?}", self)
176            }
177
178            ty::ConstKind::Unevaluated(ucv) => match translate_constant_reference(s, span, ucv) {
179                Some(val) => val,
180                None => match eval_ty_constant(s, ucv) {
181                    Some(val) => val.sinto(s),
182                    // TODO: This is triggered when compiling using `generic_const_exprs`
183                    None => supposely_unreachable_fatal!(s, "TranslateUneval"; {self, ucv}),
184                },
185            },
186
187            ty::ConstKind::Value(val) => valtree_to_constant_expr(s, val.valtree, val.ty, span),
188            ty::ConstKind::Error(_) => fatal!(s[span], "ty::ConstKind::Error"),
189            ty::ConstKind::Expr(e) => fatal!(s[span], "ty::ConstKind::Expr {:#?}", e),
190
191            ty::ConstKind::Bound(i, bound) => {
192                supposely_unreachable_fatal!(s[span], "ty::ConstKind::Bound"; {i, bound})
193            }
194            _ => fatal!(s[span], "unexpected case"),
195        }
196    }
197}
198
199impl<'tcx, S: UnderOwnerState<'tcx>> SInto<S, ConstantExpr> for ty::Value<'tcx> {
200    #[tracing::instrument(level = "trace", skip(s))]
201    fn sinto(&self, s: &S) -> ConstantExpr {
202        valtree_to_constant_expr(s, self.valtree, self.ty, rustc_span::DUMMY_SP)
203    }
204}
205
206#[tracing::instrument(level = "trace", skip(s))]
207pub(crate) fn valtree_to_constant_expr<'tcx, S: UnderOwnerState<'tcx>>(
208    s: &S,
209    valtree: rustc_middle::ty::ValTree<'tcx>,
210    ty: rustc_middle::ty::Ty<'tcx>,
211    span: rustc_span::Span,
212) -> ConstantExpr {
213    let kind = match (&*valtree, ty.kind()) {
214        (_, ty::Ref(_, inner_ty, _)) => {
215            ConstantExprKind::Borrow(valtree_to_constant_expr(s, valtree, *inner_ty, span))
216        }
217        (ty::ValTreeKind::Branch(valtrees), ty::Str) => {
218            let bytes = valtrees
219                .iter()
220                .map(|x| match &***x {
221                    ty::ValTreeKind::Leaf(leaf) => leaf.to_u8(),
222                    _ => fatal!(
223                        s[span],
224                        "Expected a flat list of leaves while translating \
225                            a str literal, got a arbitrary valtree."
226                    ),
227                })
228                .collect();
229            ConstantExprKind::Literal(ConstantLiteral::byte_str(bytes))
230        }
231        (ty::ValTreeKind::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => {
232            let contents: rustc_middle::ty::DestructuredConst = s
233                .base()
234                .tcx
235                .destructure_const(ty::Const::new_value(s.base().tcx, valtree, ty));
236            let fields = contents.fields.iter().copied();
237            match ty.kind() {
238                ty::Array(_, _) => ConstantExprKind::Array {
239                    fields: fields.map(|field| field.sinto(s)).collect(),
240                },
241                ty::Tuple(_) => ConstantExprKind::Tuple {
242                    fields: fields.map(|field| field.sinto(s)).collect(),
243                },
244                ty::Adt(def, _) => {
245                    let variant_idx = contents
246                        .variant
247                        .s_expect(s, "destructed const of adt without variant idx");
248                    let variant_def = &def.variant(variant_idx);
249
250                    ConstantExprKind::Adt {
251                        info: get_variant_information(def, variant_idx, s),
252                        fields: fields
253                            .into_iter()
254                            .zip(&variant_def.fields)
255                            .map(|(value, field)| ConstantFieldExpr {
256                                field: field.did.sinto(s),
257                                value: value.sinto(s),
258                            })
259                            .collect(),
260                    }
261                }
262                _ => unreachable!(),
263            }
264        }
265        (ty::ValTreeKind::Leaf(x), ty::RawPtr(_, _)) => {
266            use crate::rustc_type_ir::inherent::Ty;
267            let raw_address = x.to_bits_unchecked();
268            let uint_ty = UintTy::Usize;
269            let usize_ty = rustc_middle::ty::Ty::new_usize(s.base().tcx).sinto(s);
270            let lit = ConstantLiteral::Int(ConstantInt::Uint(raw_address, uint_ty));
271            ConstantExprKind::Cast {
272                source: ConstantExprKind::Literal(lit).decorate(usize_ty, span.sinto(s)),
273            }
274        }
275        (ty::ValTreeKind::Leaf(x), _) => {
276            ConstantExprKind::Literal(scalar_int_to_constant_literal(s, *x, ty))
277        }
278        _ => supposely_unreachable_fatal!(
279            s[span], "valtree_to_expr";
280            {valtree, ty}
281        ),
282    };
283    kind.decorate(ty.sinto(s), span.sinto(s))
284}
285
286/// Use the const-eval interpreter to convert an evaluated operand back to a structured
287/// constant expression.
288fn op_to_const<'tcx, S: UnderOwnerState<'tcx>>(
289    s: &S,
290    span: rustc_span::Span,
291    ecx: &rustc_const_eval::const_eval::CompileTimeInterpCx<'tcx>,
292    op: rustc_const_eval::interpret::OpTy<'tcx>,
293) -> InterpResult<'tcx, ConstantExpr> {
294    use crate::rustc_const_eval::interpret::Projectable;
295    // Code inspired from `try_destructure_mir_constant_for_user_output` and
296    // `const_eval::eval_queries::op_to_const`.
297    let tcx = s.base().tcx;
298    let ty = op.layout.ty;
299    // Helper for struct-likes.
300    let read_fields = |of: rustc_const_eval::interpret::OpTy<'tcx>, field_count| {
301        (0..field_count).map(move |i| {
302            let field_op = ecx.project_field(&of, rustc_abi::FieldIdx::from_usize(i))?;
303            op_to_const(s, span, &ecx, field_op)
304        })
305    };
306    let kind = match ty.kind() {
307        // Detect statics
308        _ if let Some(place) = op.as_mplace_or_imm().left()
309            && let ptr = place.ptr()
310            && let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr, 0)?
311            && let interpret::GlobalAlloc::Static(did) = tcx.global_alloc(alloc_id) =>
312        {
313            let item = translate_item_ref(s, did, ty::GenericArgsRef::default());
314            ConstantExprKind::GlobalName(item)
315        }
316        ty::Char | ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Float(_) => {
317            let scalar = ecx.read_scalar(&op)?;
318            let scalar_int = scalar.try_to_scalar_int().unwrap();
319            let lit = scalar_int_to_constant_literal(s, scalar_int, ty);
320            ConstantExprKind::Literal(lit)
321        }
322        ty::Adt(adt_def, ..) if adt_def.is_union() => {
323            ConstantExprKind::Todo("Cannot translate constant of union type".into())
324        }
325        ty::Adt(adt_def, ..) => {
326            let variant = ecx.read_discriminant(&op)?;
327            let down = ecx.project_downcast(&op, variant)?;
328            let field_count = adt_def.variants()[variant].fields.len();
329            let fields = read_fields(down, field_count)
330                .zip(&adt_def.variant(variant).fields)
331                .map(|(value, field)| {
332                    interp_ok(ConstantFieldExpr {
333                        field: field.did.sinto(s),
334                        value: value?,
335                    })
336                })
337                .collect::<InterpResult<Vec<_>>>()?;
338            let variants_info = get_variant_information(adt_def, variant, s);
339            ConstantExprKind::Adt {
340                info: variants_info,
341                fields,
342            }
343        }
344        ty::Closure(def_id, args) => {
345            // A closure is essentially an adt with funky generics and some builtin impls.
346            let def_id: DefId = def_id.sinto(s);
347            let field_count = args.as_closure().upvar_tys().len();
348            let fields = read_fields(op, field_count)
349                .map(|value| {
350                    interp_ok(ConstantFieldExpr {
351                        // HACK: Closure fields don't have their own def_id, but Charon doesn't use
352                        // field DefIds so we put a dummy one.
353                        field: def_id.clone(),
354                        value: value?,
355                    })
356                })
357                .collect::<InterpResult<Vec<_>>>()?;
358            let variants_info = VariantInformations {
359                type_namespace: def_id.parent.clone().unwrap(),
360                typ: def_id.clone(),
361                variant: def_id,
362                kind: VariantKind::Struct { named: false },
363            };
364            ConstantExprKind::Adt {
365                info: variants_info,
366                fields,
367            }
368        }
369        ty::Tuple(args) => {
370            let fields = read_fields(op, args.len()).collect::<InterpResult<Vec<_>>>()?;
371            ConstantExprKind::Tuple { fields }
372        }
373        ty::Array(..) | ty::Slice(..) => {
374            let len = op.len(ecx)?;
375            let fields = (0..len)
376                .map(|i| {
377                    let op = ecx.project_index(&op, i)?;
378                    op_to_const(s, span, ecx, op)
379                })
380                .collect::<InterpResult<Vec<_>>>()?;
381            ConstantExprKind::Array { fields }
382        }
383        ty::Str => {
384            let str = ecx.read_str(&op.assert_mem_place())?;
385            ConstantExprKind::Literal(ConstantLiteral::Str(str.to_owned()))
386        }
387        ty::FnDef(def_id, args) => {
388            let item = translate_item_ref(s, *def_id, args);
389            ConstantExprKind::FnPtr(item)
390        }
391        ty::RawPtr(..) | ty::Ref(..) => {
392            if let Some(op) = ecx.deref_pointer(&op).discard_err() {
393                // Valid pointer case
394                let val = op_to_const(s, span, ecx, op.into())?;
395                match ty.kind() {
396                    ty::Ref(..) => ConstantExprKind::Borrow(val),
397                    ty::RawPtr(.., mutability) => ConstantExprKind::RawBorrow {
398                        arg: val,
399                        mutability: mutability.sinto(s),
400                    },
401                    _ => unreachable!(),
402                }
403            } else {
404                // Invalid pointer; try reading it as a raw address
405                let scalar = ecx.read_scalar(&op)?;
406                let scalar_int = scalar.try_to_scalar_int().unwrap();
407                let v = scalar_int.to_uint(scalar_int.size());
408                let lit = ConstantLiteral::PtrNoProvenance(v);
409                ConstantExprKind::Literal(lit)
410            }
411        }
412        ty::FnPtr(..)
413        | ty::Dynamic(..)
414        | ty::Foreign(..)
415        | ty::Pat(..)
416        | ty::UnsafeBinder(..)
417        | ty::CoroutineClosure(..)
418        | ty::Coroutine(..)
419        | ty::CoroutineWitness(..) => ConstantExprKind::Todo("Unhandled constant type".into()),
420        ty::Alias(..) | ty::Param(..) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => {
421            fatal!(s[span], "Encountered evaluated constant of non-monomorphic type"; {op})
422        }
423        ty::Never | ty::Error(..) => {
424            fatal!(s[span], "Encountered evaluated constant of invalid type"; {ty})
425        }
426    };
427    let val = kind.decorate(ty.sinto(s), span.sinto(s));
428    interp_ok(val)
429}
430
431pub fn const_value_to_constant_expr<'tcx, S: UnderOwnerState<'tcx>>(
432    s: &S,
433    ty: rustc_middle::ty::Ty<'tcx>,
434    val: mir::ConstValue,
435    span: rustc_span::Span,
436) -> InterpResult<'tcx, ConstantExpr> {
437    let tcx = s.base().tcx;
438    let typing_env = s.typing_env();
439    let (ecx, op) =
440        rustc_const_eval::const_eval::mk_eval_cx_for_const_val(tcx.at(span), typing_env, val, ty)
441            .unwrap();
442    op_to_const(s, span, &ecx, op)
443}