]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/const_eval.rs
Optimized error reporting for recursive requirements #47720
[rust.git] / src / librustc_mir / interpret / const_eval.rs
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;
5 use rustc::mir;
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};
9 use rustc::mir::Field;
10 use rustc_data_structures::indexed_vec::Idx;
11
12 use syntax::ast::Mutability;
13 use syntax::codemap::Span;
14
15 use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal};
16 use super::{Place, EvalContext, StackPopCleanup, ValTy};
17
18 use rustc_const_math::ConstInt;
19
20 use std::fmt;
21 use std::error::Error;
22
23
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
34     ecx.push_stack_frame(
35         instance,
36         mir.span,
37         mir,
38         Place::undef(),
39         StackPopCleanup::None,
40     )?;
41     Ok(ecx)
42 }
43
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, ());
52     let cid = GlobalId {
53         instance,
54         promoted: None,
55     };
56
57     if ecx.tcx.has_attr(instance.def_id(), "linkage") {
58         return Err(ConstEvalError::NotConst("extern global".to_string()).into());
59     }
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(
66             layout.size.bytes(),
67             layout.align,
68             None,
69         )?;
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);
74         ecx.push_stack_frame(
75             instance,
76             mir.span,
77             mir,
78             Place::from_ptr(ptr, layout.align),
79             cleanup.clone(),
80         )?;
81
82         while ecx.step()? {}
83     }
84     let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
85     Ok((MemoryPointer::new(alloc, 0).into(), instance_ty))
86 }
87
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)),
99     };
100     use syntax::ast::{IntTy, UintTy};
101     use rustc::ty::TypeVariants::*;
102     use rustc_const_math::{ConstIsize, ConstUsize};
103     Ok(match ty.sty {
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"),
112         ),
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"),
121         ),
122         _ => {
123             return Err(
124                 ConstEvalError::NeedsRfc(
125                     "evaluating anything other than isize/usize during typeck".to_string(),
126                 ).into(),
127             )
128         }
129     })
130 }
131
132 pub struct CompileTimeEvaluator;
133
134 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
135     fn into(self) -> EvalError<'tcx> {
136         EvalErrorKind::MachineError(Box::new(self)).into()
137     }
138 }
139
140 #[derive(Clone, Debug)]
141 enum ConstEvalError {
142     NeedsRfc(String),
143     NotConst(String),
144 }
145
146 impl fmt::Display for ConstEvalError {
147     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148         use self::ConstEvalError::*;
149         match *self {
150             NeedsRfc(ref msg) => {
151                 write!(
152                     f,
153                     "\"{}\" needs an rfc before being allowed inside constants",
154                     msg
155                 )
156             }
157             NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg),
158         }
159     }
160 }
161
162 impl Error for ConstEvalError {
163     fn description(&self) -> &str {
164         use self::ConstEvalError::*;
165         match *self {
166             NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
167             NotConst(_) => "this feature is not compatible with constant evaluation",
168         }
169     }
170
171     fn cause(&self) -> Option<&Error> {
172         None
173     }
174 }
175
176 impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
177     type MemoryData = ();
178     type MemoryKinds = !;
179     fn eval_fn_call<'a>(
180         ecx: &mut EvalContext<'a, 'tcx, Self>,
181         instance: ty::Instance<'tcx>,
182         destination: Option<(Place, mir::BasicBlock)>,
183         _args: &[ValTy<'tcx>],
184         span: Span,
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()) {
189             return Err(
190                 ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
191             );
192         }
193         let mir = match ecx.load_mir(instance.def) {
194             Ok(mir) => mir,
195             Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
196                 // some simple things like `malloc` might get accepted in the future
197                 return Err(
198                     ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
199                         .into(),
200                 );
201             }
202             Err(other) => return Err(other),
203         };
204         let (return_place, return_to_block) = match destination {
205             Some((place, block)) => (place, StackPopCleanup::Goto(block)),
206             None => (Place::undef(), StackPopCleanup::None),
207         };
208
209         ecx.push_stack_frame(
210             instance,
211             span,
212             mir,
213             return_place,
214             return_to_block,
215         )?;
216
217         Ok(false)
218     }
219
220
221     fn call_intrinsic<'a>(
222         ecx: &mut EvalContext<'a, 'tcx, Self>,
223         instance: ty::Instance<'tcx>,
224         _args: &[ValTy<'tcx>],
225         dest: Place,
226         dest_layout: layout::TyLayout<'tcx>,
227         target: mir::BasicBlock,
228     ) -> EvalResult<'tcx> {
229         let substs = instance.substs;
230
231         let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
232         match intrinsic_name {
233             "min_align_of" => {
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)?;
238             }
239
240             "size_of" => {
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)?;
244             }
245
246             name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
247         }
248
249         ecx.goto_block(target);
250
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
253         // current frame.
254         Ok(())
255     }
256
257     fn try_ptr_op<'a>(
258         _ecx: &EvalContext<'a, 'tcx, Self>,
259         _bin_op: mir::BinOp,
260         left: PrimVal,
261         _left_ty: Ty<'tcx>,
262         right: PrimVal,
263         _right_ty: Ty<'tcx>,
264     ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
265         if left.is_bytes() && right.is_bytes() {
266             Ok(None)
267         } else {
268             Err(
269                 ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
270             )
271         }
272     }
273
274     fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
275         m
276     }
277
278     fn box_alloc<'a>(
279         _ecx: &mut EvalContext<'a, 'tcx, Self>,
280         _ty: Ty<'tcx>,
281         _dest: Place,
282     ) -> EvalResult<'tcx> {
283         Err(
284             ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
285         )
286     }
287
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> {
293         Err(
294             ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
295         )
296     }
297 }
298
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) {
305         resolved
306     } else {
307         return Err(ConstEvalErr {
308             span: tcx.def_span(key.value.0),
309             kind: TypeckError
310         });
311     };
312
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);
316
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,
322             });
323         }
324
325         tcx.mir_const_qualif(def_id);
326         tcx.hir.body(body_id)
327     } else {
328         tcx.extern_const_body(def_id).body
329     };
330
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 })
335     }
336
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);
347                 tcx.sess.span_warn(
348                     tcx.def_span(key.value.0),
349                     "miri failed to eval, while ctfe succeeded",
350                 );
351                 let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
352                 let () = unwrap_miri(&ecx, Err(err));
353                 Ok(ok)
354             },
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);
361                 Ok(ctfe)
362             }
363         }
364     } else {
365         old_result
366     }
367 }
368
369 fn check_ctfe_against_miri<'a, 'tcx>(
370     ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
371     miri_place: Place,
372     miri_ty: Ty<'tcx>,
373     ctfe: ConstVal<'tcx>,
374 ) {
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(),
380         ty: miri_ty
381     };
382     match miri_ty.sty {
383         TyInt(int_ty) => {
384             let prim = get_prim(ecx, miri_val);
385             let c = ConstInt::new_signed_truncating(prim as i128,
386                                                     int_ty,
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);
390         },
391         TyUint(uint_ty) => {
392             let prim = get_prim(ecx, miri_val);
393             let c = ConstInt::new_unsigned_truncating(prim,
394                                                      uint_ty,
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);
398         },
399         TyFloat(ty) => {
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);
403         },
404         TyBool => {
405             let bits = get_prim(ecx, miri_val);
406             if bits > 1 {
407                 bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe);
408             }
409             let b = ConstVal::Bool(bits == 1);
410             assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe);
411         },
412         TyChar => {
413             let bits = get_prim(ecx, miri_val);
414             if let Some(cm) = ::std::char::from_u32(bits as u32) {
415                 assert_eq!(
416                     ConstVal::Char(cm), ctfe,
417                     "miri evaluated to {:?}, but expected {:?}", cm, ctfe,
418                 );
419             } else {
420                 bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe);
421             }
422         },
423         TyStr => {
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 {
426                 let bytes = ecx
427                     .memory
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);
433                     } else {
434                         bug!("miri produced {:?}, but expected {:?}", s, ctfe);
435                     }
436                 } else {
437                     bug!(
438                         "miri failed to produce valid utf8 {:?}, while ctfe produced {:?}",
439                         bytes,
440                         ctfe,
441                     );
442                 }
443             } else {
444                 bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe);
445             }
446         },
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)
452                 }).collect(),
453                 ConstVal::Aggregate(Array(v)) => {
454                     v.iter().map(|c| (c.val, c.ty)).collect()
455                 },
456                 ConstVal::Aggregate(Repeat(v, n)) => {
457                     vec![(v.val, v.ty); n as usize]
458                 },
459                 _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
460             };
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);
467             }
468         },
469         TyTuple(..) => {
470             let vec = match ctfe {
471                 ConstVal::Aggregate(Tuple(v)) => v,
472                 _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
473             };
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);
479             }
480         },
481         TyAdt(def, _) => {
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]
490             } else {
491                 def.non_enum_variant()
492             };
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);
498                     return;
499                 },
500                 ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
501             };
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);
508             }
509         },
510         TySlice(_) => bug!("miri produced a slice?"),
511         // not supported by ctfe
512         TyRawPtr(_) |
513         TyRef(..) => {}
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"),
525         // should be fine
526         TyFnDef(..) => {}
527         TyFnPtr(_) => {
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),
532             };
533             let inst = ecx.memory.get_fn(ptr).unwrap();
534             match ctfe {
535                 ConstVal::Function(did, substs) => {
536                     let ctfe = ty::Instance::resolve(
537                         ecx.tcx,
538                         ecx.param_env,
539                         did,
540                         substs,
541                     ).unwrap();
542                     assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst);
543                 },
544                 _ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst),
545             }
546         },
547     }
548 }
549
550 fn get_prim<'a, 'tcx>(
551     ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
552     val: ValTy<'tcx>,
553 ) -> u128 {
554     let res = ecx.value_to_primval(val).and_then(|prim| prim.to_bytes());
555     unwrap_miri(ecx, res)
556 }
557
558 fn unwrap_miri<'a, 'tcx, T>(
559     ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>,
560     res: Result<T, EvalError<'tcx>>,
561 ) -> T {
562     match res {
563         Ok(val) => val,
564         Err(mut err) => {
565             ecx.report(&mut err);
566             ecx.tcx.sess.abort_if_errors();
567             bug!("{:#?}", err);
568         }
569     }
570 }