5 use rustc::mir::interpret::{ConstEvalErr};
7 use rustc::ty::{self, TyCtxt, Ty, Instance};
8 use rustc::ty::layout::{self, LayoutOf, Primitive};
9 use rustc::ty::subst::Subst;
11 use syntax::ast::Mutability;
12 use syntax::codemap::Span;
13 use syntax::codemap::DUMMY_SP;
15 use rustc::mir::interpret::{
16 EvalResult, EvalError, EvalErrorKind, GlobalId,
17 Value, Scalar, AllocId, Allocation, ConstValue,
19 use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory, MemoryKind};
21 pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
22 tcx: TyCtxt<'a, 'tcx, 'tcx>,
23 instance: Instance<'tcx>,
24 mir: &'mir mir::Mir<'tcx>,
26 ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
27 debug!("mk_borrowck_eval_cx: {:?}", instance);
28 let param_env = tcx.param_env(instance.def_id());
29 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
30 // insert a stack frame so any queries have the correct substs
36 StackPopCleanup::None,
41 pub fn mk_eval_cx<'a, 'tcx>(
42 tcx: TyCtxt<'a, 'tcx, 'tcx>,
43 instance: Instance<'tcx>,
44 param_env: ty::ParamEnv<'tcx>,
45 ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
46 debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
47 let span = tcx.def_span(instance.def_id());
48 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
49 let mir = ecx.load_mir(instance.def)?;
50 // insert a stack frame so any queries have the correct substs
56 StackPopCleanup::None,
61 pub fn eval_promoted<'a, 'mir, 'tcx>(
62 ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
64 mir: &'mir mir::Mir<'tcx>,
65 param_env: ty::ParamEnv<'tcx>,
66 ) -> EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)> {
67 ecx.with_fresh_body(|ecx| {
68 eval_body_using_ecx(ecx, cid, Some(mir), param_env)
72 pub fn value_to_const_value<'tcx>(
73 ecx: &EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>,
76 ) -> &'tcx ty::Const<'tcx> {
77 let layout = ecx.layout_of(ty).unwrap();
78 match (val, &layout.abi) {
79 (Value::Scalar(Scalar::Bits { defined: 0, ..}), _) if layout.is_zst() => {},
80 (Value::ByRef(..), _) |
81 (Value::Scalar(_), &layout::Abi::Scalar(_)) |
82 (Value::ScalarPair(..), &layout::Abi::ScalarPair(..)) => {},
83 _ => bug!("bad value/layout combo: {:#?}, {:#?}", val, layout),
87 Value::Scalar(val) => Ok(ConstValue::Scalar(val)),
88 Value::ScalarPair(a, b) => Ok(ConstValue::ScalarPair(a, b)),
89 Value::ByRef(ptr, align) => {
90 let ptr = ptr.to_ptr().unwrap();
91 let alloc = ecx.memory.get(ptr.alloc_id)?;
92 assert!(alloc.align.abi() >= align.abi());
93 assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= layout.size.bytes());
94 let mut alloc = alloc.clone();
96 let alloc = ecx.tcx.intern_const_alloc(alloc);
97 Ok(ConstValue::ByRef(alloc, ptr.offset))
102 Ok(val) => ty::Const::from_const_value(ecx.tcx.tcx, val, ty),
104 let (frames, span) = ecx.generate_stacktrace(None);
105 let err = ConstEvalErr {
112 "failed to convert Value to ConstValue, this is a bug",
114 span_bug!(span, "miri error occured when converting Value to ConstValue")
119 fn eval_body_and_ecx<'a, 'mir, 'tcx>(
120 tcx: TyCtxt<'a, 'tcx, 'tcx>,
122 mir: Option<&'mir mir::Mir<'tcx>>,
123 param_env: ty::ParamEnv<'tcx>,
124 ) -> (EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
125 debug!("eval_body_and_ecx: {:?}, {:?}", cid, param_env);
126 // we start out with the best span we have
127 // and try improving it down the road when more information is available
128 let span = tcx.def_span(cid.instance.def_id());
129 let span = mir.map(|mir| mir.span).unwrap_or(span);
130 let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
131 let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
135 fn eval_body_using_ecx<'a, 'mir, 'tcx>(
136 ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
138 mir: Option<&'mir mir::Mir<'tcx>>,
139 param_env: ty::ParamEnv<'tcx>,
140 ) -> EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)> {
141 debug!("eval_body: {:?}, {:?}", cid, param_env);
142 let tcx = ecx.tcx.tcx;
143 let mut mir = match mir {
145 None => ecx.load_mir(cid.instance.def)?,
147 if let Some(index) = cid.promoted {
148 mir = &mir.promoted[index];
150 let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
151 assert!(!layout.is_unsized());
152 let ptr = ecx.memory.allocate(
157 let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
158 let is_static = tcx.is_static(cid.instance.def_id());
159 let mutability = if is_static == Some(hir::Mutability::MutMutable) || internally_mutable {
162 Mutability::Immutable
164 let cleanup = StackPopCleanup::MarkStatic(mutability);
165 let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
166 let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
167 trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
168 assert!(mir.arg_count == 0);
169 ecx.push_stack_frame(
173 Place::from_ptr(ptr, layout.align),
178 let ptr = ptr.into();
179 // always try to read the value and report errors
180 let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
181 Some(val) if is_static.is_none() => val,
182 // point at the allocation
183 _ => Value::ByRef(ptr, layout.align),
185 Ok((value, ptr, layout.ty))
188 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
189 pub struct CompileTimeEvaluator;
191 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
192 fn into(self) -> EvalError<'tcx> {
193 EvalErrorKind::MachineError(self.to_string()).into()
197 #[derive(Clone, Debug)]
198 enum ConstEvalError {
203 impl fmt::Display for ConstEvalError {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 use self::ConstEvalError::*;
207 NeedsRfc(ref msg) => {
210 "\"{}\" needs an rfc before being allowed inside constants",
214 NotConst(ref msg) => write!(f, "{}", msg),
219 impl Error for ConstEvalError {
220 fn description(&self) -> &str {
221 use self::ConstEvalError::*;
223 NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
224 NotConst(_) => "this feature is not compatible with constant evaluation",
228 fn cause(&self) -> Option<&dyn Error> {
233 impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
234 type MemoryData = ();
235 type MemoryKinds = !;
237 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
238 instance: ty::Instance<'tcx>,
239 destination: Option<(Place, mir::BasicBlock)>,
240 args: &[ValTy<'tcx>],
242 sig: ty::FnSig<'tcx>,
243 ) -> EvalResult<'tcx, bool> {
244 debug!("eval_fn_call: {:?}", instance);
245 if !ecx.tcx.is_const_fn(instance.def_id()) {
246 let def_id = instance.def_id();
247 let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
251 ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
254 let (dest, bb) = destination.expect("128 lowerings can't diverge");
255 let dest_ty = sig.output();
257 ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
259 ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
264 let mir = match ecx.load_mir(instance.def) {
267 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
269 ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
276 let (return_place, return_to_block) = match destination {
277 Some((place, block)) => (place, StackPopCleanup::Goto(block)),
278 None => (Place::undef(), StackPopCleanup::None),
281 ecx.push_stack_frame(
293 fn call_intrinsic<'a>(
294 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
295 instance: ty::Instance<'tcx>,
296 args: &[ValTy<'tcx>],
298 dest_layout: layout::TyLayout<'tcx>,
299 target: mir::BasicBlock,
300 ) -> EvalResult<'tcx> {
301 let substs = instance.substs;
303 let intrinsic_name = &ecx.tcx.item_name(instance.def_id()).as_str()[..];
304 match intrinsic_name {
306 let elem_ty = substs.type_at(0);
307 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
308 let align_val = Scalar::Bits {
309 bits: elem_align as u128,
310 defined: dest_layout.size.bits() as u8,
312 ecx.write_scalar(dest, align_val, dest_layout.ty)?;
316 let ty = substs.type_at(0);
317 let size = ecx.layout_of(ty)?.size.bytes() as u128;
318 let size_val = Scalar::Bits {
320 defined: dest_layout.size.bits() as u8,
322 ecx.write_scalar(dest, size_val, dest_layout.ty)?;
326 let ty = substs.type_at(0);
327 let type_id = ecx.tcx.type_id_hash(ty) as u128;
328 let id_val = Scalar::Bits {
330 defined: dest_layout.size.bits() as u8,
332 ecx.write_scalar(dest, id_val, dest_layout.ty)?;
334 "ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => {
335 let ty = substs.type_at(0);
336 let layout_of = ecx.layout_of(ty)?;
337 let bits = ecx.value_to_scalar(args[0])?.to_bits(layout_of.size)?;
338 let kind = match layout_of.abi {
339 ty::layout::Abi::Scalar(ref scalar) => scalar.value,
340 _ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
342 let out_val = if intrinsic_name.ends_with("_nonzero") {
344 return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
346 numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)?
348 numeric_intrinsic(intrinsic_name, bits, kind)?
350 ecx.write_scalar(dest, out_val, ty)?;
354 ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()
358 ecx.goto_block(target);
360 // Since we pushed no stack frame, the main loop will act
361 // as if the call just completed and it's returning to the
367 _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
373 ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
374 if left.is_bits() && right.is_bits() {
378 ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
383 fn mark_static_initialized<'a>(
384 _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
386 _mutability: Mutability,
387 ) -> EvalResult<'tcx, bool> {
392 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
394 ) -> EvalResult<'tcx, AllocId> {
399 .intern_static(cid.instance.def_id()))
403 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
406 ) -> EvalResult<'tcx> {
408 ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
412 fn global_item_with_linkage<'a>(
413 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
414 _instance: ty::Instance<'tcx>,
415 _mutability: Mutability,
416 ) -> EvalResult<'tcx> {
418 ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
423 pub fn const_val_field<'a, 'tcx>(
424 tcx: TyCtxt<'a, 'tcx, 'tcx>,
425 param_env: ty::ParamEnv<'tcx>,
426 instance: ty::Instance<'tcx>,
427 variant: Option<usize>,
429 value: &'tcx ty::Const<'tcx>,
430 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
431 trace!("const_val_field: {:?}, {:?}, {:?}", instance, field, value);
432 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
435 let value = ecx.const_to_value(value.val)?;
436 let layout = ecx.layout_of(ty)?;
437 let (ptr, align) = match value {
438 Value::ByRef(ptr, align) => (ptr, align),
439 Value::ScalarPair(..) | Value::Scalar(_) => {
440 let ptr = ecx.alloc_ptr(ty)?.into();
441 ecx.write_value_to_ptr(value, ptr, layout.align, ty)?;
445 let place = Place::Ptr {
448 extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
450 let (place, layout) = ecx.place_field(place, field, layout)?;
451 let (ptr, align) = place.to_ptr_align();
452 let mut new_value = Value::ByRef(ptr, align);
453 new_value = ecx.try_read_by_ref(new_value, layout.ty)?;
454 use rustc_data_structures::indexed_vec::Idx;
455 match (value, new_value) {
456 (Value::Scalar(_), Value::ByRef(..)) |
457 (Value::ScalarPair(..), Value::ByRef(..)) |
458 (Value::Scalar(_), Value::ScalarPair(..)) => bug!(
459 "field {} of {:?} yielded {:?}",
466 Ok(value_to_const_value(&ecx, new_value, layout.ty))
468 result.map_err(|err| {
469 let (trace, span) = ecx.generate_stacktrace(None);
478 pub fn const_variant_index<'a, 'tcx>(
479 tcx: TyCtxt<'a, 'tcx, 'tcx>,
480 param_env: ty::ParamEnv<'tcx>,
481 instance: ty::Instance<'tcx>,
482 val: &'tcx ty::Const<'tcx>,
483 ) -> EvalResult<'tcx, usize> {
484 trace!("const_variant_index: {:?}, {:?}", instance, val);
485 let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
486 let value = ecx.const_to_value(val.val)?;
487 let (ptr, align) = match value {
488 Value::ScalarPair(..) | Value::Scalar(_) => {
489 let layout = ecx.layout_of(val.ty)?;
490 let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?.into();
491 ecx.write_value_to_ptr(value, ptr, layout.align, val.ty)?;
494 Value::ByRef(ptr, align) => (ptr, align),
496 let place = Place::from_scalar_ptr(ptr, align);
497 ecx.read_discriminant_as_variant_index(place, val.ty)
500 pub fn const_value_to_allocation_provider<'a, 'tcx>(
501 tcx: TyCtxt<'a, 'tcx, 'tcx>,
502 val: &'tcx ty::Const<'tcx>,
503 ) -> &'tcx Allocation {
505 ConstValue::ByRef(alloc, offset) => {
506 assert_eq!(offset.bytes(), 0);
511 let result = || -> EvalResult<'tcx, &'tcx Allocation> {
512 let mut ecx = EvalContext::new(
514 ty::ParamEnv::reveal_all(),
515 CompileTimeEvaluator,
517 let value = ecx.const_to_value(val.val)?;
518 let layout = ecx.layout_of(val.ty)?;
519 let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?;
520 ecx.write_value_to_ptr(value, ptr.into(), layout.align, val.ty)?;
521 let alloc = ecx.memory.get(ptr.alloc_id)?;
522 Ok(tcx.intern_const_alloc(alloc.clone()))
524 result().expect("unable to convert ConstValue to Allocation")
527 pub fn const_eval_provider<'a, 'tcx>(
528 tcx: TyCtxt<'a, 'tcx, 'tcx>,
529 key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
530 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
531 trace!("const eval: {:?}", key);
533 let def_id = cid.instance.def.def_id();
535 if let Some(id) = tcx.hir.as_local_node_id(def_id) {
536 let tables = tcx.typeck_tables_of(def_id);
537 let span = tcx.def_span(def_id);
539 // Do match-check before building MIR
540 if tcx.check_match(def_id).is_err() {
541 return Err(ConstEvalErr {
542 error: EvalErrorKind::CheckMatchError.into(),
548 if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
549 tcx.mir_const_qualif(def_id);
552 // Do not continue into miri if typeck errors occurred; it will fail horribly
553 if tables.tainted_by_errors {
554 return Err(ConstEvalErr {
555 error: EvalErrorKind::CheckMatchError.into(),
562 let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
563 res.and_then(|(mut val, _, miri_ty)| {
564 if tcx.is_static(def_id).is_none() {
565 val = ecx.try_read_by_ref(val, miri_ty)?;
567 Ok(value_to_const_value(&ecx, val, miri_ty))
569 let (trace, span) = ecx.generate_stacktrace(None);
570 let err = ConstEvalErr {
575 if tcx.is_static(def_id).is_some() {
576 err.report_as_error(ecx.tcx, "could not evaluate static initializer");
582 fn numeric_intrinsic<'tcx>(
586 ) -> EvalResult<'tcx, Scalar> {
587 let defined = match kind {
588 Primitive::Int(integer, _) => integer.size().bits() as u8,
589 _ => bug!("invalid `{}` argument: {:?}", name, bits),
591 let extra = 128 - defined as u128;
592 let bits_out = match name {
593 "ctpop" => bits.count_ones() as u128,
594 "ctlz" => bits.leading_zeros() as u128 - extra,
595 "cttz" => (bits << extra).trailing_zeros() as u128 - extra,
596 "bswap" => (bits << extra).swap_bytes(),
597 _ => bug!("not a numeric intrinsic: {}", name),
599 Ok(Scalar::Bits { bits: bits_out, defined })