1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! See docs in build/expr/mod.rs

use crate::build::{lit_to_mir_constant, Builder};
use rustc_hir::def_id::DefId;
use rustc_middle::mir::interpret::{ConstValue, LitToConstError, LitToConstInput, Scalar};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt};

impl<'a, 'tcx> Builder<'a, 'tcx> {
    /// Compile `expr`, yielding a compile-time constant. Assumes that
    /// `expr` is a valid compile-time constant!
    pub(crate) fn as_constant(&mut self, expr: &Expr<'tcx>) -> Constant<'tcx> {
        let create_uneval_from_def_id =
            |tcx: TyCtxt<'tcx>, def_id: DefId, ty: Ty<'tcx>, substs: SubstsRef<'tcx>| {
                let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
                tcx.mk_const(ty::ConstS { val: ty::ConstKind::Unevaluated(uneval), ty })
            };

        let this = self;
        let tcx = this.tcx;
        let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
        match *kind {
            ExprKind::Scope { region_scope: _, lint_level: _, value } => {
                this.as_constant(&this.thir[value])
            }
            ExprKind::Literal { lit, neg } => {
                let literal =
                    match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) {
                        Ok(c) => c,
                        Err(LitToConstError::Reported) => ConstantKind::Ty(tcx.const_error(ty)),
                        Err(LitToConstError::TypeError) => {
                            bug!("encountered type error in `lit_to_mir_constant")
                        }
                    };

                Constant { span, user_ty: None, literal }
            }
            ExprKind::NonHirLiteral { lit, user_ty } => {
                let user_ty = user_ty.map(|user_ty| {
                    this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                        span,
                        user_ty,
                        inferred_ty: ty,
                    })
                });

                let literal = ConstantKind::Val(ConstValue::Scalar(Scalar::Int(lit)), ty);

                Constant { span, user_ty: user_ty, literal }
            }
            ExprKind::NamedConst { def_id, substs, user_ty } => {
                let user_ty = user_ty.map(|user_ty| {
                    this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                        span,
                        user_ty,
                        inferred_ty: ty,
                    })
                });
                let literal = ConstantKind::Ty(create_uneval_from_def_id(tcx, def_id, ty, substs));

                Constant { user_ty, span, literal }
            }
            ExprKind::ConstParam { param, def_id: _ } => {
                let const_param =
                    tcx.mk_const(ty::ConstS { val: ty::ConstKind::Param(param), ty: expr.ty });
                let literal = ConstantKind::Ty(const_param);

                Constant { user_ty: None, span, literal }
            }
            ExprKind::ConstBlock { did: def_id, substs } => {
                let literal = ConstantKind::Ty(create_uneval_from_def_id(tcx, def_id, ty, substs));

                Constant { user_ty: None, span, literal }
            }
            ExprKind::StaticRef { alloc_id, ty, .. } => {
                let const_val = ConstValue::Scalar(Scalar::from_pointer(alloc_id.into(), &tcx));
                let literal = ConstantKind::Val(const_val, ty);

                Constant { span, user_ty: None, literal }
            }
            _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
        }
    }
}