1 use rustc::ty::{self, TyCtxt, Ty, Instance};
2 use rustc::ty::layout::{self, LayoutOf};
3 use rustc::ty::subst::Substs;
4 use rustc::hir::def_id::DefId;
6 use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError};
7 use rustc::middle::const_val::{ConstEvalErr, ConstVal};
8 use rustc_const_eval::{lookup_const_by_id, ConstContext};
10 use rustc_data_structures::indexed_vec::Idx;
12 use syntax::ast::Mutability;
13 use syntax::codemap::Span;
15 use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal};
16 use super::{Place, EvalContext, StackPopCleanup, ValTy};
18 use rustc_const_math::ConstInt;
21 use std::error::Error;
24 pub fn mk_eval_cx<'a, 'tcx>(
25 tcx: TyCtxt<'a, 'tcx, 'tcx>,
26 instance: Instance<'tcx>,
27 param_env: ty::ParamEnv<'tcx>,
28 ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, CompileTimeEvaluator>> {
29 debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
30 let limits = super::ResourceLimits::default();
31 let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
32 let mir = ecx.load_mir(instance.def)?;
33 // insert a stack frame so any queries have the correct substs
39 StackPopCleanup::None,
44 pub fn eval_body<'a, 'tcx>(
45 tcx: TyCtxt<'a, 'tcx, 'tcx>,
46 instance: Instance<'tcx>,
47 param_env: ty::ParamEnv<'tcx>,
48 ) -> EvalResult<'tcx, (Pointer, Ty<'tcx>)> {
49 debug!("eval_body: {:?}, {:?}", instance, param_env);
50 let limits = super::ResourceLimits::default();
51 let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
57 if ecx.tcx.has_attr(instance.def_id(), "linkage") {
58 return Err(ConstEvalError::NotConst("extern global".to_string()).into());
60 let instance_ty = instance.ty(tcx);
61 if tcx.interpret_interner.borrow().get_cached(cid).is_none() {
62 let mir = ecx.load_mir(instance.def)?;
63 let layout = ecx.layout_of(instance_ty)?;
64 assert!(!layout.is_unsized());
65 let ptr = ecx.memory.allocate(
70 tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id);
71 let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable);
72 let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id()));
73 trace!("const_eval: pushing stack frame for global: {}", name);
78 Place::from_ptr(ptr, layout.align),
84 let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
85 Ok((MemoryPointer::new(alloc, 0).into(), instance_ty))
88 pub fn eval_body_as_integer<'a, 'tcx>(
89 tcx: TyCtxt<'a, 'tcx, 'tcx>,
90 param_env: ty::ParamEnv<'tcx>,
91 instance: Instance<'tcx>,
92 ) -> EvalResult<'tcx, ConstInt> {
93 let ptr_ty = eval_body(tcx, instance, param_env);
94 let (ptr, ty) = ptr_ty?;
95 let ecx = mk_eval_cx(tcx, instance, param_env)?;
96 let prim = match ecx.try_read_value(ptr, ecx.layout_of(ty)?.align, ty)? {
97 Some(Value::ByVal(prim)) => prim.to_bytes()?,
98 _ => return err!(TypeNotPrimitive(ty)),
100 use syntax::ast::{IntTy, UintTy};
101 use rustc::ty::TypeVariants::*;
102 use rustc_const_math::{ConstIsize, ConstUsize};
104 TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8),
105 TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16),
106 TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32),
107 TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64),
108 TyInt(IntTy::I128) => ConstInt::I128(prim as i128),
109 TyInt(IntTy::Isize) => ConstInt::Isize(
110 ConstIsize::new(prim as i128 as i64, tcx.sess.target.isize_ty)
111 .expect("miri should already have errored"),
113 TyUint(UintTy::U8) => ConstInt::U8(prim as u8),
114 TyUint(UintTy::U16) => ConstInt::U16(prim as u16),
115 TyUint(UintTy::U32) => ConstInt::U32(prim as u32),
116 TyUint(UintTy::U64) => ConstInt::U64(prim as u64),
117 TyUint(UintTy::U128) => ConstInt::U128(prim),
118 TyUint(UintTy::Usize) => ConstInt::Usize(
119 ConstUsize::new(prim as u64, tcx.sess.target.usize_ty)
120 .expect("miri should already have errored"),
124 ConstEvalError::NeedsRfc(
125 "evaluating anything other than isize/usize during typeck".to_string(),
132 pub struct CompileTimeEvaluator;
134 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
135 fn into(self) -> EvalError<'tcx> {
136 EvalErrorKind::MachineError(Box::new(self)).into()
140 #[derive(Clone, Debug)]
141 enum ConstEvalError {
146 impl fmt::Display for ConstEvalError {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 use self::ConstEvalError::*;
150 NeedsRfc(ref msg) => {
153 "\"{}\" needs an rfc before being allowed inside constants",
157 NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg),
162 impl Error for ConstEvalError {
163 fn description(&self) -> &str {
164 use self::ConstEvalError::*;
166 NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
167 NotConst(_) => "this feature is not compatible with constant evaluation",
171 fn cause(&self) -> Option<&Error> {
176 impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
177 type MemoryData = ();
178 type MemoryKinds = !;
180 ecx: &mut EvalContext<'a, 'tcx, Self>,
181 instance: ty::Instance<'tcx>,
182 destination: Option<(Place, mir::BasicBlock)>,
183 _args: &[ValTy<'tcx>],
185 _sig: ty::FnSig<'tcx>,
186 ) -> EvalResult<'tcx, bool> {
187 debug!("eval_fn_call: {:?}", instance);
188 if !ecx.tcx.is_const_fn(instance.def_id()) {
190 ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
193 let mir = match ecx.load_mir(instance.def) {
195 Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
196 // some simple things like `malloc` might get accepted in the future
198 ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
202 Err(other) => return Err(other),
204 let (return_place, return_to_block) = match destination {
205 Some((place, block)) => (place, StackPopCleanup::Goto(block)),
206 None => (Place::undef(), StackPopCleanup::None),
209 ecx.push_stack_frame(
221 fn call_intrinsic<'a>(
222 ecx: &mut EvalContext<'a, 'tcx, Self>,
223 instance: ty::Instance<'tcx>,
224 _args: &[ValTy<'tcx>],
226 dest_layout: layout::TyLayout<'tcx>,
227 target: mir::BasicBlock,
228 ) -> EvalResult<'tcx> {
229 let substs = instance.substs;
231 let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
232 match intrinsic_name {
234 let elem_ty = substs.type_at(0);
235 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
236 let align_val = PrimVal::from_u128(elem_align as u128);
237 ecx.write_primval(dest, align_val, dest_layout.ty)?;
241 let ty = substs.type_at(0);
242 let size = ecx.layout_of(ty)?.size.bytes() as u128;
243 ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
246 name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
249 ecx.goto_block(target);
251 // Since we pushed no stack frame, the main loop will act
252 // as if the call just completed and it's returning to the
258 _ecx: &EvalContext<'a, 'tcx, Self>,
264 ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
265 if left.is_bytes() && right.is_bytes() {
269 ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
274 fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
279 _ecx: &mut EvalContext<'a, 'tcx, Self>,
282 ) -> EvalResult<'tcx> {
284 ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
288 fn global_item_with_linkage<'a>(
289 _ecx: &mut EvalContext<'a, 'tcx, Self>,
290 _instance: ty::Instance<'tcx>,
291 _mutability: Mutability,
292 ) -> EvalResult<'tcx> {
294 ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
299 pub fn const_eval_provider<'a, 'tcx>(
300 tcx: TyCtxt<'a, 'tcx, 'tcx>,
301 key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>,
302 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
303 trace!("const eval: {:?}", key);
304 let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) {
307 return Err(ConstEvalErr {
308 span: tcx.def_span(key.value.0),
313 let tables = tcx.typeck_tables_of(def_id);
314 let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
315 let body_id = tcx.hir.body_owned_by(id);
317 // Do match-check before building MIR
318 if tcx.check_match(def_id).is_err() {
319 return Err(ConstEvalErr {
320 span: tcx.def_span(key.value.0),
321 kind: CheckMatchError,
325 tcx.mir_const_qualif(def_id);
326 tcx.hir.body(body_id)
328 tcx.extern_const_body(def_id).body
331 // do not continue into miri if typeck errors occurred
332 // it will fail horribly
333 if tables.tainted_by_errors {
334 return Err(ConstEvalErr { span: body.value.span, kind: TypeckError })
337 trace!("running old const eval");
338 let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value);
339 trace!("old const eval produced {:?}", old_result);
340 if tcx.sess.opts.debugging_opts.miri {
341 let instance = ty::Instance::new(def_id, substs);
342 trace!("const eval instance: {:?}, {:?}", instance, key.param_env);
343 let miri_result = ::interpret::eval_body(tcx, instance, key.param_env);
344 match (miri_result, old_result) {
345 (Err(err), Ok(ok)) => {
346 trace!("miri failed, ctfe returned {:?}", ok);
348 tcx.def_span(key.value.0),
349 "miri failed to eval, while ctfe succeeded",
351 let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
352 let () = unwrap_miri(&ecx, Err(err));
355 (_, Err(err)) => Err(err),
356 (Ok((miri_val, miri_ty)), Ok(ctfe)) => {
357 let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
358 let layout = ecx.layout_of(miri_ty).unwrap();
359 let miri_place = Place::from_primval_ptr(miri_val, layout.align);
360 check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val);
369 fn check_ctfe_against_miri<'a, 'tcx>(
370 ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
373 ctfe: ConstVal<'tcx>,
375 use rustc::middle::const_val::ConstAggregate::*;
376 use rustc_const_math::ConstFloat;
377 use rustc::ty::TypeVariants::*;
378 let miri_val = ValTy {
379 value: ecx.read_place(miri_place).unwrap(),
384 let prim = get_prim(ecx, miri_val);
385 let c = ConstInt::new_signed_truncating(prim as i128,
387 ecx.tcx.sess.target.isize_ty);
388 let c = ConstVal::Integral(c);
389 assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
392 let prim = get_prim(ecx, miri_val);
393 let c = ConstInt::new_unsigned_truncating(prim,
395 ecx.tcx.sess.target.usize_ty);
396 let c = ConstVal::Integral(c);
397 assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
400 let prim = get_prim(ecx, miri_val);
401 let f = ConstVal::Float(ConstFloat { bits: prim, ty });
402 assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe);
405 let bits = get_prim(ecx, miri_val);
407 bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe);
409 let b = ConstVal::Bool(bits == 1);
410 assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe);
413 let bits = get_prim(ecx, miri_val);
414 if let Some(cm) = ::std::char::from_u32(bits as u32) {
416 ConstVal::Char(cm), ctfe,
417 "miri evaluated to {:?}, but expected {:?}", cm, ctfe,
420 bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe);
424 let value = ecx.follow_by_ref_value(miri_val.value, miri_val.ty);
425 if let Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) = value {
428 .read_bytes(ptr.into(), len as u64)
429 .expect("bad miri memory for str");
430 if let Ok(s) = ::std::str::from_utf8(bytes) {
431 if let ConstVal::Str(s2) = ctfe {
432 assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2);
434 bug!("miri produced {:?}, but expected {:?}", s, ctfe);
438 "miri failed to produce valid utf8 {:?}, while ctfe produced {:?}",
444 bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe);
447 TyArray(elem_ty, n) => {
448 let n = n.val.to_const_int().unwrap().to_u64().unwrap();
449 let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe {
450 ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| {
451 (ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8)
453 ConstVal::Aggregate(Array(v)) => {
454 v.iter().map(|c| (c.val, c.ty)).collect()
456 ConstVal::Aggregate(Repeat(v, n)) => {
457 vec![(v.val, v.ty); n as usize]
459 _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
461 let layout = ecx.layout_of(miri_ty).unwrap();
462 for (i, elem) in vec.into_iter().enumerate() {
463 assert!((i as u64) < n);
464 let (field_place, _) =
465 ecx.place_field(miri_place, Field::new(i), layout).unwrap();
466 check_ctfe_against_miri(ecx, field_place, elem_ty, elem.0);
470 let vec = match ctfe {
471 ConstVal::Aggregate(Tuple(v)) => v,
472 _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
474 let layout = ecx.layout_of(miri_ty).unwrap();
475 for (i, elem) in vec.into_iter().enumerate() {
476 let (field_place, _) =
477 ecx.place_field(miri_place, Field::new(i), layout).unwrap();
478 check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val);
482 let mut miri_place = miri_place;
483 let struct_variant = if def.is_enum() {
484 let discr = ecx.read_discriminant_value(miri_place, miri_ty).unwrap();
485 let variant = def.discriminants(ecx.tcx).position(|variant_discr| {
486 variant_discr.to_u128_unchecked() == discr
487 }).expect("miri produced invalid enum discriminant");
488 miri_place = ecx.place_downcast(miri_place, variant).unwrap();
489 &def.variants[variant]
491 def.non_enum_variant()
493 let vec = match ctfe {
494 ConstVal::Aggregate(Struct(v)) => v,
495 ConstVal::Variant(did) => {
496 assert_eq!(struct_variant.fields.len(), 0);
497 assert_eq!(did, struct_variant.did);
500 ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
502 let layout = ecx.layout_of(miri_ty).unwrap();
503 for &(name, elem) in vec.into_iter() {
504 let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap();
505 let (field_place, _) =
506 ecx.place_field(miri_place, Field::new(field), layout).unwrap();
507 check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val);
510 TySlice(_) => bug!("miri produced a slice?"),
511 // not supported by ctfe
514 TyDynamic(..) => bug!("miri produced a trait object"),
515 TyClosure(..) => bug!("miri produced a closure"),
516 TyGenerator(..) => bug!("miri produced a generator"),
517 TyGeneratorWitness(..) => bug!("miri produced a generator witness"),
518 TyNever => bug!("miri produced a value of the never type"),
519 TyProjection(_) => bug!("miri produced a projection"),
520 TyAnon(..) => bug!("miri produced an impl Trait type"),
521 TyParam(_) => bug!("miri produced an unmonomorphized type"),
522 TyInfer(_) => bug!("miri produced an uninferred type"),
523 TyError => bug!("miri produced a type error"),
524 TyForeign(_) => bug!("miri produced an extern type"),
528 let value = ecx.value_to_primval(miri_val);
529 let ptr = match value {
530 Ok(PrimVal::Ptr(ptr)) => ptr,
531 value => bug!("expected fn ptr, got {:?}", value),
533 let inst = ecx.memory.get_fn(ptr).unwrap();
535 ConstVal::Function(did, substs) => {
536 let ctfe = ty::Instance::resolve(
542 assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst);
544 _ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst),
550 fn get_prim<'a, 'tcx>(
551 ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
554 let res = ecx.value_to_primval(val).and_then(|prim| prim.to_bytes());
555 unwrap_miri(ecx, res)
558 fn unwrap_miri<'a, 'tcx, T>(
559 ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>,
560 res: Result<T, EvalError<'tcx>>,
565 ecx.report(&mut err);
566 ecx.tcx.sess.abort_if_errors();