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.
4 use std
::convert
::{TryFrom, TryInto}
;
6 use rustc
::ty
::layout
::{
7 self, HasDataLayout
, IntegerExt
, LayoutOf
, PrimitiveExt
, Size
, TyLayout
, VariantIdx
,
11 use super::{InterpCx, MPlaceTy, Machine, MemPlace, Place, PlaceTy}
;
12 pub use rustc
::mir
::interpret
::ScalarMaybeUndef
;
13 use rustc
::mir
::interpret
::{
14 sign_extend
, truncate
, AllocId
, ConstValue
, GlobalId
, InterpResult
, Pointer
, Scalar
,
17 use rustc_macros
::HashStable
;
19 /// An `Immediate` represents a single immediate self-contained Rust value.
21 /// For optimization of a few very common cases, there is also a representation for a pair of
22 /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
23 /// operations and wide pointers. This idea was taken from rustc's codegen.
24 /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
25 /// defined on `Immediate`, and do not have to work with a `Place`.
26 #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
27 pub enum Immediate
<Tag
= (), Id
= AllocId
> {
28 Scalar(ScalarMaybeUndef
<Tag
, Id
>),
29 ScalarPair(ScalarMaybeUndef
<Tag
, Id
>, ScalarMaybeUndef
<Tag
, Id
>),
32 impl<Tag
> From
<ScalarMaybeUndef
<Tag
>> for Immediate
<Tag
> {
34 fn from(val
: ScalarMaybeUndef
<Tag
>) -> Self {
35 Immediate
::Scalar(val
)
39 impl<Tag
> From
<Scalar
<Tag
>> for Immediate
<Tag
> {
41 fn from(val
: Scalar
<Tag
>) -> Self {
42 Immediate
::Scalar(val
.into())
46 impl<Tag
> From
<Pointer
<Tag
>> for Immediate
<Tag
> {
48 fn from(val
: Pointer
<Tag
>) -> Self {
49 Immediate
::Scalar(Scalar
::from(val
).into())
53 impl<'tcx
, Tag
> Immediate
<Tag
> {
54 pub fn new_slice(val
: Scalar
<Tag
>, len
: u64, cx
: &impl HasDataLayout
) -> Self {
55 Immediate
::ScalarPair(
57 Scalar
::from_uint(len
, cx
.data_layout().pointer_size
).into(),
61 pub fn new_dyn_trait(val
: Scalar
<Tag
>, vtable
: Pointer
<Tag
>) -> Self {
62 Immediate
::ScalarPair(val
.into(), vtable
.into())
66 pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef
<Tag
> {
68 Immediate
::Scalar(val
) => val
,
69 Immediate
::ScalarPair(..) => bug
!("Got a wide pointer where a scalar was expected"),
74 pub fn to_scalar(self) -> InterpResult
<'tcx
, Scalar
<Tag
>> {
75 self.to_scalar_or_undef().not_undef()
79 pub fn to_scalar_pair(self) -> InterpResult
<'tcx
, (Scalar
<Tag
>, Scalar
<Tag
>)> {
81 Immediate
::Scalar(..) => bug
!("Got a thin pointer where a scalar pair was expected"),
82 Immediate
::ScalarPair(a
, b
) => Ok((a
.not_undef()?
, b
.not_undef()?
)),
87 // ScalarPair needs a type to interpret, so we often have an immediate and a type together
88 // as input for binary and cast operations.
89 #[derive(Copy, Clone, Debug)]
90 pub struct ImmTy
<'tcx
, Tag
= ()> {
91 pub(crate) imm
: Immediate
<Tag
>,
92 pub layout
: TyLayout
<'tcx
>,
95 // `Tag: Copy` because some methods on `Scalar` consume them by value
96 impl<Tag
: Copy
> std
::fmt
::Display
for ImmTy
<'tcx
, Tag
> {
97 fn fmt(&self, fmt
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
99 // We cannot use `to_bits_or_ptr` as we do not have a `tcx`.
100 // So we use `is_bits` and circumvent a bunch of sanity checking -- but
101 // this is anyway only for printing.
102 Immediate
::Scalar(ScalarMaybeUndef
::Scalar(s
)) if s
.is_ptr() => {
103 fmt
.write_str("{pointer}")
105 Immediate
::Scalar(ScalarMaybeUndef
::Scalar(s
)) => {
106 let s
= s
.assert_bits(self.layout
.size
);
107 match self.layout
.ty
.kind
{
109 return write
!(fmt
, "{}", super::sign_extend(s
, self.layout
.size
) as i128
,);
111 ty
::Uint(_
) => return write
!(fmt
, "{}", s
),
112 ty
::Bool
if s
== 0 => return fmt
.write_str("false"),
113 ty
::Bool
if s
== 1 => return fmt
.write_str("true"),
115 if let Some(c
) = u32::try_from(s
).ok().and_then(std
::char::from_u32
) {
116 return write
!(fmt
, "{}", c
);
119 ty
::Float(ast
::FloatTy
::F32
) => {
120 if let Ok(u
) = u32::try_from(s
) {
121 return write
!(fmt
, "{}", f32::from_bits(u
));
124 ty
::Float(ast
::FloatTy
::F64
) => {
125 if let Ok(u
) = u64::try_from(s
) {
126 return write
!(fmt
, "{}", f64::from_bits(u
));
131 write
!(fmt
, "{:x}", s
)
133 Immediate
::Scalar(ScalarMaybeUndef
::Undef
) => fmt
.write_str("{undef}"),
134 Immediate
::ScalarPair(..) => fmt
.write_str("{wide pointer or tuple}"),
139 impl<'tcx
, Tag
> ::std
::ops
::Deref
for ImmTy
<'tcx
, Tag
> {
140 type Target
= Immediate
<Tag
>;
142 fn deref(&self) -> &Immediate
<Tag
> {
147 /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate,
148 /// or still in memory. The latter is an optimization, to delay reading that chunk of
149 /// memory and to avoid having to store arbitrary-sized data here.
150 #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
151 pub enum Operand
<Tag
= (), Id
= AllocId
> {
152 Immediate(Immediate
<Tag
, Id
>),
153 Indirect(MemPlace
<Tag
, Id
>),
156 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
157 pub struct OpTy
<'tcx
, Tag
= ()> {
158 op
: Operand
<Tag
>, // Keep this private; it helps enforce invariants.
159 pub layout
: TyLayout
<'tcx
>,
162 impl<'tcx
, Tag
> ::std
::ops
::Deref
for OpTy
<'tcx
, Tag
> {
163 type Target
= Operand
<Tag
>;
165 fn deref(&self) -> &Operand
<Tag
> {
170 impl<'tcx
, Tag
: Copy
> From
<MPlaceTy
<'tcx
, Tag
>> for OpTy
<'tcx
, Tag
> {
172 fn from(mplace
: MPlaceTy
<'tcx
, Tag
>) -> Self {
173 OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout }
177 impl<'tcx
, Tag
> From
<ImmTy
<'tcx
, Tag
>> for OpTy
<'tcx
, Tag
> {
179 fn from(val
: ImmTy
<'tcx
, Tag
>) -> Self {
180 OpTy { op: Operand::Immediate(val.imm), layout: val.layout }
184 impl<'tcx
, Tag
: Copy
> ImmTy
<'tcx
, Tag
> {
186 pub fn from_scalar(val
: Scalar
<Tag
>, layout
: TyLayout
<'tcx
>) -> Self {
187 ImmTy { imm: val.into(), layout }
191 pub fn try_from_uint(i
: impl Into
<u128
>, layout
: TyLayout
<'tcx
>) -> Option
<Self> {
192 Some(Self::from_scalar(Scalar
::try_from_uint(i
, layout
.size
)?
, layout
))
195 pub fn from_uint(i
: impl Into
<u128
>, layout
: TyLayout
<'tcx
>) -> Self {
196 Self::from_scalar(Scalar
::from_uint(i
, layout
.size
), layout
)
200 pub fn try_from_int(i
: impl Into
<i128
>, layout
: TyLayout
<'tcx
>) -> Option
<Self> {
201 Some(Self::from_scalar(Scalar
::try_from_int(i
, layout
.size
)?
, layout
))
205 pub fn from_int(i
: impl Into
<i128
>, layout
: TyLayout
<'tcx
>) -> Self {
206 Self::from_scalar(Scalar
::from_int(i
, layout
.size
), layout
)
210 // Use the existing layout if given (but sanity check in debug mode),
211 // or compute the layout.
213 pub(super) fn from_known_layout
<'tcx
>(
214 layout
: Option
<TyLayout
<'tcx
>>,
215 compute
: impl FnOnce() -> InterpResult
<'tcx
, TyLayout
<'tcx
>>,
216 ) -> InterpResult
<'tcx
, TyLayout
<'tcx
>> {
220 if cfg
!(debug_assertions
) {
221 let layout2
= compute()?
;
223 layout
.details
, layout2
.details
,
224 "mismatch in layout of supposedly equal-layout types {:?} and {:?}",
225 layout
.ty
, layout2
.ty
233 impl<'mir
, 'tcx
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
234 /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST.
235 /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
239 op
: OpTy
<'tcx
, M
::PointerTag
>,
240 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
241 match op
.try_as_mplace(self) {
242 Ok(mplace
) => Ok(self.force_mplace_ptr(mplace
)?
.into()),
243 Err(imm
) => Ok(imm
.into()), // Nothing to cast/force
247 /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
248 /// Returns `None` if the layout does not permit loading this as a value.
249 fn try_read_immediate_from_mplace(
251 mplace
: MPlaceTy
<'tcx
, M
::PointerTag
>,
252 ) -> InterpResult
<'tcx
, Option
<ImmTy
<'tcx
, M
::PointerTag
>>> {
253 if mplace
.layout
.is_unsized() {
254 // Don't touch unsized
259 .check_mplace_access(mplace
, None
)
260 .expect("places should be checked on creation")
264 return Ok(Some(ImmTy
{
266 imm
: Scalar
::zst().into(),
267 layout
: mplace
.layout
,
272 match mplace
.layout
.abi
{
273 layout
::Abi
::Scalar(..) => {
274 let scalar
= self.memory
.get_raw(ptr
.alloc_id
)?
.read_scalar(
279 Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }
))
281 layout
::Abi
::ScalarPair(ref a
, ref b
) => {
282 // We checked `ptr_align` above, so all fields will have the alignment they need.
283 // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
284 // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
285 let (a
, b
) = (&a
.value
, &b
.value
);
286 let (a_size
, b_size
) = (a
.size(self), b
.size(self));
288 let b_offset
= a_size
.align_to(b
.align(self).abi
);
289 assert
!(b_offset
.bytes() > 0); // we later use the offset to tell apart the fields
290 let b_ptr
= ptr
.offset(b_offset
, self)?
;
291 let a_val
= self.memory
.get_raw(ptr
.alloc_id
)?
.read_scalar(self, a_ptr
, a_size
)?
;
292 let b_val
= self.memory
.get_raw(ptr
.alloc_id
)?
.read_scalar(self, b_ptr
, b_size
)?
;
293 Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout }
))
299 /// Try returning an immediate for the operand.
300 /// If the layout does not permit loading this as an immediate, return where in memory
301 /// we can find the data.
302 /// Note that for a given layout, this operation will either always fail or always
303 /// succeed! Whether it succeeds depends on whether the layout can be represented
304 /// in a `Immediate`, not on which data is stored there currently.
305 pub(crate) fn try_read_immediate(
307 src
: OpTy
<'tcx
, M
::PointerTag
>,
308 ) -> InterpResult
<'tcx
, Result
<ImmTy
<'tcx
, M
::PointerTag
>, MPlaceTy
<'tcx
, M
::PointerTag
>>> {
309 Ok(match src
.try_as_mplace(self) {
311 if let Some(val
) = self.try_read_immediate_from_mplace(mplace
)?
{
321 /// Read an immediate from a place, asserting that that is possible with the given layout.
323 pub fn read_immediate(
325 op
: OpTy
<'tcx
, M
::PointerTag
>,
326 ) -> InterpResult
<'tcx
, ImmTy
<'tcx
, M
::PointerTag
>> {
327 if let Ok(imm
) = self.try_read_immediate(op
)?
{
330 bug
!("primitive read failed for type: {:?}", op
.layout
.ty
);
334 /// Read a scalar from a place
337 op
: OpTy
<'tcx
, M
::PointerTag
>,
338 ) -> InterpResult
<'tcx
, ScalarMaybeUndef
<M
::PointerTag
>> {
339 Ok(self.read_immediate(op
)?
.to_scalar_or_undef())
342 // Turn the wide MPlace into a string (must already be dereferenced!)
343 pub fn read_str(&self, mplace
: MPlaceTy
<'tcx
, M
::PointerTag
>) -> InterpResult
<'tcx
, &str> {
344 let len
= mplace
.len(self)?
;
345 let bytes
= self.memory
.read_bytes(mplace
.ptr
, Size
::from_bytes(len
as u64))?
;
346 let str = ::std
::str::from_utf8(bytes
)
347 .map_err(|err
| err_unsup
!(ValidationFailure(err
.to_string())))?
;
351 /// Projection functions
352 pub fn operand_field(
354 op
: OpTy
<'tcx
, M
::PointerTag
>,
356 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
357 let base
= match op
.try_as_mplace(self) {
360 let field
= self.mplace_field(mplace
, field
)?
;
361 return Ok(field
.into());
366 let field
= field
.try_into().unwrap();
367 let field_layout
= op
.layout
.field(self, field
)?
;
368 if field_layout
.is_zst() {
369 let immediate
= Scalar
::zst().into();
370 return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }
);
372 let offset
= op
.layout
.fields
.offset(field
);
373 let immediate
= match *base
{
374 // the field covers the entire type
375 _
if offset
.bytes() == 0 && field_layout
.size
== op
.layout
.size
=> *base
,
376 // extract fields from types with `ScalarPair` ABI
377 Immediate
::ScalarPair(a
, b
) => {
378 let val
= if offset
.bytes() == 0 { a }
else { b }
;
381 Immediate
::Scalar(val
) => {
382 bug
!("field access on non aggregate {:#?}, {:#?}", val
, op
.layout
)
385 Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }
)
388 pub fn operand_downcast(
390 op
: OpTy
<'tcx
, M
::PointerTag
>,
392 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
393 // Downcasts only change the layout
394 Ok(match op
.try_as_mplace(self) {
395 Ok(mplace
) => self.mplace_downcast(mplace
, variant
)?
.into(),
397 let layout
= op
.layout
.for_variant(self, variant
);
398 OpTy { layout, ..op }
403 pub fn operand_projection(
405 base
: OpTy
<'tcx
, M
::PointerTag
>,
406 proj_elem
: &mir
::PlaceElem
<'tcx
>,
407 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
408 use rustc
::mir
::ProjectionElem
::*;
409 Ok(match *proj_elem
{
410 Field(field
, _
) => self.operand_field(base
, field
.index() as u64)?
,
411 Downcast(_
, variant
) => self.operand_downcast(base
, variant
)?
,
412 Deref
=> self.deref_operand(base
)?
.into(),
413 Subslice { .. }
| ConstantIndex { .. }
| Index(_
) => {
414 // The rest should only occur as mplace, we do not use Immediates for types
415 // allowing such operations. This matches place_projection forcing an allocation.
416 let mplace
= base
.assert_mem_place(self);
417 self.mplace_projection(mplace
, proj_elem
)?
.into()
422 /// This is used by [priroda](https://github.com/oli-obk/priroda) to get an OpTy from a local
425 frame
: &super::Frame
<'mir
, 'tcx
, M
::PointerTag
, M
::FrameExtra
>,
427 layout
: Option
<TyLayout
<'tcx
>>,
428 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
429 assert_ne
!(local
, mir
::RETURN_PLACE
);
430 let layout
= self.layout_of_local(frame
, local
, layout
)?
;
431 let op
= if layout
.is_zst() {
432 // Do not read from ZST, they might not be initialized
433 Operand
::Immediate(Scalar
::zst().into())
435 M
::access_local(&self, frame
, local
)?
437 Ok(OpTy { op, layout }
)
440 /// Every place can be read from, so we can turn them into an operand
444 place
: PlaceTy
<'tcx
, M
::PointerTag
>,
445 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
446 let op
= match *place
{
447 Place
::Ptr(mplace
) => Operand
::Indirect(mplace
),
448 Place
::Local { frame, local }
=> *self.access_local(&self.stack
[frame
], local
, None
)?
,
450 Ok(OpTy { op, layout: place.layout }
)
453 // Evaluate a place with the goal of reading from it. This lets us sometimes
454 // avoid allocations.
455 pub fn eval_place_to_op(
457 place
: &mir
::Place
<'tcx
>,
458 layout
: Option
<TyLayout
<'tcx
>>,
459 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
460 let base_op
= match place
.local
{
461 mir
::RETURN_PLACE
=> throw_unsup
!(ReadFromReturnPointer
),
463 // Do not use the layout passed in as argument if the base we are looking at
464 // here is not the entire place.
465 let layout
= if place
.projection
.is_empty() { layout }
else { None }
;
467 self.access_local(self.frame(), local
, layout
)?
474 .try_fold(base_op
, |op
, elem
| self.operand_projection(op
, elem
))?
;
476 trace
!("eval_place_to_op: got {:?}", *op
);
480 /// Evaluate the operand, returning a place where you can then find the data.
481 /// If you already know the layout, you can save two table lookups
482 /// by passing it in here.
485 mir_op
: &mir
::Operand
<'tcx
>,
486 layout
: Option
<TyLayout
<'tcx
>>,
487 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
488 use rustc
::mir
::Operand
::*;
489 let op
= match *mir_op
{
490 // FIXME: do some more logic on `move` to invalidate the old location
491 Copy(ref place
) | Move(ref place
) => self.eval_place_to_op(place
, layout
)?
,
493 Constant(ref constant
) => {
494 let val
= self.subst_from_frame_and_normalize_erasing_regions(constant
.literal
);
495 self.eval_const_to_op(val
, layout
)?
498 trace
!("{:?}: {:?}", mir_op
, *op
);
502 /// Evaluate a bunch of operands at once
503 pub(super) fn eval_operands(
505 ops
: &[mir
::Operand
<'tcx
>],
506 ) -> InterpResult
<'tcx
, Vec
<OpTy
<'tcx
, M
::PointerTag
>>> {
507 ops
.iter().map(|op
| self.eval_operand(op
, None
)).collect()
510 // Used when the miri-engine runs into a constant and for extracting information from constants
511 // in patterns via the `const_eval` module
512 /// The `val` and `layout` are assumed to already be in our interpreter
513 /// "universe" (param_env).
514 crate fn eval_const_to_op(
516 val
: &ty
::Const
<'tcx
>,
517 layout
: Option
<TyLayout
<'tcx
>>,
518 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::PointerTag
>> {
519 let tag_scalar
= |scalar
| match scalar
{
520 Scalar
::Ptr(ptr
) => Scalar
::Ptr(self.tag_static_base_pointer(ptr
)),
521 Scalar
::Raw { data, size }
=> Scalar
::Raw { data, size }
,
523 // Early-return cases.
524 let val_val
= match val
.val
{
525 ty
::ConstKind
::Param(_
) => throw_inval
!(TooGeneric
),
526 ty
::ConstKind
::Unevaluated(def_id
, substs
, promoted
) => {
527 let instance
= self.resolve(def_id
, substs
)?
;
528 // We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation.
529 // The reason we use `const_eval_raw` everywhere else is to prevent cycles during
530 // validation, because validation automatically reads through any references, thus
531 // potentially requiring the current static to be evaluated again. This is not a
532 // problem here, because we are building an operand which means an actual read is
534 return Ok(self.const_eval(GlobalId { instance, promoted }
, val
.ty
)?
);
536 ty
::ConstKind
::Infer(..)
537 | ty
::ConstKind
::Bound(..)
538 | ty
::ConstKind
::Placeholder(..) => {
539 bug
!("eval_const_to_op: Unexpected ConstKind {:?}", val
)
541 ty
::ConstKind
::Value(val_val
) => val_val
,
543 // Other cases need layout.
544 let layout
= from_known_layout(layout
, || self.layout_of(val
.ty
))?
;
545 let op
= match val_val
{
546 ConstValue
::ByRef { alloc, offset }
=> {
547 let id
= self.tcx
.alloc_map
.lock().create_memory_alloc(alloc
);
548 // We rely on mutability being set correctly in that allocation to prevent writes
549 // where none should happen.
550 let ptr
= self.tag_static_base_pointer(Pointer
::new(id
, offset
));
551 Operand
::Indirect(MemPlace
::from_ptr(ptr
, layout
.align
.abi
))
553 ConstValue
::Scalar(x
) => Operand
::Immediate(tag_scalar(x
).into()),
554 ConstValue
::Slice { data, start, end }
=> {
555 // We rely on mutability being set correctly in `data` to prevent writes
556 // where none should happen.
557 let ptr
= Pointer
::new(
558 self.tcx
.alloc_map
.lock().create_memory_alloc(data
),
559 Size
::from_bytes(start
as u64), // offset: `start`
561 Operand
::Immediate(Immediate
::new_slice(
562 self.tag_static_base_pointer(ptr
).into(),
563 (end
- start
) as u64, // len: `end - start`
568 Ok(OpTy { op, layout }
)
571 /// Read discriminant, return the runtime value as well as the variant index.
572 pub fn read_discriminant(
574 rval
: OpTy
<'tcx
, M
::PointerTag
>,
575 ) -> InterpResult
<'tcx
, (u128
, VariantIdx
)> {
576 trace
!("read_discriminant_value {:#?}", rval
.layout
);
578 let (discr_layout
, discr_kind
, discr_index
) = match rval
.layout
.variants
{
579 layout
::Variants
::Single { index }
=> {
583 .discriminant_for_variant(*self.tcx
, index
)
584 .map_or(index
.as_u32() as u128
, |discr
| discr
.val
);
585 return Ok((discr_val
, index
));
587 layout
::Variants
::Multiple
{
588 discr
: ref discr_layout
,
592 } => (discr_layout
, discr_kind
, discr_index
),
595 // read raw discriminant value
596 let discr_op
= self.operand_field(rval
, discr_index
as u64)?
;
597 let discr_val
= self.read_immediate(discr_op
)?
;
598 let raw_discr
= discr_val
.to_scalar_or_undef();
599 trace
!("discr value: {:?}", raw_discr
);
601 Ok(match *discr_kind
{
602 layout
::DiscriminantKind
::Tag
=> {
603 let bits_discr
= raw_discr
605 .and_then(|raw_discr
| self.force_bits(raw_discr
, discr_val
.layout
.size
))
606 .map_err(|_
| err_ub
!(InvalidDiscriminant(raw_discr
.erase_tag())))?
;
607 let real_discr
= if discr_val
.layout
.ty
.is_signed() {
608 // going from layout tag type to typeck discriminant type
609 // requires first sign extending with the discriminant layout
610 let sexted
= sign_extend(bits_discr
, discr_val
.layout
.size
) as i128
;
611 // and then zeroing with the typeck discriminant type
616 .expect("tagged layout corresponds to adt")
619 let size
= layout
::Integer
::from_attr(self, discr_ty
).size();
620 let truncatee
= sexted
as u128
;
621 truncate(truncatee
, size
)
625 // Make sure we catch invalid discriminants
626 let index
= match rval
.layout
.ty
.kind
{
628 adt
.discriminants(self.tcx
.tcx
).find(|(_
, var
)| var
.val
== real_discr
)
630 ty
::Generator(def_id
, substs
, _
) => {
631 let substs
= substs
.as_generator();
633 .discriminants(def_id
, self.tcx
.tcx
)
634 .find(|(_
, var
)| var
.val
== real_discr
)
636 _
=> bug
!("tagged layout for non-adt non-generator"),
638 .ok_or_else(|| err_ub
!(InvalidDiscriminant(raw_discr
.erase_tag())))?
;
639 (real_discr
, index
.0)
641 layout
::DiscriminantKind
::Niche
{
646 let variants_start
= niche_variants
.start().as_u32();
647 let variants_end
= niche_variants
.end().as_u32();
648 let raw_discr
= raw_discr
650 .map_err(|_
| err_ub
!(InvalidDiscriminant(ScalarMaybeUndef
::Undef
)))?
;
651 match raw_discr
.to_bits_or_ptr(discr_val
.layout
.size
, self) {
653 // The niche must be just 0 (which an inbounds pointer value never is)
654 let ptr_valid
= niche_start
== 0
655 && variants_start
== variants_end
656 && !self.memory
.ptr_may_be_null(ptr
);
658 throw_ub
!(InvalidDiscriminant(raw_discr
.erase_tag().into()))
660 (dataful_variant
.as_u32() as u128
, dataful_variant
)
663 // We need to use machine arithmetic to get the relative variant idx:
664 // variant_index_relative = discr_val - niche_start_val
666 self.layout_of(discr_layout
.value
.to_int_ty(*self.tcx
))?
;
667 let discr_val
= ImmTy
::from_uint(raw_discr
, discr_layout
);
668 let niche_start_val
= ImmTy
::from_uint(niche_start
, discr_layout
);
669 let variant_index_relative_val
=
670 self.binary_op(mir
::BinOp
::Sub
, discr_val
, niche_start_val
)?
;
671 let variant_index_relative
= variant_index_relative_val
673 .assert_bits(discr_val
.layout
.size
);
674 // Check if this is in the range that indicates an actual discriminant.
675 if variant_index_relative
<= u128
::from(variants_end
- variants_start
) {
676 let variant_index_relative
= u32::try_from(variant_index_relative
)
677 .expect("we checked that this fits into a u32");
678 // Then computing the absolute variant idx should not overflow any more.
679 let variant_index
= variants_start
680 .checked_add(variant_index_relative
)
681 .expect("overflow computing absolute variant idx");
682 let variants_len
= rval
686 .expect("tagged layout for non adt")
689 assert
!((variant_index
as usize) < variants_len
);
690 (u128
::from(variant_index
), VariantIdx
::from_u32(variant_index
))
692 (u128
::from(dataful_variant
.as_u32()), dataful_variant
)