]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/const_eval.rs
Move all intrinsics out of `interpret` and fail CTFE on intrinsic calls
[rust.git] / src / librustc_mir / interpret / const_eval.rs
1 use rustc::traits::Reveal;
2 use rustc::ty::{self, TyCtxt, Ty, Instance, layout};
3 use rustc::mir;
4
5 use syntax::ast::Mutability;
6 use syntax::codemap::Span;
7
8 use super::{
9     EvalResult, EvalError,
10     Global, GlobalId, Lvalue,
11     PrimVal,
12     EvalContext, StackPopCleanup,
13 };
14
15 use rustc_const_math::ConstInt;
16
17 use std::fmt;
18 use std::error::Error;
19
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());
29     }
30     
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(
35                 ecx.tcx,
36                 ty::ParamEnv::empty(Reveal::All),
37                 mir.span);
38         let mutability = if mutable {
39             Mutability::Mutable
40         } else {
41             Mutability::Immutable
42         };
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);
46         ecx.push_stack_frame(
47             instance,
48             mir.span,
49             mir,
50             Lvalue::Global(cid),
51             cleanup,
52         )?;
53
54         while ecx.step()? {}
55     }
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))
58 }
59
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};
69     Ok(match ty.sty {
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()),
83     })
84 }
85
86 struct CompileTimeFunctionEvaluator;
87
88 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
89     fn into(self) -> EvalError<'tcx> {
90         EvalError::MachineError(Box::new(self))
91     }
92 }
93
94 #[derive(Clone, Debug)]
95 enum ConstEvalError {
96     NeedsRfc(String),
97     NotConst(String),
98 }
99
100 impl fmt::Display for ConstEvalError {
101     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102         use self::ConstEvalError::*;
103         match *self {
104             NeedsRfc(ref msg) =>
105                 write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg),
106             NotConst(ref msg) =>
107                 write!(f, "Cannot evaluate within constants: \"{}\"", msg),
108         }
109     }
110 }
111
112 impl Error for ConstEvalError {
113     fn description(&self) -> &str {
114         use self::ConstEvalError::*;
115         match *self {
116             NeedsRfc(_) =>
117                 "this feature needs an rfc before being allowed inside constants",
118             NotConst(_) =>
119                 "this feature is not compatible with constant evaluation",
120         }
121     }
122
123     fn cause(&self) -> Option<&Error> {
124         None
125     }
126 }
127
128 impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator {
129     type Data = ();
130     type MemoryData = ();
131     fn eval_fn_call<'a>(
132         ecx: &mut EvalContext<'a, 'tcx, Self>,
133         instance: ty::Instance<'tcx>,
134         destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
135         _arg_operands: &[mir::Operand<'tcx>],
136         span: Span,
137         _sig: ty::FnSig<'tcx>,
138     ) -> EvalResult<'tcx, bool> {
139         if !ecx.tcx.is_const_fn(instance.def_id()) {
140             return Err(ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into());
141         }
142         let mir = match ecx.load_mir(instance.def) {
143             Ok(mir) => mir,
144             Err(EvalError::NoMirFor(path)) => {
145                 // some simple things like `malloc` might get accepted in the future
146                 return Err(ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)).into());
147             },
148             Err(other) => return Err(other),
149         };
150         let (return_lvalue, return_to_block) = match destination {
151             Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
152             None => (Lvalue::undef(), StackPopCleanup::None),
153         };
154
155         ecx.push_stack_frame(
156             instance,
157             span,
158             mir,
159             return_lvalue,
160             return_to_block,
161         )?;
162
163         Ok(false)
164     }
165
166     fn call_intrinsic<'a>(
167         _ecx: &mut EvalContext<'a, 'tcx, Self>,
168         _instance: ty::Instance<'tcx>,
169         _args: &[mir::Operand<'tcx>],
170         _dest: Lvalue<'tcx>,
171         _dest_ty: Ty<'tcx>,
172         _dest_layout: &'tcx layout::Layout,
173         _target: mir::BasicBlock,
174     ) -> EvalResult<'tcx> {
175         Err(ConstEvalError::NeedsRfc("calling intrinsics".to_string()).into())
176     }
177
178     fn ptr_op<'a>(
179         _ecx: &EvalContext<'a, 'tcx, Self>,
180         _bin_op: mir::BinOp,
181         _left: PrimVal,
182         _left_ty: Ty<'tcx>,
183         _right: PrimVal,
184         _right_ty: Ty<'tcx>,
185     ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
186         Err(ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into())
187     }
188 }