]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/const_eval.rs
82eb28287b033c16c87e186421042ade8ff36138
[rust.git] / src / librustc_mir / interpret / const_eval.rs
1 use rustc::hir;
2 use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind};
3 use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError};
4 use rustc::mir;
5 use rustc::ty::{self, TyCtxt, Ty, Instance};
6 use rustc::ty::layout::{self, LayoutOf};
7 use rustc::ty::subst::Subst;
8
9 use syntax::ast::Mutability;
10 use syntax::codemap::Span;
11
12 use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal, AllocId};
13 use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory};
14
15 use std::fmt;
16 use std::error::Error;
17 use std::rc::Rc;
18
19 pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
20     tcx: TyCtxt<'a, 'tcx, 'tcx>,
21     instance: Instance<'tcx>,
22     mir: &'mir mir::Mir<'tcx>,
23     span: Span,
24 ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
25     debug!("mk_borrowck_eval_cx: {:?}", instance);
26     let param_env = tcx.param_env(instance.def_id());
27     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
28     // insert a stack frame so any queries have the correct substs
29     ecx.push_stack_frame(
30         instance,
31         span,
32         mir,
33         Place::undef(),
34         StackPopCleanup::None,
35     )?;
36     Ok(ecx)
37 }
38
39 pub fn mk_eval_cx<'a, 'tcx>(
40     tcx: TyCtxt<'a, 'tcx, 'tcx>,
41     instance: Instance<'tcx>,
42     param_env: ty::ParamEnv<'tcx>,
43 ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
44     debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
45     let span = tcx.def_span(instance.def_id());
46     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
47     let mir = ecx.load_mir(instance.def)?;
48     // insert a stack frame so any queries have the correct substs
49     ecx.push_stack_frame(
50         instance,
51         mir.span,
52         mir,
53         Place::undef(),
54         StackPopCleanup::None,
55     )?;
56     Ok(ecx)
57 }
58
59 pub fn eval_body_with_mir<'a, 'mir, 'tcx>(
60     tcx: TyCtxt<'a, 'tcx, 'tcx>,
61     cid: GlobalId<'tcx>,
62     mir: &'mir mir::Mir<'tcx>,
63     param_env: ty::ParamEnv<'tcx>,
64 ) -> Option<(Value, Pointer, Ty<'tcx>)> {
65     let (res, ecx) = eval_body_and_ecx(tcx, cid, Some(mir), param_env);
66     match res {
67         Ok(val) => Some(val),
68         Err(mut err) => {
69             ecx.report(&mut err, true, None);
70             None
71         }
72     }
73 }
74
75 pub fn eval_body<'a, 'tcx>(
76     tcx: TyCtxt<'a, 'tcx, 'tcx>,
77     cid: GlobalId<'tcx>,
78     param_env: ty::ParamEnv<'tcx>,
79 ) -> Option<(Value, Pointer, Ty<'tcx>)> {
80     let (res, ecx) = eval_body_and_ecx(tcx, cid, None, param_env);
81     match res {
82         Ok(val) => Some(val),
83         Err(mut err) => {
84             ecx.report(&mut err, true, None);
85             None
86         }
87     }
88 }
89
90 fn eval_body_and_ecx<'a, 'mir, 'tcx>(
91     tcx: TyCtxt<'a, 'tcx, 'tcx>,
92     cid: GlobalId<'tcx>,
93     mir: Option<&'mir mir::Mir<'tcx>>,
94     param_env: ty::ParamEnv<'tcx>,
95 ) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
96     debug!("eval_body: {:?}, {:?}", cid, param_env);
97     // we start out with the best span we have
98     // and try improving it down the road when more information is available
99     let span = tcx.def_span(cid.instance.def_id());
100     let mut span = mir.map(|mir| mir.span).unwrap_or(span);
101     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
102     let res = (|| {
103         let mut mir = match mir {
104             Some(mir) => mir,
105             None => ecx.load_mir(cid.instance.def)?,
106         };
107         if let Some(index) = cid.promoted {
108             mir = &mir.promoted[index];
109         }
110         span = mir.span;
111         let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
112         let alloc = tcx.interpret_interner.get_cached(cid.instance.def_id());
113         let is_static = tcx.is_static(cid.instance.def_id()).is_some();
114         let alloc = match alloc {
115             Some(alloc) => {
116                 assert!(cid.promoted.is_none());
117                 assert!(param_env.caller_bounds.is_empty());
118                 alloc
119             },
120             None => {
121                 assert!(!layout.is_unsized());
122                 let ptr = ecx.memory.allocate(
123                     layout.size.bytes(),
124                     layout.align,
125                     None,
126                 )?;
127                 if is_static {
128                     tcx.interpret_interner.cache(cid.instance.def_id(), ptr.alloc_id);
129                 }
130                 let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
131                 let mutability = tcx.is_static(cid.instance.def_id());
132                 let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable {
133                     Mutability::Mutable
134                 } else {
135                     Mutability::Immutable
136                 };
137                 let cleanup = StackPopCleanup::MarkStatic(mutability);
138                 let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
139                 let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
140                 trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
141                 assert!(mir.arg_count == 0);
142                 ecx.push_stack_frame(
143                     cid.instance,
144                     mir.span,
145                     mir,
146                     Place::from_ptr(ptr, layout.align),
147                     cleanup,
148                 )?;
149
150                 while ecx.step()? {}
151                 ptr.alloc_id
152             }
153         };
154         let ptr = MemoryPointer::new(alloc, 0).into();
155         // always try to read the value and report errors
156         let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
157             // if it's a constant (so it needs no address, directly compute its value)
158             Some(val) if !is_static => val,
159             // point at the allocation
160             _ => Value::ByRef(ptr, layout.align),
161         };
162         Ok((value, ptr, layout.ty))
163     })();
164     (res, ecx)
165 }
166
167 pub struct CompileTimeEvaluator;
168
169 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
170     fn into(self) -> EvalError<'tcx> {
171         EvalErrorKind::MachineError(self.to_string()).into()
172     }
173 }
174
175 #[derive(Clone, Debug)]
176 enum ConstEvalError {
177     NeedsRfc(String),
178     NotConst(String),
179 }
180
181 impl fmt::Display for ConstEvalError {
182     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183         use self::ConstEvalError::*;
184         match *self {
185             NeedsRfc(ref msg) => {
186                 write!(
187                     f,
188                     "\"{}\" needs an rfc before being allowed inside constants",
189                     msg
190                 )
191             }
192             NotConst(ref msg) => write!(f, "{}", msg),
193         }
194     }
195 }
196
197 impl Error for ConstEvalError {
198     fn description(&self) -> &str {
199         use self::ConstEvalError::*;
200         match *self {
201             NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
202             NotConst(_) => "this feature is not compatible with constant evaluation",
203         }
204     }
205
206     fn cause(&self) -> Option<&dyn Error> {
207         None
208     }
209 }
210
211 impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
212     type MemoryData = ();
213     type MemoryKinds = !;
214     fn eval_fn_call<'a>(
215         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
216         instance: ty::Instance<'tcx>,
217         destination: Option<(Place, mir::BasicBlock)>,
218         args: &[ValTy<'tcx>],
219         span: Span,
220         sig: ty::FnSig<'tcx>,
221     ) -> EvalResult<'tcx, bool> {
222         debug!("eval_fn_call: {:?}", instance);
223         if !ecx.tcx.is_const_fn(instance.def_id()) {
224             let def_id = instance.def_id();
225             let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
226                 op
227             } else {
228                 return Err(
229                     ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
230                 );
231             };
232             let (dest, bb) = destination.expect("128 lowerings can't diverge");
233             let dest_ty = sig.output();
234             if oflo {
235                 ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
236             } else {
237                 ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
238             }
239             ecx.goto_block(bb);
240             return Ok(true);
241         }
242         let mir = match ecx.load_mir(instance.def) {
243             Ok(mir) => mir,
244             Err(err) => {
245                 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
246                     return Err(
247                         ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
248                             .into(),
249                     );
250                 }
251                 return Err(err);
252             }
253         };
254         let (return_place, return_to_block) = match destination {
255             Some((place, block)) => (place, StackPopCleanup::Goto(block)),
256             None => (Place::undef(), StackPopCleanup::None),
257         };
258
259         ecx.push_stack_frame(
260             instance,
261             span,
262             mir,
263             return_place,
264             return_to_block,
265         )?;
266
267         Ok(false)
268     }
269
270
271     fn call_intrinsic<'a>(
272         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
273         instance: ty::Instance<'tcx>,
274         _args: &[ValTy<'tcx>],
275         dest: Place,
276         dest_layout: layout::TyLayout<'tcx>,
277         target: mir::BasicBlock,
278     ) -> EvalResult<'tcx> {
279         let substs = instance.substs;
280
281         let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
282         match intrinsic_name {
283             "min_align_of" => {
284                 let elem_ty = substs.type_at(0);
285                 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
286                 let align_val = PrimVal::from_u128(elem_align as u128);
287                 ecx.write_primval(dest, align_val, dest_layout.ty)?;
288             }
289
290             "size_of" => {
291                 let ty = substs.type_at(0);
292                 let size = ecx.layout_of(ty)?.size.bytes() as u128;
293                 ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
294             }
295
296             "type_id" => {
297                 let ty = substs.type_at(0);
298                 let type_id = ecx.tcx.type_id_hash(ty) as u128;
299                 ecx.write_primval(dest, PrimVal::from_u128(type_id), dest_layout.ty)?;
300             }
301
302             name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
303         }
304
305         ecx.goto_block(target);
306
307         // Since we pushed no stack frame, the main loop will act
308         // as if the call just completed and it's returning to the
309         // current frame.
310         Ok(())
311     }
312
313     fn try_ptr_op<'a>(
314         _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
315         _bin_op: mir::BinOp,
316         left: PrimVal,
317         _left_ty: Ty<'tcx>,
318         right: PrimVal,
319         _right_ty: Ty<'tcx>,
320     ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
321         if left.is_bytes() && right.is_bytes() {
322             Ok(None)
323         } else {
324             Err(
325                 ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
326             )
327         }
328     }
329
330     fn mark_static_initialized<'a>(
331         _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
332         _id: AllocId,
333         _mutability: Mutability,
334     ) -> EvalResult<'tcx, bool> {
335         Ok(false)
336     }
337
338     fn init_static<'a>(
339         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
340         cid: GlobalId<'tcx>,
341     ) -> EvalResult<'tcx, AllocId> {
342         // ensure the static is computed
343         ecx.const_eval(cid)?;
344         Ok(ecx
345             .tcx
346             .interpret_interner
347             .get_cached(cid.instance.def_id())
348             .expect("uncached static"))
349     }
350
351     fn box_alloc<'a>(
352         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
353         _ty: Ty<'tcx>,
354         _dest: Place,
355     ) -> EvalResult<'tcx> {
356         Err(
357             ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
358         )
359     }
360
361     fn global_item_with_linkage<'a>(
362         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
363         _instance: ty::Instance<'tcx>,
364         _mutability: Mutability,
365     ) -> EvalResult<'tcx> {
366         Err(
367             ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
368         )
369     }
370 }
371
372 pub fn const_val_field<'a, 'tcx>(
373     tcx: TyCtxt<'a, 'tcx, 'tcx>,
374     param_env: ty::ParamEnv<'tcx>,
375     instance: ty::Instance<'tcx>,
376     variant: Option<usize>,
377     field: mir::Field,
378     value: Value,
379     ty: Ty<'tcx>,
380 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
381     trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty);
382     let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
383     let result = (|| {
384         let (mut field, ty) = match value {
385             Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"),
386             Value::ByRef(ptr, align) => {
387                 let place = Place::Ptr {
388                     ptr,
389                     align,
390                     extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
391                 };
392                 let layout = ecx.layout_of(ty)?;
393                 let (place, layout) = ecx.place_field(place, field, layout)?;
394                 let (ptr, align) = place.to_ptr_align();
395                 (Value::ByRef(ptr, align), layout.ty)
396             }
397         };
398         if let Value::ByRef(ptr, align) = field {
399             if let Some(val) = ecx.try_read_value(ptr, align, ty)? {
400                 field = val;
401             }
402         }
403         Ok((field, ty))
404     })();
405     match result {
406         Ok((field, ty)) => Ok(tcx.mk_const(ty::Const {
407             val: ConstVal::Value(field),
408             ty,
409         })),
410         Err(err) => {
411             let (trace, span) = ecx.generate_stacktrace(None);
412             let err = ErrKind::Miri(err, trace);
413             Err(ConstEvalErr {
414                 kind: err.into(),
415                 span,
416             })
417         },
418     }
419 }
420
421 pub fn const_discr<'a, 'tcx>(
422     tcx: TyCtxt<'a, 'tcx, 'tcx>,
423     param_env: ty::ParamEnv<'tcx>,
424     instance: ty::Instance<'tcx>,
425     value: Value,
426     ty: Ty<'tcx>,
427 ) -> EvalResult<'tcx, u128> {
428     trace!("const_discr: {:?}, {:?}, {:?}", instance, value, ty);
429     let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
430     let (ptr, align) = match value {
431         Value::ByValPair(..) | Value::ByVal(_) => {
432             let layout = ecx.layout_of(ty)?;
433             use super::MemoryKind;
434             let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?;
435             let ptr: Pointer = ptr.into();
436             ecx.write_value_to_ptr(value, ptr, layout.align, ty)?;
437             (ptr, layout.align)
438         },
439         Value::ByRef(ptr, align) => (ptr, align),
440     };
441     let place = Place::from_primval_ptr(ptr, align);
442     ecx.read_discriminant_value(place, ty)
443 }
444
445 pub fn const_eval_provider<'a, 'tcx>(
446     tcx: TyCtxt<'a, 'tcx, 'tcx>,
447     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
448 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
449     trace!("const eval: {:?}", key);
450     let cid = key.value;
451     let def_id = cid.instance.def.def_id();
452
453     if tcx.is_foreign_item(def_id) {
454         let id = tcx.interpret_interner.get_cached(def_id);
455         let id = match id {
456             // FIXME: due to caches this shouldn't happen, add some assertions
457             Some(id) => id,
458             None => {
459                 let id = tcx.interpret_interner.reserve();
460                 tcx.interpret_interner.cache(def_id, id);
461                 id
462             },
463         };
464         let ty = tcx.type_of(def_id);
465         let layout = tcx.layout_of(key.param_env.and(ty)).unwrap();
466         let ptr = MemoryPointer::new(id, 0);
467         return Ok(tcx.mk_const(ty::Const {
468             val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)),
469             ty,
470         }))
471     }
472
473     if let Some(id) = tcx.hir.as_local_node_id(def_id) {
474         let tables = tcx.typeck_tables_of(def_id);
475         let span = tcx.def_span(def_id);
476
477         // Do match-check before building MIR
478         if tcx.check_match(def_id).is_err() {
479             return Err(ConstEvalErr {
480                 kind: Rc::new(CheckMatchError),
481                 span,
482             });
483         }
484
485         if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
486             tcx.mir_const_qualif(def_id);
487         }
488
489         // Do not continue into miri if typeck errors occurred; it will fail horribly
490         if tables.tainted_by_errors {
491             return Err(ConstEvalErr {
492                 kind: Rc::new(TypeckError),
493                 span,
494             });
495         }
496     };
497
498     let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
499     res.map(|(miri_value, _, miri_ty)| {
500         tcx.mk_const(ty::Const {
501             val: ConstVal::Value(miri_value),
502             ty: miri_ty,
503         })
504     }).map_err(|mut err| {
505         if tcx.is_static(def_id).is_some() {
506             ecx.report(&mut err, true, None);
507         }
508         let (trace, span) = ecx.generate_stacktrace(None);
509         let err = ErrKind::Miri(err, trace);
510         ConstEvalErr {
511             kind: err.into(),
512             span,
513         }
514     })
515 }