2 use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind};
3 use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError};
5 use rustc::ty::{self, TyCtxt, Ty, Instance};
6 use rustc::ty::layout::{self, LayoutOf};
7 use rustc::ty::subst::Subst;
9 use syntax::ast::Mutability;
10 use syntax::codemap::Span;
12 use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal, AllocId};
13 use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory};
16 use std::error::Error;
19 pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
20 tcx: TyCtxt<'a, 'tcx, 'tcx>,
21 instance: Instance<'tcx>,
22 mir: &'mir mir::Mir<'tcx>,
24 ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
25 debug!("mk_borrowck_eval_cx: {:?}", instance);
26 let param_env = tcx.param_env(instance.def_id());
27 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
28 // insert a stack frame so any queries have the correct substs
34 StackPopCleanup::None,
39 pub fn mk_eval_cx<'a, 'tcx>(
40 tcx: TyCtxt<'a, 'tcx, 'tcx>,
41 instance: Instance<'tcx>,
42 param_env: ty::ParamEnv<'tcx>,
43 ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
44 debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
45 let span = tcx.def_span(instance.def_id());
46 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
47 let mir = ecx.load_mir(instance.def)?;
48 // insert a stack frame so any queries have the correct substs
54 StackPopCleanup::None,
59 pub fn eval_body_with_mir<'a, 'mir, 'tcx>(
60 tcx: TyCtxt<'a, 'tcx, 'tcx>,
62 mir: &'mir mir::Mir<'tcx>,
63 param_env: ty::ParamEnv<'tcx>,
64 ) -> Option<(Value, Pointer, Ty<'tcx>)> {
65 let (res, ecx) = eval_body_and_ecx(tcx, cid, Some(mir), param_env);
69 ecx.report(&mut err, true, None);
75 pub fn eval_body<'a, 'tcx>(
76 tcx: TyCtxt<'a, 'tcx, 'tcx>,
78 param_env: ty::ParamEnv<'tcx>,
79 ) -> Option<(Value, Pointer, Ty<'tcx>)> {
80 let (res, ecx) = eval_body_and_ecx(tcx, cid, None, param_env);
84 ecx.report(&mut err, true, None);
90 fn eval_body_and_ecx<'a, 'mir, 'tcx>(
91 tcx: TyCtxt<'a, 'tcx, 'tcx>,
93 mir: Option<&'mir mir::Mir<'tcx>>,
94 param_env: ty::ParamEnv<'tcx>,
95 ) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
96 debug!("eval_body: {:?}, {:?}", cid, param_env);
97 // we start out with the best span we have
98 // and try improving it down the road when more information is available
99 let span = tcx.def_span(cid.instance.def_id());
100 let mut span = mir.map(|mir| mir.span).unwrap_or(span);
101 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
103 let mut mir = match mir {
105 None => ecx.load_mir(cid.instance.def)?,
107 if let Some(index) = cid.promoted {
108 mir = &mir.promoted[index];
111 let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
112 let alloc = tcx.interpret_interner.get_cached(cid.instance.def_id());
113 let is_static = tcx.is_static(cid.instance.def_id()).is_some();
114 let alloc = match alloc {
116 assert!(cid.promoted.is_none());
117 assert!(param_env.caller_bounds.is_empty());
121 assert!(!layout.is_unsized());
122 let ptr = ecx.memory.allocate(
128 tcx.interpret_interner.cache(cid.instance.def_id(), ptr.alloc_id);
130 let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
131 let mutability = tcx.is_static(cid.instance.def_id());
132 let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable {
135 Mutability::Immutable
137 let cleanup = StackPopCleanup::MarkStatic(mutability);
138 let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
139 let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
140 trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
141 assert!(mir.arg_count == 0);
142 ecx.push_stack_frame(
146 Place::from_ptr(ptr, layout.align),
154 let ptr = MemoryPointer::new(alloc, 0).into();
155 // always try to read the value and report errors
156 let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
157 // if it's a constant (so it needs no address, directly compute its value)
158 Some(val) if !is_static => val,
159 // point at the allocation
160 _ => Value::ByRef(ptr, layout.align),
162 Ok((value, ptr, layout.ty))
167 pub struct CompileTimeEvaluator;
169 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
170 fn into(self) -> EvalError<'tcx> {
171 EvalErrorKind::MachineError(self.to_string()).into()
175 #[derive(Clone, Debug)]
176 enum ConstEvalError {
181 impl fmt::Display for ConstEvalError {
182 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183 use self::ConstEvalError::*;
185 NeedsRfc(ref msg) => {
188 "\"{}\" needs an rfc before being allowed inside constants",
192 NotConst(ref msg) => write!(f, "{}", msg),
197 impl Error for ConstEvalError {
198 fn description(&self) -> &str {
199 use self::ConstEvalError::*;
201 NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
202 NotConst(_) => "this feature is not compatible with constant evaluation",
206 fn cause(&self) -> Option<&dyn Error> {
211 impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
212 type MemoryData = ();
213 type MemoryKinds = !;
215 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
216 instance: ty::Instance<'tcx>,
217 destination: Option<(Place, mir::BasicBlock)>,
218 args: &[ValTy<'tcx>],
220 sig: ty::FnSig<'tcx>,
221 ) -> EvalResult<'tcx, bool> {
222 debug!("eval_fn_call: {:?}", instance);
223 if !ecx.tcx.is_const_fn(instance.def_id()) {
224 let def_id = instance.def_id();
225 let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
229 ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
232 let (dest, bb) = destination.expect("128 lowerings can't diverge");
233 let dest_ty = sig.output();
235 ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
237 ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
242 let mir = match ecx.load_mir(instance.def) {
245 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
247 ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
254 let (return_place, return_to_block) = match destination {
255 Some((place, block)) => (place, StackPopCleanup::Goto(block)),
256 None => (Place::undef(), StackPopCleanup::None),
259 ecx.push_stack_frame(
271 fn call_intrinsic<'a>(
272 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
273 instance: ty::Instance<'tcx>,
274 _args: &[ValTy<'tcx>],
276 dest_layout: layout::TyLayout<'tcx>,
277 target: mir::BasicBlock,
278 ) -> EvalResult<'tcx> {
279 let substs = instance.substs;
281 let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
282 match intrinsic_name {
284 let elem_ty = substs.type_at(0);
285 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
286 let align_val = PrimVal::from_u128(elem_align as u128);
287 ecx.write_primval(dest, align_val, dest_layout.ty)?;
291 let ty = substs.type_at(0);
292 let size = ecx.layout_of(ty)?.size.bytes() as u128;
293 ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
297 let ty = substs.type_at(0);
298 let type_id = ecx.tcx.type_id_hash(ty) as u128;
299 ecx.write_primval(dest, PrimVal::from_u128(type_id), dest_layout.ty)?;
302 name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
305 ecx.goto_block(target);
307 // Since we pushed no stack frame, the main loop will act
308 // as if the call just completed and it's returning to the
314 _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
320 ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
321 if left.is_bytes() && right.is_bytes() {
325 ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
330 fn mark_static_initialized<'a>(
331 _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
333 _mutability: Mutability,
334 ) -> EvalResult<'tcx, bool> {
339 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
341 ) -> EvalResult<'tcx, AllocId> {
342 // ensure the static is computed
343 ecx.const_eval(cid)?;
347 .get_cached(cid.instance.def_id())
348 .expect("uncached static"))
352 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
355 ) -> EvalResult<'tcx> {
357 ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
361 fn global_item_with_linkage<'a>(
362 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
363 _instance: ty::Instance<'tcx>,
364 _mutability: Mutability,
365 ) -> EvalResult<'tcx> {
367 ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
372 pub fn const_val_field<'a, 'tcx>(
373 tcx: TyCtxt<'a, 'tcx, 'tcx>,
374 param_env: ty::ParamEnv<'tcx>,
375 instance: ty::Instance<'tcx>,
376 variant: Option<usize>,
380 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
381 trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty);
382 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
384 let (mut field, ty) = match value {
385 Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"),
386 Value::ByRef(ptr, align) => {
387 let place = Place::Ptr {
390 extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
392 let layout = ecx.layout_of(ty)?;
393 let (place, layout) = ecx.place_field(place, field, layout)?;
394 let (ptr, align) = place.to_ptr_align();
395 (Value::ByRef(ptr, align), layout.ty)
398 if let Value::ByRef(ptr, align) = field {
399 if let Some(val) = ecx.try_read_value(ptr, align, ty)? {
406 Ok((field, ty)) => Ok(tcx.mk_const(ty::Const {
407 val: ConstVal::Value(field),
411 let (trace, span) = ecx.generate_stacktrace(None);
412 let err = ErrKind::Miri(err, trace);
421 pub fn const_discr<'a, 'tcx>(
422 tcx: TyCtxt<'a, 'tcx, 'tcx>,
423 param_env: ty::ParamEnv<'tcx>,
424 instance: ty::Instance<'tcx>,
427 ) -> EvalResult<'tcx, u128> {
428 trace!("const_discr: {:?}, {:?}, {:?}", instance, value, ty);
429 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
430 let (ptr, align) = match value {
431 Value::ByValPair(..) | Value::ByVal(_) => {
432 let layout = ecx.layout_of(ty)?;
433 use super::MemoryKind;
434 let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?;
435 let ptr: Pointer = ptr.into();
436 ecx.write_value_to_ptr(value, ptr, layout.align, ty)?;
439 Value::ByRef(ptr, align) => (ptr, align),
441 let place = Place::from_primval_ptr(ptr, align);
442 ecx.read_discriminant_value(place, ty)
445 pub fn const_eval_provider<'a, 'tcx>(
446 tcx: TyCtxt<'a, 'tcx, 'tcx>,
447 key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
448 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
449 trace!("const eval: {:?}", key);
451 let def_id = cid.instance.def.def_id();
453 if tcx.is_foreign_item(def_id) {
454 let id = tcx.interpret_interner.get_cached(def_id);
456 // FIXME: due to caches this shouldn't happen, add some assertions
459 let id = tcx.interpret_interner.reserve();
460 tcx.interpret_interner.cache(def_id, id);
464 let ty = tcx.type_of(def_id);
465 let layout = tcx.layout_of(key.param_env.and(ty)).unwrap();
466 let ptr = MemoryPointer::new(id, 0);
467 return Ok(tcx.mk_const(ty::Const {
468 val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)),
473 if let Some(id) = tcx.hir.as_local_node_id(def_id) {
474 let tables = tcx.typeck_tables_of(def_id);
475 let span = tcx.def_span(def_id);
477 // Do match-check before building MIR
478 if tcx.check_match(def_id).is_err() {
479 return Err(ConstEvalErr {
480 kind: Rc::new(CheckMatchError),
485 if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
486 tcx.mir_const_qualif(def_id);
489 // Do not continue into miri if typeck errors occurred; it will fail horribly
490 if tables.tainted_by_errors {
491 return Err(ConstEvalErr {
492 kind: Rc::new(TypeckError),
498 let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
499 res.map(|(miri_value, _, miri_ty)| {
500 tcx.mk_const(ty::Const {
501 val: ConstVal::Value(miri_value),
504 }).map_err(|mut err| {
505 if tcx.is_static(def_id).is_some() {
506 ecx.report(&mut err, true, None);
508 let (trace, span) = ecx.generate_stacktrace(None);
509 let err = ErrKind::Miri(err, trace);