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;
17 use rustc_data_structures::sync::Lrc;
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_promoted<'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, false, 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 assert!(!layout.is_unsized());
113 let ptr = ecx.memory.allocate(
118 let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
119 let mutability = tcx.is_static(cid.instance.def_id());
120 let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable {
123 Mutability::Immutable
125 let cleanup = StackPopCleanup::MarkStatic(mutability);
126 let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
127 let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
128 trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
129 assert!(mir.arg_count == 0);
130 ecx.push_stack_frame(
134 Place::from_ptr(ptr, layout.align),
139 let ptr = ptr.into();
140 // always try to read the value and report errors
141 let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
142 // if it's a constant (so it needs no address, directly compute its value)
143 Some(val) if tcx.is_static(cid.instance.def_id()).is_none() => val,
144 // point at the allocation
145 _ => Value::ByRef(ptr, layout.align),
147 Ok((value, ptr, layout.ty))
152 pub struct CompileTimeEvaluator;
154 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
155 fn into(self) -> EvalError<'tcx> {
156 EvalErrorKind::MachineError(self.to_string()).into()
160 #[derive(Clone, Debug)]
161 enum ConstEvalError {
166 impl fmt::Display for ConstEvalError {
167 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168 use self::ConstEvalError::*;
170 NeedsRfc(ref msg) => {
173 "\"{}\" needs an rfc before being allowed inside constants",
177 NotConst(ref msg) => write!(f, "{}", msg),
182 impl Error for ConstEvalError {
183 fn description(&self) -> &str {
184 use self::ConstEvalError::*;
186 NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
187 NotConst(_) => "this feature is not compatible with constant evaluation",
191 fn cause(&self) -> Option<&dyn Error> {
196 impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
197 type MemoryData = ();
198 type MemoryKinds = !;
200 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
201 instance: ty::Instance<'tcx>,
202 destination: Option<(Place, mir::BasicBlock)>,
203 args: &[ValTy<'tcx>],
205 sig: ty::FnSig<'tcx>,
206 ) -> EvalResult<'tcx, bool> {
207 debug!("eval_fn_call: {:?}", instance);
208 if !ecx.tcx.is_const_fn(instance.def_id()) {
209 let def_id = instance.def_id();
210 let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
214 ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
217 let (dest, bb) = destination.expect("128 lowerings can't diverge");
218 let dest_ty = sig.output();
220 ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
222 ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
227 let mir = match ecx.load_mir(instance.def) {
230 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
232 ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
239 let (return_place, return_to_block) = match destination {
240 Some((place, block)) => (place, StackPopCleanup::Goto(block)),
241 None => (Place::undef(), StackPopCleanup::None),
244 ecx.push_stack_frame(
256 fn call_intrinsic<'a>(
257 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
258 instance: ty::Instance<'tcx>,
259 _args: &[ValTy<'tcx>],
261 dest_layout: layout::TyLayout<'tcx>,
262 target: mir::BasicBlock,
263 ) -> EvalResult<'tcx> {
264 let substs = instance.substs;
266 let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
267 match intrinsic_name {
269 let elem_ty = substs.type_at(0);
270 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
271 let align_val = PrimVal::from_u128(elem_align as u128);
272 ecx.write_primval(dest, align_val, dest_layout.ty)?;
276 let ty = substs.type_at(0);
277 let size = ecx.layout_of(ty)?.size.bytes() as u128;
278 ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
282 let ty = substs.type_at(0);
283 let type_id = ecx.tcx.type_id_hash(ty) as u128;
284 ecx.write_primval(dest, PrimVal::from_u128(type_id), dest_layout.ty)?;
287 name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
290 ecx.goto_block(target);
292 // Since we pushed no stack frame, the main loop will act
293 // as if the call just completed and it's returning to the
299 _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
305 ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
306 if left.is_bytes() && right.is_bytes() {
310 ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
315 fn mark_static_initialized<'a>(
316 _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
318 _mutability: Mutability,
319 ) -> EvalResult<'tcx, bool> {
324 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
326 ) -> EvalResult<'tcx, AllocId> {
330 .cache_static(cid.instance.def_id()))
334 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
337 ) -> EvalResult<'tcx> {
339 ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
343 fn global_item_with_linkage<'a>(
344 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
345 _instance: ty::Instance<'tcx>,
346 _mutability: Mutability,
347 ) -> EvalResult<'tcx> {
349 ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
354 pub fn const_val_field<'a, 'tcx>(
355 tcx: TyCtxt<'a, 'tcx, 'tcx>,
356 param_env: ty::ParamEnv<'tcx>,
357 instance: ty::Instance<'tcx>,
358 variant: Option<usize>,
362 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
363 trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty);
364 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
366 let (mut field, ty) = match value {
367 Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"),
368 Value::ByRef(ptr, align) => {
369 let place = Place::Ptr {
372 extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
374 let layout = ecx.layout_of(ty)?;
375 let (place, layout) = ecx.place_field(place, field, layout)?;
376 let (ptr, align) = place.to_ptr_align();
377 (Value::ByRef(ptr, align), layout.ty)
380 if let Value::ByRef(ptr, align) = field {
381 if let Some(val) = ecx.try_read_value(ptr, align, ty)? {
388 Ok((field, ty)) => Ok(tcx.mk_const(ty::Const {
389 val: ConstVal::Value(field),
393 let (trace, span) = ecx.generate_stacktrace(None);
394 let err = ErrKind::Miri(err, trace);
403 pub fn const_variant_index<'a, 'tcx>(
404 tcx: TyCtxt<'a, 'tcx, 'tcx>,
405 param_env: ty::ParamEnv<'tcx>,
406 instance: ty::Instance<'tcx>,
409 ) -> EvalResult<'tcx, usize> {
410 trace!("const_variant_index: {:?}, {:?}, {:?}", instance, value, ty);
411 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
412 let (ptr, align) = match value {
413 Value::ByValPair(..) | Value::ByVal(_) => {
414 let layout = ecx.layout_of(ty)?;
415 use super::MemoryKind;
416 let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?;
417 let ptr: Pointer = ptr.into();
418 ecx.write_value_to_ptr(value, ptr, layout.align, ty)?;
421 Value::ByRef(ptr, align) => (ptr, align),
423 let place = Place::from_primval_ptr(ptr, align);
424 ecx.read_discriminant_as_variant_index(place, ty)
427 pub fn const_eval_provider<'a, 'tcx>(
428 tcx: TyCtxt<'a, 'tcx, 'tcx>,
429 key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
430 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
431 trace!("const eval: {:?}", key);
433 let def_id = cid.instance.def.def_id();
435 if tcx.is_foreign_item(def_id) {
436 let id = tcx.interpret_interner.cache_static(def_id);
437 let ty = tcx.type_of(def_id);
438 let layout = tcx.layout_of(key.param_env.and(ty)).unwrap();
439 let ptr = MemoryPointer::new(id, 0);
440 return Ok(tcx.mk_const(ty::Const {
441 val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)),
446 if let Some(id) = tcx.hir.as_local_node_id(def_id) {
447 let tables = tcx.typeck_tables_of(def_id);
448 let span = tcx.def_span(def_id);
450 // Do match-check before building MIR
451 if tcx.check_match(def_id).is_err() {
452 return Err(ConstEvalErr {
453 kind: Lrc::new(CheckMatchError),
458 if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
459 tcx.mir_const_qualif(def_id);
462 // Do not continue into miri if typeck errors occurred; it will fail horribly
463 if tables.tainted_by_errors {
464 return Err(ConstEvalErr {
465 kind: Lrc::new(TypeckError),
471 let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
472 res.map(|(miri_value, _, miri_ty)| {
473 tcx.mk_const(ty::Const {
474 val: ConstVal::Value(miri_value),
477 }).map_err(|mut err| {
478 if tcx.is_static(def_id).is_some() {
479 ecx.report(&mut err, true, None);
481 let (trace, span) = ecx.generate_stacktrace(None);
482 let err = ErrKind::Miri(err, trace);