1 use rustc::traits::Reveal;
2 use rustc::ty::{self, TyCtxt, Ty, Instance, layout};
5 use syntax::ast::Mutability;
6 use syntax::codemap::Span;
10 Global, GlobalId, Lvalue,
12 EvalContext, StackPopCleanup,
15 use rustc_const_math::ConstInt;
18 use std::error::Error;
20 pub fn eval_body_as_primval<'a, 'tcx>(
21 tcx: TyCtxt<'a, 'tcx, 'tcx>,
22 instance: Instance<'tcx>,
23 ) -> EvalResult<'tcx, (PrimVal, Ty<'tcx>)> {
24 let limits = super::ResourceLimits::default();
25 let mut ecx = EvalContext::<CompileTimeFunctionEvaluator>::new(tcx, limits, (), ());
26 let cid = GlobalId { instance, promoted: None };
27 if ecx.tcx.has_attr(instance.def_id(), "linkage") {
28 return Err(ConstEvalError::NotConst("extern global".to_string()).into());
31 let mir = ecx.load_mir(instance.def)?;
32 if !ecx.globals.contains_key(&cid) {
33 ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
34 let mutable = !mir.return_ty.is_freeze(
36 ty::ParamEnv::empty(Reveal::All),
38 let mutability = if mutable {
43 let cleanup = StackPopCleanup::MarkStatic(mutability);
44 let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id()));
45 trace!("pushing stack frame for global: {}", name);
56 let value = ecx.globals.get(&cid).expect("global not cached").value;
57 Ok((ecx.value_to_primval(value, mir.return_ty)?, mir.return_ty))
60 pub fn eval_body_as_integer<'a, 'tcx>(
61 tcx: TyCtxt<'a, 'tcx, 'tcx>,
62 instance: Instance<'tcx>,
63 ) -> EvalResult<'tcx, ConstInt> {
64 let (prim, ty) = eval_body_as_primval(tcx, instance)?;
65 let prim = prim.to_bytes()?;
66 use syntax::ast::{IntTy, UintTy};
67 use rustc::ty::TypeVariants::*;
68 use rustc_const_math::{ConstIsize, ConstUsize};
70 TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8),
71 TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16),
72 TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32),
73 TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64),
74 TyInt(IntTy::I128) => ConstInt::I128(prim as i128),
75 TyInt(IntTy::Is) => ConstInt::Isize(ConstIsize::new(prim as i128 as i64, tcx.sess.target.int_type).expect("miri should already have errored")),
76 TyUint(UintTy::U8) => ConstInt::U8(prim as u8),
77 TyUint(UintTy::U16) => ConstInt::U16(prim as u16),
78 TyUint(UintTy::U32) => ConstInt::U32(prim as u32),
79 TyUint(UintTy::U64) => ConstInt::U64(prim as u64),
80 TyUint(UintTy::U128) => ConstInt::U128(prim),
81 TyUint(UintTy::Us) => ConstInt::Usize(ConstUsize::new(prim as u64, tcx.sess.target.uint_type).expect("miri should already have errored")),
82 _ => return Err(ConstEvalError::NeedsRfc("evaluating anything other than isize/usize during typeck".to_string()).into()),
86 struct CompileTimeFunctionEvaluator;
88 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
89 fn into(self) -> EvalError<'tcx> {
90 EvalError::MachineError(Box::new(self))
94 #[derive(Clone, Debug)]
100 impl fmt::Display for ConstEvalError {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 use self::ConstEvalError::*;
105 write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg),
107 write!(f, "Cannot evaluate within constants: \"{}\"", msg),
112 impl Error for ConstEvalError {
113 fn description(&self) -> &str {
114 use self::ConstEvalError::*;
117 "this feature needs an rfc before being allowed inside constants",
119 "this feature is not compatible with constant evaluation",
123 fn cause(&self) -> Option<&Error> {
128 impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator {
130 type MemoryData = ();
131 type MemoryKinds = !;
133 ecx: &mut EvalContext<'a, 'tcx, Self>,
134 instance: ty::Instance<'tcx>,
135 destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
136 _arg_operands: &[mir::Operand<'tcx>],
138 _sig: ty::FnSig<'tcx>,
139 ) -> EvalResult<'tcx, bool> {
140 if !ecx.tcx.is_const_fn(instance.def_id()) {
141 return Err(ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into());
143 let mir = match ecx.load_mir(instance.def) {
145 Err(EvalError::NoMirFor(path)) => {
146 // some simple things like `malloc` might get accepted in the future
147 return Err(ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)).into());
149 Err(other) => return Err(other),
151 let (return_lvalue, return_to_block) = match destination {
152 Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
153 None => (Lvalue::undef(), StackPopCleanup::None),
156 ecx.push_stack_frame(
167 fn call_intrinsic<'a>(
168 _ecx: &mut EvalContext<'a, 'tcx, Self>,
169 _instance: ty::Instance<'tcx>,
170 _args: &[mir::Operand<'tcx>],
173 _dest_layout: &'tcx layout::Layout,
174 _target: mir::BasicBlock,
175 ) -> EvalResult<'tcx> {
176 Err(ConstEvalError::NeedsRfc("calling intrinsics".to_string()).into())
180 _ecx: &EvalContext<'a, 'tcx, Self>,
186 ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
187 if left.is_bytes() && right.is_bytes() {
190 Err(ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into())
194 fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
199 _ecx: &mut EvalContext<'a, 'tcx, Self>,
201 ) -> EvalResult<'tcx, PrimVal> {
202 Err(ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into())