]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/const_eval.rs
Auto merge of #49891 - cuviper:compiletest-crash, r=alexcrichton
[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 rustc_data_structures::sync::Lrc;
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_promoted<'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, false, 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         assert!(!layout.is_unsized());
113         let ptr = ecx.memory.allocate(
114             layout.size.bytes(),
115             layout.align,
116             None,
117         )?;
118         let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
119         let mutability = tcx.is_static(cid.instance.def_id());
120         let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable {
121             Mutability::Mutable
122         } else {
123             Mutability::Immutable
124         };
125         let cleanup = StackPopCleanup::MarkStatic(mutability);
126         let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
127         let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
128         trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
129         assert!(mir.arg_count == 0);
130         ecx.push_stack_frame(
131             cid.instance,
132             mir.span,
133             mir,
134             Place::from_ptr(ptr, layout.align),
135             cleanup,
136         )?;
137
138         while ecx.step()? {}
139         let ptr = ptr.into();
140         // always try to read the value and report errors
141         let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
142             // if it's a constant (so it needs no address, directly compute its value)
143             Some(val) if tcx.is_static(cid.instance.def_id()).is_none() => val,
144             // point at the allocation
145             _ => Value::ByRef(ptr, layout.align),
146         };
147         Ok((value, ptr, layout.ty))
148     })();
149     (res, ecx)
150 }
151
152 pub struct CompileTimeEvaluator;
153
154 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
155     fn into(self) -> EvalError<'tcx> {
156         EvalErrorKind::MachineError(self.to_string()).into()
157     }
158 }
159
160 #[derive(Clone, Debug)]
161 enum ConstEvalError {
162     NeedsRfc(String),
163     NotConst(String),
164 }
165
166 impl fmt::Display for ConstEvalError {
167     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168         use self::ConstEvalError::*;
169         match *self {
170             NeedsRfc(ref msg) => {
171                 write!(
172                     f,
173                     "\"{}\" needs an rfc before being allowed inside constants",
174                     msg
175                 )
176             }
177             NotConst(ref msg) => write!(f, "{}", msg),
178         }
179     }
180 }
181
182 impl Error for ConstEvalError {
183     fn description(&self) -> &str {
184         use self::ConstEvalError::*;
185         match *self {
186             NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
187             NotConst(_) => "this feature is not compatible with constant evaluation",
188         }
189     }
190
191     fn cause(&self) -> Option<&dyn Error> {
192         None
193     }
194 }
195
196 impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
197     type MemoryData = ();
198     type MemoryKinds = !;
199     fn eval_fn_call<'a>(
200         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
201         instance: ty::Instance<'tcx>,
202         destination: Option<(Place, mir::BasicBlock)>,
203         args: &[ValTy<'tcx>],
204         span: Span,
205         sig: ty::FnSig<'tcx>,
206     ) -> EvalResult<'tcx, bool> {
207         debug!("eval_fn_call: {:?}", instance);
208         if !ecx.tcx.is_const_fn(instance.def_id()) {
209             let def_id = instance.def_id();
210             let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
211                 op
212             } else {
213                 return Err(
214                     ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
215                 );
216             };
217             let (dest, bb) = destination.expect("128 lowerings can't diverge");
218             let dest_ty = sig.output();
219             if oflo {
220                 ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
221             } else {
222                 ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
223             }
224             ecx.goto_block(bb);
225             return Ok(true);
226         }
227         let mir = match ecx.load_mir(instance.def) {
228             Ok(mir) => mir,
229             Err(err) => {
230                 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
231                     return Err(
232                         ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
233                             .into(),
234                     );
235                 }
236                 return Err(err);
237             }
238         };
239         let (return_place, return_to_block) = match destination {
240             Some((place, block)) => (place, StackPopCleanup::Goto(block)),
241             None => (Place::undef(), StackPopCleanup::None),
242         };
243
244         ecx.push_stack_frame(
245             instance,
246             span,
247             mir,
248             return_place,
249             return_to_block,
250         )?;
251
252         Ok(false)
253     }
254
255
256     fn call_intrinsic<'a>(
257         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
258         instance: ty::Instance<'tcx>,
259         _args: &[ValTy<'tcx>],
260         dest: Place,
261         dest_layout: layout::TyLayout<'tcx>,
262         target: mir::BasicBlock,
263     ) -> EvalResult<'tcx> {
264         let substs = instance.substs;
265
266         let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
267         match intrinsic_name {
268             "min_align_of" => {
269                 let elem_ty = substs.type_at(0);
270                 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
271                 let align_val = PrimVal::from_u128(elem_align as u128);
272                 ecx.write_primval(dest, align_val, dest_layout.ty)?;
273             }
274
275             "size_of" => {
276                 let ty = substs.type_at(0);
277                 let size = ecx.layout_of(ty)?.size.bytes() as u128;
278                 ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
279             }
280
281             "type_id" => {
282                 let ty = substs.type_at(0);
283                 let type_id = ecx.tcx.type_id_hash(ty) as u128;
284                 ecx.write_primval(dest, PrimVal::from_u128(type_id), dest_layout.ty)?;
285             }
286
287             name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
288         }
289
290         ecx.goto_block(target);
291
292         // Since we pushed no stack frame, the main loop will act
293         // as if the call just completed and it's returning to the
294         // current frame.
295         Ok(())
296     }
297
298     fn try_ptr_op<'a>(
299         _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
300         _bin_op: mir::BinOp,
301         left: PrimVal,
302         _left_ty: Ty<'tcx>,
303         right: PrimVal,
304         _right_ty: Ty<'tcx>,
305     ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
306         if left.is_bytes() && right.is_bytes() {
307             Ok(None)
308         } else {
309             Err(
310                 ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
311             )
312         }
313     }
314
315     fn mark_static_initialized<'a>(
316         _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
317         _id: AllocId,
318         _mutability: Mutability,
319     ) -> EvalResult<'tcx, bool> {
320         Ok(false)
321     }
322
323     fn init_static<'a>(
324         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
325         cid: GlobalId<'tcx>,
326     ) -> EvalResult<'tcx, AllocId> {
327         Ok(ecx
328             .tcx
329             .interpret_interner
330             .cache_static(cid.instance.def_id()))
331     }
332
333     fn box_alloc<'a>(
334         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
335         _ty: Ty<'tcx>,
336         _dest: Place,
337     ) -> EvalResult<'tcx> {
338         Err(
339             ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
340         )
341     }
342
343     fn global_item_with_linkage<'a>(
344         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
345         _instance: ty::Instance<'tcx>,
346         _mutability: Mutability,
347     ) -> EvalResult<'tcx> {
348         Err(
349             ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
350         )
351     }
352 }
353
354 pub fn const_val_field<'a, 'tcx>(
355     tcx: TyCtxt<'a, 'tcx, 'tcx>,
356     param_env: ty::ParamEnv<'tcx>,
357     instance: ty::Instance<'tcx>,
358     variant: Option<usize>,
359     field: mir::Field,
360     value: Value,
361     ty: Ty<'tcx>,
362 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
363     trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty);
364     let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
365     let result = (|| {
366         let (mut field, ty) = match value {
367             Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"),
368             Value::ByRef(ptr, align) => {
369                 let place = Place::Ptr {
370                     ptr,
371                     align,
372                     extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
373                 };
374                 let layout = ecx.layout_of(ty)?;
375                 let (place, layout) = ecx.place_field(place, field, layout)?;
376                 let (ptr, align) = place.to_ptr_align();
377                 (Value::ByRef(ptr, align), layout.ty)
378             }
379         };
380         if let Value::ByRef(ptr, align) = field {
381             if let Some(val) = ecx.try_read_value(ptr, align, ty)? {
382                 field = val;
383             }
384         }
385         Ok((field, ty))
386     })();
387     match result {
388         Ok((field, ty)) => Ok(tcx.mk_const(ty::Const {
389             val: ConstVal::Value(field),
390             ty,
391         })),
392         Err(err) => {
393             let (trace, span) = ecx.generate_stacktrace(None);
394             let err = ErrKind::Miri(err, trace);
395             Err(ConstEvalErr {
396                 kind: err.into(),
397                 span,
398             })
399         },
400     }
401 }
402
403 pub fn const_variant_index<'a, 'tcx>(
404     tcx: TyCtxt<'a, 'tcx, 'tcx>,
405     param_env: ty::ParamEnv<'tcx>,
406     instance: ty::Instance<'tcx>,
407     value: Value,
408     ty: Ty<'tcx>,
409 ) -> EvalResult<'tcx, usize> {
410     trace!("const_variant_index: {:?}, {:?}, {:?}", instance, value, ty);
411     let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
412     let (ptr, align) = match value {
413         Value::ByValPair(..) | Value::ByVal(_) => {
414             let layout = ecx.layout_of(ty)?;
415             use super::MemoryKind;
416             let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?;
417             let ptr: Pointer = ptr.into();
418             ecx.write_value_to_ptr(value, ptr, layout.align, ty)?;
419             (ptr, layout.align)
420         },
421         Value::ByRef(ptr, align) => (ptr, align),
422     };
423     let place = Place::from_primval_ptr(ptr, align);
424     ecx.read_discriminant_as_variant_index(place, ty)
425 }
426
427 pub fn const_eval_provider<'a, 'tcx>(
428     tcx: TyCtxt<'a, 'tcx, 'tcx>,
429     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
430 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
431     trace!("const eval: {:?}", key);
432     let cid = key.value;
433     let def_id = cid.instance.def.def_id();
434
435     if tcx.is_foreign_item(def_id) {
436         let id = tcx.interpret_interner.cache_static(def_id);
437         let ty = tcx.type_of(def_id);
438         let layout = tcx.layout_of(key.param_env.and(ty)).unwrap();
439         let ptr = MemoryPointer::new(id, 0);
440         return Ok(tcx.mk_const(ty::Const {
441             val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)),
442             ty,
443         }))
444     }
445
446     if let Some(id) = tcx.hir.as_local_node_id(def_id) {
447         let tables = tcx.typeck_tables_of(def_id);
448         let span = tcx.def_span(def_id);
449
450         // Do match-check before building MIR
451         if tcx.check_match(def_id).is_err() {
452             return Err(ConstEvalErr {
453                 kind: Lrc::new(CheckMatchError),
454                 span,
455             });
456         }
457
458         if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
459             tcx.mir_const_qualif(def_id);
460         }
461
462         // Do not continue into miri if typeck errors occurred; it will fail horribly
463         if tables.tainted_by_errors {
464             return Err(ConstEvalErr {
465                 kind: Lrc::new(TypeckError),
466                 span,
467             });
468         }
469     };
470
471     let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
472     res.map(|(miri_value, _, miri_ty)| {
473         tcx.mk_const(ty::Const {
474             val: ConstVal::Value(miri_value),
475             ty: miri_ty,
476         })
477     }).map_err(|mut err| {
478         if tcx.is_static(def_id).is_some() {
479             ecx.report(&mut err, true, None);
480         }
481         let (trace, span) = ecx.generate_stacktrace(None);
482         let err = ErrKind::Miri(err, trace);
483         ConstEvalErr {
484             kind: err.into(),
485             span,
486         }
487     })
488 }