5 use rustc::mir::interpret::{ConstEvalErr, ScalarMaybeUndef};
7 use rustc::ty::{self, TyCtxt, Ty, Instance};
8 use rustc::ty::layout::{self, LayoutOf, Primitive, TyLayout};
9 use rustc::ty::subst::Subst;
10 use rustc_data_structures::indexed_vec::IndexVec;
12 use syntax::ast::Mutability;
13 use syntax::codemap::Span;
14 use syntax::codemap::DUMMY_SP;
16 use rustc::mir::interpret::{
17 EvalResult, EvalError, EvalErrorKind, GlobalId,
18 Value, Scalar, AllocId, Allocation, ConstValue,
20 use super::{Place, EvalContext, StackPopCleanup, ValTy, Memory, MemoryKind};
22 pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
23 tcx: TyCtxt<'a, 'tcx, 'tcx>,
24 instance: Instance<'tcx>,
25 mir: &'mir mir::Mir<'tcx>,
27 ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
28 debug!("mk_borrowck_eval_cx: {:?}", instance);
29 let param_env = tcx.param_env(instance.def_id());
30 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
31 // insert a stack frame so any queries have the correct substs
32 ecx.stack.push(super::eval_context::Frame {
33 block: mir::START_BLOCK,
34 locals: IndexVec::new(),
38 return_place: Place::undef(),
39 return_to_block: StackPopCleanup::None,
45 pub fn mk_eval_cx<'a, 'tcx>(
46 tcx: TyCtxt<'a, 'tcx, 'tcx>,
47 instance: Instance<'tcx>,
48 param_env: ty::ParamEnv<'tcx>,
49 ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
50 debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
51 let span = tcx.def_span(instance.def_id());
52 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
53 let mir = ecx.load_mir(instance.def)?;
54 // insert a stack frame so any queries have the correct substs
60 StackPopCleanup::None,
65 pub fn eval_promoted<'a, 'mir, 'tcx>(
66 ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
68 mir: &'mir mir::Mir<'tcx>,
69 param_env: ty::ParamEnv<'tcx>,
70 ) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
71 ecx.with_fresh_body(|ecx| {
72 eval_body_using_ecx(ecx, cid, Some(mir), param_env)
76 pub fn value_to_const_value<'tcx>(
77 ecx: &EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>,
80 ) -> &'tcx ty::Const<'tcx> {
81 let layout = ecx.layout_of(ty).unwrap();
82 match (val, &layout.abi) {
83 (Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { size: 0, ..})), _) if layout.is_zst() => {},
84 (Value::ByRef(..), _) |
85 (Value::Scalar(_), &layout::Abi::Scalar(_)) |
86 (Value::ScalarPair(..), &layout::Abi::ScalarPair(..)) => {},
87 _ => bug!("bad value/layout combo: {:#?}, {:#?}", val, layout),
91 Value::Scalar(val) => Ok(ConstValue::Scalar(val.read()?)),
92 Value::ScalarPair(a, b) => Ok(ConstValue::ScalarPair(a.read()?, b.read()?)),
93 Value::ByRef(ptr, align) => {
94 let ptr = ptr.to_ptr().unwrap();
95 let alloc = ecx.memory.get(ptr.alloc_id)?;
96 assert!(alloc.align.abi() >= align.abi());
97 assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= layout.size.bytes());
98 let mut alloc = alloc.clone();
100 let alloc = ecx.tcx.intern_const_alloc(alloc);
101 Ok(ConstValue::ByRef(alloc, ptr.offset))
106 Ok(val) => ty::Const::from_const_value(ecx.tcx.tcx, val, ty),
108 let (frames, span) = ecx.generate_stacktrace(None);
109 let err = ConstEvalErr {
116 "failed to convert Value to ConstValue, this is a bug",
118 span_bug!(span, "miri error occured when converting Value to ConstValue")
123 fn eval_body_and_ecx<'a, 'mir, 'tcx>(
124 tcx: TyCtxt<'a, 'tcx, 'tcx>,
126 mir: Option<&'mir mir::Mir<'tcx>>,
127 param_env: ty::ParamEnv<'tcx>,
128 ) -> (EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
129 debug!("eval_body_and_ecx: {:?}, {:?}", cid, param_env);
130 // we start out with the best span we have
131 // and try improving it down the road when more information is available
132 let span = tcx.def_span(cid.instance.def_id());
133 let span = mir.map(|mir| mir.span).unwrap_or(span);
134 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
135 let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
139 fn eval_body_using_ecx<'a, 'mir, 'tcx>(
140 ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
142 mir: Option<&'mir mir::Mir<'tcx>>,
143 param_env: ty::ParamEnv<'tcx>,
144 ) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
145 debug!("eval_body: {:?}, {:?}", cid, param_env);
146 let tcx = ecx.tcx.tcx;
147 let mut mir = match mir {
149 None => ecx.load_mir(cid.instance.def)?,
151 if let Some(index) = cid.promoted {
152 mir = &mir.promoted[index];
154 let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
155 assert!(!layout.is_unsized());
156 let ptr = ecx.memory.allocate(
161 let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
162 let is_static = tcx.is_static(cid.instance.def_id());
163 let mutability = if is_static == Some(hir::Mutability::MutMutable) || internally_mutable {
166 Mutability::Immutable
168 let cleanup = StackPopCleanup::MarkStatic(mutability);
169 let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
170 let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
171 trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
172 assert!(mir.arg_count == 0);
173 ecx.push_stack_frame(
177 Place::from_ptr(ptr, layout.align),
182 let ptr = ptr.into();
183 // always try to read the value and report errors
184 let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
185 Some(val) if is_static.is_none() && cid.promoted.is_none() => val,
186 // point at the allocation
187 _ => Value::ByRef(ptr, layout.align),
189 Ok((value, ptr, layout))
192 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
193 pub struct CompileTimeEvaluator;
195 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
196 fn into(self) -> EvalError<'tcx> {
197 EvalErrorKind::MachineError(self.to_string()).into()
201 #[derive(Clone, Debug)]
202 enum ConstEvalError {
207 impl fmt::Display for ConstEvalError {
208 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209 use self::ConstEvalError::*;
211 NeedsRfc(ref msg) => {
214 "\"{}\" needs an rfc before being allowed inside constants",
218 NotConst(ref msg) => write!(f, "{}", msg),
223 impl Error for ConstEvalError {
224 fn description(&self) -> &str {
225 use self::ConstEvalError::*;
227 NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
228 NotConst(_) => "this feature is not compatible with constant evaluation",
232 fn cause(&self) -> Option<&dyn Error> {
237 impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
238 type MemoryData = ();
239 type MemoryKinds = !;
241 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
242 instance: ty::Instance<'tcx>,
243 destination: Option<(Place, mir::BasicBlock)>,
244 args: &[ValTy<'tcx>],
246 sig: ty::FnSig<'tcx>,
247 ) -> EvalResult<'tcx, bool> {
248 debug!("eval_fn_call: {:?}", instance);
249 if !ecx.tcx.is_const_fn(instance.def_id()) {
250 let def_id = instance.def_id();
251 let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
255 ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
258 let (dest, bb) = destination.expect("128 lowerings can't diverge");
259 let dest_ty = sig.output();
261 ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
263 ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
268 let mir = match ecx.load_mir(instance.def) {
271 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
273 ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
280 let (return_place, return_to_block) = match destination {
281 Some((place, block)) => (place, StackPopCleanup::Goto(block)),
282 None => (Place::undef(), StackPopCleanup::None),
285 ecx.push_stack_frame(
297 fn call_intrinsic<'a>(
298 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
299 instance: ty::Instance<'tcx>,
300 args: &[ValTy<'tcx>],
302 dest_layout: layout::TyLayout<'tcx>,
303 target: mir::BasicBlock,
304 ) -> EvalResult<'tcx> {
305 let substs = instance.substs;
307 let intrinsic_name = &ecx.tcx.item_name(instance.def_id()).as_str()[..];
308 match intrinsic_name {
310 let elem_ty = substs.type_at(0);
311 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
312 let align_val = Scalar::Bits {
313 bits: elem_align as u128,
314 size: dest_layout.size.bytes() as u8,
316 ecx.write_scalar(dest, align_val, dest_layout.ty)?;
320 let ty = substs.type_at(0);
321 let size = ecx.layout_of(ty)?.size.bytes() as u128;
322 let size_val = Scalar::Bits {
324 size: dest_layout.size.bytes() as u8,
326 ecx.write_scalar(dest, size_val, dest_layout.ty)?;
330 let ty = substs.type_at(0);
331 let type_id = ecx.tcx.type_id_hash(ty) as u128;
332 let id_val = Scalar::Bits {
334 size: dest_layout.size.bytes() as u8,
336 ecx.write_scalar(dest, id_val, dest_layout.ty)?;
338 "ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => {
339 let ty = substs.type_at(0);
340 let layout_of = ecx.layout_of(ty)?;
341 let bits = ecx.value_to_scalar(args[0])?.to_bits(layout_of.size)?;
342 let kind = match layout_of.abi {
343 ty::layout::Abi::Scalar(ref scalar) => scalar.value,
344 _ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
346 let out_val = if intrinsic_name.ends_with("_nonzero") {
348 return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
350 numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)?
352 numeric_intrinsic(intrinsic_name, bits, kind)?
354 ecx.write_scalar(dest, out_val, ty)?;
358 ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()
362 ecx.goto_block(target);
364 // Since we pushed no stack frame, the main loop will act
365 // as if the call just completed and it's returning to the
371 _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
377 ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
378 if left.is_bits() && right.is_bits() {
382 ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
387 fn mark_static_initialized<'a>(
388 _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
390 _mutability: Mutability,
391 ) -> EvalResult<'tcx, bool> {
396 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
398 ) -> EvalResult<'tcx, AllocId> {
403 .intern_static(cid.instance.def_id()))
407 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
410 ) -> EvalResult<'tcx> {
412 ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
416 fn global_item_with_linkage<'a>(
417 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
418 _instance: ty::Instance<'tcx>,
419 _mutability: Mutability,
420 ) -> EvalResult<'tcx> {
422 ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
427 pub fn const_val_field<'a, 'tcx>(
428 tcx: TyCtxt<'a, 'tcx, 'tcx>,
429 param_env: ty::ParamEnv<'tcx>,
430 instance: ty::Instance<'tcx>,
431 variant: Option<usize>,
433 value: &'tcx ty::Const<'tcx>,
434 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
435 trace!("const_val_field: {:?}, {:?}, {:?}", instance, field, value);
436 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
439 let value = ecx.const_to_value(value.val)?;
440 let layout = ecx.layout_of(ty)?;
441 let place = ecx.allocate_place_for_value(value, layout, variant)?;
442 let (place, layout) = ecx.place_field(place, field, layout)?;
443 let (ptr, align) = place.to_ptr_align();
444 let mut new_value = Value::ByRef(ptr.read()?, align);
445 new_value = ecx.try_read_by_ref(new_value, layout.ty)?;
446 use rustc_data_structures::indexed_vec::Idx;
447 match (value, new_value) {
448 (Value::Scalar(_), Value::ByRef(..)) |
449 (Value::ScalarPair(..), Value::ByRef(..)) |
450 (Value::Scalar(_), Value::ScalarPair(..)) => bug!(
451 "field {} of {:?} yielded {:?}",
458 Ok(value_to_const_value(&ecx, new_value, layout.ty))
460 result.map_err(|err| {
461 let (trace, span) = ecx.generate_stacktrace(None);
470 pub fn const_variant_index<'a, 'tcx>(
471 tcx: TyCtxt<'a, 'tcx, 'tcx>,
472 param_env: ty::ParamEnv<'tcx>,
473 instance: ty::Instance<'tcx>,
474 val: &'tcx ty::Const<'tcx>,
475 ) -> EvalResult<'tcx, usize> {
476 trace!("const_variant_index: {:?}, {:?}", instance, val);
477 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
478 let value = ecx.const_to_value(val.val)?;
479 let layout = ecx.layout_of(val.ty)?;
480 let (ptr, align) = match value {
481 Value::ScalarPair(..) | Value::Scalar(_) => {
482 let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?.into();
483 ecx.write_value_to_ptr(value, ptr, layout.align, val.ty)?;
486 Value::ByRef(ptr, align) => (ptr, align),
488 let place = Place::from_scalar_ptr(ptr, align);
489 ecx.read_discriminant_as_variant_index(place, layout)
492 pub fn const_value_to_allocation_provider<'a, 'tcx>(
493 tcx: TyCtxt<'a, 'tcx, 'tcx>,
494 val: &'tcx ty::Const<'tcx>,
495 ) -> &'tcx Allocation {
497 ConstValue::ByRef(alloc, offset) => {
498 assert_eq!(offset.bytes(), 0);
503 let result = || -> EvalResult<'tcx, &'tcx Allocation> {
504 let mut ecx = EvalContext::new(
506 ty::ParamEnv::reveal_all(),
507 CompileTimeEvaluator,
509 let value = ecx.const_to_value(val.val)?;
510 let layout = ecx.layout_of(val.ty)?;
511 let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?;
512 ecx.write_value_to_ptr(value, ptr.into(), layout.align, val.ty)?;
513 let alloc = ecx.memory.get(ptr.alloc_id)?;
514 Ok(tcx.intern_const_alloc(alloc.clone()))
516 result().expect("unable to convert ConstValue to Allocation")
519 pub fn const_eval_provider<'a, 'tcx>(
520 tcx: TyCtxt<'a, 'tcx, 'tcx>,
521 key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
522 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
523 trace!("const eval: {:?}", key);
525 let def_id = cid.instance.def.def_id();
527 if let Some(id) = tcx.hir.as_local_node_id(def_id) {
528 let tables = tcx.typeck_tables_of(def_id);
529 let span = tcx.def_span(def_id);
531 // Do match-check before building MIR
532 if tcx.check_match(def_id).is_err() {
533 return Err(ConstEvalErr {
534 error: EvalErrorKind::CheckMatchError.into(),
540 if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
541 tcx.mir_const_qualif(def_id);
544 // Do not continue into miri if typeck errors occurred; it will fail horribly
545 if tables.tainted_by_errors {
546 return Err(ConstEvalErr {
547 error: EvalErrorKind::CheckMatchError.into(),
554 let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
555 res.and_then(|(mut val, _, layout)| {
556 if tcx.is_static(def_id).is_none() && cid.promoted.is_none() {
557 val = ecx.try_read_by_ref(val, layout.ty)?;
559 Ok(value_to_const_value(&ecx, val, layout.ty))
561 let (trace, span) = ecx.generate_stacktrace(None);
562 let err = ConstEvalErr {
567 if tcx.is_static(def_id).is_some() {
568 err.report_as_error(ecx.tcx, "could not evaluate static initializer");
569 if tcx.sess.err_count() == 0 {
570 span_bug!(span, "static eval failure didn't emit an error: {:#?}", err);
577 fn numeric_intrinsic<'tcx>(
581 ) -> EvalResult<'tcx, Scalar> {
582 let size = match kind {
583 Primitive::Int(integer, _) => integer.size(),
584 _ => bug!("invalid `{}` argument: {:?}", name, bits),
586 let extra = 128 - size.bits() as u128;
587 let bits_out = match name {
588 "ctpop" => bits.count_ones() as u128,
589 "ctlz" => bits.leading_zeros() as u128 - extra,
590 "cttz" => (bits << extra).trailing_zeros() as u128 - extra,
591 "bswap" => (bits << extra).swap_bytes(),
592 _ => bug!("not a numeric intrinsic: {}", name),
594 Ok(Scalar::Bits { bits: bits_out, size: size.bytes() as u8 })