]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/interpret/const_eval.rs
New upstream version 1.25.0+dfsg1
[rustc.git] / src / librustc_mir / interpret / const_eval.rs
CommitLineData
ff7c6d11
XL
1use rustc::ty::{self, TyCtxt, Ty, Instance};
2use rustc::ty::layout::{self, LayoutOf};
3use rustc::ty::subst::Substs;
4use rustc::hir::def_id::DefId;
5use rustc::mir;
6use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError};
7use rustc::middle::const_val::{ConstEvalErr, ConstVal};
8use rustc_const_eval::{lookup_const_by_id, ConstContext};
9use rustc::mir::Field;
10use rustc_data_structures::indexed_vec::Idx;
11
12use syntax::ast::Mutability;
13use syntax::codemap::Span;
14
2c00a5a8 15use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal};
ff7c6d11
XL
16use super::{Place, EvalContext, StackPopCleanup, ValTy};
17
18use rustc_const_math::ConstInt;
19
20use std::fmt;
21use std::error::Error;
22
23
24pub 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
44pub 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 )?;
2c00a5a8 70 tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id);
ff7c6d11
XL
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 }
2c00a5a8
XL
84 let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
85 Ok((MemoryPointer::new(alloc, 0).into(), instance_ty))
ff7c6d11
XL
86}
87
88pub 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),
2c00a5a8 109 TyInt(IntTy::Isize) => ConstInt::Isize(
ff7c6d11
XL
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),
2c00a5a8 118 TyUint(UintTy::Usize) => ConstInt::Usize(
ff7c6d11
XL
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
132pub struct CompileTimeEvaluator;
133
134impl<'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)]
141enum ConstEvalError {
142 NeedsRfc(String),
143 NotConst(String),
144}
145
146impl 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
162impl 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
176impl<'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
2c00a5a8
XL
246 "type_id" => {
247 let ty = substs.type_at(0);
248 let type_id = ecx.tcx.type_id_hash(ty) as u128;
249 ecx.write_primval(dest, PrimVal::from_u128(type_id), dest_layout.ty)?;
250 }
251
ff7c6d11
XL
252 name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
253 }
254
255 ecx.goto_block(target);
256
257 // Since we pushed no stack frame, the main loop will act
258 // as if the call just completed and it's returning to the
259 // current frame.
260 Ok(())
261 }
262
263 fn try_ptr_op<'a>(
264 _ecx: &EvalContext<'a, 'tcx, Self>,
265 _bin_op: mir::BinOp,
266 left: PrimVal,
267 _left_ty: Ty<'tcx>,
268 right: PrimVal,
269 _right_ty: Ty<'tcx>,
270 ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
271 if left.is_bytes() && right.is_bytes() {
272 Ok(None)
273 } else {
274 Err(
275 ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
276 )
277 }
278 }
279
280 fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
281 m
282 }
283
284 fn box_alloc<'a>(
285 _ecx: &mut EvalContext<'a, 'tcx, Self>,
286 _ty: Ty<'tcx>,
287 _dest: Place,
288 ) -> EvalResult<'tcx> {
289 Err(
290 ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
291 )
292 }
293
294 fn global_item_with_linkage<'a>(
295 _ecx: &mut EvalContext<'a, 'tcx, Self>,
296 _instance: ty::Instance<'tcx>,
297 _mutability: Mutability,
298 ) -> EvalResult<'tcx> {
299 Err(
300 ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
301 )
302 }
303}
304
305pub fn const_eval_provider<'a, 'tcx>(
306 tcx: TyCtxt<'a, 'tcx, 'tcx>,
307 key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>,
308) -> ::rustc::middle::const_val::EvalResult<'tcx> {
309 trace!("const eval: {:?}", key);
310 let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) {
311 resolved
312 } else {
313 return Err(ConstEvalErr {
314 span: tcx.def_span(key.value.0),
315 kind: TypeckError
316 });
317 };
318
319 let tables = tcx.typeck_tables_of(def_id);
320 let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
321 let body_id = tcx.hir.body_owned_by(id);
322
323 // Do match-check before building MIR
324 if tcx.check_match(def_id).is_err() {
325 return Err(ConstEvalErr {
326 span: tcx.def_span(key.value.0),
327 kind: CheckMatchError,
328 });
329 }
330
331 tcx.mir_const_qualif(def_id);
332 tcx.hir.body(body_id)
333 } else {
334 tcx.extern_const_body(def_id).body
335 };
336
337 // do not continue into miri if typeck errors occurred
338 // it will fail horribly
339 if tables.tainted_by_errors {
340 return Err(ConstEvalErr { span: body.value.span, kind: TypeckError })
341 }
342
343 trace!("running old const eval");
344 let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value);
345 trace!("old const eval produced {:?}", old_result);
346 if tcx.sess.opts.debugging_opts.miri {
347 let instance = ty::Instance::new(def_id, substs);
348 trace!("const eval instance: {:?}, {:?}", instance, key.param_env);
349 let miri_result = ::interpret::eval_body(tcx, instance, key.param_env);
350 match (miri_result, old_result) {
351 (Err(err), Ok(ok)) => {
352 trace!("miri failed, ctfe returned {:?}", ok);
353 tcx.sess.span_warn(
354 tcx.def_span(key.value.0),
355 "miri failed to eval, while ctfe succeeded",
356 );
357 let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
358 let () = unwrap_miri(&ecx, Err(err));
359 Ok(ok)
360 },
361 (_, Err(err)) => Err(err),
362 (Ok((miri_val, miri_ty)), Ok(ctfe)) => {
363 let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
364 let layout = ecx.layout_of(miri_ty).unwrap();
365 let miri_place = Place::from_primval_ptr(miri_val, layout.align);
366 check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val);
367 Ok(ctfe)
368 }
369 }
370 } else {
371 old_result
372 }
373}
374
375fn check_ctfe_against_miri<'a, 'tcx>(
376 ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
377 miri_place: Place,
378 miri_ty: Ty<'tcx>,
379 ctfe: ConstVal<'tcx>,
380) {
381 use rustc::middle::const_val::ConstAggregate::*;
382 use rustc_const_math::ConstFloat;
383 use rustc::ty::TypeVariants::*;
384 let miri_val = ValTy {
385 value: ecx.read_place(miri_place).unwrap(),
386 ty: miri_ty
387 };
388 match miri_ty.sty {
389 TyInt(int_ty) => {
390 let prim = get_prim(ecx, miri_val);
391 let c = ConstInt::new_signed_truncating(prim as i128,
392 int_ty,
393 ecx.tcx.sess.target.isize_ty);
394 let c = ConstVal::Integral(c);
395 assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
396 },
397 TyUint(uint_ty) => {
398 let prim = get_prim(ecx, miri_val);
399 let c = ConstInt::new_unsigned_truncating(prim,
400 uint_ty,
401 ecx.tcx.sess.target.usize_ty);
402 let c = ConstVal::Integral(c);
403 assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
404 },
405 TyFloat(ty) => {
406 let prim = get_prim(ecx, miri_val);
407 let f = ConstVal::Float(ConstFloat { bits: prim, ty });
408 assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe);
409 },
410 TyBool => {
411 let bits = get_prim(ecx, miri_val);
412 if bits > 1 {
413 bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe);
414 }
415 let b = ConstVal::Bool(bits == 1);
416 assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe);
417 },
418 TyChar => {
419 let bits = get_prim(ecx, miri_val);
420 if let Some(cm) = ::std::char::from_u32(bits as u32) {
421 assert_eq!(
422 ConstVal::Char(cm), ctfe,
423 "miri evaluated to {:?}, but expected {:?}", cm, ctfe,
424 );
425 } else {
426 bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe);
427 }
428 },
429 TyStr => {
430 let value = ecx.follow_by_ref_value(miri_val.value, miri_val.ty);
431 if let Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) = value {
432 let bytes = ecx
433 .memory
434 .read_bytes(ptr.into(), len as u64)
435 .expect("bad miri memory for str");
436 if let Ok(s) = ::std::str::from_utf8(bytes) {
437 if let ConstVal::Str(s2) = ctfe {
438 assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2);
439 } else {
440 bug!("miri produced {:?}, but expected {:?}", s, ctfe);
441 }
442 } else {
443 bug!(
444 "miri failed to produce valid utf8 {:?}, while ctfe produced {:?}",
445 bytes,
446 ctfe,
447 );
448 }
449 } else {
450 bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe);
451 }
452 },
453 TyArray(elem_ty, n) => {
454 let n = n.val.to_const_int().unwrap().to_u64().unwrap();
455 let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe {
456 ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| {
457 (ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8)
458 }).collect(),
459 ConstVal::Aggregate(Array(v)) => {
460 v.iter().map(|c| (c.val, c.ty)).collect()
461 },
462 ConstVal::Aggregate(Repeat(v, n)) => {
463 vec![(v.val, v.ty); n as usize]
464 },
465 _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
466 };
467 let layout = ecx.layout_of(miri_ty).unwrap();
468 for (i, elem) in vec.into_iter().enumerate() {
469 assert!((i as u64) < n);
470 let (field_place, _) =
471 ecx.place_field(miri_place, Field::new(i), layout).unwrap();
472 check_ctfe_against_miri(ecx, field_place, elem_ty, elem.0);
473 }
474 },
475 TyTuple(..) => {
476 let vec = match ctfe {
477 ConstVal::Aggregate(Tuple(v)) => v,
478 _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
479 };
480 let layout = ecx.layout_of(miri_ty).unwrap();
481 for (i, elem) in vec.into_iter().enumerate() {
482 let (field_place, _) =
483 ecx.place_field(miri_place, Field::new(i), layout).unwrap();
484 check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val);
485 }
486 },
487 TyAdt(def, _) => {
488 let mut miri_place = miri_place;
489 let struct_variant = if def.is_enum() {
490 let discr = ecx.read_discriminant_value(miri_place, miri_ty).unwrap();
491 let variant = def.discriminants(ecx.tcx).position(|variant_discr| {
492 variant_discr.to_u128_unchecked() == discr
493 }).expect("miri produced invalid enum discriminant");
494 miri_place = ecx.place_downcast(miri_place, variant).unwrap();
495 &def.variants[variant]
496 } else {
2c00a5a8 497 def.non_enum_variant()
ff7c6d11
XL
498 };
499 let vec = match ctfe {
500 ConstVal::Aggregate(Struct(v)) => v,
501 ConstVal::Variant(did) => {
502 assert_eq!(struct_variant.fields.len(), 0);
503 assert_eq!(did, struct_variant.did);
504 return;
505 },
506 ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
507 };
508 let layout = ecx.layout_of(miri_ty).unwrap();
509 for &(name, elem) in vec.into_iter() {
510 let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap();
511 let (field_place, _) =
512 ecx.place_field(miri_place, Field::new(field), layout).unwrap();
513 check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val);
514 }
515 },
516 TySlice(_) => bug!("miri produced a slice?"),
517 // not supported by ctfe
518 TyRawPtr(_) |
519 TyRef(..) => {}
520 TyDynamic(..) => bug!("miri produced a trait object"),
521 TyClosure(..) => bug!("miri produced a closure"),
522 TyGenerator(..) => bug!("miri produced a generator"),
2c00a5a8 523 TyGeneratorWitness(..) => bug!("miri produced a generator witness"),
ff7c6d11
XL
524 TyNever => bug!("miri produced a value of the never type"),
525 TyProjection(_) => bug!("miri produced a projection"),
526 TyAnon(..) => bug!("miri produced an impl Trait type"),
527 TyParam(_) => bug!("miri produced an unmonomorphized type"),
528 TyInfer(_) => bug!("miri produced an uninferred type"),
529 TyError => bug!("miri produced a type error"),
530 TyForeign(_) => bug!("miri produced an extern type"),
531 // should be fine
532 TyFnDef(..) => {}
533 TyFnPtr(_) => {
534 let value = ecx.value_to_primval(miri_val);
535 let ptr = match value {
536 Ok(PrimVal::Ptr(ptr)) => ptr,
537 value => bug!("expected fn ptr, got {:?}", value),
538 };
539 let inst = ecx.memory.get_fn(ptr).unwrap();
540 match ctfe {
541 ConstVal::Function(did, substs) => {
542 let ctfe = ty::Instance::resolve(
543 ecx.tcx,
544 ecx.param_env,
545 did,
546 substs,
547 ).unwrap();
548 assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst);
549 },
550 _ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst),
551 }
552 },
553 }
554}
555
556fn get_prim<'a, 'tcx>(
557 ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
558 val: ValTy<'tcx>,
559) -> u128 {
560 let res = ecx.value_to_primval(val).and_then(|prim| prim.to_bytes());
561 unwrap_miri(ecx, res)
562}
563
564fn unwrap_miri<'a, 'tcx, T>(
565 ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>,
566 res: Result<T, EvalError<'tcx>>,
567) -> T {
568 match res {
569 Ok(val) => val,
570 Err(mut err) => {
571 ecx.report(&mut err);
572 ecx.tcx.sess.abort_if_errors();
573 bug!("{:#?}", err);
574 }
575 }
576}