]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir/src/interpret/operand.rs
Rollup merge of #86803 - xfix:remove-unnecessary-ampersand-from-command-args-calls...
[rust.git] / compiler / rustc_mir / src / interpret / operand.rs
1 //! Functions concerning immediate values and operands, and reading from operands.
2 //! All high-level functions to read from memory work on operands as sources.
3
4 use std::convert::TryFrom;
5 use std::fmt::Write;
6
7 use rustc_errors::ErrorReported;
8 use rustc_hir::def::Namespace;
9 use rustc_macros::HashStable;
10 use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout};
11 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer};
12 use rustc_middle::ty::{ConstInt, Ty};
13 use rustc_middle::{mir, ty};
14 use rustc_target::abi::{Abi, HasDataLayout, LayoutOf, Size, TagEncoding};
15 use rustc_target::abi::{VariantIdx, Variants};
16
17 use super::{
18     alloc_range, from_known_layout, mir_assign_valid_types, ConstValue, GlobalId, InterpCx,
19     InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit,
20 };
21
22 /// An `Immediate` represents a single immediate self-contained Rust value.
23 ///
24 /// For optimization of a few very common cases, there is also a representation for a pair of
25 /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
26 /// operations and wide pointers. This idea was taken from rustc's codegen.
27 /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
28 /// defined on `Immediate`, and do not have to work with a `Place`.
29 #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
30 pub enum Immediate<Tag = ()> {
31     Scalar(ScalarMaybeUninit<Tag>),
32     ScalarPair(ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>),
33 }
34
35 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
36 rustc_data_structures::static_assert_size!(Immediate, 56);
37
38 impl<Tag> From<ScalarMaybeUninit<Tag>> for Immediate<Tag> {
39     #[inline(always)]
40     fn from(val: ScalarMaybeUninit<Tag>) -> Self {
41         Immediate::Scalar(val)
42     }
43 }
44
45 impl<Tag> From<Scalar<Tag>> for Immediate<Tag> {
46     #[inline(always)]
47     fn from(val: Scalar<Tag>) -> Self {
48         Immediate::Scalar(val.into())
49     }
50 }
51
52 impl<Tag> From<Pointer<Tag>> for Immediate<Tag> {
53     #[inline(always)]
54     fn from(val: Pointer<Tag>) -> Self {
55         Immediate::Scalar(Scalar::from(val).into())
56     }
57 }
58
59 impl<'tcx, Tag> Immediate<Tag> {
60     pub fn new_slice(val: Scalar<Tag>, len: u64, cx: &impl HasDataLayout) -> Self {
61         Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into())
62     }
63
64     pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>) -> Self {
65         Immediate::ScalarPair(val.into(), vtable.into())
66     }
67
68     #[inline]
69     pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> {
70         match self {
71             Immediate::Scalar(val) => val,
72             Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"),
73         }
74     }
75
76     #[inline]
77     pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> {
78         self.to_scalar_or_uninit().check_init()
79     }
80 }
81
82 // ScalarPair needs a type to interpret, so we often have an immediate and a type together
83 // as input for binary and cast operations.
84 #[derive(Copy, Clone, Debug)]
85 pub struct ImmTy<'tcx, Tag = ()> {
86     imm: Immediate<Tag>,
87     pub layout: TyAndLayout<'tcx>,
88 }
89
90 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
91 rustc_data_structures::static_assert_size!(ImmTy<'_>, 72);
92
93 impl<Tag: Copy> std::fmt::Display for ImmTy<'tcx, Tag> {
94     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95         /// Helper function for printing a scalar to a FmtPrinter
96         fn p<'a, 'tcx, F: std::fmt::Write, Tag>(
97             cx: FmtPrinter<'a, 'tcx, F>,
98             s: ScalarMaybeUninit<Tag>,
99             ty: Ty<'tcx>,
100         ) -> Result<FmtPrinter<'a, 'tcx, F>, std::fmt::Error> {
101             match s {
102                 ScalarMaybeUninit::Scalar(s) => {
103                     cx.pretty_print_const_scalar(s.erase_tag(), ty, true)
104                 }
105                 ScalarMaybeUninit::Uninit => cx.typed_value(
106                     |mut this| {
107                         this.write_str("uninit ")?;
108                         Ok(this)
109                     },
110                     |this| this.print_type(ty),
111                     " ",
112                 ),
113             }
114         }
115         ty::tls::with(|tcx| {
116             match self.imm {
117                 Immediate::Scalar(s) => {
118                     if let Some(ty) = tcx.lift(self.layout.ty) {
119                         let cx = FmtPrinter::new(tcx, f, Namespace::ValueNS);
120                         p(cx, s, ty)?;
121                         return Ok(());
122                     }
123                     write!(f, "{}: {}", s.erase_tag(), self.layout.ty)
124                 }
125                 Immediate::ScalarPair(a, b) => {
126                     // FIXME(oli-obk): at least print tuples and slices nicely
127                     write!(f, "({}, {}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,)
128                 }
129             }
130         })
131     }
132 }
133
134 impl<'tcx, Tag> std::ops::Deref for ImmTy<'tcx, Tag> {
135     type Target = Immediate<Tag>;
136     #[inline(always)]
137     fn deref(&self) -> &Immediate<Tag> {
138         &self.imm
139     }
140 }
141
142 /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate,
143 /// or still in memory. The latter is an optimization, to delay reading that chunk of
144 /// memory and to avoid having to store arbitrary-sized data here.
145 #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
146 pub enum Operand<Tag = ()> {
147     Immediate(Immediate<Tag>),
148     Indirect(MemPlace<Tag>),
149 }
150
151 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
152 pub struct OpTy<'tcx, Tag = ()> {
153     op: Operand<Tag>, // Keep this private; it helps enforce invariants.
154     pub layout: TyAndLayout<'tcx>,
155 }
156
157 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
158 rustc_data_structures::static_assert_size!(OpTy<'_, ()>, 80);
159
160 impl<'tcx, Tag> std::ops::Deref for OpTy<'tcx, Tag> {
161     type Target = Operand<Tag>;
162     #[inline(always)]
163     fn deref(&self) -> &Operand<Tag> {
164         &self.op
165     }
166 }
167
168 impl<'tcx, Tag: Copy> From<MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> {
169     #[inline(always)]
170     fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self {
171         OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout }
172     }
173 }
174
175 impl<'tcx, Tag: Copy> From<&'_ MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> {
176     #[inline(always)]
177     fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self {
178         OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout }
179     }
180 }
181
182 impl<'tcx, Tag> From<ImmTy<'tcx, Tag>> for OpTy<'tcx, Tag> {
183     #[inline(always)]
184     fn from(val: ImmTy<'tcx, Tag>) -> Self {
185         OpTy { op: Operand::Immediate(val.imm), layout: val.layout }
186     }
187 }
188
189 impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> {
190     #[inline]
191     pub fn from_scalar(val: Scalar<Tag>, layout: TyAndLayout<'tcx>) -> Self {
192         ImmTy { imm: val.into(), layout }
193     }
194
195     #[inline]
196     pub fn from_immediate(imm: Immediate<Tag>, layout: TyAndLayout<'tcx>) -> Self {
197         ImmTy { imm, layout }
198     }
199
200     #[inline]
201     pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
202         Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout))
203     }
204     #[inline]
205     pub fn from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Self {
206         Self::from_scalar(Scalar::from_uint(i, layout.size), layout)
207     }
208
209     #[inline]
210     pub fn try_from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
211         Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout))
212     }
213
214     #[inline]
215     pub fn from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Self {
216         Self::from_scalar(Scalar::from_int(i, layout.size), layout)
217     }
218
219     #[inline]
220     pub fn to_const_int(self) -> ConstInt {
221         assert!(self.layout.ty.is_integral());
222         let int = self.to_scalar().expect("to_const_int doesn't work on scalar pairs").assert_int();
223         ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral())
224     }
225 }
226
227 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
228     /// Normalize `place.ptr` to a `Pointer` if this is a place and not a ZST.
229     /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
230     #[inline]
231     pub fn force_op_ptr(
232         &self,
233         op: &OpTy<'tcx, M::PointerTag>,
234     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
235         match op.try_as_mplace(self) {
236             Ok(mplace) => Ok(self.force_mplace_ptr(mplace)?.into()),
237             Err(imm) => Ok(imm.into()), // Nothing to cast/force
238         }
239     }
240
241     /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
242     /// Returns `None` if the layout does not permit loading this as a value.
243     fn try_read_immediate_from_mplace(
244         &self,
245         mplace: &MPlaceTy<'tcx, M::PointerTag>,
246     ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> {
247         if mplace.layout.is_unsized() {
248             // Don't touch unsized
249             return Ok(None);
250         }
251
252         let alloc = match self.get_alloc(mplace)? {
253             Some(ptr) => ptr,
254             None => {
255                 return Ok(Some(ImmTy {
256                     // zero-sized type
257                     imm: Scalar::ZST.into(),
258                     layout: mplace.layout,
259                 }));
260             }
261         };
262
263         match mplace.layout.abi {
264             Abi::Scalar(..) => {
265                 let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
266                 Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }))
267             }
268             Abi::ScalarPair(ref a, ref b) => {
269                 // We checked `ptr_align` above, so all fields will have the alignment they need.
270                 // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
271                 // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
272                 let (a, b) = (&a.value, &b.value);
273                 let (a_size, b_size) = (a.size(self), b.size(self));
274                 let b_offset = a_size.align_to(b.align(self).abi);
275                 assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
276                 let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
277                 let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
278                 Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout }))
279             }
280             _ => Ok(None),
281         }
282     }
283
284     /// Try returning an immediate for the operand.
285     /// If the layout does not permit loading this as an immediate, return where in memory
286     /// we can find the data.
287     /// Note that for a given layout, this operation will either always fail or always
288     /// succeed!  Whether it succeeds depends on whether the layout can be represented
289     /// in a `Immediate`, not on which data is stored there currently.
290     pub(crate) fn try_read_immediate(
291         &self,
292         src: &OpTy<'tcx, M::PointerTag>,
293     ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> {
294         Ok(match src.try_as_mplace(self) {
295             Ok(ref mplace) => {
296                 if let Some(val) = self.try_read_immediate_from_mplace(mplace)? {
297                     Ok(val)
298                 } else {
299                     Err(*mplace)
300                 }
301             }
302             Err(val) => Ok(val),
303         })
304     }
305
306     /// Read an immediate from a place, asserting that that is possible with the given layout.
307     #[inline(always)]
308     pub fn read_immediate(
309         &self,
310         op: &OpTy<'tcx, M::PointerTag>,
311     ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
312         if let Ok(imm) = self.try_read_immediate(op)? {
313             Ok(imm)
314         } else {
315             span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty);
316         }
317     }
318
319     /// Read a scalar from a place
320     pub fn read_scalar(
321         &self,
322         op: &OpTy<'tcx, M::PointerTag>,
323     ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> {
324         Ok(self.read_immediate(op)?.to_scalar_or_uninit())
325     }
326
327     // Turn the wide MPlace into a string (must already be dereferenced!)
328     pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> {
329         let len = mplace.len(self)?;
330         let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?;
331         let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
332         Ok(str)
333     }
334
335     /// Projection functions
336     pub fn operand_field(
337         &self,
338         op: &OpTy<'tcx, M::PointerTag>,
339         field: usize,
340     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
341         let base = match op.try_as_mplace(self) {
342             Ok(ref mplace) => {
343                 // We can reuse the mplace field computation logic for indirect operands.
344                 let field = self.mplace_field(mplace, field)?;
345                 return Ok(field.into());
346             }
347             Err(value) => value,
348         };
349
350         let field_layout = op.layout.field(self, field)?;
351         if field_layout.is_zst() {
352             let immediate = Scalar::ZST.into();
353             return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout });
354         }
355         let offset = op.layout.fields.offset(field);
356         let immediate = match *base {
357             // the field covers the entire type
358             _ if offset.bytes() == 0 && field_layout.size == op.layout.size => *base,
359             // extract fields from types with `ScalarPair` ABI
360             Immediate::ScalarPair(a, b) => {
361                 let val = if offset.bytes() == 0 { a } else { b };
362                 Immediate::from(val)
363             }
364             Immediate::Scalar(val) => span_bug!(
365                 self.cur_span(),
366                 "field access on non aggregate {:#?}, {:#?}",
367                 val,
368                 op.layout
369             ),
370         };
371         Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout })
372     }
373
374     pub fn operand_index(
375         &self,
376         op: &OpTy<'tcx, M::PointerTag>,
377         index: u64,
378     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
379         if let Ok(index) = usize::try_from(index) {
380             // We can just treat this as a field.
381             self.operand_field(op, index)
382         } else {
383             // Indexing into a big array. This must be an mplace.
384             let mplace = op.assert_mem_place(self);
385             Ok(self.mplace_index(&mplace, index)?.into())
386         }
387     }
388
389     pub fn operand_downcast(
390         &self,
391         op: &OpTy<'tcx, M::PointerTag>,
392         variant: VariantIdx,
393     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
394         // Downcasts only change the layout
395         Ok(match op.try_as_mplace(self) {
396             Ok(ref mplace) => self.mplace_downcast(mplace, variant)?.into(),
397             Err(..) => {
398                 let layout = op.layout.for_variant(self, variant);
399                 OpTy { layout, ..*op }
400             }
401         })
402     }
403
404     pub fn operand_projection(
405         &self,
406         base: &OpTy<'tcx, M::PointerTag>,
407         proj_elem: mir::PlaceElem<'tcx>,
408     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
409         use rustc_middle::mir::ProjectionElem::*;
410         Ok(match proj_elem {
411             Field(field, _) => self.operand_field(base, field.index())?,
412             Downcast(_, variant) => self.operand_downcast(base, variant)?,
413             Deref => self.deref_operand(base)?.into(),
414             Subslice { .. } | ConstantIndex { .. } | Index(_) => {
415                 // The rest should only occur as mplace, we do not use Immediates for types
416                 // allowing such operations.  This matches place_projection forcing an allocation.
417                 let mplace = base.assert_mem_place(self);
418                 self.mplace_projection(&mplace, proj_elem)?.into()
419             }
420         })
421     }
422
423     /// Read from a local. Will not actually access the local if reading from a ZST.
424     /// Will not access memory, instead an indirect `Operand` is returned.
425     ///
426     /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an
427     /// OpTy from a local
428     pub fn access_local(
429         &self,
430         frame: &super::Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
431         local: mir::Local,
432         layout: Option<TyAndLayout<'tcx>>,
433     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
434         let layout = self.layout_of_local(frame, local, layout)?;
435         let op = if layout.is_zst() {
436             // Do not read from ZST, they might not be initialized
437             Operand::Immediate(Scalar::ZST.into())
438         } else {
439             M::access_local(&self, frame, local)?
440         };
441         Ok(OpTy { op, layout })
442     }
443
444     /// Every place can be read from, so we can turn them into an operand.
445     /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this
446     /// will never actually read from memory.
447     #[inline(always)]
448     pub fn place_to_op(
449         &self,
450         place: &PlaceTy<'tcx, M::PointerTag>,
451     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
452         let op = match **place {
453             Place::Ptr(mplace) => Operand::Indirect(mplace),
454             Place::Local { frame, local } => {
455                 *self.access_local(&self.stack()[frame], local, None)?
456             }
457         };
458         Ok(OpTy { op, layout: place.layout })
459     }
460
461     // Evaluate a place with the goal of reading from it.  This lets us sometimes
462     // avoid allocations.
463     pub fn eval_place_to_op(
464         &self,
465         place: mir::Place<'tcx>,
466         layout: Option<TyAndLayout<'tcx>>,
467     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
468         // Do not use the layout passed in as argument if the base we are looking at
469         // here is not the entire place.
470         let layout = if place.projection.is_empty() { layout } else { None };
471
472         let base_op = self.access_local(self.frame(), place.local, layout)?;
473
474         let op = place
475             .projection
476             .iter()
477             .try_fold(base_op, |op, elem| self.operand_projection(&op, elem))?;
478
479         trace!("eval_place_to_op: got {:?}", *op);
480         // Sanity-check the type we ended up with.
481         debug_assert!(mir_assign_valid_types(
482             *self.tcx,
483             self.param_env,
484             self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
485                 place.ty(&self.frame().body.local_decls, *self.tcx).ty
486             ))?,
487             op.layout,
488         ));
489         Ok(op)
490     }
491
492     /// Evaluate the operand, returning a place where you can then find the data.
493     /// If you already know the layout, you can save two table lookups
494     /// by passing it in here.
495     #[inline]
496     pub fn eval_operand(
497         &self,
498         mir_op: &mir::Operand<'tcx>,
499         layout: Option<TyAndLayout<'tcx>>,
500     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
501         use rustc_middle::mir::Operand::*;
502         let op = match *mir_op {
503             // FIXME: do some more logic on `move` to invalidate the old location
504             Copy(place) | Move(place) => self.eval_place_to_op(place, layout)?,
505
506             Constant(ref constant) => {
507                 let val =
508                     self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal);
509                 // This can still fail:
510                 // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
511                 //   checked yet.
512                 // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
513
514                 self.mir_const_to_op(&val, layout)?
515             }
516         };
517         trace!("{:?}: {:?}", mir_op, *op);
518         Ok(op)
519     }
520
521     /// Evaluate a bunch of operands at once
522     pub(super) fn eval_operands(
523         &self,
524         ops: &[mir::Operand<'tcx>],
525     ) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::PointerTag>>> {
526         ops.iter().map(|op| self.eval_operand(op, None)).collect()
527     }
528
529     // Used when the miri-engine runs into a constant and for extracting information from constants
530     // in patterns via the `const_eval` module
531     /// The `val` and `layout` are assumed to already be in our interpreter
532     /// "universe" (param_env).
533     crate fn const_to_op(
534         &self,
535         val: &ty::Const<'tcx>,
536         layout: Option<TyAndLayout<'tcx>>,
537     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
538         match val.val {
539             ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
540             ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)),
541             ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => {
542                 let instance = self.resolve(def, substs)?;
543                 Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into())
544             }
545             ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => {
546                 span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val)
547             }
548             ty::ConstKind::Value(val_val) => self.const_val_to_op(val_val, val.ty, layout),
549         }
550     }
551
552     crate fn mir_const_to_op(
553         &self,
554         val: &mir::ConstantKind<'tcx>,
555         layout: Option<TyAndLayout<'tcx>>,
556     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
557         match val {
558             mir::ConstantKind::Ty(ct) => self.const_to_op(ct, layout),
559             mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, ty, layout),
560         }
561     }
562
563     crate fn const_val_to_op(
564         &self,
565         val_val: ConstValue<'tcx>,
566         ty: Ty<'tcx>,
567         layout: Option<TyAndLayout<'tcx>>,
568     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
569         // Other cases need layout.
570         let tag_scalar = |scalar| -> InterpResult<'tcx, _> {
571             Ok(match scalar {
572                 Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?),
573                 Scalar::Int(int) => Scalar::Int(int),
574             })
575         };
576         let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
577         let op = match val_val {
578             ConstValue::ByRef { alloc, offset } => {
579                 let id = self.tcx.create_memory_alloc(alloc);
580                 // We rely on mutability being set correctly in that allocation to prevent writes
581                 // where none should happen.
582                 let ptr = self.global_base_pointer(Pointer::new(id, offset))?;
583                 Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi))
584             }
585             ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x)?.into()),
586             ConstValue::Slice { data, start, end } => {
587                 // We rely on mutability being set correctly in `data` to prevent writes
588                 // where none should happen.
589                 let ptr = Pointer::new(
590                     self.tcx.create_memory_alloc(data),
591                     Size::from_bytes(start), // offset: `start`
592                 );
593                 Operand::Immediate(Immediate::new_slice(
594                     self.global_base_pointer(ptr)?.into(),
595                     u64::try_from(end.checked_sub(start).unwrap()).unwrap(), // len: `end - start`
596                     self,
597                 ))
598             }
599         };
600         Ok(OpTy { op, layout })
601     }
602
603     /// Read discriminant, return the runtime value as well as the variant index.
604     pub fn read_discriminant(
605         &self,
606         op: &OpTy<'tcx, M::PointerTag>,
607     ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, VariantIdx)> {
608         trace!("read_discriminant_value {:#?}", op.layout);
609         // Get type and layout of the discriminant.
610         let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
611         trace!("discriminant type: {:?}", discr_layout.ty);
612
613         // We use "discriminant" to refer to the value associated with a particular enum variant.
614         // This is not to be confused with its "variant index", which is just determining its position in the
615         // declared list of variants -- they can differ with explicitly assigned discriminants.
616         // We use "tag" to refer to how the discriminant is encoded in memory, which can be either
617         // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
618         let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
619             Variants::Single { index } => {
620                 let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
621                     Some(discr) => {
622                         // This type actually has discriminants.
623                         assert_eq!(discr.ty, discr_layout.ty);
624                         Scalar::from_uint(discr.val, discr_layout.size)
625                     }
626                     None => {
627                         // On a type without actual discriminants, variant is 0.
628                         assert_eq!(index.as_u32(), 0);
629                         Scalar::from_uint(index.as_u32(), discr_layout.size)
630                     }
631                 };
632                 return Ok((discr, index));
633             }
634             Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => {
635                 (tag, tag_encoding, tag_field)
636             }
637         };
638
639         // There are *three* layouts that come into play here:
640         // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
641         //   the `Scalar` we return.
642         // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
643         //   and used to interpret the value we read from the tag field.
644         //   For the return value, a cast to `discr_layout` is performed.
645         // - The field storing the tag has a layout, which is very similar to `tag_layout` but
646         //   may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.
647
648         // Get layout for tag.
649         let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?;
650
651         // Read tag and sanity-check `tag_layout`.
652         let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
653         assert_eq!(tag_layout.size, tag_val.layout.size);
654         assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
655         let tag_val = tag_val.to_scalar()?;
656         trace!("tag value: {:?}", tag_val);
657
658         // Figure out which discriminant and variant this corresponds to.
659         Ok(match *tag_encoding {
660             TagEncoding::Direct => {
661                 let tag_bits = self
662                     .force_bits(tag_val, tag_layout.size)
663                     .map_err(|_| err_ub!(InvalidTag(tag_val.erase_tag())))?;
664                 // Cast bits from tag layout to discriminant layout.
665                 let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty);
666                 let discr_bits = discr_val.assert_bits(discr_layout.size);
667                 // Convert discriminant to variant index, and catch invalid discriminants.
668                 let index = match *op.layout.ty.kind() {
669                     ty::Adt(adt, _) => {
670                         adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
671                     }
672                     ty::Generator(def_id, substs, _) => {
673                         let substs = substs.as_generator();
674                         substs
675                             .discriminants(def_id, *self.tcx)
676                             .find(|(_, var)| var.val == discr_bits)
677                     }
678                     _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"),
679                 }
680                 .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_tag())))?;
681                 // Return the cast value, and the index.
682                 (discr_val, index.0)
683             }
684             TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => {
685                 // Compute the variant this niche value/"tag" corresponds to. With niche layout,
686                 // discriminant (encoded in niche/tag) and variant index are the same.
687                 let variants_start = niche_variants.start().as_u32();
688                 let variants_end = niche_variants.end().as_u32();
689                 let variant = match tag_val.to_bits_or_ptr(tag_layout.size, self) {
690                     Err(ptr) => {
691                         // The niche must be just 0 (which an inbounds pointer value never is)
692                         let ptr_valid = niche_start == 0
693                             && variants_start == variants_end
694                             && !self.memory.ptr_may_be_null(ptr);
695                         if !ptr_valid {
696                             throw_ub!(InvalidTag(tag_val.erase_tag()))
697                         }
698                         dataful_variant
699                     }
700                     Ok(tag_bits) => {
701                         // We need to use machine arithmetic to get the relative variant idx:
702                         // variant_index_relative = tag_val - niche_start_val
703                         let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
704                         let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
705                         let variant_index_relative_val =
706                             self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
707                         let variant_index_relative = variant_index_relative_val
708                             .to_scalar()?
709                             .assert_bits(tag_val.layout.size);
710                         // Check if this is in the range that indicates an actual discriminant.
711                         if variant_index_relative <= u128::from(variants_end - variants_start) {
712                             let variant_index_relative = u32::try_from(variant_index_relative)
713                                 .expect("we checked that this fits into a u32");
714                             // Then computing the absolute variant idx should not overflow any more.
715                             let variant_index = variants_start
716                                 .checked_add(variant_index_relative)
717                                 .expect("overflow computing absolute variant idx");
718                             let variants_len = op
719                                 .layout
720                                 .ty
721                                 .ty_adt_def()
722                                 .expect("tagged layout for non adt")
723                                 .variants
724                                 .len();
725                             assert!(usize::try_from(variant_index).unwrap() < variants_len);
726                             VariantIdx::from_u32(variant_index)
727                         } else {
728                             dataful_variant
729                         }
730                     }
731                 };
732                 // Compute the size of the scalar we need to return.
733                 // No need to cast, because the variant index directly serves as discriminant and is
734                 // encoded in the tag.
735                 (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
736             }
737         })
738     }
739 }