]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/value.rs
Move some value-and-memory related things out of eval_context
[rust.git] / src / librustc_mir / interpret / value.rs
1 //! Reading and writing values from/to memory, handling LocalValue and the ByRef optimization,
2 //! reading/writing discriminants
3
4 use std::mem;
5
6 use rustc::mir;
7 use rustc::ty::layout::{self, Size, Align, IntegerExt, LayoutOf, TyLayout, Primitive};
8 use rustc::ty::{self, Ty, TyCtxt, TypeAndMut};
9 use rustc_data_structures::indexed_vec::{IndexVec, Idx};
10 use rustc::mir::interpret::{
11     GlobalId, Value, Scalar, FrameInfo, AllocType,
12     EvalResult, EvalErrorKind, Pointer, ConstValue,
13     ScalarMaybeUndef,
14 };
15
16 use super::{Place, PlaceExtra, Memory, Frame,
17             HasMemory, MemoryKind,
18             Machine, ValTy, EvalContext};
19
20 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
21 pub enum LocalValue {
22     Dead,
23     Live(Value),
24 }
25
26 impl LocalValue {
27     pub fn access(self) -> EvalResult<'static, Value> {
28         match self {
29             LocalValue::Dead => err!(DeadLocal),
30             LocalValue::Live(val) => Ok(val),
31         }
32     }
33 }
34
35 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
36     pub fn write_ptr(&mut self, dest: Place, val: Scalar, dest_ty: Ty<'tcx>) -> EvalResult<'tcx> {
37         let valty = ValTy {
38             value: val.to_value(),
39             ty: dest_ty,
40         };
41         self.write_value(valty, dest)
42     }
43
44     pub fn write_scalar(
45         &mut self,
46         dest: Place,
47         val: impl Into<ScalarMaybeUndef>,
48         dest_ty: Ty<'tcx>,
49     ) -> EvalResult<'tcx> {
50         let valty = ValTy {
51             value: Value::Scalar(val.into()),
52             ty: dest_ty,
53         };
54         self.write_value(valty, dest)
55     }
56
57     pub fn write_value(
58         &mut self,
59         ValTy { value: src_val, ty: dest_ty } : ValTy<'tcx>,
60         dest: Place,
61     ) -> EvalResult<'tcx> {
62         //trace!("Writing {:?} to {:?} at type {:?}", src_val, dest, dest_ty);
63         // Note that it is really important that the type here is the right one, and matches the type things are read at.
64         // In case `src_val` is a `ScalarPair`, we don't do any magic here to handle padding properly, which is only
65         // correct if we never look at this data with the wrong type.
66
67         match dest {
68             Place::Ptr { ptr, align, extra } => {
69                 assert_eq!(extra, PlaceExtra::None);
70                 self.write_value_to_ptr(src_val, ptr.unwrap_or_err()?, align, dest_ty)
71             }
72
73             Place::Local { frame, local } => {
74                 let old_val = self.stack[frame].locals[local].access()?;
75                 self.write_value_possibly_by_val(
76                     src_val,
77                     |this, val| this.stack[frame].set_local(local, val),
78                     old_val,
79                     dest_ty,
80                 )
81             }
82         }
83     }
84
85     // The cases here can be a bit subtle. Read carefully!
86     fn write_value_possibly_by_val<F: FnOnce(&mut Self, Value) -> EvalResult<'tcx>>(
87         &mut self,
88         src_val: Value,
89         write_dest: F,
90         old_dest_val: Value,
91         dest_ty: Ty<'tcx>,
92     ) -> EvalResult<'tcx> {
93         // FIXME: this should be a layout check, not underlying value
94         if let Value::ByRef(dest_ptr, align) = old_dest_val {
95             // If the value is already `ByRef` (that is, backed by an `Allocation`),
96             // then we must write the new value into this allocation, because there may be
97             // other pointers into the allocation. These other pointers are logically
98             // pointers into the local variable, and must be able to observe the change.
99             //
100             // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we
101             // knew for certain that there were no outstanding pointers to this allocation.
102             self.write_value_to_ptr(src_val, dest_ptr, align, dest_ty)?;
103         } else if let Value::ByRef(src_ptr, align) = src_val {
104             // If the value is not `ByRef`, then we know there are no pointers to it
105             // and we can simply overwrite the `Value` in the locals array directly.
106             //
107             // In this specific case, where the source value is `ByRef`, we must duplicate
108             // the allocation, because this is a by-value operation. It would be incorrect
109             // if they referred to the same allocation, since then a change to one would
110             // implicitly change the other.
111             //
112             // It is a valid optimization to attempt reading a primitive value out of the
113             // source and write that into the destination without making an allocation, so
114             // we do so here.
115             if let Ok(Some(src_val)) = self.try_read_value(src_ptr, align, dest_ty) {
116                 write_dest(self, src_val)?;
117             } else {
118                 let layout = self.layout_of(dest_ty)?;
119                 let dest_ptr = self.alloc_ptr(layout)?.into();
120                 self.memory.copy(src_ptr, align.min(layout.align), dest_ptr, layout.align, layout.size, false)?;
121                 write_dest(self, Value::ByRef(dest_ptr, layout.align))?;
122             }
123         } else {
124             // Finally, we have the simple case where neither source nor destination are
125             // `ByRef`. We may simply copy the source value over the the destintion.
126             write_dest(self, src_val)?;
127         }
128         Ok(())
129     }
130
131     pub fn write_value_to_ptr(
132         &mut self,
133         value: Value,
134         dest: Scalar,
135         dest_align: Align,
136         dest_ty: Ty<'tcx>,
137     ) -> EvalResult<'tcx> {
138         let layout = self.layout_of(dest_ty)?;
139         trace!("write_value_to_ptr: {:#?}, {}, {:#?}", value, dest_ty, layout);
140         match value {
141             Value::ByRef(ptr, align) => {
142                 self.memory.copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size, false)
143             }
144             Value::Scalar(scalar) => {
145                 let signed = match layout.abi {
146                     layout::Abi::Scalar(ref scal) => match scal.value {
147                         layout::Primitive::Int(_, signed) => signed,
148                         _ => false,
149                     },
150                     _ => false,
151                 };
152                 self.memory.write_scalar(dest, dest_align, scalar, layout.size, layout.align, signed)
153             }
154             Value::ScalarPair(a_val, b_val) => {
155                 trace!("write_value_to_ptr valpair: {:#?}", layout);
156                 let (a, b) = match layout.abi {
157                     layout::Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value),
158                     _ => bug!("write_value_to_ptr: invalid ScalarPair layout: {:#?}", layout)
159                 };
160                 let (a_size, b_size) = (a.size(&self), b.size(&self));
161                 let (a_align, b_align) = (a.align(&self), b.align(&self));
162                 let a_ptr = dest;
163                 let b_offset = a_size.abi_align(b_align);
164                 let b_ptr = dest.ptr_offset(b_offset, &self)?.into();
165                 // TODO: What about signedess?
166                 self.memory.write_scalar(a_ptr, dest_align, a_val, a_size, a_align, false)?;
167                 self.memory.write_scalar(b_ptr, dest_align, b_val, b_size, b_align, false)
168             }
169         }
170     }
171
172     pub fn try_read_value(&self, ptr: Scalar, ptr_align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
173         let layout = self.layout_of(ty)?;
174         self.memory.check_align(ptr, ptr_align)?;
175
176         if layout.size.bytes() == 0 {
177             return Ok(Some(Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { bits: 0, size: 0 }))));
178         }
179
180         let ptr = ptr.to_ptr()?;
181
182         match layout.abi {
183             layout::Abi::Scalar(..) => {
184                 let scalar = self.memory.read_scalar(ptr, ptr_align, layout.size)?;
185                 Ok(Some(Value::Scalar(scalar)))
186             }
187             layout::Abi::ScalarPair(ref a, ref b) => {
188                 let (a, b) = (&a.value, &b.value);
189                 let (a_size, b_size) = (a.size(self), b.size(self));
190                 let a_ptr = ptr;
191                 let b_offset = a_size.abi_align(b.align(self));
192                 let b_ptr = ptr.offset(b_offset, self)?.into();
193                 let a_val = self.memory.read_scalar(a_ptr, ptr_align, a_size)?;
194                 let b_val = self.memory.read_scalar(b_ptr, ptr_align, b_size)?;
195                 Ok(Some(Value::ScalarPair(a_val, b_val)))
196             }
197             _ => Ok(None),
198         }
199     }
200
201     pub fn read_value(&self, ptr: Scalar, align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
202         if let Some(val) = self.try_read_value(ptr, align, ty)? {
203             Ok(val)
204         } else {
205             bug!("primitive read failed for type: {:?}", ty);
206         }
207     }
208
209     pub(super) fn eval_operand_to_scalar(
210         &mut self,
211         op: &mir::Operand<'tcx>,
212     ) -> EvalResult<'tcx, Scalar> {
213         let valty = self.eval_operand(op)?;
214         self.value_to_scalar(valty)
215     }
216
217     pub(crate) fn operands_to_args(
218         &mut self,
219         ops: &[mir::Operand<'tcx>],
220     ) -> EvalResult<'tcx, Vec<ValTy<'tcx>>> {
221         ops.into_iter()
222             .map(|op| self.eval_operand(op))
223             .collect()
224     }
225
226     pub fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, ValTy<'tcx>> {
227         use rustc::mir::Operand::*;
228         let ty = self.monomorphize(op.ty(self.mir(), *self.tcx), self.substs());
229         match *op {
230             // FIXME: do some more logic on `move` to invalidate the old location
231             Copy(ref place) |
232             Move(ref place) => {
233                 Ok(ValTy {
234                     value: self.eval_and_read_place(place)?,
235                     ty
236                 })
237             },
238
239             Constant(ref constant) => {
240                 let value = self.const_to_value(constant.literal.val)?;
241
242                 Ok(ValTy {
243                     value,
244                     ty,
245                 })
246             }
247         }
248     }
249
250     pub fn deallocate_local(&mut self, local: LocalValue) -> EvalResult<'tcx> {
251         // FIXME: should we tell the user that there was a local which was never written to?
252         if let LocalValue::Live(Value::ByRef(ptr, _align)) = local {
253             trace!("deallocating local");
254             let ptr = ptr.to_ptr()?;
255             self.memory.dump_alloc(ptr.alloc_id);
256             self.memory.deallocate_local(ptr)?;
257         };
258         Ok(())
259     }
260
261     pub fn allocate_place_for_value(
262         &mut self,
263         value: Value,
264         layout: TyLayout<'tcx>,
265         variant: Option<usize>,
266     ) -> EvalResult<'tcx, Place> {
267         let (ptr, align) = match value {
268             Value::ByRef(ptr, align) => (ptr, align),
269             Value::ScalarPair(..) | Value::Scalar(_) => {
270                 let ptr = self.alloc_ptr(layout)?.into();
271                 self.write_value_to_ptr(value, ptr, layout.align, layout.ty)?;
272                 (ptr, layout.align)
273             },
274         };
275         Ok(Place::Ptr {
276             ptr: ptr.into(),
277             align,
278             extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
279         })
280     }
281
282     pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> {
283         let new_place = match place {
284             Place::Local { frame, local } => {
285                 match self.stack[frame].locals[local].access()? {
286                     Value::ByRef(ptr, align) => {
287                         Place::Ptr {
288                             ptr: ptr.into(),
289                             align,
290                             extra: PlaceExtra::None,
291                         }
292                     }
293                     val => {
294                         let ty = self.stack[frame].mir.local_decls[local].ty;
295                         let ty = self.monomorphize(ty, self.stack[frame].instance.substs);
296                         let layout = self.layout_of(ty)?;
297                         let ptr = self.alloc_ptr(layout)?;
298                         self.stack[frame].locals[local] =
299                             LocalValue::Live(Value::ByRef(ptr.into(), layout.align)); // it stays live
300
301                         let place = Place::from_ptr(ptr, layout.align);
302                         self.write_value(ValTy { value: val, ty }, place)?;
303                         place
304                     }
305                 }
306             }
307             Place::Ptr { .. } => place,
308         };
309         Ok(new_place)
310     }
311
312     /// Convert to ByVal or ScalarPair *if possible*, leave `ByRef` otherwise
313     pub fn try_read_by_ref(&self, mut val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
314         if let Value::ByRef(ptr, align) = val {
315             if let Some(read_val) = self.try_read_value(ptr, align, ty)? {
316                 val = read_val;
317             }
318         }
319         Ok(val)
320     }
321
322     pub fn value_to_scalar(
323         &self,
324         ValTy { value, ty } : ValTy<'tcx>,
325     ) -> EvalResult<'tcx, Scalar> {
326         let value = match value {
327             Value::ByRef(ptr, align) => self.read_value(ptr, align, ty)?,
328             scalar_or_pair => scalar_or_pair,
329         };
330         match value {
331             Value::ByRef(..) => bug!("read_value can't result in `ByRef`"),
332
333             Value::Scalar(scalar) => scalar.unwrap_or_err(),
334
335             Value::ScalarPair(..) => bug!("value_to_scalar can't work with fat pointers"),
336         }
337     }
338
339     pub fn storage_live(&mut self, local: mir::Local) -> EvalResult<'tcx, LocalValue> {
340         trace!("{:?} is now live", local);
341
342         let ty = self.frame().mir.local_decls[local].ty;
343         let init = self.init_value(ty)?;
344         // StorageLive *always* kills the value that's currently stored
345         Ok(mem::replace(&mut self.frame_mut().locals[local], LocalValue::Live(init)))
346     }
347
348     pub(super) fn init_value(&mut self, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
349         let ty = self.monomorphize(ty, self.substs());
350         let layout = self.layout_of(ty)?;
351         Ok(match layout.abi {
352             layout::Abi::Scalar(..) => Value::Scalar(ScalarMaybeUndef::Undef),
353             layout::Abi::ScalarPair(..) => Value::ScalarPair(
354                 ScalarMaybeUndef::Undef,
355                 ScalarMaybeUndef::Undef,
356             ),
357             _ => Value::ByRef(self.alloc_ptr(layout)?.into(), layout.align),
358         })
359     }
360
361     /// reads a tag and produces the corresponding variant index
362     pub fn read_discriminant_as_variant_index(
363         &self,
364         place: Place,
365         layout: TyLayout<'tcx>,
366     ) -> EvalResult<'tcx, usize> {
367         match layout.variants {
368             ty::layout::Variants::Single { index } => Ok(index),
369             ty::layout::Variants::Tagged { .. } => {
370                 let discr_val = self.read_discriminant_value(place, layout)?;
371                 layout
372                     .ty
373                     .ty_adt_def()
374                     .expect("tagged layout for non adt")
375                     .discriminants(self.tcx.tcx)
376                     .position(|var| var.val == discr_val)
377                     .ok_or_else(|| EvalErrorKind::InvalidDiscriminant.into())
378             }
379             ty::layout::Variants::NicheFilling { .. } => {
380                 let discr_val = self.read_discriminant_value(place, layout)?;
381                 assert_eq!(discr_val as usize as u128, discr_val);
382                 Ok(discr_val as usize)
383             },
384         }
385     }
386
387     pub fn read_discriminant_value(
388         &self,
389         place: Place,
390         layout: TyLayout<'tcx>,
391     ) -> EvalResult<'tcx, u128> {
392         trace!("read_discriminant_value {:#?}", layout);
393         if layout.abi == layout::Abi::Uninhabited {
394             return Ok(0);
395         }
396
397         match layout.variants {
398             layout::Variants::Single { index } => {
399                 let discr_val = layout.ty.ty_adt_def().map_or(
400                     index as u128,
401                     |def| def.discriminant_for_variant(*self.tcx, index).val);
402                 return Ok(discr_val);
403             }
404             layout::Variants::Tagged { .. } |
405             layout::Variants::NicheFilling { .. } => {},
406         }
407         let discr_place_val = self.read_place(place)?;
408         let (discr_val, discr) = self.read_field(discr_place_val, None, mir::Field::new(0), layout)?;
409         trace!("discr value: {:?}, {:?}", discr_val, discr);
410         let raw_discr = self.value_to_scalar(ValTy {
411             value: discr_val,
412             ty: discr.ty
413         })?;
414         let discr_val = match layout.variants {
415             layout::Variants::Single { .. } => bug!(),
416             // FIXME: should we catch invalid discriminants here?
417             layout::Variants::Tagged { .. } => {
418                 if discr.ty.is_signed() {
419                     let i = raw_discr.to_bits(discr.size)? as i128;
420                     // going from layout tag type to typeck discriminant type
421                     // requires first sign extending with the layout discriminant
422                     let shift = 128 - discr.size.bits();
423                     let sexted = (i << shift) >> shift;
424                     // and then zeroing with the typeck discriminant type
425                     let discr_ty = layout
426                         .ty
427                         .ty_adt_def().expect("tagged layout corresponds to adt")
428                         .repr
429                         .discr_type();
430                     let discr_ty = layout::Integer::from_attr(self.tcx.tcx, discr_ty);
431                     let shift = 128 - discr_ty.size().bits();
432                     let truncatee = sexted as u128;
433                     (truncatee << shift) >> shift
434                 } else {
435                     raw_discr.to_bits(discr.size)?
436                 }
437             },
438             layout::Variants::NicheFilling {
439                 dataful_variant,
440                 ref niche_variants,
441                 niche_start,
442                 ..
443             } => {
444                 let variants_start = *niche_variants.start() as u128;
445                 let variants_end = *niche_variants.end() as u128;
446                 match raw_discr {
447                     Scalar::Ptr(_) => {
448                         assert!(niche_start == 0);
449                         assert!(variants_start == variants_end);
450                         dataful_variant as u128
451                     },
452                     Scalar::Bits { bits: raw_discr, size } => {
453                         assert_eq!(size as u64, discr.size.bytes());
454                         let discr = raw_discr.wrapping_sub(niche_start)
455                             .wrapping_add(variants_start);
456                         if variants_start <= discr && discr <= variants_end {
457                             discr
458                         } else {
459                             dataful_variant as u128
460                         }
461                     },
462                 }
463             }
464         };
465
466         Ok(discr_val)
467     }
468
469
470     pub fn write_discriminant_value(
471         &mut self,
472         dest_ty: Ty<'tcx>,
473         dest: Place,
474         variant_index: usize,
475     ) -> EvalResult<'tcx> {
476         let layout = self.layout_of(dest_ty)?;
477
478         match layout.variants {
479             layout::Variants::Single { index } => {
480                 if index != variant_index {
481                     // If the layout of an enum is `Single`, all
482                     // other variants are necessarily uninhabited.
483                     assert_eq!(layout.for_variant(&self, variant_index).abi,
484                                layout::Abi::Uninhabited);
485                 }
486             }
487             layout::Variants::Tagged { ref tag, .. } => {
488                 let discr_val = dest_ty.ty_adt_def().unwrap()
489                     .discriminant_for_variant(*self.tcx, variant_index)
490                     .val;
491
492                 // raw discriminants for enums are isize or bigger during
493                 // their computation, but the in-memory tag is the smallest possible
494                 // representation
495                 let size = tag.value.size(self.tcx.tcx);
496                 let shift = 128 - size.bits();
497                 let discr_val = (discr_val << shift) >> shift;
498
499                 let (discr_dest, tag) = self.place_field(dest, mir::Field::new(0), layout)?;
500                 self.write_scalar(discr_dest, Scalar::Bits {
501                     bits: discr_val,
502                     size: size.bytes() as u8,
503                 }, tag.ty)?;
504             }
505             layout::Variants::NicheFilling {
506                 dataful_variant,
507                 ref niche_variants,
508                 niche_start,
509                 ..
510             } => {
511                 if variant_index != dataful_variant {
512                     let (niche_dest, niche) =
513                         self.place_field(dest, mir::Field::new(0), layout)?;
514                     let niche_value = ((variant_index - niche_variants.start()) as u128)
515                         .wrapping_add(niche_start);
516                     self.write_scalar(niche_dest, Scalar::Bits {
517                         bits: niche_value,
518                         size: niche.size.bytes() as u8,
519                     }, niche.ty)?;
520                 }
521             }
522         }
523
524         Ok(())
525     }
526
527     pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> {
528         let ptr = self.memory.allocate_bytes(s.as_bytes());
529         Ok(Scalar::Ptr(ptr).to_value_with_len(s.len() as u64, self.tcx.tcx))
530     }
531
532     pub fn const_to_value(
533         &mut self,
534         val: ConstValue<'tcx>,
535     ) -> EvalResult<'tcx, Value> {
536         match val {
537             ConstValue::Unevaluated(def_id, substs) => {
538                 let instance = self.resolve(def_id, substs)?;
539                 self.read_global_as_value(GlobalId {
540                     instance,
541                     promoted: None,
542                 })
543             }
544             ConstValue::ByRef(alloc, offset) => {
545                 // FIXME: Allocate new AllocId for all constants inside
546                 let id = self.memory.allocate_value(alloc.clone(), MemoryKind::Stack)?;
547                 Ok(Value::ByRef(Pointer::new(id, offset).into(), alloc.align))
548             },
549             ConstValue::ScalarPair(a, b) => Ok(Value::ScalarPair(a.into(), b.into())),
550             ConstValue::Scalar(val) => Ok(Value::Scalar(val.into())),
551         }
552     }
553 }
554
555 impl<'mir, 'tcx> Frame<'mir, 'tcx> {
556     pub(super) fn set_local(&mut self, local: mir::Local, value: Value) -> EvalResult<'tcx> {
557         match self.locals[local] {
558             LocalValue::Dead => err!(DeadLocal),
559             LocalValue::Live(ref mut local) => {
560                 *local = value;
561                 Ok(())
562             }
563         }
564     }
565
566     /// Returns the old value of the local
567     pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue {
568         trace!("{:?} is now dead", local);
569
570         mem::replace(&mut self.locals[local], LocalValue::Dead)
571     }
572 }