]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #55549 - RalfJung:miri-visitor, r=oli-obk
authorbors <bors@rust-lang.org>
Wed, 7 Nov 2018 09:14:12 +0000 (09:14 +0000)
committerbors <bors@rust-lang.org>
Wed, 7 Nov 2018 09:14:12 +0000 (09:14 +0000)
Value visitors for miri

Generalize the traversal part of validation to a `ValueVisitor`.

~~This includes https://github.com/rust-lang/rust/pull/55316, [click here](https://github.com/RalfJung/rust/compare/retagging...RalfJung:miri-visitor) for just the new commits.~~

26 files changed:
src/librustc/mir/interpret/error.rs
src/librustc/mir/interpret/mod.rs
src/librustc/mir/interpret/value.rs
src/librustc_mir/const_eval.rs
src/librustc_mir/interpret/eval_context.rs
src/librustc_mir/interpret/machine.rs
src/librustc_mir/interpret/mod.rs
src/librustc_mir/interpret/operand.rs
src/librustc_mir/interpret/place.rs
src/librustc_mir/interpret/validity.rs
src/librustc_mir/interpret/visitor.rs [new file with mode: 0644]
src/test/ui/consts/const-eval/double_check2.stderr
src/test/ui/consts/const-eval/ub-enum.rs
src/test/ui/consts/const-eval/ub-enum.stderr
src/test/ui/consts/const-eval/ub-nonnull.rs
src/test/ui/consts/const-eval/ub-nonnull.stderr
src/test/ui/consts/const-eval/ub-ref.rs
src/test/ui/consts/const-eval/ub-ref.stderr
src/test/ui/consts/const-eval/ub-uninhabit.rs
src/test/ui/consts/const-eval/ub-uninhabit.stderr
src/test/ui/consts/const-eval/ub-upvars.rs
src/test/ui/consts/const-eval/ub-upvars.stderr
src/test/ui/consts/const-eval/union-ub-fat-ptr.rs
src/test/ui/consts/const-eval/union-ub-fat-ptr.stderr
src/test/ui/consts/const-eval/union-ub.rs
src/test/ui/consts/const-eval/union-ub.stderr

index 8e025f0d029b72f41237e1b1a5f6a6133c3fbd54..f28aa41ed42221a22053b389bd9cbdc429309312 100644 (file)
@@ -15,7 +15,7 @@
 use ty::layout::{Size, Align, LayoutError};
 use rustc_target::spec::abi::Abi;
 
-use super::Pointer;
+use super::{Pointer, Scalar};
 
 use backtrace::Backtrace;
 
@@ -240,7 +240,7 @@ pub enum EvalErrorKind<'tcx, O> {
     InvalidMemoryAccess,
     InvalidFunctionPointer,
     InvalidBool,
-    InvalidDiscriminant(u128),
+    InvalidDiscriminant(Scalar),
     PointerOutOfBounds {
         ptr: Pointer,
         access: bool,
index f8a5dbc6905ca9be31429b042d183fe26926a95e..e2abf7970d6d821fe9808ac3b2cf4b120fd22eb8 100644 (file)
@@ -91,42 +91,43 @@ fn pointer_size(&self) -> Size {
     }
 
     //// Trunace the given value to the pointer size; also return whether there was an overflow
+    #[inline]
     fn truncate_to_ptr(&self, val: u128) -> (u64, bool) {
         let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
         ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1)
     }
 
-    // Overflow checking only works properly on the range from -u64 to +u64.
-    fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) {
-        // FIXME: is it possible to over/underflow here?
-        if i < 0 {
-            // trickery to ensure that i64::min_value() works fine
-            // this formula only works for true negative values, it panics for zero!
-            let n = u64::max_value() - (i as u64) + 1;
-            val.overflowing_sub(n)
-        } else {
-            self.overflowing_offset(val, i as u64)
-        }
+    #[inline]
+    fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
+        let (res, over) = self.overflowing_offset(val, i);
+        if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
     }
 
+    #[inline]
     fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
         let (res, over1) = val.overflowing_add(i);
-        let (res, over2) = self.truncate_to_ptr(res as u128);
+        let (res, over2) = self.truncate_to_ptr(u128::from(res));
         (res, over1 || over2)
     }
 
+    #[inline]
     fn signed_offset<'tcx>(&self, val: u64, i: i64) -> EvalResult<'tcx, u64> {
-        let (res, over) = self.overflowing_signed_offset(val, i as i128);
+        let (res, over) = self.overflowing_signed_offset(val, i128::from(i));
         if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
     }
 
-    fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
-        let (res, over) = self.overflowing_offset(val, i);
-        if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
-    }
-
-    fn wrapping_signed_offset(&self, val: u64, i: i64) -> u64 {
-        self.overflowing_signed_offset(val, i as i128).0
+    // Overflow checking only works properly on the range from -u64 to +u64.
+    #[inline]
+    fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) {
+        // FIXME: is it possible to over/underflow here?
+        if i < 0 {
+            // trickery to ensure that i64::min_value() works fine
+            // this formula only works for true negative values, it panics for zero!
+            let n = u64::max_value() - (i as u64) + 1;
+            val.overflowing_sub(n)
+        } else {
+            self.overflowing_offset(val, i as u64)
+        }
     }
 }
 
@@ -176,19 +177,27 @@ pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self {
         Pointer { alloc_id, offset, tag }
     }
 
-    pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
-        Pointer::new_with_tag(
+    #[inline]
+    pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
+        Ok(Pointer::new_with_tag(
             self.alloc_id,
-            Size::from_bytes(cx.data_layout().wrapping_signed_offset(self.offset.bytes(), i)),
-            self.tag,
-        )
+            Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
+            self.tag
+        ))
     }
 
-    pub fn overflowing_signed_offset(self, i: i128, cx: &impl HasDataLayout) -> (Self, bool) {
-        let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
+    #[inline]
+    pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
+        let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
         (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
     }
 
+    #[inline(always)]
+    pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
+        self.overflowing_offset(i, cx).0
+    }
+
+    #[inline]
     pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
         Ok(Pointer::new_with_tag(
             self.alloc_id,
@@ -197,20 +206,18 @@ pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx,
         ))
     }
 
-    pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
-        let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
+    #[inline]
+    pub fn overflowing_signed_offset(self, i: i128, cx: &impl HasDataLayout) -> (Self, bool) {
+        let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
         (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
     }
 
-    pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
-        Ok(Pointer::new_with_tag(
-            self.alloc_id,
-            Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
-            self.tag
-        ))
+    #[inline(always)]
+    pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
+        self.overflowing_signed_offset(i128::from(i), cx).0
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn erase_tag(self) -> Pointer {
         Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () }
     }
index 3b8e19c6ecaa9b4c1c7b272da17ddc95a1757a5f..3f5399396abcf837ea2f3b874427cd197ffae868 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![allow(unknown_lints)]
+use std::fmt;
 
 use ty::layout::{HasDataLayout, Size};
 use ty::subst::Substs;
@@ -99,6 +99,15 @@ pub enum Scalar<Tag=(), Id=AllocId> {
     Ptr(Pointer<Tag, Id>),
 }
 
+impl<Tag> fmt::Display for Scalar<Tag> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Scalar::Ptr(_) => write!(f, "a pointer"),
+            Scalar::Bits { bits, .. } => write!(f, "{}", bits),
+        }
+    }
+}
+
 impl<'tcx> Scalar<()> {
     #[inline]
     pub fn with_default_tag<Tag>(self) -> Scalar<Tag>
@@ -134,32 +143,47 @@ pub fn zst() -> Self {
     }
 
     #[inline]
-    pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
+    pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
         let dl = cx.data_layout();
         match self {
             Scalar::Bits { bits, size } => {
                 assert_eq!(size as u64, dl.pointer_size.bytes());
                 Ok(Scalar::Bits {
-                    bits: dl.signed_offset(bits as u64, i)? as u128,
+                    bits: dl.offset(bits as u64, i.bytes())? as u128,
                     size,
                 })
             }
-            Scalar::Ptr(ptr) => ptr.signed_offset(i, dl).map(Scalar::Ptr),
+            Scalar::Ptr(ptr) => ptr.offset(i, dl).map(Scalar::Ptr),
         }
     }
 
     #[inline]
-    pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
+    pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
         let dl = cx.data_layout();
         match self {
             Scalar::Bits { bits, size } => {
                 assert_eq!(size as u64, dl.pointer_size.bytes());
+                Scalar::Bits {
+                    bits: dl.overflowing_offset(bits as u64, i.bytes()).0 as u128,
+                    size,
+                }
+            }
+            Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_offset(i, dl)),
+        }
+    }
+
+    #[inline]
+    pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
+        let dl = cx.data_layout();
+        match self {
+            Scalar::Bits { bits, size } => {
+                assert_eq!(size as u64, dl.pointer_size().bytes());
                 Ok(Scalar::Bits {
-                    bits: dl.offset(bits as u64, i.bytes())? as u128,
+                    bits: dl.signed_offset(bits as u64, i)? as u128,
                     size,
                 })
             }
-            Scalar::Ptr(ptr) => ptr.offset(i, dl).map(Scalar::Ptr),
+            Scalar::Ptr(ptr) => ptr.signed_offset(i, dl).map(Scalar::Ptr),
         }
     }
 
@@ -170,7 +194,7 @@ pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self
             Scalar::Bits { bits, size } => {
                 assert_eq!(size as u64, dl.pointer_size.bytes());
                 Scalar::Bits {
-                    bits: dl.wrapping_signed_offset(bits as u64, i) as u128,
+                    bits: dl.overflowing_signed_offset(bits as u64, i128::from(i)).0 as u128,
                     size,
                 }
             }
