5 use rustc::mir::interpret::{ConstEvalErr, ScalarMaybeUndef};
7 use rustc::ty::{self, TyCtxt, Ty, Instance};
8 use rustc::ty::layout::{self, LayoutOf, Primitive, TyLayout, Size};
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>,
79 layout: TyLayout<'tcx>,
80 ) -> &'tcx ty::Const<'tcx> {
81 match (val, &layout.abi) {
82 (Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { size: 0, ..})), _) if layout.is_zst() => {},
83 (Value::ByRef(..), _) |
84 (Value::Scalar(_), &layout::Abi::Scalar(_)) |
85 (Value::ScalarPair(..), &layout::Abi::ScalarPair(..)) => {},
86 _ => bug!("bad value/layout combo: {:#?}, {:#?}", val, layout),
90 Value::Scalar(val) => Ok(ConstValue::Scalar(val.unwrap_or_err()?)),
91 Value::ScalarPair(a, b) => Ok(ConstValue::ScalarPair(a.unwrap_or_err()?, b.unwrap_or_err()?)),
92 Value::ByRef(ptr, align) => {
93 let ptr = ptr.to_ptr().unwrap();
94 let alloc = ecx.memory.get(ptr.alloc_id)?;
95 assert!(alloc.align.abi() >= align.abi());
96 assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= layout.size.bytes());
97 let mut alloc = alloc.clone();
99 let alloc = ecx.tcx.intern_const_alloc(alloc);
100 Ok(ConstValue::ByRef(alloc, ptr.offset))
105 Ok(val) => ty::Const::from_const_value(ecx.tcx.tcx, val, layout.ty),
107 let (stacktrace, span) = ecx.generate_stacktrace(None);
108 let err = ConstEvalErr { span, error, stacktrace };
109 if let Some(mut err) = err.struct_error(ecx.tcx, "failed to convert Value to ConstValue") {
112 span_bug!(span, "failed to convert Value to ConstValue")
114 let alloc = Allocation::undef(layout.size, layout.align);
115 let alloc = ecx.tcx.intern_const_alloc(alloc);
116 let val = ConstValue::ByRef(alloc, Size::ZERO);
117 ty::Const::from_const_value(ecx.tcx.tcx, val, layout.ty)
122 fn eval_body_and_ecx<'a, 'mir, 'tcx>(
123 tcx: TyCtxt<'a, 'tcx, 'tcx>,
125 mir: Option<&'mir mir::Mir<'tcx>>,
126 param_env: ty::ParamEnv<'tcx>,
127 ) -> (EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
128 debug!("eval_body_and_ecx: {:?}, {:?}", cid, param_env);
129 // we start out with the best span we have
130 // and try improving it down the road when more information is available
131 let span = tcx.def_span(cid.instance.def_id());
132 let span = mir.map(|mir| mir.span).unwrap_or(span);
133 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
134 let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
138 fn eval_body_using_ecx<'a, 'mir, 'tcx>(
139 ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
141 mir: Option<&'mir mir::Mir<'tcx>>,
142 param_env: ty::ParamEnv<'tcx>,
143 ) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
144 debug!("eval_body: {:?}, {:?}", cid, param_env);
145 let tcx = ecx.tcx.tcx;
146 let mut mir = match mir {
148 None => ecx.load_mir(cid.instance.def)?,
150 if let Some(index) = cid.promoted {
151 mir = &mir.promoted[index];
153 let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
154 assert!(!layout.is_unsized());
155 let ptr = ecx.memory.allocate(
160 let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
161 let is_static = tcx.is_static(cid.instance.def_id());
162 let mutability = if is_static == Some(hir::Mutability::MutMutable) || internally_mutable {
165 Mutability::Immutable
167 let cleanup = StackPopCleanup::MarkStatic(mutability);
168 let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
169 let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
170 trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
171 assert!(mir.arg_count == 0);
172 ecx.push_stack_frame(
176 Place::from_ptr(ptr, layout.align),
181 let ptr = ptr.into();
182 // always try to read the value and report errors
183 let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
184 Some(val) if is_static.is_none() && cid.promoted.is_none() => val,
185 // point at the allocation
186 _ => Value::ByRef(ptr, layout.align),
188 Ok((value, ptr, layout))
191 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
192 pub struct CompileTimeEvaluator;
194 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
195 fn into(self) -> EvalError<'tcx> {
196 EvalErrorKind::MachineError(self.to_string()).into()
200 #[derive(Clone, Debug)]
201 enum ConstEvalError {
206 impl fmt::Display for ConstEvalError {
207 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208 use self::ConstEvalError::*;
210 NeedsRfc(ref msg) => {
213 "\"{}\" needs an rfc before being allowed inside constants",
217 NotConst(ref msg) => write!(f, "{}", msg),
222 impl Error for ConstEvalError {
223 fn description(&self) -> &str {
224 use self::ConstEvalError::*;
226 NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
227 NotConst(_) => "this feature is not compatible with constant evaluation",
231 fn cause(&self) -> Option<&dyn Error> {
236 impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
237 type MemoryData = ();
238 type MemoryKinds = !;
240 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
241 instance: ty::Instance<'tcx>,
242 destination: Option<(Place, mir::BasicBlock)>,
243 args: &[ValTy<'tcx>],
245 sig: ty::FnSig<'tcx>,
246 ) -> EvalResult<'tcx, bool> {
247 debug!("eval_fn_call: {:?}", instance);
248 if !ecx.tcx.is_const_fn(instance.def_id()) {
249 let def_id = instance.def_id();
250 let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
254 ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
257 let (dest, bb) = destination.expect("128 lowerings can't diverge");
258 let dest_ty = sig.output();
260 ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
262 ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
267 let mir = match ecx.load_mir(instance.def) {
270 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
272 ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
279 let (return_place, return_to_block) = match destination {
280 Some((place, block)) => (place, StackPopCleanup::Goto(block)),
281 None => (Place::undef(), StackPopCleanup::None),
284 ecx.push_stack_frame(
296 fn call_intrinsic<'a>(
297 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
298 instance: ty::Instance<'tcx>,
299 args: &[ValTy<'tcx>],
301 dest_layout: layout::TyLayout<'tcx>,
302 target: mir::BasicBlock,
303 ) -> EvalResult<'tcx> {
304 let substs = instance.substs;
306 let intrinsic_name = &ecx.tcx.item_name(instance.def_id()).as_str()[..];
307 match intrinsic_name {
309 let elem_ty = substs.type_at(0);
310 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
311 let align_val = Scalar::Bits {
312 bits: elem_align as u128,
313 size: dest_layout.size.bytes() as u8,
315 ecx.write_scalar(dest, align_val, dest_layout.ty)?;
319 let ty = substs.type_at(0);
320 let size = ecx.layout_of(ty)?.size.bytes() as u128;
321 let size_val = Scalar::Bits {
323 size: dest_layout.size.bytes() as u8,
325 ecx.write_scalar(dest, size_val, dest_layout.ty)?;
329 let ty = substs.type_at(0);
330 let type_id = ecx.tcx.type_id_hash(ty) as u128;
331 let id_val = Scalar::Bits {
333 size: dest_layout.size.bytes() as u8,
335 ecx.write_scalar(dest, id_val, dest_layout.ty)?;
337 "ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => {
338 let ty = substs.type_at(0);
339 let layout_of = ecx.layout_of(ty)?;
340 let bits = ecx.value_to_scalar(args[0])?.to_bits(layout_of.size)?;
341 let kind = match layout_of.abi {
342 ty::layout::Abi::Scalar(ref scalar) => scalar.value,
343 _ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
345 let out_val = if intrinsic_name.ends_with("_nonzero") {
347 return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
349 numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)?
351 numeric_intrinsic(intrinsic_name, bits, kind)?
353 ecx.write_scalar(dest, out_val, ty)?;
357 ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()
361 ecx.goto_block(target);
363 // Since we pushed no stack frame, the main loop will act
364 // as if the call just completed and it's returning to the
370 _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
376 ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
377 if left.is_bits() && right.is_bits() {
381 ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
386 fn mark_static_initialized<'a>(
387 _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
389 _mutability: Mutability,
390 ) -> EvalResult<'tcx, bool> {
395 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
397 ) -> EvalResult<'tcx, AllocId> {
402 .intern_static(cid.instance.def_id()))
406 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
409 ) -> EvalResult<'tcx> {
411 ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
415 fn global_item_with_linkage<'a>(
416 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
417 _instance: ty::Instance<'tcx>,
418 _mutability: Mutability,
419 ) -> EvalResult<'tcx> {
421 ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
426 pub fn const_val_field<'a, 'tcx>(
427 tcx: TyCtxt<'a, 'tcx, 'tcx>,
428 param_env: ty::ParamEnv<'tcx>,
429 instance: ty::Instance<'tcx>,
430 variant: Option<usize>,
432 value: &'tcx ty::Const<'tcx>,
433 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
434 trace!("const_val_field: {:?}, {:?}, {:?}", instance, field, value);
435 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
438 let value = ecx.const_to_value(value.val)?;
439 let layout = ecx.layout_of(ty)?;
440 let place = ecx.allocate_place_for_value(value, layout, variant)?;
441 let (place, layout) = ecx.place_field(place, field, layout)?;
442 let (ptr, align) = place.to_ptr_align();
443 let mut new_value = Value::ByRef(ptr.unwrap_or_err()?, align);
444 new_value = ecx.try_read_by_ref(new_value, layout.ty)?;
445 use rustc_data_structures::indexed_vec::Idx;
446 match (value, new_value) {
447 (Value::Scalar(_), Value::ByRef(..)) |
448 (Value::ScalarPair(..), Value::ByRef(..)) |
449 (Value::Scalar(_), Value::ScalarPair(..)) => bug!(
450 "field {} of {:?} yielded {:?}",
457 Ok(value_to_const_value(&ecx, new_value, layout))
459 result.map_err(|err| {
460 let (trace, span) = ecx.generate_stacktrace(None);
469 pub fn const_variant_index<'a, 'tcx>(
470 tcx: TyCtxt<'a, 'tcx, 'tcx>,
471 param_env: ty::ParamEnv<'tcx>,
472 instance: ty::Instance<'tcx>,
473 val: &'tcx ty::Const<'tcx>,
474 ) -> EvalResult<'tcx, usize> {
475 trace!("const_variant_index: {:?}, {:?}", instance, val);
476 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
477 let value = ecx.const_to_value(val.val)?;
478 let layout = ecx.layout_of(val.ty)?;
479 let (ptr, align) = match value {
480 Value::ScalarPair(..) | Value::Scalar(_) => {
481 let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?.into();
482 ecx.write_value_to_ptr(value, ptr, layout.align, val.ty)?;
485 Value::ByRef(ptr, align) => (ptr, align),
487 let place = Place::from_scalar_ptr(ptr.into(), align);
488 ecx.read_discriminant_as_variant_index(place, layout)
491 pub fn const_value_to_allocation_provider<'a, 'tcx>(
492 tcx: TyCtxt<'a, 'tcx, 'tcx>,
493 val: &'tcx ty::Const<'tcx>,
494 ) -> &'tcx Allocation {
496 ConstValue::ByRef(alloc, offset) => {
497 assert_eq!(offset.bytes(), 0);
502 let result = || -> EvalResult<'tcx, &'tcx Allocation> {
503 let mut ecx = EvalContext::new(
505 ty::ParamEnv::reveal_all(),
506 CompileTimeEvaluator,
508 let value = ecx.const_to_value(val.val)?;
509 let layout = ecx.layout_of(val.ty)?;
510 let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?;
511 ecx.write_value_to_ptr(value, ptr.into(), layout.align, val.ty)?;
512 let alloc = ecx.memory.get(ptr.alloc_id)?;
513 Ok(tcx.intern_const_alloc(alloc.clone()))
515 result().expect("unable to convert ConstValue to Allocation")
518 pub fn const_eval_provider<'a, 'tcx>(
519 tcx: TyCtxt<'a, 'tcx, 'tcx>,
520 key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
521 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
522 trace!("const eval: {:?}", key);
524 let def_id = cid.instance.def.def_id();
526 if let Some(id) = tcx.hir.as_local_node_id(def_id) {
527 let tables = tcx.typeck_tables_of(def_id);
528 let span = tcx.def_span(def_id);
530 // Do match-check before building MIR
531 if tcx.check_match(def_id).is_err() {
532 return Err(ConstEvalErr {
533 error: EvalErrorKind::CheckMatchError.into(),
539 if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
540 tcx.mir_const_qualif(def_id);
543 // Do not continue into miri if typeck errors occurred; it will fail horribly
544 if tables.tainted_by_errors {
545 return Err(ConstEvalErr {
546 error: EvalErrorKind::CheckMatchError.into(),
553 let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
554 res.and_then(|(mut val, _, layout)| {
555 if tcx.is_static(def_id).is_none() && cid.promoted.is_none() {
556 val = ecx.try_read_by_ref(val, layout.ty)?;
558 Ok(value_to_const_value(&ecx, val, layout))
560 let (trace, span) = ecx.generate_stacktrace(None);
561 let err = ConstEvalErr {
566 if tcx.is_static(def_id).is_some() {
567 err.report_as_error(ecx.tcx, "could not evaluate static initializer");
568 if tcx.sess.err_count() == 0 {
569 span_bug!(span, "static eval failure didn't emit an error: {:#?}", err);
576 fn numeric_intrinsic<'tcx>(
580 ) -> EvalResult<'tcx, Scalar> {
581 let size = match kind {
582 Primitive::Int(integer, _) => integer.size(),
583 _ => bug!("invalid `{}` argument: {:?}", name, bits),
585 let extra = 128 - size.bits() as u128;
586 let bits_out = match name {
587 "ctpop" => bits.count_ones() as u128,
588 "ctlz" => bits.leading_zeros() as u128 - extra,
589 "cttz" => (bits << extra).trailing_zeros() as u128 - extra,
590 "bswap" => (bits << extra).swap_bytes(),
591 _ => bug!("not a numeric intrinsic: {}", name),
593 Ok(Scalar::Bits { bits: bits_out, size: size.bytes() as u8 })