diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 3ff0034fbbee7..e83b514c69190 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -560,6 +560,10 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> { a.hash_stable(hcx, hasher); b.hash_stable(hcx, hasher) }, + FunctionRetMismatch(a, b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, NoMirFor(ref s) => s.hash_stable(hcx, hasher), UnterminatedCString(ptr) => ptr.hash_stable(hcx, hasher), PointerOutOfBounds { diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 566673857b975..fe466e247c917 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -186,6 +186,7 @@ pub enum EvalErrorKind<'tcx, O> { FunctionAbiMismatch(Abi, Abi), FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>), + FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>), FunctionArgCountMismatch, NoMirFor(String), UnterminatedCString(Pointer), @@ -294,7 +295,8 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> { use self::EvalErrorKind::*; match *self { MachineError(ref inner) => inner, - FunctionAbiMismatch(..) | FunctionArgMismatch(..) | FunctionArgCountMismatch => + FunctionAbiMismatch(..) | FunctionArgMismatch(..) | FunctionRetMismatch(..) + | FunctionArgCountMismatch => "tried to call a function through a function pointer of incompatible type", InvalidMemoryAccess => "tried to access memory through an invalid pointer", @@ -470,6 +472,10 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> { write!(f, "tried to call a function with argument of type {:?} \ passing data of type {:?}", callee_ty, caller_ty), + FunctionRetMismatch(caller_ty, callee_ty) => + write!(f, "tried to call a function with return type {:?} \ + passing return place of type {:?}", + callee_ty, caller_ty), FunctionArgCountMismatch => write!(f, "tried to call a function with incorrect number of arguments"), BoundsCheck { ref len, ref index } => diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 83a9491cf4673..3f62883c6a57b 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -492,6 +492,10 @@ impl<'a, 'tcx, O: Lift<'tcx>> Lift<'tcx> for interpret::EvalErrorKind<'a, O> { tcx.lift(&a)?, tcx.lift(&b)?, ), + FunctionRetMismatch(a, b) => FunctionRetMismatch( + tcx.lift(&a)?, + tcx.lift(&b)?, + ), FunctionArgCountMismatch => FunctionArgCountMismatch, NoMirFor(ref s) => NoMirFor(s.clone()), UnterminatedCString(ptr) => UnterminatedCString(ptr), diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index f04e5496ce443..d18edf22dc10c 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1558,15 +1558,13 @@ fn validate_const<'a, 'tcx>( let ecx = ::rustc_mir::const_eval::mk_eval_cx(tcx, gid.instance, param_env).unwrap(); let result = (|| { let op = ecx.const_to_op(constant)?; - let mut todo = vec![(op, Vec::new())]; - let mut seen = FxHashSet(); - seen.insert(op); - while let Some((op, mut path)) = todo.pop() { + let mut ref_tracking = ::rustc_mir::interpret::RefTracking::new(op); + while let Some((op, mut path)) = ref_tracking.todo.pop() { ecx.validate_operand( op, &mut path, - &mut seen, - &mut todo, + Some(&mut ref_tracking), + /* const_mode */ true, )?; } Ok(()) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 3b1eba51aaf45..0c669b9ec31a7 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -274,6 +274,7 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> type MemoryKinds = !; const MUT_STATIC_KIND: Option = None; // no mutating of statics allowed + const ENFORCE_VALIDITY: bool = false; // for now, we don't fn find_fn( ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index ff059e7d1853b..e15a721731e87 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -231,6 +231,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc /// Mark a storage as live, killing the previous content and returning it. /// Remember to deallocate that! pub fn storage_live(&mut self, local: mir::Local) -> EvalResult<'tcx, LocalValue> { + assert!(local != mir::RETURN_PLACE, "Cannot make return place live"); trace!("{:?} is now live", local); let layout = self.layout_of_local(self.cur_frame(), local)?; @@ -242,6 +243,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc /// Returns the old value of the local. /// Remember to deallocate that! pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue { + assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); trace!("{:?} is now dead", local); mem::replace(&mut self.frame_mut().locals[local], LocalValue::Dead) @@ -446,6 +448,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc let dummy = LocalValue::Live(Operand::Immediate(Value::Scalar(ScalarMaybeUndef::Undef))); let mut locals = IndexVec::from_elem(dummy, &mir.local_decls); + // Return place is handled specially by the `eval_place` functions, and the + // entry in `locals` should never be used. Make it dead, to be sure. + locals[mir::RETURN_PLACE] = LocalValue::Dead; // Now mark those locals as dead that we do not want to initialize match self.tcx.describe_def(instance.def_id()) { // statics and constants don't have `Storage*` statements, no need to look for them diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 1eb0280409527..f90a7efce47b3 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -33,6 +33,9 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { /// The memory kind to use for mutated statics -- or None if those are not supported. const MUT_STATIC_KIND: Option; + /// Whether to enforce the validity invariant + const ENFORCE_VALIDITY: bool; + /// Called before a basic block terminator is executed. /// You can use this to detect endlessly running programs. fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>; diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 222d1164667d3..5437c8ababc27 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -19,7 +19,7 @@ use std::collections::VecDeque; use std::ptr; -use rustc::ty::{self, Instance, query::TyCtxtAt}; +use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt}; use rustc::ty::layout::{self, Align, TargetDataLayout, Size, HasDataLayout}; use rustc::mir::interpret::{Pointer, AllocId, Allocation, ConstValue, GlobalId, EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic, @@ -235,7 +235,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // Check non-NULL/Undef, extract offset let (offset, alloc_align) = match ptr { Scalar::Ptr(ptr) => { - let (size, align) = self.get_size_and_align(ptr.alloc_id)?; + let (size, align) = self.get_size_and_align(ptr.alloc_id); // check this is not NULL -- which we can ensure only if this is in-bounds // of some (potentially dead) allocation. if ptr.offset > size { @@ -284,7 +284,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { /// If you want to check bounds before doing a memory access, be sure to /// check the pointer one past the end of your access, then everything will /// work out exactly. - pub fn check_bounds(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> { + pub fn check_bounds_ptr(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> { let alloc = self.get(ptr.alloc_id)?; let allocation_size = alloc.bytes.len() as u64; if ptr.offset.bytes() > allocation_size { @@ -296,6 +296,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } Ok(()) } + + /// Check if the memory range beginning at `ptr` and of size `Size` is "in-bounds". + #[inline(always)] + pub fn check_bounds(&self, ptr: Pointer, size: Size, access: bool) -> EvalResult<'tcx> { + // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds_ptr(ptr.offset(size, &*self)?, access) + } } /// Allocation accessors @@ -352,19 +359,28 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } } - pub fn get_size_and_align(&self, id: AllocId) -> EvalResult<'tcx, (Size, Align)> { - Ok(match self.get(id) { - Ok(alloc) => (Size::from_bytes(alloc.bytes.len() as u64), alloc.align), - Err(err) => match err.kind { - EvalErrorKind::DanglingPointerDeref => - // This should be in the dead allocation map - *self.dead_alloc_map.get(&id).expect( - "allocation missing in dead_alloc_map" - ), - // E.g. a function ptr allocation - _ => return Err(err) + pub fn get_size_and_align(&self, id: AllocId) -> (Size, Align) { + if let Ok(alloc) = self.get(id) { + return (Size::from_bytes(alloc.bytes.len() as u64), alloc.align); + } + // Could also be a fn ptr or extern static + match self.tcx.alloc_map.lock().get(id) { + Some(AllocType::Function(..)) => (Size::ZERO, Align::from_bytes(1, 1).unwrap()), + Some(AllocType::Static(did)) => { + // The only way `get` couldnÄt have worked here is if this is an extern static + assert!(self.tcx.is_foreign_item(did)); + // Use size and align of the type + let ty = self.tcx.type_of(did); + let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap(); + (layout.size, layout.align) } - }) + _ => { + // Must be a deallocated pointer + *self.dead_alloc_map.get(&id).expect( + "allocation missing in dead_alloc_map" + ) + } + } } pub fn get_mut( @@ -524,8 +540,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { ) -> EvalResult<'tcx, &[u8]> { assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); self.check_align(ptr.into(), align)?; - // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) - self.check_bounds(ptr.offset(size, &*self)?, true)?; + self.check_bounds(ptr, size, true)?; if check_defined_and_ptr { self.check_defined(ptr, size)?; @@ -569,8 +584,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { ) -> EvalResult<'tcx, &mut [u8]> { assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); self.check_align(ptr.into(), align)?; - // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) - self.check_bounds(ptr.offset(size, &self)?, true)?; + self.check_bounds(ptr, size, true)?; self.mark_definedness(ptr, size, true)?; self.clear_relocations(ptr, size)?; diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index b840af193b64a..9e0efaa9c78ef 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -35,3 +35,5 @@ pub use self::memory::{Memory, MemoryKind}; pub use self::machine::Machine; pub use self::operand::{ScalarMaybeUndef, Value, ValTy, Operand, OpTy}; + +pub use self::validity::RefTracking; diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 8878e5ca83f41..b75ceb61febb7 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -131,6 +131,18 @@ impl MemPlace { } impl<'tcx> MPlaceTy<'tcx> { + /// Produces a MemPlace that works for ZST but nothing else + #[inline] + pub fn dangling(layout: TyLayout<'tcx>, cx: impl HasDataLayout) -> Self { + MPlaceTy { + mplace: MemPlace::from_scalar_ptr( + Scalar::from_uint(layout.align.abi(), cx.pointer_size()), + layout.align + ), + layout + } + } + #[inline] fn from_aligned_ptr(ptr: Pointer, layout: TyLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align), layout } @@ -555,6 +567,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> dest: PlaceTy<'tcx>, ) -> EvalResult<'tcx> { trace!("write_value: {:?} <- {:?}", *dest, src_val); + // Check that the value actually is okay for that type + if M::ENFORCE_VALIDITY { + // Something changed somewhere, better make sure it matches the type! + let op = OpTy { op: Operand::Immediate(src_val), layout: dest.layout }; + self.validate_operand(op, &mut vec![], None, /*const_mode*/false)?; + } + // See if we can avoid an allocation. This is the counterpart to `try_read_value`, // but not factored as a separate function. let mplace = match dest.place { @@ -576,7 +595,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> self.write_value_to_mplace(src_val, dest) } - /// Write a value to memory + /// Write a value to memory. This does NOT do validation, so you better had already + /// done that before calling this! fn write_value_to_mplace( &mut self, value: Value, @@ -640,12 +660,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> }; // Slow path, this does not fit into an immediate. Just memcpy. trace!("copy_op: {:?} <- {:?}", *dest, *src); - let (dest_ptr, dest_align) = self.force_allocation(dest)?.to_scalar_ptr_align(); + let dest = self.force_allocation(dest)?; + let (dest_ptr, dest_align) = dest.to_scalar_ptr_align(); self.memory.copy( src_ptr, src_align, dest_ptr, dest_align, src.layout.size, false - ) + )?; + if M::ENFORCE_VALIDITY { + // Something changed somewhere, better make sure it matches the type! + self.validate_operand(dest.into(), &mut vec![], None, /*const_mode*/false)?; + } + Ok(()) } /// Make sure that a place is in memory, and return where it is. @@ -668,6 +694,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // that has different alignment than the outer field. let local_layout = self.layout_of_local(frame, local)?; let ptr = self.allocate(local_layout, MemoryKind::Stack)?; + // We don't have to validate as we can assume the local + // was already valid for its type. self.write_value_to_mplace(value, ptr)?; let mplace = ptr.mplace; // Update the local diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index c7ed69e0cb66d..862f61df227be 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -287,7 +287,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let return_place = match dest { Some(place) => *place, - None => Place::null(&self), + None => Place::null(&self), // any access will error. good! }; self.push_stack_frame( instance, @@ -373,6 +373,20 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> trace!("Caller has too many args over"); return err!(FunctionArgCountMismatch); } + // Don't forget to check the return type! + if let Some(caller_ret) = dest { + let callee_ret = self.eval_place(&mir::Place::Local(mir::RETURN_PLACE))?; + if !Self::check_argument_compat(caller_ret.layout, callee_ret.layout) { + return err!(FunctionRetMismatch( + caller_ret.layout.ty, callee_ret.layout.ty + )); + } + } else { + // FIXME: The caller thinks this function cannot return. How do + // we verify that the callee agrees? + // On the plus side, the the callee ever writes to its return place, + // that will be detected as UB (because we set that to NULL above). + } Ok(()) })(); match res { diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index c5238d24cf7ed..f481238bd5ba7 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -11,18 +11,18 @@ use std::fmt::Write; use syntax_pos::symbol::Symbol; -use rustc::ty::layout::{self, Size, Primitive}; -use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, Size, Align, TyLayout}; +use rustc::ty; use rustc_data_structures::fx::FxHashSet; use rustc::mir::interpret::{ - Scalar, AllocType, EvalResult, EvalErrorKind, PointerArithmetic + Scalar, AllocType, EvalResult, EvalErrorKind }; use super::{ - OpTy, Machine, EvalContext, ScalarMaybeUndef + ValTy, OpTy, MPlaceTy, Machine, EvalContext, ScalarMaybeUndef }; -macro_rules! validation_failure{ +macro_rules! validation_failure { ($what:expr, $where:expr, $details:expr) => {{ let where_ = path_format($where); let where_ = if where_.is_empty() { @@ -49,6 +49,22 @@ macro_rules! validation_failure{ }}; } +macro_rules! try_validation { + ($e:expr, $what:expr, $where:expr, $details:expr) => {{ + match $e { + Ok(x) => x, + Err(_) => return validation_failure!($what, $where, $details), + } + }}; + + ($e:expr, $what:expr, $where:expr) => {{ + match $e { + Ok(x) => x, + Err(_) => return validation_failure!($what, $where), + } + }} +} + /// We want to show a nice path to the invalid field for diagnotsics, /// but avoid string operations in the happy case where no error happens. /// So we track a `Vec` where `PathElem` contains all the data we @@ -63,6 +79,23 @@ pub enum PathElem { Tag, } +/// State for tracking recursive validation of references +pub struct RefTracking<'tcx> { + pub seen: FxHashSet<(OpTy<'tcx>)>, + pub todo: Vec<(OpTy<'tcx>, Vec)>, +} + +impl<'tcx> RefTracking<'tcx> { + pub fn new(op: OpTy<'tcx>) -> Self { + let mut ref_tracking = RefTracking { + seen: FxHashSet(), + todo: vec![(op, Vec::new())], + }; + ref_tracking.seen.insert(op); + ref_tracking + } +} + // 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) -> Vec { @@ -95,133 +128,251 @@ fn path_format(path: &Vec) -> String { out } +fn scalar_format(value: ScalarMaybeUndef) -> 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(), + } +} + impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { - fn validate_scalar( + /// Make sure that `value` is valid for `ty`, *assuming* `ty` is a primitive type. + fn validate_primitive_type( &self, - value: ScalarMaybeUndef, - size: Size, - scalar: &layout::Scalar, + value: ValTy<'tcx>, path: &Vec, - ty: Ty, + ref_tracking: Option<&mut RefTracking<'tcx>>, + const_mode: bool, ) -> EvalResult<'tcx> { - trace!("validate scalar: {:#?}, {:#?}, {:#?}, {}", value, size, scalar, ty); - let (lo, hi) = scalar.valid_range.clone().into_inner(); - - let value = match value { - ScalarMaybeUndef::Scalar(scalar) => scalar, - ScalarMaybeUndef::Undef => return validation_failure!("undefined bytes", path), - }; - - let bits = match value { - Scalar::Bits { bits, size: value_size } => { - assert_eq!(value_size as u64, size.bytes()); - bits + // 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"); }, - Scalar::Ptr(_) => { - match ty.sty { - ty::Bool | - ty::Char | - ty::Float(_) | - ty::Int(_) | - ty::Uint(_) => { - return validation_failure!( - "a pointer", - path, - format!("the type {}", ty.sty) - ); + ty::Char => { + let value = value.to_scalar_or_undef(); + try_validation!(value.to_char(), + scalar_format(value), path, "a valid unicode codepoint"); + }, + ty::Float(_) | ty::Int(_) | ty::Uint(_) => { + let size = value.layout.size; + let value = value.to_scalar_or_undef(); + if 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"); + } else { + // At run-time, for now, we accept *anything* for these types, including + // undef. We should fix that, but let's start low. + } + } + _ if ty.is_box() || ty.is_region_ptr() || ty.is_unsafe_ptr() => { + // Handle fat pointers. We also check fat raw pointers, + // their metadata must be valid! + // This also checks that the ptr itself is initialized, which + // seems reasonable even for raw pointers. + let place = try_validation!(self.ref_to_mplace(value), + "undefined data in pointer", path); + // Check metadata early, for better diagnostics + if place.layout.is_unsized() { + let tail = self.tcx.struct_tail(place.layout.ty); + match tail.sty { + ty::Dynamic(..) => { + let vtable = try_validation!(place.extra.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); + // FIXME: More checks for the vtable. + } + ty::Slice(..) | ty::Str => { + try_validation!(place.extra.unwrap().to_usize(self), + "non-integer slice length in fat pointer", path); + } + ty::Foreign(..) => { + // Unsized, but not fat. + } + _ => + bug!("Unexpected unsized type tail: {:?}", tail), + } + } + // for safe ptrs, also check the ptr values itself + if !ty.is_unsafe_ptr() { + // Make sure this is non-NULL and aligned + let (size, align) = self.size_and_align_of(place.extra, place.layout)?; + match self.memory.check_align(place.ptr, align) { + Ok(_) => {}, + Err(err) => match err.kind { + EvalErrorKind::InvalidNullPointerUsage => + return validation_failure!("NULL reference", path), + EvalErrorKind::AlignmentCheckFailed { .. } => + return validation_failure!("unaligned reference", path), + _ => + return validation_failure!( + "dangling (deallocated) reference", path + ), + } + } + // non-ZST also have to be dereferencable + if size != Size::ZERO { + let ptr = try_validation!(place.ptr.to_ptr(), + "integer pointer in non-ZST reference", path); + if const_mode { + // Skip validation entirely for some external statics + let alloc_kind = self.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) { + return Ok(()); + } + } + } + try_validation!(self.memory.check_bounds(ptr, size, false), + "dangling (not entirely in bounds) reference", path); + } + if let Some(ref_tracking) = ref_tracking { + // Check if we have encountered this pointer+layout combination + // before. Proceed recursively even for integer pointers, no + // reason to skip them! They are (recursively) valid for some ZST, + // but not for others (e.g. `!` is a ZST). + 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))); + } } - ty::RawPtr(_) | - ty::Ref(_, _, _) | - ty::FnPtr(_) => {} - _ => { unreachable!(); } } + } + 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"); + // FIXME: Check if the signature matches + } + // This should be all the primitive types + ty::Never => bug!("Uninhabited type should have been catched earlier"), + _ => bug!("Unexpected primitive type {}", value.layout.ty) + } + Ok(()) + } - let ptr_size = self.pointer_size(); - let ptr_max = u128::max_value() >> (128 - ptr_size.bits()); - return if lo > hi { - if lo - hi == 1 { - // no gap, all values are ok - Ok(()) - } else if hi < ptr_max || lo > 1 { - let max = u128::max_value() >> (128 - size.bits()); - validation_failure!( - "pointer", - path, - format!("something in the range {:?} or {:?}", 0..=lo, hi..=max) - ) - } else { - Ok(()) + /// Make sure that `value` matches the + fn validate_scalar_layout( + &self, + value: ScalarMaybeUndef, + size: Size, + path: &Vec, + layout: &layout::Scalar, + ) -> EvalResult<'tcx> { + let (lo, hi) = layout.valid_range.clone().into_inner(); + let max_hi = u128::max_value() >> (128 - size.bits()); // as big as the size fits + assert!(hi <= max_hi); + if lo == 0 && hi == max_hi { + // Nothing to check + return Ok(()); + } + // 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)); + let bits = match value { + Scalar::Ptr(ptr) => { + if lo == 1 && hi == max_hi { + // only NULL is not allowed. + // 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( + Scalar::Ptr(ptr), Align::from_bytes(1, 1).unwrap() + ).is_ok() || + self.memory.get_fn(ptr).is_ok(); + if !non_null { + // could be NULL + return validation_failure!("a potentially NULL pointer", path); } - } else if hi < ptr_max || lo > 1 { - validation_failure!( - "pointer", - path, - format!("something in the range {:?}", scalar.valid_range) - ) + return Ok(()); } else { - Ok(()) - }; - }, - }; - - // char gets a special treatment, because its number space is not contiguous so `TyLayout` - // has no special checks for chars - match ty.sty { - ty::Char => { - debug_assert_eq!(size.bytes(), 4); - if ::std::char::from_u32(bits as u32).is_none() { + // Conservatively, we reject, because the pointer *could* have this + // value. return validation_failure!( - "character", + "a pointer", path, - "a valid unicode codepoint" + format!( + "something that cannot possibly be outside the (wrapping) range {:?}", + layout.valid_range + ) ); } } - _ => {}, - } - + Scalar::Bits { bits, size: value_size } => { + assert_eq!(value_size as u64, size.bytes()); + bits + } + }; + // Now compare. This is slightly subtle because this is a special "wrap-around" range. use std::ops::RangeInclusive; let in_range = |bound: RangeInclusive| bound.contains(&bits); if lo > hi { - if in_range(0..=hi) || in_range(lo..=u128::max_value()) { + // wrapping around + if in_range(0..=hi) || in_range(lo..=max_hi) { Ok(()) } else { validation_failure!( bits, path, - format!("something in the range {:?} or {:?}", ..=hi, lo..) + format!("something in the range {:?} or {:?}", 0..=hi, lo..=max_hi) ) } } else { - if in_range(scalar.valid_range.clone()) { + if in_range(layout.valid_range.clone()) { Ok(()) } else { validation_failure!( bits, path, - format!("something in the range {:?}", scalar.valid_range) + if hi == max_hi { + format!("something greater or equal to {}", lo) + } else { + format!("something in the range {:?}", layout.valid_range) + } ) } } } - /// This function checks the data at `op`. + /// 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>, path: &mut Vec, - seen: &mut FxHashSet<(OpTy<'tcx>)>, - todo: &mut Vec<(OpTy<'tcx>, Vec)>, + mut ref_tracking: Option<&mut RefTracking<'tcx>>, + const_mode: bool, ) -> EvalResult<'tcx> { - trace!("validate_operand: {:?}, {:#?}", *dest, dest.layout); + trace!("validate_operand: {:?}, {:?}", *dest, dest.layout.ty); - // Find the right variant. We have to handle this as a prelude, not via - // proper recursion with the new inner layout, to be able to later nicely - // print the field names of the enum field that is being accessed. - let (variant, dest) = match dest.layout.variants { + // 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) { @@ -237,124 +388,104 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> ), } }; - let inner_dest = self.operand_downcast(dest, variant)?; // 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); - (variant, inner_dest) + dest }, - layout::Variants::Single { index } => { - // Pre-processing for trait objects: Treat them at their real type. - // (We do not do this for slices and strings: For slices it is not needed, - // `mplace_array_fields` does the right thing, and for strings there is no - // real type that would show the actual length.) - let dest = match dest.layout.ty.sty { - ty::Dynamic(..) => { - let dest = dest.to_mem_place(); // immediate trait objects are not a thing - match self.unpack_dyn_trait(dest) { - Ok(res) => res.1.into(), - Err(_) => - return validation_failure!( - "invalid vtable in fat pointer", path - ), - } - } - _ => dest - }; - (index, dest) - } + layout::Variants::Single { .. } => dest, }; - // Remember the length, in case we need to truncate - let path_len = path.len(); + // 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 + }; - // Validate all fields - match dest.layout.fields { - // primitives are unions with zero fields - // We still check `layout.fields`, not `layout.abi`, because `layout.abi` - // is `Scalar` for newtypes around scalars, but we want to descend through the - // fields to get a proper `path`. - layout::FieldPlacement::Union(0) => { - match dest.layout.abi { - // nothing to do, whatever the pointer points to, it is never going to be read - layout::Abi::Uninhabited => - return validation_failure!("a value of an uninhabited type", path), - // check that the scalar is a valid pointer or that its bit range matches the - // expectation. - layout::Abi::Scalar(ref scalar_layout) => { - let size = scalar_layout.value.size(self); - let value = match self.read_value(dest) { - Ok(val) => val, - Err(err) => match err.kind { - EvalErrorKind::PointerOutOfBounds { .. } | - EvalErrorKind::ReadUndefBytes(_) => - return validation_failure!( - "uninitialized or out-of-bounds memory", path - ), - _ => - return validation_failure!( - "unrepresentable data", path - ), - } - }; - let scalar = value.to_scalar_or_undef(); - self.validate_scalar(scalar, size, scalar_layout, &path, dest.layout.ty)?; - if scalar_layout.value == Primitive::Pointer { - // ignore integer pointers, we can't reason about the final hardware - if let Scalar::Ptr(ptr) = scalar.not_undef()? { - let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id); - if let Some(AllocType::Static(did)) = alloc_kind { - // statics from other crates are already checked. - // extern statics cannot be validated as they have no body. - if !did.is_local() || self.tcx.is_foreign_item(did) { - return Ok(()); - } - } - if value.layout.ty.builtin_deref(false).is_some() { - let ptr_op = self.ref_to_mplace(value)?.into(); - // we have not encountered this pointer+layout combination - // before. - if seen.insert(ptr_op) { - trace!("Recursing below ptr {:#?}", *value); - todo.push((ptr_op, path_clone_and_deref(path))); - } - } - } - } - }, - _ => bug!("bad abi for FieldPlacement::Union(0): {:#?}", dest.layout.abi), - } + // 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)?; } - layout::FieldPlacement::Union(_) => { + // 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_value(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(..) => { // 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::Array { stride, .. } if !dest.layout.is_zst() => { - let dest = dest.to_mem_place(); // non-ZST array/slice/str cannot be immediate + 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); + } + } + layout::FieldPlacement::Array { stride, .. } => { + let dest = if dest.layout.is_zst() { + // it's a ZST, the memory content cannot matter + MPlaceTy::dangling(dest.layout, self) + } else { + // non-ZST array/slice/str cannot be immediate + dest.to_mem_place() + }; match dest.layout.ty.sty { // Special handling for strings to verify UTF-8 ty::Str => { - match self.read_str(dest) { - Ok(_) => {}, - Err(err) => match err.kind { - EvalErrorKind::PointerOutOfBounds { .. } | - EvalErrorKind::ReadUndefBytes(_) => - // The error here looks slightly different than it does - // for slices, because we do not report the index into the - // str at which we are OOB. - return validation_failure!( - "uninitialized or out-of-bounds memory", path - ), - _ => - return validation_failure!( - "non-UTF-8 data in str", path - ), - } - } + 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 { @@ -390,18 +521,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> "undefined bytes", path ) }, - EvalErrorKind::PointerOutOfBounds { allocation_size, .. } => { - // If the array access is out-of-bounds, the first - // undefined access is the after the end of the array. - let i = (allocation_size.bytes() * ty_size) as usize; - path.push(PathElem::ArrayElem(i)); - }, - _ => (), + // Other errors shouldn't be possible + _ => return Err(err), } - - return validation_failure!( - "uninitialized or out-of-bounds memory", path - ) } } }, @@ -411,83 +533,32 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> for (i, field) in self.mplace_array_fields(dest)?.enumerate() { let field = field?; path.push(PathElem::ArrayElem(i)); - self.validate_operand(field.into(), path, seen, todo)?; + self.validate_operand( + field.into(), + path, + ref_tracking.as_mut().map(|r| &mut **r), + const_mode, + )?; path.truncate(path_len); } } } }, - layout::FieldPlacement::Array { .. } => { - // An empty array. Nothing to do. - } - layout::FieldPlacement::Arbitrary { ref offsets, .. } => { - // Fat pointers are treated like pointers, not aggregates. - if dest.layout.ty.builtin_deref(true).is_some() { - // This is a fat pointer. - let ptr = match self.read_value(dest.into()) - .and_then(|val| self.ref_to_mplace(val)) - { - Ok(ptr) => ptr, - Err(_) => - return validation_failure!( - "undefined location or metadata in fat pointer", path - ), - }; - // check metadata early, for better diagnostics - match self.tcx.struct_tail(ptr.layout.ty).sty { - ty::Dynamic(..) => { - match ptr.extra.unwrap().to_ptr() { - Ok(_) => {}, - Err(_) => - return validation_failure!( - "non-pointer vtable in fat pointer", path - ), - } - // FIXME: More checks for the vtable. - } - ty::Slice(..) | ty::Str => { - match ptr.extra.unwrap().to_usize(self) { - Ok(_) => {}, - Err(_) => - return validation_failure!( - "non-integer slice length in fat pointer", path - ), - } - } - _ => - bug!("Unexpected unsized type tail: {:?}", - self.tcx.struct_tail(ptr.layout.ty) - ), - } - // for safe ptrs, recursively check it - if !dest.layout.ty.is_unsafe_ptr() { - let ptr = ptr.into(); - if seen.insert(ptr) { - trace!("Recursing below fat ptr {:?}", ptr); - todo.push((ptr, path_clone_and_deref(path))); - } - } - } else { - // Not a pointer, perform regular aggregate handling below - for i in 0..offsets.len() { - let field = self.operand_field(dest, i as u64)?; - path.push(self.aggregate_field_path_elem(dest.layout.ty, variant, i)); - self.validate_operand(field, path, seen, todo)?; - path.truncate(path_len); - } - } - } } Ok(()) } - fn aggregate_field_path_elem(&self, ty: Ty<'tcx>, variant: usize, field: usize) -> PathElem { - match ty.sty { + 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, _, _) => { - let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap(); - let freevar = self.tcx.with_freevars(node_id, |fv| fv[field]); - PathElem::ClosureVar(self.tcx.hir.name(freevar.var_id())) + if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { + let freevar = self.tcx.with_freevars(node_id, |fv| fv[field]); + PathElem::ClosureVar(self.tcx.hir.name(freevar.var_id())) + } else { + // The closure is not local, so we cannot get the name + PathElem::ClosureVar(Symbol::intern(&field.to_string())) + } } // tuples @@ -495,7 +566,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // enums ty::Adt(def, ..) if def.is_enum() => { - let variant = &def.variants[variant]; + 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) } @@ -503,7 +577,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name), // nothing else has an aggregate layout - _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", ty), + _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty), } } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 70d50d589d1a0..626baf207eebc 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -154,6 +154,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { // FIXME: figure out the rules and start linting | FunctionAbiMismatch(..) | FunctionArgMismatch(..) + | FunctionRetMismatch(..) | FunctionArgCountMismatch // fine at runtime, might be a register address or sth | ReadBytesAsPointer diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr index 751f4113fbf60..7be9345b6b423 100644 --- a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr @@ -2,7 +2,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:24:5 | LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 }.u }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected the type usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits | = 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 @@ -36,7 +36,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:36:5 | LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uint_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected the type u64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits | = 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 @@ -74,7 +74,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:51:5 | LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected the type i64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits | = 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 @@ -96,7 +96,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:60:5 | LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.float_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected the type f64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits | = 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 @@ -144,7 +144,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:78:5 | LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected the type u64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits | = 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 @@ -184,7 +184,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:93:5 | LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected the type i64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits | = 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 @@ -208,7 +208,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:102:5 | LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected the type f64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits | = 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 diff --git a/src/test/ui/consts/const-eval/transmute-const.rs b/src/test/ui/consts/const-eval/transmute-const.rs index a585a4404adf6..477e7119ba937 100644 --- a/src/test/ui/consts/const-eval/transmute-const.rs +++ b/src/test/ui/consts/const-eval/transmute-const.rs @@ -14,6 +14,5 @@ use std::mem; static FOO: bool = unsafe { mem::transmute(3u8) }; //~^ ERROR this static likely exhibits undefined behavior -//~^^ type validation failed: encountered 3, but expected something in the range 0..=1 fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-enum.stderr b/src/test/ui/consts/const-eval/ub-enum.stderr index 572d08ddfeebd..243343c94b065 100644 --- a/src/test/ui/consts/const-eval/ub-enum.stderr +++ b/src/test/ui/consts/const-eval/ub-enum.stderr @@ -18,7 +18,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/ub-enum.rs:45:1 | LL | const BAD_ENUM_CHAR : Option<(char, char)> = Some(('x', unsafe { TransmuteChar { a: !0 }.b })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered character at .Some.0.1, but expected a valid unicode codepoint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 diff --git a/src/test/ui/consts/const-eval/ub-nonnull.rs b/src/test/ui/consts/const-eval/ub-nonnull.rs new file mode 100644 index 0000000000000..2b07eee3ccb46 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-nonnull.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_transmute)] + +use std::mem; +use std::ptr::NonNull; +use std::num::{NonZeroU8, NonZeroUsize}; + +const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; +//~^ ERROR this constant likely exhibits undefined behavior + +const NULL_U8: NonZeroU8 = unsafe { mem::transmute(0u8) }; +//~^ ERROR this constant likely exhibits undefined behavior +const NULL_USIZE: NonZeroUsize = unsafe { mem::transmute(0usize) }; +//~^ ERROR this constant likely exhibits undefined behavior + +fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-nonnull.stderr b/src/test/ui/consts/const-eval/ub-nonnull.stderr new file mode 100644 index 0000000000000..8d1ca885b5aba --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-nonnull.stderr @@ -0,0 +1,27 @@ +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/ub-nonnull.rs:17:1 + | +LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, 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 + +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/ub-nonnull.rs:20:1 + | +LL | const NULL_U8: NonZeroU8 = unsafe { mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, 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 + +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/ub-nonnull.rs:22:1 + | +LL | const NULL_USIZE: NonZeroUsize = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, 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 + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-ref.rs b/src/test/ui/consts/const-eval/ub-ref.rs new file mode 100644 index 0000000000000..7ee13f20dd2d9 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-ref.rs @@ -0,0 +1,27 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_transmute)] + +use std::mem; + +const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; +//~^ ERROR this constant likely exhibits undefined behavior + +const NULL: &u16 = unsafe { mem::transmute(0usize) }; +//~^ ERROR this constant likely exhibits undefined behavior + +const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; +//~^ ERROR this constant likely exhibits undefined behavior + +const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; +//~^ ERROR this constant likely exhibits undefined behavior + +fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-ref.stderr b/src/test/ui/consts/const-eval/ub-ref.stderr new file mode 100644 index 0000000000000..9907c780d2ccb --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-ref.stderr @@ -0,0 +1,35 @@ +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/ub-ref.rs:15:1 + | +LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned reference + | + = 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]: this constant likely exhibits undefined behavior + --> $DIR/ub-ref.rs:18:1 + | +LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, 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 + +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/ub-ref.rs:21:1 + | +LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits + | + = 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]: this constant likely exhibits undefined behavior + --> $DIR/ub-ref.rs:24:1 + | +LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered integer pointer in non-ZST reference + | + = 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 4 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.rs b/src/test/ui/consts/const-eval/ub-uninhabit.rs index a5e341524bc73..99305beee5281 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.rs +++ b/src/test/ui/consts/const-eval/ub-uninhabit.rs @@ -8,15 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -union Foo { - a: u8, - b: Bar, -} +#![feature(const_transmute)] + +use std::mem; #[derive(Copy, Clone)] enum Bar {} -const BAD_BAD_BAD: Bar = unsafe { Foo { a: 1 }.b}; +const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) }; +//~^ ERROR this constant likely exhibits undefined behavior + +const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; +//~^ ERROR this constant likely exhibits undefined behavior + +const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) }; //~^ ERROR this constant likely exhibits undefined behavior fn main() { diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.stderr index 623b98dc4531b..136d5f2919946 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.stderr +++ b/src/test/ui/consts/const-eval/ub-uninhabit.stderr @@ -1,11 +1,27 @@ error[E0080]: this constant likely exhibits undefined behavior - --> $DIR/ub-uninhabit.rs:19:1 + --> $DIR/ub-uninhabit.rs:18:1 | -LL | const BAD_BAD_BAD: Bar = unsafe { Foo { a: 1 }.b}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type +LL | const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type | = 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 previous error +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/ub-uninhabit.rs:21:1 + | +LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at . + | + = 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]: this constant likely exhibits undefined behavior + --> $DIR/ub-uninhabit.rs:24:1 + | +LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at [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: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-usize-in-ref.rs b/src/test/ui/consts/const-eval/ub-usize-in-ref.rs deleted file mode 100644 index aaff2f233815b..0000000000000 --- a/src/test/ui/consts/const-eval/ub-usize-in-ref.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-pass - -union Foo { - a: &'static u8, - b: usize, -} - -// This might point to an invalid address, but that's the user's problem -const USIZE_AS_STATIC_REF: &'static u8 = unsafe { Foo { b: 1337 }.a}; - -fn main() { -} diff --git a/src/test/ui/consts/const-eval/union-ice.stderr b/src/test/ui/consts/const-eval/union-ice.stderr index ec51802681e0d..4484dd6a14740 100644 --- a/src/test/ui/consts/const-eval/union-ice.stderr +++ b/src/test/ui/consts/const-eval/union-ice.stderr @@ -13,7 +13,7 @@ LL | / const FIELD_PATH: Struct = Struct { //~ ERROR this constant likely exhibi LL | | a: 42, LL | | b: unsafe { UNION.field3 }, LL | | }; - | |__^ type validation failed: encountered undefined bytes at .b + | |__^ type validation failed: encountered uninitialized bytes at .b, but expected initialized plain bits | = 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 diff --git a/src/test/ui/union-ub-fat-ptr.rs b/src/test/ui/consts/const-eval/union-ub-fat-ptr.rs similarity index 100% rename from src/test/ui/union-ub-fat-ptr.rs rename to src/test/ui/consts/const-eval/union-ub-fat-ptr.rs diff --git a/src/test/ui/union-ub-fat-ptr.stderr b/src/test/ui/consts/const-eval/union-ub-fat-ptr.stderr similarity index 95% rename from src/test/ui/union-ub-fat-ptr.stderr rename to src/test/ui/consts/const-eval/union-ub-fat-ptr.stderr index 5d817dce205b1..c4632ffe30974 100644 --- a/src/test/ui/union-ub-fat-ptr.stderr +++ b/src/test/ui/consts/const-eval/union-ub-fat-ptr.stderr @@ -2,7 +2,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-ub-fat-ptr.rs:87:1 | LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or out-of-bounds memory at . + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling (not entirely in bounds) reference | = 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 @@ -26,7 +26,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-ub-fat-ptr.rs:99:1 | LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or out-of-bounds memory at .[1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling (not entirely in bounds) reference | = 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 @@ -42,7 +42,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-ub-fat-ptr.rs:106:1 | LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid vtable in fat pointer at . + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop fn in 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 @@ -50,7 +50,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-ub-fat-ptr.rs:109:1 | LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid vtable in fat pointer at . + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop fn in 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 @@ -98,7 +98,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-ub-fat-ptr.rs:132:1 | LL | const J1: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-UTF-8 data in str at . + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at . | = 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 @@ -106,7 +106,7 @@ error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-ub-fat-ptr.rs:135:1 | LL | const J2: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-UTF-8 data in str at ..0 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at ..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 diff --git a/src/test/ui/consts/const-eval/union-ub.rs b/src/test/ui/consts/const-eval/union-ub.rs index db36764c4a306..86b3bdaa6b79e 100644 --- a/src/test/ui/consts/const-eval/union-ub.rs +++ b/src/test/ui/consts/const-eval/union-ub.rs @@ -41,5 +41,4 @@ const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; const BAD_UNION: Foo = unsafe { Bar { u8: 42 }.foo }; -fn main() { -} +fn main() {} diff --git a/src/test/ui/consts/const-eval/valid-const.rs b/src/test/ui/consts/const-eval/valid-const.rs new file mode 100644 index 0000000000000..a195e12defcea --- /dev/null +++ b/src/test/ui/consts/const-eval/valid-const.rs @@ -0,0 +1,18 @@ +// compile-pass + +// Some constants that *are* valid +#![feature(const_transmute)] + +use std::mem; +use std::ptr::NonNull; +use std::num::{NonZeroU8, NonZeroUsize}; + +const NON_NULL_PTR1: NonNull = unsafe { mem::transmute(1usize) }; +const NON_NULL_PTR2: NonNull = unsafe { mem::transmute(&0) }; + +const NON_NULL_U8: NonZeroU8 = unsafe { mem::transmute(1u8) }; +const NON_NULL_USIZE: NonZeroUsize = unsafe { mem::transmute(1usize) }; + +const UNIT: () = (); + +fn main() {} diff --git a/src/test/ui/issues/issue-14227.rs b/src/test/ui/issues/issue-14227.rs index 250e78ce24640..857db50edbbc1 100644 --- a/src/test/ui/issues/issue-14227.rs +++ b/src/test/ui/issues/issue-14227.rs @@ -11,9 +11,9 @@ #![allow(safe_extern_statics, warnings)] extern { - pub static symbol: (); + pub static symbol: u32; } -static CRASH: () = symbol; +static CRASH: u32 = symbol; //~^ ERROR could not evaluate static initializer //~| tried to read from foreign (extern) static diff --git a/src/test/ui/issues/issue-14227.stderr b/src/test/ui/issues/issue-14227.stderr index f5f39465b187b..dc6c72d8a7256 100644 --- a/src/test/ui/issues/issue-14227.stderr +++ b/src/test/ui/issues/issue-14227.stderr @@ -1,8 +1,8 @@ error[E0080]: could not evaluate static initializer - --> $DIR/issue-14227.rs:16:20 + --> $DIR/issue-14227.rs:16:21 | -LL | static CRASH: () = symbol; - | ^^^^^^ tried to read from foreign (extern) static +LL | static CRASH: u32 = symbol; + | ^^^^^^ tried to read from foreign (extern) static error: aborting due to previous error diff --git a/src/tools/miri b/src/tools/miri index e8f6973e2d40a..cc275c63a90d4 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit e8f6973e2d40ab39e30cdbe0cf8e77a72c867d4f +Subproject commit cc275c63a90d4bea394e76607b2e10611eb1be36