@@ -178,6 +202,19 @@ pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self
         }
     }
 
+    /// Returns this pointers offset from the allocation base, or from NULL (for
+    /// integer pointers).
+    #[inline]
+    pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size {
+        match self {
+            Scalar::Bits { bits, size } => {
+                assert_eq!(size as u64, cx.pointer_size().bytes());
+                Size::from_bytes(bits as u64)
+            }
+            Scalar::Ptr(ptr) => ptr.offset,
+        }
+    }
+
     #[inline]
     pub fn is_null_ptr(self, cx: &impl HasDataLayout) -> bool {
         match self {
index 2658d7f59a07f40f240bb0f3ac694366cb0febe1..011887090eefe8002f3ffffc9b02f2ef415b718c 100644 (file)
@@ -539,10 +539,10 @@ fn validate_const<'a, 'tcx>(
     let val = (|| {
         let op = ecx.const_to_op(constant)?;
         let mut ref_tracking = RefTracking::new(op);
-        while let Some((op, mut path)) = ref_tracking.todo.pop() {
+        while let Some((op, path)) = ref_tracking.todo.pop() {
             ecx.validate_operand(
                 op,
-                &mut path,
+                path,
                 Some(&mut ref_tracking),
                 /* const_mode */ true,
             )?;
index fc13c5fef2dda2a599fd5b5835348a0aacd91cb7..e6267012dc27587848c9b42c105f429b2cb8ded8 100644 (file)
@@ -521,7 +521,7 @@ pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx> {
                 // return place is always a local and then this cannot happen.
                 self.validate_operand(
                     self.place_to_op(return_place)?,
-                    &mut vec![],
+                    vec![],
                     None,
                     /*const_mode*/false,
                 )?;
index e9d181479e52e9962735625b392a825d6d05a8e6..27cf28ef41e8ac3a261d2d0a0a1d01dd3cdc97b7 100644 (file)
 
 use rustc::hir::{self, def_id::DefId};
 use rustc::mir;
-use rustc::ty::{self, Ty, layout::{Size, TyLayout}, query::TyCtxtAt};
+use rustc::ty::{self, layout::{Size, TyLayout}, query::TyCtxtAt};
 
 use super::{
     Allocation, AllocId, EvalResult, Scalar,
-    EvalContext, PlaceTy, OpTy, Pointer, MemPlace, MemoryKind,
+    EvalContext, PlaceTy, MPlaceTy, OpTy, Pointer, MemoryKind,
 };
 
 /// Whether this kind of memory is allowed to leak
@@ -217,26 +217,22 @@ fn tag_new_allocation(
     #[inline]
     fn tag_reference(
         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
-        place: MemPlace<Self::PointerTag>,
-        _ty: Ty<'tcx>,
-        _size: Size,
+        place: MPlaceTy<'tcx, Self::PointerTag>,
         _mutability: Option<hir::Mutability>,
-    ) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> {
-        Ok(place)
+    ) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
+        Ok(place.ptr)
     }
 
     /// Executed when evaluating the `*` operator: Following a reference.
-    /// This has the change to adjust the tag.  It should not change anything else!
+    /// This has the chance to adjust the tag.  It should not change anything else!
     /// `mutability` can be `None` in case a raw ptr is being dereferenced.
     #[inline]
     fn tag_dereference(
         _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
-        place: MemPlace<Self::PointerTag>,
-        _ty: Ty<'tcx>,
-        _size: Size,
+        place: MPlaceTy<'tcx, Self::PointerTag>,
         _mutability: Option<hir::Mutability>,
-    ) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> {
-        Ok(place)
+    ) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
+        Ok(place.ptr)
     }
 
     /// Execute a validation operation
index 6b31c675cc7d6a1a5ce9bfd881c8df696fc01372..82fe08fa038a4ad32c9deba323f34c4214aab597 100644 (file)
@@ -23,6 +23,7 @@
 mod traits;
 mod validity;
 mod intrinsics;
+mod visitor;
 
 pub use rustc::mir::interpret::*; // have all the `interpret` symbols in one place: here
 
@@ -38,4 +39,6 @@
 
 pub use self::operand::{ScalarMaybeUndef, Immediate, ImmTy, Operand, OpTy};
 
+pub use self::visitor::ValueVisitor;
+
 pub use self::validity::RefTracking;
index 6f66dd1e70a55bc0b53d75a5473fe38b0739ea3f..83a2d14b7ca4cdf9896d81e46758e697b6b67de0 100644 (file)
@@ -12,6 +12,7 @@
 //! All high-level functions to read from memory work on operands as sources.
 
 use std::convert::TryInto;
+use std::fmt;
 
 use rustc::{mir, ty};
 use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt};
@@ -36,6 +37,15 @@ fn from(s: Scalar<Tag>) -> Self {
     }
 }
 
+impl<Tag> fmt::Display for ScalarMaybeUndef<Tag> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            ScalarMaybeUndef::Undef => write!(f, "uninitialized bytes"),
+            ScalarMaybeUndef::Scalar(s) => write!(f, "{}", s),
+        }
+    }
+}
+
 impl<'tcx> ScalarMaybeUndef<()> {
     #[inline]
     pub fn with_default_tag<Tag>(self) -> ScalarMaybeUndef<Tag>
@@ -732,8 +742,12 @@ pub fn read_discriminant(
         Ok(match rval.layout.variants {
             layout::Variants::Single { .. } => bug!(),
             layout::Variants::Tagged { .. } => {
+                let bits_discr = match raw_discr.to_bits(discr_val.layout.size) {
+                    Ok(raw_discr) => raw_discr,
+                    Err(_) => return err!(InvalidDiscriminant(raw_discr.erase_tag())),
+                };
                 let real_discr = if discr_val.layout.ty.is_signed() {
-                    let i = raw_discr.to_bits(discr_val.layout.size)? as i128;
+                    let i = bits_discr as i128;
                     // going from layout tag type to typeck discriminant type
                     // requires first sign extending with the layout discriminant
                     let shift = 128 - discr_val.layout.size.bits();
@@ -748,7 +762,7 @@ pub fn read_discriminant(
                     let truncatee = sexted as u128;
                     (truncatee << shift) >> shift
                 } else {
-                    raw_discr.to_bits(discr_val.layout.size)?
+                    bits_discr
                 };
                 // Make sure we catch invalid discriminants
                 let index = rval.layout.ty
@@ -756,7 +770,7 @@ pub fn read_discriminant(
                     .expect("tagged layout for non adt")
                     .discriminants(self.tcx.tcx)
                     .position(|var| var.val == real_discr)
-                    .ok_or_else(|| EvalErrorKind::InvalidDiscriminant(real_discr))?;
+                    .ok_or_else(|| EvalErrorKind::InvalidDiscriminant(raw_discr.erase_tag()))?;
                 (real_discr, index)
             },
             layout::Variants::NicheFilling {
index fa4d31846df4a3de276c2fa8799a248c5c09e316..19430c85cf73c20877b02ab7451bcb0e19f82a2a 100644 (file)
@@ -278,11 +278,9 @@ pub fn ref_to_mplace(
         let meta = val.to_meta()?;
         let ptr = val.to_scalar_ptr()?;
         let mplace = MemPlace { ptr, align, meta };
+        let mut mplace = MPlaceTy { mplace, layout };
         // Pointer tag tracking might want to adjust the tag.
-        let mplace = if M::ENABLE_PTR_TRACKING_HOOKS {
-            let (size, _) = self.size_and_align_of(meta, layout)?
-                // for extern types, just cover what we can
-                .unwrap_or_else(|| layout.size_and_align());
+        if M::ENABLE_PTR_TRACKING_HOOKS {
             let mutbl = match val.layout.ty.sty {
                 // `builtin_deref` considers boxes immutable, that's useless for our purposes
                 ty::Ref(_, _, mutbl) => Some(mutbl),
@@ -290,11 +288,10 @@ pub fn ref_to_mplace(
                 ty::RawPtr(_) => None,
                 _ => bug!("Unexpected pointer type {}", val.layout.ty.sty),
             };
-            M::tag_dereference(self, mplace, pointee_type, size, mutbl)?
-        } else {
-            mplace
-        };
-        Ok(MPlaceTy { mplace, layout })
+            mplace.mplace.ptr = M::tag_dereference(self, mplace, mutbl)?;
+        }
+        // Done
+        Ok(mplace)
     }
 
     /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
@@ -302,18 +299,13 @@ pub fn ref_to_mplace(
     /// `mutbl` indicates whether we are create a shared or mutable ref, or a raw pointer (`None`).
     pub fn create_ref(
         &mut self,
-        place: MPlaceTy<'tcx, M::PointerTag>,
+        mut place: MPlaceTy<'tcx, M::PointerTag>,
         mutbl: Option<hir::Mutability>,
     ) -> EvalResult<'tcx, Immediate<M::PointerTag>> {
         // Pointer tag tracking might want to adjust the tag
-        let place = if M::ENABLE_PTR_TRACKING_HOOKS {
-            let (size, _) = self.size_and_align_of_mplace(place)?
-                // for extern types, just cover what we can
-                .unwrap_or_else(|| place.layout.size_and_align());
-            M::tag_reference(self, *place, place.layout.ty, size, mutbl)?
-        } else {
-            *place
-        };
+        if M::ENABLE_PTR_TRACKING_HOOKS {
+            place.mplace.ptr = M::tag_reference(self, place, mutbl)?
+        }
         Ok(match place.meta {
             None => Immediate::Scalar(place.ptr.into()),
             Some(meta) => Immediate::ScalarPair(place.ptr.into(), meta.into()),
@@ -489,6 +481,8 @@ pub fn mplace_projection(
 
     /// Get the place of a field inside the place, and also the field's type.
     /// Just a convenience function, but used quite a bit.
+    /// This is the only projection that might have a side-effect: We cannot project
+    /// into the field of a local `ScalarPair`, we have to first allocate it.
     pub fn place_field(
         &mut self,
         base: PlaceTy<'tcx, M::PointerTag>,
@@ -501,7 +495,7 @@ pub fn place_field(
     }
 
     pub fn place_downcast(
-        &mut self,
+        &self,
         base: PlaceTy<'tcx, M::PointerTag>,
         variant: usize,
     ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
@@ -643,7 +637,7 @@ pub fn write_immediate(
 
         if M::enforce_validity(self) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(self.place_to_op(dest)?, &mut vec![], None, /*const_mode*/false)?;
+            self.validate_operand(self.place_to_op(dest)?, vec![], None, /*const_mode*/false)?;
         }
 
         Ok(())
@@ -765,7 +759,7 @@ pub fn copy_op(
 
         if M::enforce_validity(self) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(self.place_to_op(dest)?, &mut vec![], None, /*const_mode*/false)?;
+            self.validate_operand(self.place_to_op(dest)?, vec![], None, /*const_mode*/false)?;
         }
 
         Ok(())
@@ -843,7 +837,7 @@ pub fn copy_op_transmute(
 
         if M::enforce_validity(self) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(dest.into(), &mut vec![], None, /*const_mode*/false)?;
+            self.validate_operand(dest.into(), vec![], None, /*const_mode*/false)?;
         }
 
         Ok(())
index 4dbae3c8c3d280ab465ff57318951287f7b74b7b..8c8b3e2ca77c49ddea6244d1ce8318a12a25a195 100644 (file)
 };
 
 use super::{
-    ImmTy, OpTy, MPlaceTy, Machine, EvalContext, ScalarMaybeUndef
+    OpTy, MPlaceTy, ImmTy, Machine, EvalContext, ValueVisitor
 };
 
 macro_rules! validation_failure {
     ($what:expr, $where:expr, $details:expr) => {{
-        let where_ = path_format($where);
+        let where_ = path_format(&$where);
         let where_ = if where_.is_empty() {
             String::new()
         } else {
@@ -37,7 +37,7 @@ macro_rules! validation_failure {
         )))
     }};
     ($what:expr, $where:expr) => {{
-        let where_ = path_format($where);
+        let where_ = path_format(&$where);
         let where_ = if where_.is_empty() {
             String::new()
         } else {
@@ -78,6 +78,7 @@ pub enum PathElem {
     TupleElem(usize),
     Deref,
     Tag,
+    DynDowncast,
 }
 
 /// State for tracking recursive validation of references
@@ -97,15 +98,6 @@ pub fn new(op: OpTy<'tcx, Tag>) -> Self {
     }
 }
 
-// Adding a Deref and making a copy of the path to be put into the queue
-// always go together.  This one does it with only new allocation.
-fn path_clone_and_deref(path: &Vec<PathElem>) -> Vec<PathElem> {
-    let mut new_path = Vec::with_capacity(path.len()+1);
-    new_path.clone_from(path);
-    new_path.push(PathElem::Deref);
-    new_path
-}
-
 /// Format a path
 fn path_format(path: &Vec<PathElem>) -> String {
     use self::PathElem::*;
@@ -113,64 +105,155 @@ fn path_format(path: &Vec<PathElem>) -> String {
     let mut out = String::new();
     for elem in path.iter() {
         match elem {
-            Field(name) => write!(out, ".{}", name).unwrap(),
-            ClosureVar(name) => write!(out, ".<closure-var({})>", name).unwrap(),
-            TupleElem(idx) => write!(out, ".{}", idx).unwrap(),
-            ArrayElem(idx) => write!(out, "[{}]", idx).unwrap(),
+            Field(name) => write!(out, ".{}", name),
+            ClosureVar(name) => write!(out, ".<closure-var({})>", name),
+            TupleElem(idx) => write!(out, ".{}", idx),
+            ArrayElem(idx) => write!(out, "[{}]", idx),
             Deref =>
                 // This does not match Rust syntax, but it is more readable for long paths -- and
                 // some of the other items here also are not Rust syntax.  Actually we can't
                 // even use the usual syntax because we are just showing the projections,
                 // not the root.
-                write!(out, ".<deref>").unwrap(),
-            Tag => write!(out, ".<enum-tag>").unwrap(),
-        }
+                write!(out, ".<deref>"),
+            Tag => write!(out, ".<enum-tag>"),
+            DynDowncast => write!(out, ".<dyn-downcast>"),
+        }.unwrap()
     }
     out
 }
 
-fn scalar_format<Tag>(value: ScalarMaybeUndef<Tag>) -> String {
-    match value {
-        ScalarMaybeUndef::Undef =>
-            "uninitialized bytes".to_owned(),
-        ScalarMaybeUndef::Scalar(Scalar::Ptr(_)) =>
-            "a pointer".to_owned(),
-        ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. }) =>
-            bits.to_string(),
+struct ValidityVisitor<'rt, 'a: 'rt, 'mir: 'rt, 'tcx: 'a+'rt+'mir, M: Machine<'a, 'mir, 'tcx>+'rt> {
+    /// The `path` may be pushed to, but the part that is present when a function
+    /// starts must not be changed!  `visit_fields` and `visit_array` rely on
+    /// this stack discipline.
+    path: Vec<PathElem>,
+    ref_tracking: Option<&'rt mut RefTracking<'tcx, M::PointerTag>>,
+    const_mode: bool,
+    ecx: &'rt EvalContext<'a, 'mir, 'tcx, M>,
+}
+
+impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> ValidityVisitor<'rt, 'a, 'mir, 'tcx, M> {
+    fn push_aggregate_field_path_elem(
+        &mut self,
+        layout: TyLayout<'tcx>,
+        field: usize,
+    ) {
+        let elem = match layout.ty.sty {
+            // generators and closures.
+            ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
+                if let Some(upvar) = self.ecx.tcx.optimized_mir(def_id).upvar_decls.get(field) {
+                    PathElem::ClosureVar(upvar.debug_name)
+                } else {
+                    // Sometimes the index is beyond the number of freevars (seen
+                    // for a generator).
+                    PathElem::ClosureVar(Symbol::intern(&field.to_string()))
+                }
+            }
+
+            // tuples
+            ty::Tuple(_) => PathElem::TupleElem(field),
+
+            // enums
+            ty::Adt(def, ..) if def.is_enum() => {
+                // we might be projecting *to* a variant, or to a field *in*a variant.
+                match layout.variants {
+                    layout::Variants::Single { index } =>
+                        // Inside a variant
+                        PathElem::Field(def.variants[index].fields[field].ident.name),
+                    _ =>
+                        // To a variant
+                        PathElem::Field(def.variants[field].name)
+                }
+            }
+
+            // other ADTs
+            ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
+
+            // arrays/slices
+            ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field),
+
+            // dyn traits
+            ty::Dynamic(..) => PathElem::DynDowncast,
+
+            // nothing else has an aggregate layout
+            _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
+        };
+        self.path.push(elem);
     }
 }
 
-impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
-    /// Make sure that `value` is valid for `ty`, *assuming* `ty` is a primitive type.
-    fn validate_primitive_type(
-        &self,
-        value: ImmTy<'tcx, M::PointerTag>,
-        path: &Vec<PathElem>,
-        ref_tracking: Option<&mut RefTracking<'tcx, M::PointerTag>>,
-        const_mode: bool,
+impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
+    ValueVisitor<'a, 'mir, 'tcx, M> for ValidityVisitor<'rt, 'a, 'mir, 'tcx, M>
+{
+    type V = OpTy<'tcx, M::PointerTag>;
+
+    #[inline(always)]
+    fn ecx(&self) -> &EvalContext<'a, 'mir, 'tcx, M> {
+        &self.ecx
+    }
+
+    #[inline]
+    fn visit_field(
+        &mut self,
+        old_op: OpTy<'tcx, M::PointerTag>,
+        field: usize,
+        new_op: OpTy<'tcx, M::PointerTag>
     ) -> EvalResult<'tcx> {
+        // Remember the old state
+        let path_len = self.path.len();
+        // Perform operation
+        self.push_aggregate_field_path_elem(old_op.layout, field);
+        self.visit_value(new_op)?;
+        // Undo changes
+        self.path.truncate(path_len);
+        Ok(())
+    }
+
+    #[inline]
+    fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> EvalResult<'tcx>
+    {
+        trace!("visit_value: {:?}, {:?}", *op, op.layout);
+        // Translate some possible errors to something nicer.
+        match self.walk_value(op) {
+            Ok(()) => Ok(()),
+            Err(err) => match err.kind {
+                EvalErrorKind::InvalidDiscriminant(val) =>
+                    validation_failure!(
+                        val, self.path, "a valid enum discriminant"
+                    ),
+                EvalErrorKind::ReadPointerAsBytes =>
+                    validation_failure!(
+                        "a pointer", self.path, "plain bytes"
+                    ),
+                _ => Err(err),
+            }
+        }
+    }
+
+    fn visit_primitive(&mut self, value: ImmTy<'tcx, M::PointerTag>) -> EvalResult<'tcx>
+    {
         // Go over all the primitive types
         let ty = value.layout.ty;
         match ty.sty {
             ty::Bool => {
                 let value = value.to_scalar_or_undef();
                 try_validation!(value.to_bool(),
-                    scalar_format(value), path, "a boolean");
+                    value, self.path, "a boolean");
             },
             ty::Char => {
                 let value = value.to_scalar_or_undef();
                 try_validation!(value.to_char(),
-                    scalar_format(value), path, "a valid unicode codepoint");
+                    value, self.path, "a valid unicode codepoint");
             },
             ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
                 // NOTE: Keep this in sync with the array optimization for int/float
                 // types below!
                 let size = value.layout.size;
                 let value = value.to_scalar_or_undef();
-                if const_mode {
+                if self.const_mode {
                     // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
                     try_validation!(value.to_bits(size),
-                        scalar_format(value), path, "initialized plain bits");
+                        value, self.path, "initialized plain bits");
                 } else {
                     // At run-time, for now, we accept *anything* for these types, including
                     // undef. We should fix that, but let's start low.
@@ -180,33 +263,33 @@ fn validate_primitive_type(
                 // No undef allowed here.  Eventually this should be consistent with
                 // the integer types.
                 let _ptr = try_validation!(value.to_scalar_ptr(),
-                    "undefined address in pointer", path);
+                    "undefined address in pointer", self.path);
                 let _meta = try_validation!(value.to_meta(),
-                    "uninitialized data in fat pointer metadata", path);
+                    "uninitialized data in fat pointer metadata", self.path);
             }
             _ if ty.is_box() || ty.is_region_ptr() => {
                 // Handle fat pointers.
                 // Check metadata early, for better diagnostics
                 let ptr = try_validation!(value.to_scalar_ptr(),
-                    "undefined address in pointer", path);
+                    "undefined address in pointer", self.path);
                 let meta = try_validation!(value.to_meta(),
-                    "uninitialized data in fat pointer metadata", path);
-                let layout = self.layout_of(value.layout.ty.builtin_deref(true).unwrap().ty)?;
+                    "uninitialized data in fat pointer metadata", self.path);
+                let layout = self.ecx.layout_of(value.layout.ty.builtin_deref(true).unwrap().ty)?;
                 if layout.is_unsized() {
-                    let tail = self.tcx.struct_tail(layout.ty);
+                    let tail = self.ecx.tcx.struct_tail(layout.ty);
                     match tail.sty {
                         ty::Dynamic(..) => {
                             let vtable = try_validation!(meta.unwrap().to_ptr(),
-                                "non-pointer vtable in fat pointer", path);
-                            try_validation!(self.read_drop_type_from_vtable(vtable),
-                                "invalid drop fn in vtable", path);
-                            try_validation!(self.read_size_and_align_from_vtable(vtable),
-                                "invalid size or align in vtable", path);
+                                "non-pointer vtable in fat pointer", self.path);
+                            try_validation!(self.ecx.read_drop_type_from_vtable(vtable),
+                                "invalid drop fn in vtable", self.path);
+                            try_validation!(self.ecx.read_size_and_align_from_vtable(vtable),
+                                "invalid size or align in vtable", self.path);
                             // FIXME: More checks for the vtable.
                         }
                         ty::Slice(..) | ty::Str => {
-                            try_validation!(meta.unwrap().to_usize(self),
-                                "non-integer slice length in fat pointer", path);
+                            try_validation!(meta.unwrap().to_usize(self.ecx),
+                                "non-integer slice length in fat pointer", self.path);
                         }
                         ty::Foreign(..) => {
                             // Unsized, but not fat.
@@ -216,25 +299,25 @@ fn validate_primitive_type(
                     }
                 }
                 // Make sure this is non-NULL and aligned
-                let (size, align) = self.size_and_align_of(meta, layout)?
+                let (size, align) = self.ecx.size_and_align_of(meta, layout)?
                     // for the purpose of validity, consider foreign types to have
                     // alignment and size determined by the layout (size will be 0,
                     // alignment should take attributes into account).
                     .unwrap_or_else(|| layout.size_and_align());
-                match self.memory.check_align(ptr, align) {
+                match self.ecx.memory.check_align(ptr, align) {
                     Ok(_) => {},
                     Err(err) => {
                         error!("{:?} is not aligned to {:?}", ptr, align);
                         match err.kind {
                             EvalErrorKind::InvalidNullPointerUsage =>
-                                return validation_failure!("NULL reference", path),
+                                return validation_failure!("NULL reference", self.path),
                             EvalErrorKind::AlignmentCheckFailed { .. } =>
-                                return validation_failure!("unaligned reference", path),
+                                return validation_failure!("unaligned reference", self.path),
                             _ =>
                                 return validation_failure!(
                                     "dangling (out-of-bounds) reference (might be NULL at \
                                         run-time)",
-                                    path
+                                    self.path
                                 ),
                         }
                     }
@@ -242,29 +325,29 @@ fn validate_primitive_type(
                 // Turn ptr into place.
                 // `ref_to_mplace` also calls the machine hook for (re)activating the tag,
                 // which in turn will (in full miri) check if the pointer is dereferencable.
-                let place = self.ref_to_mplace(value)?;
+                let place = self.ecx.ref_to_mplace(value)?;
                 // Recursive checking
-                if let Some(ref_tracking) = ref_tracking {
-                    assert!(const_mode, "We should only do recursie checking in const mode");
+                if let Some(ref mut ref_tracking) = self.ref_tracking {
+                    assert!(self.const_mode, "We should only do recursie checking in const mode");
                     if size != Size::ZERO {
                         // Non-ZST also have to be dereferencable
                         let ptr = try_validation!(place.ptr.to_ptr(),
-                            "integer pointer in non-ZST reference", path);
+                            "integer pointer in non-ZST reference", self.path);
                         // Skip validation entirely for some external statics
-                        let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id);
+                        let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id);
                         if let Some(AllocType::Static(did)) = alloc_kind {
                             // `extern static` cannot be validated as they have no body.
                             // FIXME: Statics from other crates are also skipped.
                             // They might be checked at a different type, but for now we
                             // want to avoid recursing too deeply.  This is not sound!
-                            if !did.is_local() || self.tcx.is_foreign_item(did) {
+                            if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
                                 return Ok(());
                             }
                         }
                         // Maintain the invariant that the place we are checking is
                         // already verified to be in-bounds.
-                        try_validation!(self.memory.check_bounds(ptr, size, false),
-                            "dangling (not entirely in bounds) reference", path);
+                        try_validation!(self.ecx.memory.check_bounds(ptr, size, false),
+                            "dangling (not entirely in bounds) reference", self.path);
                     }
                     // Check if we have encountered this pointer+layout combination
                     // before.  Proceed recursively even for integer pointers, no
@@ -273,35 +356,45 @@ fn validate_primitive_type(
                     let op = place.into();
                     if ref_tracking.seen.insert(op) {
                         trace!("Recursing below ptr {:#?}", *op);
-                        ref_tracking.todo.push((op, path_clone_and_deref(path)));
+                        // We need to clone the path anyway, make sure it gets created
+                        // with enough space for the additional `Deref`.
+                        let mut new_path = Vec::with_capacity(self.path.len()+1);
+                        new_path.clone_from(&self.path);
+                        new_path.push(PathElem::Deref);
+                        // Remember to come back to this later.
+                        ref_tracking.todo.push((op, new_path));
                     }
                 }
             }
             ty::FnPtr(_sig) => {
                 let value = value.to_scalar_or_undef();
                 let ptr = try_validation!(value.to_ptr(),
-                    scalar_format(value), path, "a pointer");
-                let _fn = try_validation!(self.memory.get_fn(ptr),
-                    scalar_format(value), path, "a function pointer");
+                    value, self.path, "a pointer");
+                let _fn = try_validation!(self.ecx.memory.get_fn(ptr),
+                    value, self.path, "a function pointer");
                 // FIXME: Check if the signature matches
             }
             // This should be all the primitive types
-            ty::Never => bug!("Uninhabited type should have been caught earlier"),
             _ => bug!("Unexpected primitive type {}", value.layout.ty)
         }
         Ok(())
     }
 
-    /// Make sure that `value` matches the
-    fn validate_scalar_layout(
-        &self,
-        value: ScalarMaybeUndef<M::PointerTag>,
-        size: Size,
-        path: &Vec<PathElem>,
+    fn visit_uninhabited(&mut self) -> EvalResult<'tcx>
+    {
+        validation_failure!("a value of an uninhabited type", self.path)
+    }
+
+    fn visit_scalar(
+        &mut self,
+        op: OpTy<'tcx, M::PointerTag>,
         layout: &layout::Scalar,
     ) -> EvalResult<'tcx> {
+        let value = self.ecx.read_scalar(op)?;
+        // Determine the allowed range
         let (lo, hi) = layout.valid_range.clone().into_inner();
-        let max_hi = u128::max_value() >> (128 - size.bits()); // as big as the size fits
+        // `max_hi` is as big as the size fits
+        let max_hi = u128::max_value() >> (128 - op.layout.size.bits());
         assert!(hi <= max_hi);
         // We could also write `(hi + 1) % (max_hi + 1) == lo` but `max_hi + 1` overflows for `u128`
         if (lo == 0 && hi == max_hi) || (hi + 1 == lo) {
@@ -310,7 +403,8 @@ fn validate_scalar_layout(
         }
         // At least one value is excluded. Get the bits.
         let value = try_validation!(value.not_undef(),
-            scalar_format(value), path, format!("something in the range {:?}", layout.valid_range));
+            value, self.path,
+            format!("something in the range {:?}", layout.valid_range));
         let bits = match value {
             Scalar::Ptr(ptr) => {
                 if lo == 1 && hi == max_hi {
@@ -318,13 +412,13 @@ fn validate_scalar_layout(
                     // We can call `check_align` to check non-NULL-ness, but have to also look
                     // for function pointers.
                     let non_null =
-                        self.memory.check_align(
+                        self.ecx.memory.check_align(
                             Scalar::Ptr(ptr), Align::from_bytes(1, 1).unwrap()
                         ).is_ok() ||
-                        self.memory.get_fn(ptr).is_ok();
+                        self.ecx.memory.get_fn(ptr).is_ok();
                     if !non_null {
                         // could be NULL
-                        return validation_failure!("a potentially NULL pointer", path);
+                        return validation_failure!("a potentially NULL pointer", self.path);
                     }
                     return Ok(());
                 } else {
@@ -332,7 +426,7 @@ fn validate_scalar_layout(
                     // value.
                     return validation_failure!(
                         "a pointer",
-                        path,
+                        self.path,
                         format!(
                             "something that cannot possibly be outside the (wrapping) range {:?}",
                             layout.valid_range
@@ -340,8 +434,8 @@ fn validate_scalar_layout(
                     );
                 }
             }
-            Scalar::Bits { bits, size: value_size } => {
-                assert_eq!(value_size as u64, size.bytes());
+            Scalar::Bits { bits, size } => {
+                assert_eq!(size as u64, op.layout.size.bytes());
                 bits
             }
         };
@@ -355,7 +449,7 @@ fn validate_scalar_layout(
             } else {
                 validation_failure!(
                     bits,
-                    path,
+                    self.path,
                     format!("something in the range {:?} or {:?}", 0..=hi, lo..=max_hi)
                 )
             }
@@ -365,7 +459,7 @@ fn validate_scalar_layout(
             } else {
                 validation_failure!(
                     bits,
-                    path,
+                    self.path,
                     if hi == max_hi {
                         format!("something greater or equal to {}", lo)
                     } else {
@@ -376,250 +470,110 @@ fn validate_scalar_layout(
         }
     }
 
-    /// This function checks the data at `op`.  `op` is assumed to cover valid memory if it
-    /// is an indirect operand.
-    /// It will error if the bits at the destination do not match the ones described by the layout.
-    /// The `path` may be pushed to, but the part that is present when the function
-    /// starts must not be changed!
-    ///
-    /// `ref_tracking` can be None to avoid recursive checking below references.
-    /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
-    /// validation (e.g., pointer values are fine in integers at runtime).
-    pub fn validate_operand(
-        &self,
-        dest: OpTy<'tcx, M::PointerTag>,
-        path: &mut Vec<PathElem>,
-        mut ref_tracking: Option<&mut RefTracking<'tcx, M::PointerTag>>,
-        const_mode: bool,
+    fn visit_aggregate(
+        &mut self,
+        op: OpTy<'tcx, M::PointerTag>,
+        fields: impl Iterator<Item=EvalResult<'tcx, Self::V>>,
     ) -> EvalResult<'tcx> {
-        trace!("validate_operand: {:?}, {:?}", *dest, dest.layout.ty);
-
-        // If this is a multi-variant layout, we have find the right one and proceed with that.
-        // (No good reasoning to make this recursion, but it is equivalent to that.)
-        let dest = match dest.layout.variants {
-            layout::Variants::NicheFilling { .. } |
-            layout::Variants::Tagged { .. } => {
-                let variant = match self.read_discriminant(dest) {
-                    Ok(res) => res.1,
-                    Err(err) => match err.kind {
-                        EvalErrorKind::InvalidDiscriminant(val) =>
-                            return validation_failure!(
-                                format!("invalid enum discriminant {}", val), path
-                            ),
-                        _ =>
-                            return validation_failure!(
-                                String::from("non-integer enum discriminant"), path
-                            ),
-                    }
-                };
-                // Put the variant projection onto the path, as a field
-                path.push(PathElem::Field(dest.layout.ty
-                                          .ty_adt_def()
-                                          .unwrap()
-                                          .variants[variant].name));
-                // Proceed with this variant
-                let dest = self.operand_downcast(dest, variant)?;
-                trace!("variant layout: {:#?}", dest.layout);
-                dest
-            },
-            layout::Variants::Single { .. } => dest,
-        };
-
-        // First thing, find the real type:
-        // If it is a trait object, switch to the actual type that was used to create it.
-        let dest = match dest.layout.ty.sty {
-            ty::Dynamic(..) => {
-                let dest = dest.to_mem_place(); // immediate trait objects are not a thing
-                self.unpack_dyn_trait(dest)?.1.into()
-            },
-            _ => dest
-        };
-
-        // If this is a scalar, validate the scalar layout.
-        // Things can be aggregates and have scalar layout at the same time, and that
-        // is very relevant for `NonNull` and similar structs: We need to validate them
-        // at their scalar layout *before* descending into their fields.
-        // FIXME: We could avoid some redundant checks here. For newtypes wrapping
-        // scalars, we do the same check on every "level" (e.g. first we check
-        // MyNewtype and then the scalar in there).
-        match dest.layout.abi {
-            layout::Abi::Uninhabited =>
-                return validation_failure!("a value of an uninhabited type", path),
-            layout::Abi::Scalar(ref layout) => {
-                let value = try_validation!(self.read_scalar(dest),
-                            "uninitialized or unrepresentable data", path);
-                self.validate_scalar_layout(value, dest.layout.size, &path, layout)?;
+        match op.layout.ty.sty {
+            ty::Str => {
+                let mplace = op.to_mem_place(); // strings are never immediate
+                try_validation!(self.ecx.read_str(mplace),
+                    "uninitialized or non-UTF-8 data in str", self.path);
             }
-            // FIXME: Should we do something for ScalarPair? Vector?
-            _ => {}
-        }
-
-        // Check primitive types.  We do this after checking the scalar layout,
-        // just to have that done as well.  Primitives can have varying layout,
-        // so we check them separately and before aggregate handling.
-        // It is CRITICAL that we get this check right, or we might be
-        // validating the wrong thing!
-        let primitive = match dest.layout.fields {
-            // Primitives appear as Union with 0 fields -- except for fat pointers.
-            layout::FieldPlacement::Union(0) => true,
-            _ => dest.layout.ty.builtin_deref(true).is_some(),
-        };
-        if primitive {
-            let value = try_validation!(self.read_immediate(dest),
-                "uninitialized or unrepresentable data", path);
-            return self.validate_primitive_type(
-                value,
-                &path,
-                ref_tracking,
-                const_mode,
-            );
-        }
-
-        // Validate all fields of compound data structures
-        let path_len = path.len(); // Remember the length, in case we need to truncate
-        match dest.layout.fields {
-            layout::FieldPlacement::Union(fields) => {
-                // Empty unions are not accepted by rustc. That's great, it means we can
-                // use that as an unambiguous signal for detecting primitives.  Make sure
-                // we did not miss any primitive.
-                debug_assert!(fields > 0);
-                // We can't check unions, their bits are allowed to be anything.
-                // The fields don't need to correspond to any bit pattern of the union's fields.
-                // See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
-            },
-            layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
-                // Go look at all the fields
-                for i in 0..offsets.len() {
-                    let field = self.operand_field(dest, i as u64)?;
-                    path.push(self.aggregate_field_path_elem(dest.layout, i));
-                    self.validate_operand(
-                        field,
-                        path,
-                        ref_tracking.as_mut().map(|r| &mut **r),
-                        const_mode,
-                    )?;
-                    path.truncate(path_len);
+            ty::Array(tys, ..) | ty::Slice(tys) if {
+                // This optimization applies only for integer and floating point types
+                // (i.e., types that can hold arbitrary bytes).
+                match tys.sty {
+                    ty::Int(..) | ty::Uint(..) | ty::Float(..) => true,
+                    _ => false,
                 }
-            }
-            layout::FieldPlacement::Array { stride, .. } => {
-                let dest = if dest.layout.is_zst() {
+            } => {
+                let mplace = if op.layout.is_zst() {
                     // it's a ZST, the memory content cannot matter
-                    MPlaceTy::dangling(dest.layout, self)
+                    MPlaceTy::dangling(op.layout, self.ecx)
                 } else {
                     // non-ZST array/slice/str cannot be immediate
-                    dest.to_mem_place()
+                    op.to_mem_place()
                 };
-                match dest.layout.ty.sty {
-                    // Special handling for strings to verify UTF-8
-                    ty::Str => {
-                        try_validation!(self.read_str(dest),
-                            "uninitialized or non-UTF-8 data in str", path);
-                    }
-                    // Special handling for arrays/slices of builtin integer types
-                    ty::Array(tys, ..) | ty::Slice(tys) if {
-                        // This optimization applies only for integer and floating point types
-                        // (i.e., types that can hold arbitrary bytes).
-                        match tys.sty {
-                            ty::Int(..) | ty::Uint(..) | ty::Float(..) => true,
-                            _ => false,
-                        }
-                    } => {
-                        // This is the length of the array/slice.
-                        let len = dest.len(self)?;
-                        // Since primitive types are naturally aligned and tightly packed in arrays,
-                        // we can use the stride to get the size of the integral type.
-                        let ty_size = stride.bytes();
-                        // This is the size in bytes of the whole array.
-                        let size = Size::from_bytes(ty_size * len);
-
-                        // NOTE: Keep this in sync with the handling of integer and float
-                        // types above, in `validate_primitive_type`.
-                        // In run-time mode, we accept pointers in here.  This is actually more
-                        // permissive than a per-element check would be, e.g. we accept
-                        // an &[u8] that contains a pointer even though bytewise checking would
-                        // reject it.  However, that's good: We don't inherently want
-                        // to reject those pointers, we just do not have the machinery to
-                        // talk about parts of a pointer.
-                        // We also accept undef, for consistency with the type-based checks.
-                        match self.memory.check_bytes(
-                            dest.ptr,
-                            size,
-                            /*allow_ptr_and_undef*/!const_mode,
-                        ) {
-                            // In the happy case, we needn't check anything else.
-                            Ok(()) => {},
-                            // Some error happened, try to provide a more detailed description.
-                            Err(err) => {
-                                // For some errors we might be able to provide extra information
-                                match err.kind {
-                                    EvalErrorKind::ReadUndefBytes(offset) => {
-                                        // Some byte was undefined, determine which
-                                        // element that byte belongs to so we can
-                                        // provide an index.
-                                        let i = (offset.bytes() / ty_size) as usize;
-                                        path.push(PathElem::ArrayElem(i));
-
-                                        return validation_failure!(
-                                            "undefined bytes", path
-                                        )
-                                    },
-                                    // Other errors shouldn't be possible
-                                    _ => return Err(err),
-                                }
-                            }
-                        }
-                    },
-                    _ => {
-                        // This handles the unsized case correctly as well, as well as
-                        // SIMD an all sorts of other array-like types.
-                        for (i, field) in self.mplace_array_fields(dest)?.enumerate() {
-                            let field = field?;
-                            path.push(PathElem::ArrayElem(i));
-                            self.validate_operand(
-                                field.into(),
-                                path,
-                                ref_tracking.as_mut().map(|r| &mut **r),
-                                const_mode,
-                            )?;
-                            path.truncate(path_len);
+                // This is the length of the array/slice.
+                let len = mplace.len(self.ecx)?;
+                // This is the element type size.
+                let ty_size = self.ecx.layout_of(tys)?.size;
+                // This is the size in bytes of the whole array.
+                let size = ty_size * len;
+
+                // NOTE: Keep this in sync with the handling of integer and float
+                // types above, in `visit_primitive`.
+                // In run-time mode, we accept pointers in here.  This is actually more
+                // permissive than a per-element check would be, e.g. we accept
+                // an &[u8] that contains a pointer even though bytewise checking would
+                // reject it.  However, that's good: We don't inherently want
+                // to reject those pointers, we just do not have the machinery to
+                // talk about parts of a pointer.
+                // We also accept undef, for consistency with the type-based checks.
+                match self.ecx.memory.check_bytes(
+                    mplace.ptr,
+                    size,
+                    /*allow_ptr_and_undef*/!self.const_mode,
+                ) {
+                    // In the happy case, we needn't check anything else.
+                    Ok(()) => {},
+                    // Some error happened, try to provide a more detailed description.
+                    Err(err) => {
+                        // For some errors we might be able to provide extra information
+                        match err.kind {
+                            EvalErrorKind::ReadUndefBytes(offset) => {
+                                // Some byte was undefined, determine which
+                                // element that byte belongs to so we can
+                                // provide an index.
+                                let i = (offset.bytes() / ty_size.bytes()) as usize;
+                                self.path.push(PathElem::ArrayElem(i));
+
+                                return validation_failure!(
+                                    "undefined bytes", self.path
+                                )
+                            },
+                            // Other errors shouldn't be possible
+                            _ => return Err(err),
                         }
                     }
                 }
-            },
+            }
+            _ => {
+                self.walk_aggregate(op, fields)? // default handler
+            }
         }
         Ok(())
     }
+}
 
-    fn aggregate_field_path_elem(&self, layout: TyLayout<'tcx>, field: usize) -> PathElem {
-        match layout.ty.sty {
-            // generators and closures.
-            ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
-                if let Some(upvar) = self.tcx.optimized_mir(def_id).upvar_decls.get(field) {
-                    PathElem::ClosureVar(upvar.debug_name)
-                } else {
-                    // Sometimes the index is beyond the number of freevars (seen
-                    // for a generator).
-                    PathElem::ClosureVar(Symbol::intern(&field.to_string()))
-                }
-            }
-
-            // tuples
-            ty::Tuple(_) => PathElem::TupleElem(field),
-
-            // enums
-            ty::Adt(def, ..) if def.is_enum() => {
-                let variant = match layout.variants {
-                    layout::Variants::Single { index } => &def.variants[index],
-                    _ => bug!("aggregate_field_path_elem: got enum but not in a specific variant"),
-                };
-                PathElem::Field(variant.fields[field].ident.name)
-            }
+impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
+    /// This function checks the data at `op`.  `op` is assumed to cover valid memory if it
+    /// is an indirect operand.
+    /// It will error if the bits at the destination do not match the ones described by the layout.
+    ///
+    /// `ref_tracking` can be None to avoid recursive checking below references.
+    /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
+    /// validation (e.g., pointer values are fine in integers at runtime).
+    pub fn validate_operand(
+        &self,
+        op: OpTy<'tcx, M::PointerTag>,
+        path: Vec<PathElem>,
+        ref_tracking: Option<&mut RefTracking<'tcx, M::PointerTag>>,
+        const_mode: bool,
+    ) -> EvalResult<'tcx> {
+        trace!("validate_operand: {:?}, {:?}", *op, op.layout.ty);
 
-            // other ADTs
-            ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
+        // Construct a visitor
+        let mut visitor = ValidityVisitor {
+            path,
+            ref_tracking,
+            const_mode,
+            ecx: self,
+        };
 
-            // nothing else has an aggregate layout
-            _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
-        }
+        // Run it
+        visitor.visit_value(op)
     }
 }
diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs
new file mode 100644 (file)
index 0000000..392e279
--- /dev/null
@@ -0,0 +1,320 @@
+//! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound
+//! types until we arrive at the leaves, with custom handling for primitive types.
+
+use rustc::ty::layout::{self, TyLayout};
+use rustc::ty;
+use rustc::mir::interpret::{
+    EvalResult,
+};
+
+use super::{
+    Machine, EvalContext, MPlaceTy, OpTy, ImmTy,
+};
+
+// A thing that we can project into, and that has a layout.
+// This wouldn't have to depend on `Machine` but with the current type inference,
+// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
+pub trait Value<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: Copy
+{
+    /// Get this value's layout.
+    fn layout(&self) -> TyLayout<'tcx>;
+
+    /// Make this into an `OpTy`.
+    fn to_op(
+        self,
+        ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+    ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>>;
+
+    /// Create this from an `MPlaceTy`.
+    fn from_mem_place(MPlaceTy<'tcx, M::PointerTag>) -> Self;
+
+    /// Project to the given enum variant.
+    fn project_downcast(
+        self,
+        ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+        variant: usize,
+    ) -> EvalResult<'tcx, Self>;
+
+    /// Project to the n-th field.
+    fn project_field(
+        self,
+        ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+        field: u64,
+    ) -> EvalResult<'tcx, Self>;
+}
+
+// Operands and memory-places are both values.
+// Places in general are not due to `place_field` having to do `force_allocation`.
+impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
+    for OpTy<'tcx, M::PointerTag>
+{
+    #[inline(always)]
+    fn layout(&self) -> TyLayout<'tcx> {
+        self.layout
+    }
+
+    #[inline(always)]
+    fn to_op(
+        self,
+        _ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+    ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        Ok(self)
+    }
+
+    #[inline(always)]
+    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
+        mplace.into()
+    }
+
+    #[inline(always)]
+    fn project_downcast(
+        self,
+        ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+        variant: usize,
+    ) -> EvalResult<'tcx, Self> {
+        ecx.operand_downcast(self, variant)
+    }
+
+    #[inline(always)]
+    fn project_field(
+        self,
+        ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+        field: u64,
+    ) -> EvalResult<'tcx, Self> {
+        ecx.operand_field(self, field)
+    }
+}
+impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
+    for MPlaceTy<'tcx, M::PointerTag>
+{
+    #[inline(always)]
+    fn layout(&self) -> TyLayout<'tcx> {
+        self.layout
+    }
+
+    #[inline(always)]
+    fn to_op(
+        self,
+        _ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+    ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        Ok(self.into())
+    }
+
+    #[inline(always)]
+    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
+        mplace
+    }
+
+    #[inline(always)]
+    fn project_downcast(
+        self,
+        ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+        variant: usize,
+    ) -> EvalResult<'tcx, Self> {
+        ecx.mplace_downcast(self, variant)
+    }
+
+    #[inline(always)]
+    fn project_field(
+        self,
+        ecx: &EvalContext<'a, 'mir, 'tcx, M>,
+        field: u64,
+    ) -> EvalResult<'tcx, Self> {
+        ecx.mplace_field(self, field)
+    }
+}
+
+macro_rules! make_value_visitor {
+    ($visitor_trait_name:ident, $($mutability:ident)*) => {
+        // How to traverse a value and what to do when we are at the leaves.
+        pub trait $visitor_trait_name<'a, 'mir, 'tcx: 'mir+'a, M: Machine<'a, 'mir, 'tcx>>: Sized {
+            type V: Value<'a, 'mir, 'tcx, M>;
+
+            /// The visitor must have an `EvalContext` in it.
+            fn ecx(&$($mutability)* self)
+                -> &$($mutability)* EvalContext<'a, 'mir, 'tcx, M>;
+
+            // Recursive actions, ready to be overloaded.
+            /// Visit the given value, dispatching as appropriate to more specialized visitors.
+            #[inline(always)]
+            fn visit_value(&mut self, v: Self::V) -> EvalResult<'tcx>
+            {
+                self.walk_value(v)
+            }
+            /// Visit the given value as a union.  No automatic recursion can happen here.
+            #[inline(always)]
+            fn visit_union(&mut self, _v: Self::V) -> EvalResult<'tcx>
+            {
+                Ok(())
+            }
+            /// Visit this vale as an aggregate, you are even getting an iterator yielding
+            /// all the fields (still in an `EvalResult`, you have to do error handling yourself).
+            /// Recurses into the fields.
+            #[inline(always)]
+            fn visit_aggregate(
+                &mut self,
+                v: Self::V,
+                fields: impl Iterator<Item=EvalResult<'tcx, Self::V>>,
+            ) -> EvalResult<'tcx> {
+                self.walk_aggregate(v, fields)
+            }
+            /// Called each time we recurse down to a field, passing in old and new value.
+            /// This gives the visitor the chance to track the stack of nested fields that
+            /// we are descending through.
+            #[inline(always)]
+            fn visit_field(
+                &mut self,
+                _old_val: Self::V,
+                _field: usize,
+                new_val: Self::V,
+            ) -> EvalResult<'tcx> {
+                self.visit_value(new_val)
+            }
+
+            /// Called whenever we reach a value with uninhabited layout.
+            /// Recursing to fields will *always* continue after this!  This is not meant to control
+            /// whether and how we descend recursively/ into the scalar's fields if there are any,
+            /// it is meant to provide the chance for additional checks when a value of uninhabited
+            /// layout is detected.
+            #[inline(always)]
+            fn visit_uninhabited(&mut self) -> EvalResult<'tcx>
+            { Ok(()) }
+            /// Called whenever we reach a value with scalar layout.
+            /// We do NOT provide a `ScalarMaybeUndef` here to avoid accessing memory if the
+            /// visitor is not even interested in scalars.
+            /// Recursing to fields will *always* continue after this!  This is not meant to control
+            /// whether and how we descend recursively/ into the scalar's fields if there are any,
+            /// it is meant to provide the chance for additional checks when a value of scalar
+            /// layout is detected.
+            #[inline(always)]
+            fn visit_scalar(&mut self, _v: Self::V, _layout: &layout::Scalar) -> EvalResult<'tcx>
+            { Ok(()) }
+
+            /// Called whenever we reach a value of primitive type.  There can be no recursion
+            /// below such a value.  This is the leave function.
+            #[inline(always)]
+            fn visit_primitive(&mut self, _val: ImmTy<'tcx, M::PointerTag>) -> EvalResult<'tcx>
+            { Ok(()) }
+
+            // Default recursors. Not meant to be overloaded.
+            fn walk_aggregate(
+                &mut self,
+                v: Self::V,
+                fields: impl Iterator<Item=EvalResult<'tcx, Self::V>>,
+            ) -> EvalResult<'tcx> {
+                // Now iterate over it.
+                for (idx, field_val) in fields.enumerate() {
+                    self.visit_field(v, idx, field_val?)?;
+                }
+                Ok(())
+            }
+            fn walk_value(&mut self, v: Self::V) -> EvalResult<'tcx>
+            {
+                trace!("walk_value: type: {}", v.layout().ty);
+                // If this is a multi-variant layout, we have find the right one and proceed with
+                // that.
+                match v.layout().variants {
+                    layout::Variants::NicheFilling { .. } |
+                    layout::Variants::Tagged { .. } => {
+                        let op = v.to_op(self.ecx())?;
+                        let idx = self.ecx().read_discriminant(op)?.1;
+                        let inner = v.project_downcast(self.ecx(), idx)?;
+                        trace!("walk_value: variant layout: {:#?}", inner.layout());
+                        // recurse with the inner type
+                        return self.visit_field(v, idx, inner);
+                    }
+                    layout::Variants::Single { .. } => {}
+                }
+
+                // Even for single variants, we might be able to get a more refined type:
+                // If it is a trait object, switch to the actual type that was used to create it.
+                match v.layout().ty.sty {
+                    ty::Dynamic(..) => {
+                        // immediate trait objects are not a thing
+                        let dest = v.to_op(self.ecx())?.to_mem_place();
+                        let inner = self.ecx().unpack_dyn_trait(dest)?.1;
+                        trace!("walk_value: dyn object layout: {:#?}", inner.layout);
+                        // recurse with the inner type
+                        return self.visit_field(v, 0, Value::from_mem_place(inner));
+                    },
+                    _ => {},
+                };
+
+                // If this is a scalar, visit it as such.
+                // Things can be aggregates and have scalar layout at the same time, and that
+                // is very relevant for `NonNull` and similar structs: We need to visit them
+                // at their scalar layout *before* descending into their fields.
+                // FIXME: We could avoid some redundant checks here. For newtypes wrapping
+                // scalars, we do the same check on every "level" (e.g. first we check
+                // MyNewtype and then the scalar in there).
+                match v.layout().abi {
+                    layout::Abi::Uninhabited => {
+                        self.visit_uninhabited()?;
+                    }
+                    layout::Abi::Scalar(ref layout) => {
+                        self.visit_scalar(v, layout)?;
+                    }
+                    // FIXME: Should we do something for ScalarPair? Vector?
+                    _ => {}
+                }
+
+                // Check primitive types.  We do this after checking the scalar layout,
+                // just to have that done as well.  Primitives can have varying layout,
+                // so we check them separately and before aggregate handling.
+                // It is CRITICAL that we get this check right, or we might be
+                // validating the wrong thing!
+                let primitive = match v.layout().fields {
+                    // Primitives appear as Union with 0 fields - except for Boxes and fat pointers.
+                    layout::FieldPlacement::Union(0) => true,
+                    _ => v.layout().ty.builtin_deref(true).is_some(),
+                };
+                if primitive {
+                    let op = v.to_op(self.ecx())?;
+                    let val = self.ecx().read_immediate(op)?;
+                    return self.visit_primitive(val);
+                }
+
+                // Proceed into the fields.
+                match v.layout().fields {
+                    layout::FieldPlacement::Union(fields) => {
+                        // Empty unions are not accepted by rustc. That's great, it means we can
+                        // use that as an unambiguous signal for detecting primitives.  Make sure
+                        // we did not miss any primitive.
+                        debug_assert!(fields > 0);
+                        self.visit_union(v)?;
+                    },
+                    layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
+                        // FIXME: We collect in a vec because otherwise there are lifetime errors:
+                        // Projecting to a field needs (mutable!) access to `ecx`.
+                        let fields: Vec<EvalResult<'tcx, Self::V>> =
+                            (0..offsets.len()).map(|i| {
+                                v.project_field(self.ecx(), i as u64)
+                            })
+                            .collect();
+                        self.visit_aggregate(v, fields.into_iter())?;
+                    },
+                    layout::FieldPlacement::Array { .. } => {
+                        // Let's get an mplace first.
+                        let mplace = if v.layout().is_zst() {
+                            // it's a ZST, the memory content cannot matter
+                            MPlaceTy::dangling(v.layout(), self.ecx())
+                        } else {
+                            // non-ZST array/slice/str cannot be immediate
+                            v.to_op(self.ecx())?.to_mem_place()
+                        };
+                        // Now we can go over all the fields.
+                        let iter = self.ecx().mplace_array_fields(mplace)?
+                            .map(|f| f.and_then(|f| {
+                                Ok(Value::from_mem_place(f))
+                            }));
+                        self.visit_aggregate(v, iter)?;
+                    }
+                }
+                Ok(())
+            }
+        }
+    }
+}
+
+make_value_visitor!(ValueVisitor,);
+make_value_visitor!(MutValueVisitor,mut);
index 28825477c810276f25c5b30876d49a5aab3eb839..7e82d4fc264f564ca81820fdffe93d4215a3898f 100644 (file)
@@ -5,7 +5,7 @@ LL | / static FOO: (&Foo, &Bar) = unsafe {( //~ undefined behavior
 LL | |     Union { u8: &BAR }.foo,
 LL | |     Union { u8: &BAR }.bar,
 LL | | )};
-   | |___^ type validation failed: encountered invalid enum discriminant 5 at .1.<deref>
+   | |___^ type validation failed: encountered 5 at .1.<deref>, but expected a valid enum discriminant
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
index 2f7a5dda9ffcbd4d52355a26e530a6e1ccc9a69b..309627581d0512cd5a881ceb013b7f03cdb7d306 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(const_err)] // make sure we cannot allow away the errors tested here
+
 #[repr(usize)]
 #[derive(Copy, Clone)]
 enum Enum {
@@ -31,10 +33,15 @@ enum Enum2 {
 union TransmuteEnum2 {
     a: usize,
     b: Enum2,
+    c: (),
 }
 const BAD_ENUM2 : Enum2 = unsafe { TransmuteEnum2 { a: 0 }.b };
 //~^ ERROR is undefined behavior
 
+// Undef enum discriminant. In an arry to avoid `Scalar` layout.
+const BAD_ENUM3 : [Enum2; 2] = [unsafe { TransmuteEnum2 { c: () }.b }; 2];
+//~^ ERROR is undefined behavior
+
 // Invalid enum field content (mostly to test printing of apths for enum tuple
 // variants and tuples).
 union TransmuteChar {
index 4cbaa2f3a906fe9319fe0e72f00469b39db2a2d7..57c41711536d9485749b501a0f5cf85d87258306 100644 (file)
@@ -1,27 +1,35 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-enum.rs:22:1
+  --> $DIR/ub-enum.rs:24:1
    |
 LL | const BAD_ENUM: Enum = unsafe { TransmuteEnum { a: &1 }.b };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer enum discriminant
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected a valid enum discriminant
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-enum.rs:35:1
+  --> $DIR/ub-enum.rs:38:1
    |
 LL | const BAD_ENUM2 : Enum2 = unsafe { TransmuteEnum2 { a: 0 }.b };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid enum discriminant 0
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected a valid enum discriminant
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-enum.rs:45:1
+  --> $DIR/ub-enum.rs:42:1
+   |
+LL | const BAD_ENUM3 : [Enum2; 2] = [unsafe { TransmuteEnum2 { c: () }.b }; 2];
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/ub-enum.rs:52:1
    |
 LL | const BAD_ENUM_CHAR : Option<(char, char)> = Some(('x', unsafe { TransmuteChar { a: !0 }.b }));
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 4294967295 at .Some.0.1, but expected something in the range 0..=1114111
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
index 6d6ad38afdb96127c874653643bd779b61195e9a..8b83a1747cabdebeff01800942f1cee77ca467f9 100644 (file)
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![feature(const_transmute)]
+#![allow(const_err)] // make sure we cannot allow away the errors tested here
 
 use std::mem;
 use std::ptr::NonNull;
index 1fdea3f38d38d5ca4f6ed9e6cb14573689bd6ee6..7c0ff851d8848c96da8f7caa6c516202b16ca0dc 100644 (file)
@@ -1,5 +1,5 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-nonnull.rs:17:1
+  --> $DIR/ub-nonnull.rs:18:1
    |
 LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1
@@ -7,7 +7,7 @@ LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) };
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-nonnull.rs:20:1
+  --> $DIR/ub-nonnull.rs:21:1
    |
 LL | const NULL_U8: NonZeroU8 = unsafe { mem::transmute(0u8) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1
@@ -15,7 +15,7 @@ LL | const NULL_U8: NonZeroU8 = unsafe { mem::transmute(0u8) };
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-nonnull.rs:22:1
+  --> $DIR/ub-nonnull.rs:23:1
    |
 LL | const NULL_USIZE: NonZeroUsize = unsafe { mem::transmute(0usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1
index 2c2abd9127582a32dd3178ebf13bab47fd4814a0..c0dd94a375bcbb0c69d36886fa3e582b51a63619 100644 (file)
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![feature(const_transmute)]
+#![allow(const_err)] // make sure we cannot allow away the errors tested here
 
 use std::mem;
 
index 4ae4640d074e3a2b3b750fae93d45256d6a3ed23..c3f5f4a26f55594cbc6b2545df8774de73d588ab 100644 (file)
@@ -1,5 +1,5 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref.rs:15:1
+  --> $DIR/ub-ref.rs:16:1
    |
 LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned reference
@@ -7,7 +7,7 @@ LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) };
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref.rs:18:1
+  --> $DIR/ub-ref.rs:19:1
    |
 LL | const NULL: &u16 = unsafe { mem::transmute(0usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1
@@ -15,7 +15,7 @@ LL | const NULL: &u16 = unsafe { mem::transmute(0usize) };
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref.rs:21:1
+  --> $DIR/ub-ref.rs:22:1
    |
 LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
@@ -23,15 +23,15 @@ LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref.rs:24:1
+  --> $DIR/ub-ref.rs:25:1
    |
 LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a raw memory access tried to access part of a pointer value as raw bytes
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .<deref>, but expected plain bytes
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref.rs:27:1
+  --> $DIR/ub-ref.rs:28:1
    |
 LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered integer pointer in non-ZST reference
index 22262fd0b977449c558ec1acdb8305c6fc410f1b..74713af2ea033114ee9ed28e9b45338efa55f52f 100644 (file)
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![feature(const_transmute)]
+#![allow(const_err)] // make sure we cannot allow away the errors tested here
 
 use std::mem;
 
index 2ac0a6e4e86739589476e6ea6f089d8e33da36ca..c5ac72b639c05a94292c40ec0722d284d9c6633a 100644 (file)
@@ -1,5 +1,5 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-uninhabit.rs:18:1
+  --> $DIR/ub-uninhabit.rs:19:1
    |
 LL | const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
@@ -7,7 +7,7 @@ LL | const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) };
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-uninhabit.rs:21:1
+  --> $DIR/ub-uninhabit.rs:22:1
    |
 LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref>
@@ -15,7 +15,7 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-uninhabit.rs:24:1
+  --> $DIR/ub-uninhabit.rs:25:1
    |
 LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at [0]
index f591a5affe5da2b2adb85ef0bba8baf57493228b..6661de4ab2cb586bcaa737211848c2a56d51ca7b 100644 (file)
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![feature(const_transmute,const_let)]
+#![allow(const_err)] // make sure we cannot allow away the errors tested here
 
 use std::mem;
 
index 178f80f88e8d6c4a05c189f44eb4481148cd4824..3617a53a9788da4f2fde860a1f3e0ebc480f019d 100644 (file)
@@ -1,12 +1,12 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-upvars.rs:15:1
+  --> $DIR/ub-upvars.rs:16:1
    |
 LL | / const BAD_UPVAR: &FnOnce() = &{ //~ ERROR it is undefined behavior to use this value
 LL | |     let bad_ref: &'static u16 = unsafe { mem::transmute(0usize) };
 LL | |     let another_var = 13;
 LL | |     move || { let _ = bad_ref; let _ = another_var; }
 LL | | };
-   | |__^ type validation failed: encountered 0 at .<deref>.<closure-var(bad_ref)>, but expected something greater or equal to 1
+   | |__^ type validation failed: encountered 0 at .<deref>.<dyn-downcast>.<closure-var(bad_ref)>, but expected something greater or equal to 1
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
index 479cee92b94b375258f9060998dda38d97d79676..31540b46631c0558b181344158f8588b75131452 100644 (file)
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![allow(unused)]
+#![allow(const_err)] // make sure we cannot allow away the errors tested here
 
 // normalize-stderr-test "alignment \d+" -> "alignment N"
 // normalize-stderr-test "offset \d+" -> "offset N"
index b61ea9ca6f95b3f482333c03c32da9ab3d2992f2..9a799b98cfc573e77b2f7971b3f1beccfb77d303 100644 (file)
@@ -1,5 +1,5 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:87:1
+  --> $DIR/union-ub-fat-ptr.rs:88:1
    |
 LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling (not entirely in bounds) reference
@@ -7,7 +7,7 @@ LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len:
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:90:1
+  --> $DIR/union-ub-fat-ptr.rs:91:1
    |
 LL | const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat pointer
@@ -15,7 +15,7 @@ LL | const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:93:1
+  --> $DIR/union-ub-fat-ptr.rs:94:1
    |
 LL | const C2: &MyStr = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat pointer
@@ -23,7 +23,7 @@ LL | const C2: &MyStr = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42,
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:99:1
+  --> $DIR/union-ub-fat-ptr.rs:100:1
    |
 LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling (not entirely in bounds) reference
@@ -31,7 +31,7 @@ LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:102:1
+  --> $DIR/union-ub-fat-ptr.rs:103:1
    |
 LL | const C3: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat pointer
@@ -39,7 +39,7 @@ LL | const C3: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, l
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:106:1
+  --> $DIR/union-ub-fat-ptr.rs:107:1
    |
 LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop fn in vtable
@@ -47,7 +47,7 @@ LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable:
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:109:1
+  --> $DIR/union-ub-fat-ptr.rs:110:1
    |
 LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop fn in vtable
@@ -55,7 +55,7 @@ LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtabl
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:112:1
+  --> $DIR/union-ub-fat-ptr.rs:113:1
    |
 LL | const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-pointer vtable in fat pointer
@@ -63,15 +63,15 @@ LL | const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtabl
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:116:1
+  --> $DIR/union-ub-fat-ptr.rs:117:1
    |
 LL | const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>, but expected something in the range 0..=1
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>.<dyn-downcast>, but expected something in the range 0..=1
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:120:1
+  --> $DIR/union-ub-fat-ptr.rs:121:1
    |
 LL | const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>[0], but expected something in the range 0..=1
@@ -79,7 +79,7 @@ LL | const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:126:1
+  --> $DIR/union-ub-fat-ptr.rs:127:1
    |
 LL | const I2: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [false]);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>.0, but expected something in the range 0..=1
@@ -87,7 +87,7 @@ LL | const I2: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:129:1
+  --> $DIR/union-ub-fat-ptr.rs:130:1
    |
 LL | const I3: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.bl }]);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>.1[0], but expected something in the range 0..=1
@@ -95,7 +95,7 @@ LL | const I3: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:133:1
+  --> $DIR/union-ub-fat-ptr.rs:134:1
    |
 LL | const J1: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at .<deref>
@@ -103,7 +103,7 @@ LL | const J1: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str };
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub-fat-ptr.rs:136:1
+  --> $DIR/union-ub-fat-ptr.rs:137:1
    |
 LL | const J2: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at .<deref>.0
index 008f1f2364866b7d2bbca736190c0fad24c9a17d..712147b52e96465dda4024e8aef4f89af5963317 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(const_err)] // make sure we cannot allow away the errors tested here
+
 union DummyUnion {
     u8: u8,
     bool: bool,
index bb916ddbbcfd797eda1e48984b7786172949fd19..2b6f6a47cd92f67c9e31c2fad99384485820afdb 100644 (file)
@@ -1,5 +1,5 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/union-ub.rs:36:1
+  --> $DIR/union-ub.rs:38:1
    |
 LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool};
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected something in the range 0..=1