diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index b6cb336648b..8e71df3c34b 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -73,6 +73,7 @@ #![feature(in_band_lifetimes)] #![feature(macro_at_most_once_rep)] #![feature(inclusive_range_methods)] +#![feature(crate_in_paths)] #![recursion_limit="512"] diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 9125597a727..1e9584bc55b 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -47,7 +47,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { tcx: TyCtxtAt<'a, 'gcx, 'tcx>, message: &str ) { - let err = self.struct_generic(tcx, message, None); + let err = self.struct_error(tcx, message); if let Some(mut err) = err { err.emit(); } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index faad32a5d99..81cc897232a 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1599,7 +1599,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> // Potentially-fat pointers. ty::TyRef(_, pointee, _) | ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - assert!(i < 2); + assert!(i < this.fields.count()); // Reuse the fat *T type as its own thin pointer data field. // This provides information about e.g. DST struct pointees @@ -1621,10 +1621,25 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> match tcx.struct_tail(pointee).sty { ty::TySlice(_) | ty::TyStr => tcx.types.usize, - ty::TyDynamic(..) => { - // FIXME(eddyb) use an usize/fn() array with - // the correct number of vtables slots. - tcx.mk_imm_ref(tcx.types.re_static, tcx.mk_nil()) + ty::TyDynamic(data, _) => { + let trait_def_id = data.principal().unwrap().def_id(); + let num_fns: u64 = crate::traits::supertrait_def_ids(tcx, trait_def_id) + .map(|trait_def_id| { + tcx.associated_items(trait_def_id) + .filter(|item| item.kind == ty::AssociatedKind::Method) + .count() as u64 + }) + .sum(); + tcx.mk_imm_ref( + tcx.types.re_static, + tcx.mk_array(tcx.types.usize, 3 + num_fns), + ) + /* FIXME use actual fn pointers + tcx.mk_tup(&[ + tcx.mk_array(tcx.types.usize, 3), + tcx.mk_array(Option), + ]) + */ } _ => bug!("TyLayout::field_type({:?}): not applicable", this) } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 8df6df15511..7cb4f7d3860 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -40,6 +40,7 @@ use lint::{LateContext, LintContext, LintArray}; use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext}; use std::collections::HashSet; +use rustc::util::nodemap::FxHashSet; use syntax::tokenstream::{TokenTree, TokenStream}; use syntax::ast; @@ -1576,6 +1577,57 @@ impl LintPass for UnusedBrokenConst { } } +fn validate_const<'a, 'tcx>( + tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + constant: &ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, + gid: ::rustc::mir::interpret::GlobalId<'tcx>, + what: &str, +) { + let mut ecx = ::rustc_mir::interpret::mk_eval_cx(tcx, gid.instance, param_env).unwrap(); + let result = (|| { + let val = ecx.const_to_value(constant.val)?; + use rustc_target::abi::LayoutOf; + let layout = ecx.layout_of(constant.ty)?; + let place = ecx.allocate_place_for_value(val, layout, None)?; + let ptr = place.to_ptr()?; + let mut todo = vec![(ptr, layout.ty, String::new())]; + let mut seen = FxHashSet(); + seen.insert((ptr, layout.ty)); + while let Some((ptr, ty, path)) = todo.pop() { + let layout = ecx.layout_of(ty)?; + ecx.validate_ptr_target( + ptr, + layout.align, + layout, + path, + &mut seen, + &mut todo, + )?; + } + Ok(()) + })(); + if let Err(err) = result { + let (trace, span) = ecx.generate_stacktrace(None); + let err = ::rustc::mir::interpret::ConstEvalErr { + error: err, + stacktrace: trace, + span, + }; + let err = err.struct_error( + tcx.at(span), + &format!("this {} likely exhibits undefined behavior", what), + ); + if let Some(mut err) = err { + err.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", + ); + err.emit(); + } + } +} + fn check_const(cx: &LateContext, body_id: hir::BodyId, what: &str) { let def_id = cx.tcx.hir.body_owner_def_id(body_id); let param_env = cx.tcx.param_env(def_id); @@ -1583,13 +1635,19 @@ fn check_const(cx: &LateContext, body_id: hir::BodyId, what: &str) { instance: ty::Instance::mono(cx.tcx, def_id), promoted: None }; - if let Err(err) = cx.tcx.const_eval(param_env.and(cid)) { - let span = cx.tcx.def_span(def_id); - err.report_as_lint( - cx.tcx.at(span), - &format!("this {} cannot be used", what), - cx.current_lint_root(), - ); + match cx.tcx.const_eval(param_env.and(cid)) { + Ok(val) => validate_const(cx.tcx, val, param_env, cid, what), + Err(err) => { + // errors for statics are already reported directly in the query + if cx.tcx.is_static(def_id).is_none() { + let span = cx.tcx.def_span(def_id); + err.report_as_lint( + cx.tcx.at(span), + &format!("this {} cannot be used", what), + cx.current_lint_root(), + ); + } + }, } } @@ -1610,6 +1668,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedBrokenConst { hir::ItemKind::Const(_, body_id) => { check_const(cx, body_id, "constant"); }, + hir::ItemKind::Static(_, _, body_id) => { + check_const(cx, body_id, "static"); + }, hir::ItemKind::Ty(ref ty, _) => hir::intravisit::walk_ty( &mut UnusedBrokenConstVisitor(cx), ty diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 3c92f3f6235..873fef75bb9 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -5,7 +5,7 @@ use rustc::hir; use rustc::mir::interpret::{ConstEvalErr}; use rustc::mir; use rustc::ty::{self, TyCtxt, Ty, Instance}; -use rustc::ty::layout::{self, LayoutOf, Primitive}; +use rustc::ty::layout::{self, LayoutOf, Primitive, TyLayout}; use rustc::ty::subst::Subst; use syntax::ast::Mutability; @@ -16,7 +16,7 @@ use rustc::mir::interpret::{ EvalResult, EvalError, EvalErrorKind, GlobalId, Value, Scalar, AllocId, Allocation, ConstValue, }; -use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory, MemoryKind}; +use super::{Place, EvalContext, StackPopCleanup, ValTy, Memory, MemoryKind}; pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -63,7 +63,7 @@ pub fn eval_promoted<'a, 'mir, 'tcx>( cid: GlobalId<'tcx>, mir: &'mir mir::Mir<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)> { +) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> { ecx.with_fresh_body(|ecx| { eval_body_using_ecx(ecx, cid, Some(mir), param_env) }) @@ -121,7 +121,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( cid: GlobalId<'tcx>, mir: Option<&'mir mir::Mir<'tcx>>, param_env: ty::ParamEnv<'tcx>, -) -> (EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) { +) -> (EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) { debug!("eval_body_and_ecx: {:?}, {:?}", cid, param_env); // we start out with the best span we have // and try improving it down the road when more information is available @@ -137,7 +137,7 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>( cid: GlobalId<'tcx>, mir: Option<&'mir mir::Mir<'tcx>>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)> { +) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> { debug!("eval_body: {:?}, {:?}", cid, param_env); let tcx = ecx.tcx.tcx; let mut mir = match mir { @@ -182,7 +182,7 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>( // point at the allocation _ => Value::ByRef(ptr, layout.align), }; - Ok((value, ptr, layout.ty)) + Ok((value, ptr, layout)) } #[derive(Debug, Clone, Eq, PartialEq, Hash)] @@ -434,19 +434,7 @@ pub fn const_val_field<'a, 'tcx>( let ty = value.ty; let value = ecx.const_to_value(value.val)?; let layout = ecx.layout_of(ty)?; - let (ptr, align) = match value { - Value::ByRef(ptr, align) => (ptr, align), - Value::ScalarPair(..) | Value::Scalar(_) => { - let ptr = ecx.alloc_ptr(ty)?.into(); - ecx.write_value_to_ptr(value, ptr, layout.align, ty)?; - (ptr, layout.align) - }, - }; - let place = Place::Ptr { - ptr, - align, - extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), - }; + let place = ecx.allocate_place_for_value(value, layout, variant)?; let (place, layout) = ecx.place_field(place, field, layout)?; let (ptr, align) = place.to_ptr_align(); let mut new_value = Value::ByRef(ptr, align); @@ -484,9 +472,9 @@ pub fn const_variant_index<'a, 'tcx>( trace!("const_variant_index: {:?}, {:?}", instance, val); let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); let value = ecx.const_to_value(val.val)?; + let layout = ecx.layout_of(val.ty)?; let (ptr, align) = match value { Value::ScalarPair(..) | Value::Scalar(_) => { - let layout = ecx.layout_of(val.ty)?; let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?.into(); ecx.write_value_to_ptr(value, ptr, layout.align, val.ty)?; (ptr, layout.align) @@ -494,7 +482,7 @@ pub fn const_variant_index<'a, 'tcx>( Value::ByRef(ptr, align) => (ptr, align), }; let place = Place::from_scalar_ptr(ptr, align); - ecx.read_discriminant_as_variant_index(place, val.ty) + ecx.read_discriminant_as_variant_index(place, layout) } pub fn const_value_to_allocation_provider<'a, 'tcx>( @@ -560,11 +548,11 @@ pub fn const_eval_provider<'a, 'tcx>( }; let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env); - res.and_then(|(mut val, _, miri_ty)| { + res.and_then(|(mut val, _, layout)| { if tcx.is_static(def_id).is_none() && cid.promoted.is_none() { - val = ecx.try_read_by_ref(val, miri_ty)?; + val = ecx.try_read_by_ref(val, layout.ty)?; } - Ok(value_to_const_value(&ecx, val, miri_ty)) + Ok(value_to_const_value(&ecx, val, layout.ty)) }).map_err(|err| { let (trace, span) = ecx.generate_stacktrace(None); let err = ConstEvalErr { diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 10d3af85337..c6c1a1d1ebb 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -6,14 +6,14 @@ use rustc::hir::def_id::DefId; use rustc::hir::def::Def; use rustc::hir::map::definitions::DefPathData; use rustc::mir; -use rustc::ty::layout::{self, Size, Align, HasDataLayout, IntegerExt, LayoutOf, TyLayout}; +use rustc::ty::layout::{self, Size, Align, HasDataLayout, IntegerExt, LayoutOf, TyLayout, Primitive}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeAndMut}; use rustc::ty::query::TyCtxtAt; use rustc_data_structures::fx::{FxHashSet, FxHasher}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc::mir::interpret::{ - FrameInfo, GlobalId, Value, Scalar, + GlobalId, Value, Scalar, FrameInfo, AllocType, EvalResult, EvalErrorKind, Pointer, ConstValue, }; @@ -24,6 +24,31 @@ use super::{Place, PlaceExtra, Memory, HasMemory, MemoryKind, Machine}; +macro_rules! validation_failure{ + ($what:expr, $where:expr, $details:expr) => {{ + let where_ = if $where.is_empty() { + String::new() + } else { + format!(" at {}", $where) + }; + err!(ValidationFailure(format!( + "encountered {}{}, but expected {}", + $what, where_, $details, + ))) + }}; + ($what:expr, $where:expr) => {{ + let where_ = if $where.is_empty() { + String::new() + } else { + format!(" at {}", $where) + }; + err!(ValidationFailure(format!( + "encountered {}{}", + $what, where_, + ))) + }}; +} + pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Stores the `Machine` instance. pub machine: M, @@ -324,8 +349,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M r } - pub fn alloc_ptr(&mut self, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { - let layout = self.layout_of(ty)?; + pub fn alloc_ptr(&mut self, layout: TyLayout<'tcx>) -> EvalResult<'tcx, Pointer> { assert!(!layout.is_unsized(), "cannot alloc memory for unsized type"); self.memory.allocate(layout.size, layout.align, MemoryKind::Stack) @@ -776,8 +800,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M Discriminant(ref place) => { let ty = self.place_ty(place); + let layout = self.layout_of(ty)?; let place = self.eval_place(place)?; - let discr_val = self.read_discriminant_value(place, ty)?; + let discr_val = self.read_discriminant_value(place, layout)?; let defined = self.layout_of(dest_ty).unwrap().size.bits() as u8; self.write_scalar(dest, Scalar::Bits { bits: discr_val, @@ -843,16 +868,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M /// reads a tag and produces the corresponding variant index pub fn read_discriminant_as_variant_index( - &mut self, + &self, place: Place, - ty: Ty<'tcx>, + layout: TyLayout<'tcx>, ) -> EvalResult<'tcx, usize> { - let layout = self.layout_of(ty)?; match layout.variants { ty::layout::Variants::Single { index } => Ok(index), ty::layout::Variants::Tagged { .. } => { - let discr_val = self.read_discriminant_value(place, ty)?; - ty + let discr_val = self.read_discriminant_value(place, layout)?; + layout + .ty .ty_adt_def() .expect("tagged layout for non adt") .discriminants(self.tcx.tcx) @@ -860,7 +885,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M .ok_or_else(|| EvalErrorKind::InvalidDiscriminant.into()) } ty::layout::Variants::NicheFilling { .. } => { - let discr_val = self.read_discriminant_value(place, ty)?; + let discr_val = self.read_discriminant_value(place, layout)?; assert_eq!(discr_val as usize as u128, discr_val); Ok(discr_val as usize) }, @@ -868,11 +893,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } pub fn read_discriminant_value( - &mut self, + &self, place: Place, - ty: Ty<'tcx>, + layout: TyLayout<'tcx>, ) -> EvalResult<'tcx, u128> { - let layout = self.layout_of(ty)?; trace!("read_discriminant_value {:#?}", layout); if layout.abi == layout::Abi::Uninhabited { return Ok(0); @@ -880,7 +904,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M match layout.variants { layout::Variants::Single { index } => { - let discr_val = ty.ty_adt_def().map_or( + let discr_val = layout.ty.ty_adt_def().map_or( index as u128, |def| def.discriminant_for_variant(*self.tcx, index).val); return Ok(discr_val); @@ -888,11 +912,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M layout::Variants::Tagged { .. } | layout::Variants::NicheFilling { .. } => {}, } - - let (discr_place, discr) = self.place_field(place, mir::Field::new(0), layout)?; - trace!("discr place: {:?}, {:?}", discr_place, discr); + let discr_place_val = self.read_place(place)?; + let (discr_val, discr) = self.read_field(discr_place_val, None, mir::Field::new(0), layout)?; + trace!("discr value: {:?}, {:?}", discr_val, discr); let raw_discr = self.value_to_scalar(ValTy { - value: self.read_place(discr_place)?, + value: discr_val, ty: discr.ty })?; let discr_val = match layout.variants { @@ -906,7 +930,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let shift = 128 - discr.size.bits(); let sexted = (i << shift) >> shift; // and then zeroing with the typeck discriminant type - let discr_ty = ty + let discr_ty = layout + .ty .ty_adt_def().expect("tagged layout corresponds to adt") .repr .discr_type(); @@ -1023,6 +1048,27 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M self.tcx.const_eval(param_env.and(gid)).map_err(|err| EvalErrorKind::ReferencedConstant(err).into()) } + pub fn allocate_place_for_value( + &mut self, + value: Value, + layout: TyLayout<'tcx>, + variant: Option, + ) -> EvalResult<'tcx, Place> { + let (ptr, align) = match value { + Value::ByRef(ptr, align) => (ptr, align), + Value::ScalarPair(..) | Value::Scalar(_) => { + let ptr = self.alloc_ptr(layout)?.into(); + self.write_value_to_ptr(value, ptr, layout.align, layout.ty)?; + (ptr, layout.align) + }, + }; + Ok(Place::Ptr { + ptr, + align, + extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), + }) + } + pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> { let new_place = match place { Place::Local { frame, local } => { @@ -1039,7 +1085,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].instance.substs); let layout = self.layout_of(ty)?; - let ptr = self.alloc_ptr(ty)?; + let ptr = self.alloc_ptr(layout)?; self.stack[frame].locals[local] = Some(Value::ByRef(ptr.into(), layout.align)); // it stays live let place = Place::from_ptr(ptr, layout.align); @@ -1074,11 +1120,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M match self.follow_by_ref_value(value, ty)? { Value::ByRef { .. } => bug!("follow_by_ref_value can't result in `ByRef`"), - Value::Scalar(scalar) => { - // TODO: Do we really want insta-UB here? - self.ensure_valid_value(scalar, ty)?; - Ok(scalar) - } + Value::Scalar(scalar) => Ok(scalar), Value::ScalarPair(..) => bug!("value_to_scalar can't work with fat pointers"), } @@ -1165,8 +1207,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M if let Ok(Some(src_val)) = self.try_read_value(src_ptr, align, dest_ty) { write_dest(self, src_val)?; } else { - let dest_ptr = self.alloc_ptr(dest_ty)?.into(); let layout = self.layout_of(dest_ty)?; + let dest_ptr = self.alloc_ptr(layout)?.into(); self.memory.copy(src_ptr, align.min(layout.align), dest_ptr, layout.align, layout.size, false)?; write_dest(self, Value::ByRef(dest_ptr, layout.align))?; } @@ -1221,18 +1263,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } } - fn ensure_valid_value(&self, val: Scalar, ty: Ty<'tcx>) -> EvalResult<'tcx> { - match ty.sty { - ty::TyBool => val.to_bool().map(|_| ()), - - ty::TyChar if ::std::char::from_u32(val.to_bits(Size::from_bytes(4))? as u32).is_none() => { - err!(InvalidChar(val.to_bits(Size::from_bytes(4))? as u32 as u128)) - } - - _ => Ok(()), - } - } - pub fn read_value(&self, ptr: Scalar, align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { if let Some(val) = self.try_read_value(ptr, align, ty)? { Ok(val) @@ -1270,47 +1300,223 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } } + fn validate_scalar( + &self, + value: Scalar, + size: Size, + scalar: &layout::Scalar, + path: &str, + ty: Ty, + ) -> EvalResult<'tcx> { + trace!("validate scalar: {:#?}, {:#?}, {:#?}, {}", value, size, scalar, ty); + let (lo, hi) = scalar.valid_range.clone().into_inner(); + + let (bits, defined) = match value { + Scalar::Bits { bits, defined } => (bits, defined), + Scalar::Ptr(_) => { + let ptr_size = self.memory.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(()) + } + } else if hi < ptr_max || lo > 1 { + validation_failure!( + "pointer", + path, + format!("something in the range {:?}", scalar.valid_range) + ) + } 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::TyChar => { + assert_eq!(size.bytes(), 4); + if ::std::char::from_u32(bits as u32).is_none() { + return err!(InvalidChar(bits)); + } + } + _ => {}, + } + + use std::ops::RangeInclusive; + let in_range = |bound: RangeInclusive| { + defined as u64 >= size.bits() && bound.contains(&bits) + }; + if lo > hi { + if in_range(0..=hi) || in_range(lo..=u128::max_value()) { + Ok(()) + } else if defined as u64 >= size.bits() { + validation_failure!( + bits, + path, + format!("something in the range {:?} or {:?}", ..=hi, lo..) + ) + } else { + validation_failure!("undefined bytes", path) + } + } else { + if in_range(scalar.valid_range.clone()) { + Ok(()) + } else if defined as u64 >= size.bits() { + validation_failure!( + bits, + path, + format!("something in the range {:?}", scalar.valid_range) + ) + } else { + validation_failure!("undefined bytes", path) + } + } + } + + /// This function checks the memory where `ptr` points to. + /// It will error if the bits at the destination do not match the ones described by the layout. pub fn validate_ptr_target( &self, ptr: Pointer, ptr_align: Align, - ty: Ty<'tcx> + mut layout: TyLayout<'tcx>, + path: String, + seen: &mut FxHashSet<(Pointer, Ty<'tcx>)>, + todo: &mut Vec<(Pointer, Ty<'tcx>, String)>, ) -> EvalResult<'tcx> { - match ty.sty { - ty::TyBool => { - self.memory.read_scalar(ptr, ptr_align, Size::from_bytes(1))?.to_bool()?; - } - ty::TyChar => { - let c = self.memory.read_scalar(ptr, ptr_align, Size::from_bytes(4))?.to_bits(Size::from_bytes(4))? as u32; - match ::std::char::from_u32(c) { - Some(..) => (), - None => return err!(InvalidChar(c as u128)), - } - } + self.memory.dump_alloc(ptr.alloc_id); + trace!("validate_ptr_target: {:?}, {:#?}", ptr, layout); - ty::TyFnPtr(_) => { - self.memory.read_ptr_sized(ptr, ptr_align)?; + let variant; + match layout.variants { + layout::Variants::NicheFilling { niche: ref tag, .. } | + layout::Variants::Tagged { ref tag, .. } => { + let size = tag.value.size(self); + let (tag_value, tag_layout) = self.read_field( + Value::ByRef(ptr.into(), ptr_align), + None, + mir::Field::new(0), + layout, + )?; + let tag_value = self.value_to_scalar(ValTy { + value: tag_value, + ty: tag_layout.ty, + })?; + let path = format!("{}.TAG", path); + self.validate_scalar(tag_value, size, tag, &path, tag_layout.ty)?; + let variant_index = self.read_discriminant_as_variant_index( + Place::from_ptr(ptr, ptr_align), + layout, + )?; + variant = variant_index; + layout = layout.for_variant(self, variant_index); + trace!("variant layout: {:#?}", layout); }, - ty::TyRef(_, rty, _) | - ty::TyRawPtr(ty::TypeAndMut { ty: rty, .. }) => { - self.read_ptr(ptr, ptr_align, rty)?; - } - - ty::TyAdt(def, _) => { - if def.is_box() { - self.read_ptr(ptr, ptr_align, ty.boxed_ty())?; - return Ok(()); - } - - if let layout::Abi::Scalar(ref scalar) = self.layout_of(ty)?.abi { - let size = scalar.value.size(self); - self.memory.read_scalar(ptr, ptr_align, size)?; - } - } - - _ => (), + layout::Variants::Single { index } => variant = index, + } + match layout.fields { + // primitives are unions with zero fields + layout::FieldPlacement::Union(0) => { + match layout.abi { + // nothing to do, whatever the pointer points to, it is never going to be read + layout::Abi::Uninhabited => 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) => { + let size = scalar.value.size(self); + let value = self.memory.read_scalar(ptr, ptr_align, size)?; + self.validate_scalar(value, size, scalar, &path, layout.ty)?; + if scalar.value == Primitive::Pointer { + // ignore integer pointers, we can't reason about the final hardware + if let Scalar::Ptr(ptr) = value { + 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 should not be validated as they have no body + if !did.is_local() || self.tcx.is_foreign_item(did) { + return Ok(()); + } + } + if let Some(tam) = layout.ty.builtin_deref(false) { + // we have not encountered this pointer+layout combination before + if seen.insert((ptr, tam.ty)) { + todo.push((ptr, tam.ty, format!("(*{})", path))) + } + } + } + } + Ok(()) + }, + _ => bug!("bad abi for FieldPlacement::Union(0): {:#?}", layout.abi), + } + } + 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 + Ok(()) + }, + layout::FieldPlacement::Array { stride, count } => { + let elem_layout = layout.field(self, 0)?; + for i in 0..count { + let mut path = path.clone(); + self.write_field_name(&mut path, layout.ty, i as usize, variant).unwrap(); + self.validate_ptr_target(ptr.offset(stride * i, self)?, ptr_align, elem_layout, path, seen, todo)?; + } + Ok(()) + }, + layout::FieldPlacement::Arbitrary { ref offsets, .. } => { + + // check length field and vtable field + match layout.ty.builtin_deref(false).map(|tam| &tam.ty.sty) { + | Some(ty::TyStr) + | Some(ty::TySlice(_)) => { + let (len, len_layout) = self.read_field( + Value::ByRef(ptr.into(), ptr_align), + None, + mir::Field::new(1), + layout, + )?; + let len = self.value_to_scalar(ValTy { value: len, ty: len_layout.ty })?; + if len.to_bits(len_layout.size).is_err() { + return validation_failure!("length is not a valid integer", path); + } + }, + Some(ty::TyDynamic(..)) => { + let (vtable, vtable_layout) = self.read_field( + Value::ByRef(ptr.into(), ptr_align), + None, + mir::Field::new(1), + layout, + )?; + let vtable = self.value_to_scalar(ValTy { value: vtable, ty: vtable_layout.ty })?; + if vtable.to_ptr().is_err() { + return validation_failure!("vtable address is not a pointer", path); + } + } + _ => {}, + } + for (i, &offset) in offsets.iter().enumerate() { + let field_layout = layout.field(self, i)?; + let mut path = path.clone(); + self.write_field_name(&mut path, layout.ty, i, variant).unwrap(); + self.validate_ptr_target(ptr.offset(offset, self)?, ptr_align, field_layout, path, seen, todo)?; + } + Ok(()) + } } - Ok(()) } pub fn try_read_by_ref(&self, mut val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { @@ -1333,9 +1539,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let ptr = ptr.to_ptr()?; - // Not the right place to do this - //self.validate_ptr_target(ptr, ptr_align, ty)?; - match layout.abi { layout::Abi::Scalar(..) => { let scalar = self.memory.read_scalar(ptr, ptr_align, layout.size)?; @@ -1623,6 +1826,73 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M pub fn truncate(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { super::truncate(self.tcx.tcx, value, ty) } + + fn write_field_name(&self, s: &mut String, ty: Ty<'tcx>, i: usize, variant: usize) -> ::std::fmt::Result { + match ty.sty { + ty::TyBool | + ty::TyChar | + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyFloat(_) | + ty::TyFnPtr(_) | + ty::TyNever | + ty::TyFnDef(..) | + ty::TyGeneratorWitness(..) | + ty::TyForeign(..) | + ty::TyDynamic(..) => { + bug!("field_name({:?}): not applicable", ty) + } + + // Potentially-fat pointers. + ty::TyRef(_, pointee, _) | + ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + assert!(i < 2); + + // Reuse the fat *T type as its own thin pointer data field. + // This provides information about e.g. DST struct pointees + // (which may have no non-DST form), and will work as long + // as the `Abi` or `FieldPlacement` is checked by users. + if i == 0 { + return write!(s, ".data_ptr"); + } + + match self.tcx.struct_tail(pointee).sty { + ty::TySlice(_) | + ty::TyStr => write!(s, ".len"), + ty::TyDynamic(..) => write!(s, ".vtable_ptr"), + _ => bug!("field_name({:?}): not applicable", ty) + } + } + + // Arrays and slices. + ty::TyArray(_, _) | + ty::TySlice(_) | + ty::TyStr => write!(s, "[{}]", i), + + // generators and closures. + ty::TyClosure(def_id, _) | ty::TyGenerator(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[i]); + write!(s, ".upvar({})", self.tcx.hir.name(freevar.var_id())) + } + + ty::TyTuple(_) => write!(s, ".{}", i), + + // enums + ty::TyAdt(def, ..) if def.is_enum() => { + let variant = &def.variants[variant]; + write!(s, ".{}::{}", variant.name, variant.fields[i].ident) + } + + // other ADTs. + ty::TyAdt(def, _) => write!(s, ".{}", def.non_enum_variant().fields[i].ident), + + ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) | + ty::TyInfer(_) | ty::TyError => { + bug!("write_field_name: unexpected type `{}`", ty) + } + } + } } impl<'mir, 'tcx> Frame<'mir, 'tcx> { diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index ac4d1c74b8c..2765feb23f1 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -763,6 +763,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // Undef check happens *after* we established that the alignment is correct. // We must not return Ok() for unaligned pointers! if self.check_defined(ptr, size).is_err() { + // this inflates undefined bytes to the entire scalar, even if only a few bytes are undefined return Ok(Scalar::undef().into()); } // Now we do the actual reading diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index 3afcd6f2d9b..0c921f66198 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -21,6 +21,7 @@ pub use self::memory::{Memory, MemoryKind, HasMemory}; pub use self::const_eval::{ eval_promoted, mk_borrowck_eval_cx, + mk_eval_cx, CompileTimeEvaluator, const_value_to_allocation_provider, const_eval_provider, diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 28373741c2f..59bf2ae6c0f 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -98,7 +98,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Reads a value from the place without going through the intermediate step of obtaining /// a `miri::Place` pub fn try_read_place( - &mut self, + &self, place: &mir::Place<'tcx>, ) -> EvalResult<'tcx, Option> { use rustc::mir::Place::*; @@ -120,19 +120,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { base: Value, variant: Option, field: mir::Field, - base_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ValTy<'tcx>> { - let mut base_layout = self.layout_of(base_ty)?; + mut base_layout: TyLayout<'tcx>, + ) -> EvalResult<'tcx, (Value, TyLayout<'tcx>)> { if let Some(variant_index) = variant { base_layout = base_layout.for_variant(self, variant_index); } let field_index = field.index(); let field = base_layout.field(self, field_index)?; if field.size.bytes() == 0 { - return Ok(ValTy { - value: Value::Scalar(Scalar::undef()), - ty: field.ty, - }); + return Ok(( + Value::Scalar(Scalar::undef()), + field, + )); } let offset = base_layout.fields.offset(field_index); let value = match base { @@ -151,16 +150,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { assert!(!field.is_unsized()); Value::ByRef(ptr, align) }, - Value::Scalar(val) => bug!("field access on non aggregate {:?}, {:?}", val, base_ty), + Value::Scalar(val) => bug!("field access on non aggregate {:#?}, {:#?}", val, base_layout), }; - Ok(ValTy { - value, - ty: field.ty, - }) + Ok((value, field)) } fn try_read_place_projection( - &mut self, + &self, proj: &mir::PlaceProjection<'tcx>, ) -> EvalResult<'tcx, Option> { use rustc::mir::ProjectionElem::*; @@ -169,8 +165,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { None => return Ok(None), }; let base_ty = self.place_ty(&proj.base); + let base_layout = self.layout_of(base_ty)?; match proj.elem { - Field(field, _) => Ok(Some(self.read_field(base, None, field, base_ty)?.value)), + Field(field, _) => Ok(Some(self.read_field(base, None, field, base_layout)?.0)), // The NullablePointer cases should work fine, need to take care for normal enums Downcast(..) | Subslice { .. } | diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index e281ba79639..56dd3f603b6 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -351,8 +351,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { if self.frame().mir.args_iter().count() == layout.fields.count() + 1 { for (i, arg_local) in arg_locals.enumerate() { let field = mir::Field::new(i); - let valty = self.read_field(args[1].value, None, field, args[1].ty)?; + let (value, layout) = self.read_field(args[1].value, None, field, layout)?; let dest = self.eval_place(&mir::Place::Local(arg_local))?; + let valty = ValTy { + value, + ty: layout.ty, + }; self.write_value(valty, dest)?; } } else { diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index c8d4ce88f27..9902fe98cc0 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -65,7 +65,7 @@ impl MirPass for ConstProp { } } -type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); +type Const<'tcx> = (Value, TyLayout<'tcx>, Span); /// Finds optimization opportunities on the MIR. struct ConstPropagator<'b, 'a, 'tcx:'a+'b> { @@ -258,7 +258,10 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { ) -> Option> { self.ecx.tcx.span = source_info.span; match self.ecx.const_to_value(c.literal.val) { - Ok(val) => Some((val, c.literal.ty, c.span)), + Ok(val) => { + let layout = self.tcx.layout_of(self.param_env.and(c.literal.ty)).ok()?; + Some((val, layout, c.span)) + }, Err(error) => { let (stacktrace, span) = self.ecx.generate_stacktrace(None); let err = ConstEvalErr { @@ -281,11 +284,11 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { Place::Projection(ref proj) => match proj.elem { ProjectionElem::Field(field, _) => { trace!("field proj on {:?}", proj.base); - let (base, ty, span) = self.eval_place(&proj.base, source_info)?; + let (base, layout, span) = self.eval_place(&proj.base, source_info)?; let valty = self.use_ecx(source_info, |this| { - this.ecx.read_field(base, None, field, ty) + this.ecx.read_field(base, None, field, layout) })?; - Some((valty.value, valty.ty, span)) + Some((valty.0, valty.1, span)) }, _ => None, }, @@ -325,14 +328,14 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { fn const_prop( &mut self, rvalue: &Rvalue<'tcx>, - place_ty: ty::Ty<'tcx>, + place_layout: TyLayout<'tcx>, source_info: SourceInfo, ) -> Option> { let span = source_info.span; match *rvalue { // This branch exists for the sanity type check Rvalue::Use(Operand::Constant(ref c)) => { - assert_eq!(c.ty, place_ty); + assert_eq!(c.ty, place_layout.ty); self.eval_constant(c, source_info) }, Rvalue::Use(ref op) => { @@ -345,15 +348,15 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { Rvalue::Discriminant(..) => None, Rvalue::Cast(kind, ref operand, _) => { - let (value, ty, span) = self.eval_operand(operand, source_info)?; + let (value, layout, span) = self.eval_operand(operand, source_info)?; self.use_ecx(source_info, |this| { - let dest_ptr = this.ecx.alloc_ptr(place_ty)?; - let place_align = this.ecx.layout_of(place_ty)?.align; + let dest_ptr = this.ecx.alloc_ptr(place_layout)?; + let place_align = place_layout.align; let dest = ::interpret::Place::from_ptr(dest_ptr, place_align); - this.ecx.cast(ValTy { value, ty }, kind, place_ty, dest)?; + this.ecx.cast(ValTy { value, ty: layout.ty }, kind, place_layout.ty, dest)?; Ok(( Value::ByRef(dest_ptr.into(), place_align), - place_ty, + place_layout, span, )) }) @@ -362,15 +365,14 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { // FIXME(oli-obk): evaluate static/constant slice lengths Rvalue::Len(_) => None, Rvalue::NullaryOp(NullOp::SizeOf, ty) => { - let param_env = self.tcx.param_env(self.source.def_id); - type_size_of(self.tcx, param_env, ty).map(|n| ( + type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some(( Value::Scalar(Scalar::Bits { bits: n as u128, defined: self.tcx.data_layout.pointer_size.bits() as u8, }), - self.tcx.types.usize, + self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?, span, - )) + ))) } Rvalue::UnaryOp(op, ref arg) => { let def_id = if self.tcx.is_closure(self.source.def_id) { @@ -386,10 +388,10 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { let val = self.eval_operand(arg, source_info)?; let prim = self.use_ecx(source_info, |this| { - this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1 }) + this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1.ty }) })?; - let val = self.use_ecx(source_info, |this| this.ecx.unary_op(op, prim, val.1))?; - Some((Value::Scalar(val), place_ty, span)) + let val = self.use_ecx(source_info, |this| this.ecx.unary_op(op, prim, val.1.ty))?; + Some((Value::Scalar(val), place_layout, span)) } Rvalue::CheckedBinaryOp(op, ref left, ref right) | Rvalue::BinaryOp(op, ref left, ref right) => { @@ -407,7 +409,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { } let r = self.use_ecx(source_info, |this| { - this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1 }) + this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1.ty }) })?; if op == BinOp::Shr || op == BinOp::Shl { let left_ty = left.ty(self.mir, self.tcx); @@ -417,7 +419,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { .unwrap() .size .bits(); - let right_size = self.tcx.layout_of(self.param_env.and(right.1)).unwrap().size; + let right_size = right.1.size; if r.to_bits(right_size).ok().map_or(false, |b| b >= left_bits as u128) { let source_scope_local_data = match self.mir.source_scope_local_data { ClearCrossCrate::Set(ref data) => data, @@ -439,11 +441,11 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { } let left = self.eval_operand(left, source_info)?; let l = self.use_ecx(source_info, |this| { - this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1 }) + this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1.ty }) })?; trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); let (val, overflow) = self.use_ecx(source_info, |this| { - this.ecx.binary_op(op, l, left.1, r, right.1) + this.ecx.binary_op(op, l, left.1.ty, r, right.1.ty) })?; let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { Value::ScalarPair( @@ -458,7 +460,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { } Value::Scalar(val) }; - Some((val, place_ty, span)) + Some((val, place_layout, span)) }, } } @@ -544,16 +546,18 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { ) { trace!("visit_statement: {:?}", statement); if let StatementKind::Assign(ref place, ref rval) = statement.kind { - let place_ty = place + let place_ty: ty::Ty<'tcx> = place .ty(&self.mir.local_decls, self.tcx) .to_ty(self.tcx); - if let Some(value) = self.const_prop(rval, place_ty, statement.source_info) { - if let Place::Local(local) = *place { - trace!("checking whether {:?} can be stored to {:?}", value, local); - if self.can_const_prop[local] { - trace!("storing {:?} to {:?}", value, local); - assert!(self.places[local].is_none()); - self.places[local] = Some(value); + if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) { + if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) { + if let Place::Local(local) = *place { + trace!("checking whether {:?} can be stored to {:?}", value, local); + if self.can_const_prop[local] { + trace!("storing {:?} to {:?}", value, local); + assert!(self.places[local].is_none()); + self.places[local] = Some(value); + } } } } diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index df09be00c34..dac4738e2b4 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -634,6 +634,8 @@ impl Scalar { #[derive(PartialEq, Eq, Hash, Debug)] pub enum FieldPlacement { /// All fields start at no offset. The `usize` is the field count. + /// + /// In the case of primitives the number of fields is `0`. Union(usize), /// Array/vector-like placement, with all fields of identical types. diff --git a/src/test/compile-fail/issue-26548.rs b/src/test/compile-fail/issue-26548.rs index fc4f3d1fb53..85ddf8d9493 100644 --- a/src/test/compile-fail/issue-26548.rs +++ b/src/test/compile-fail/issue-26548.rs @@ -11,12 +11,11 @@ //~^^^^^^^^^^ ERROR cycle detected when computing layout of //~| NOTE ...which requires computing layout of //~| NOTE ...which again requires computing layout of -//~| NOTE cycle used when compile_codegen_unit trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } struct S(Option<::It>); -fn main() { +fn main() { //~ NOTE cycle used when processing `main` let _s = S(None); } diff --git a/src/test/ui/const-eval/double_check.rs b/src/test/ui/const-eval/double_check.rs new file mode 100644 index 00000000000..81f6e7ddd2d --- /dev/null +++ b/src/test/ui/const-eval/double_check.rs @@ -0,0 +1,32 @@ +// 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 + +enum Foo { + A = 5, + B = 42, +} +enum Bar { + C = 42, + D = 99, +} +union Union { + foo: &'static Foo, + bar: &'static Bar, + usize: &'static usize, +} +static BAR: usize = 42; +static FOO: (&Foo, &Bar) = unsafe {( + Union { usize: &BAR }.foo, + Union { usize: &BAR }.bar, +)}; + +fn main() {} diff --git a/src/test/ui/const-eval/double_check2.rs b/src/test/ui/const-eval/double_check2.rs new file mode 100644 index 00000000000..b661ee92475 --- /dev/null +++ b/src/test/ui/const-eval/double_check2.rs @@ -0,0 +1,30 @@ +// 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. + +enum Foo { + A = 5, + B = 42, +} +enum Bar { + C = 42, + D = 99, +} +union Union { + foo: &'static Foo, + bar: &'static Bar, + usize: &'static usize, +} +static BAR: usize = 5; +static FOO: (&Foo, &Bar) = unsafe {( //~ undefined behavior + Union { usize: &BAR }.foo, + Union { usize: &BAR }.bar, +)}; + +fn main() {} diff --git a/src/test/ui/const-eval/double_check2.stderr b/src/test/ui/const-eval/double_check2.stderr new file mode 100644 index 00000000000..2a0a674e237 --- /dev/null +++ b/src/test/ui/const-eval/double_check2.stderr @@ -0,0 +1,14 @@ +error[E0080]: this static likely exhibits undefined behavior + --> $DIR/double_check2.rs:25:1 + | +LL | / static FOO: (&Foo, &Bar) = unsafe {( //~ undefined behavior +LL | | Union { usize: &BAR }.foo, +LL | | Union { usize: &BAR }.bar, +LL | | )}; + | |___^ type validation failed: encountered 5 at (*.1).TAG, but expected something in the range 42..=99 + | + = 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 + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-eval/index_out_of_bounds.rs b/src/test/ui/const-eval/index_out_of_bounds.rs index f3578bcef6e..e7ffbe81b9a 100644 --- a/src/test/ui/const-eval/index_out_of_bounds.rs +++ b/src/test/ui/const-eval/index_out_of_bounds.rs @@ -11,7 +11,4 @@ static FOO: i32 = [][0]; //~^ ERROR E0080 -fn main() { - let array = [std::env::args().len()]; - array[1]; //~ ERROR index out of bounds -} +fn main() {} diff --git a/src/test/ui/const-eval/index_out_of_bounds.stderr b/src/test/ui/const-eval/index_out_of_bounds.stderr index 464ba8ff927..a08d405d449 100644 --- a/src/test/ui/const-eval/index_out_of_bounds.stderr +++ b/src/test/ui/const-eval/index_out_of_bounds.stderr @@ -4,14 +4,6 @@ error[E0080]: could not evaluate static initializer LL | static FOO: i32 = [][0]; | ^^^^^ index out of bounds: the len is 0 but the index is 0 -error: index out of bounds: the len is 1 but the index is 1 - --> $DIR/index_out_of_bounds.rs:16:5 - | -LL | array[1]; //~ ERROR index out of bounds - | ^^^^^^^^ - | - = note: #[deny(const_err)] on by default - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-eval/index_out_of_bounds_propagated.rs b/src/test/ui/const-eval/index_out_of_bounds_propagated.rs new file mode 100644 index 00000000000..7a7e2ef0b6b --- /dev/null +++ b/src/test/ui/const-eval/index_out_of_bounds_propagated.rs @@ -0,0 +1,14 @@ +// 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. + +fn main() { + let array = [std::env::args().len()]; + array[1]; //~ ERROR index out of bounds +} diff --git a/src/test/ui/const-eval/index_out_of_bounds_propagated.stderr b/src/test/ui/const-eval/index_out_of_bounds_propagated.stderr new file mode 100644 index 00000000000..97badc19c94 --- /dev/null +++ b/src/test/ui/const-eval/index_out_of_bounds_propagated.stderr @@ -0,0 +1,10 @@ +error: index out of bounds: the len is 1 but the index is 1 + --> $DIR/index_out_of_bounds_propagated.rs:13:5 + | +LL | array[1]; //~ ERROR index out of bounds + | ^^^^^^^^ + | + = note: #[deny(const_err)] on by default + +error: aborting due to previous error + diff --git a/src/test/ui/const-eval/ub-enum-ptr.rs b/src/test/ui/const-eval/ub-enum-ptr.rs new file mode 100644 index 00000000000..8538dd14afe --- /dev/null +++ b/src/test/ui/const-eval/ub-enum-ptr.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. + +#[repr(usize)] +#[derive(Copy, Clone)] +enum Enum { + A = 0, +} + +union Foo { + a: &'static u8, + b: Enum, +} + +// A pointer is guaranteed non-null +const BAD_ENUM: Enum = unsafe { Foo { a: &1 }.b}; +//~^ ERROR this constant likely exhibits undefined behavior + +fn main() { +} diff --git a/src/test/ui/const-eval/ub-enum-ptr.stderr b/src/test/ui/const-eval/ub-enum-ptr.stderr new file mode 100644 index 00000000000..4b7ccc25c6c --- /dev/null +++ b/src/test/ui/const-eval/ub-enum-ptr.stderr @@ -0,0 +1,11 @@ +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/ub-enum-ptr.rs:23:1 + | +LL | const BAD_ENUM: Enum = unsafe { Foo { a: &1 }.b}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer at .TAG, but expected something in the range 0..=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 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-eval/ub-ptr-in-usize.rs b/src/test/ui/const-eval/ub-ptr-in-usize.rs new file mode 100644 index 00000000000..b405f766f91 --- /dev/null +++ b/src/test/ui/const-eval/ub-ptr-in-usize.rs @@ -0,0 +1,22 @@ +// 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, +} + +// a usize's value may be a pointer, that's fine +const PTR_AS_USIZE: usize = unsafe { Foo { a: &1 }.b}; + +fn main() { +} diff --git a/src/test/ui/const-eval/ub-uninhabit.rs b/src/test/ui/const-eval/ub-uninhabit.rs new file mode 100644 index 00000000000..a5e341524bc --- /dev/null +++ b/src/test/ui/const-eval/ub-uninhabit.rs @@ -0,0 +1,23 @@ +// 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. + +union Foo { + a: u8, + b: Bar, +} + +#[derive(Copy, Clone)] +enum Bar {} + +const BAD_BAD_BAD: Bar = unsafe { Foo { a: 1 }.b}; +//~^ ERROR this constant likely exhibits undefined behavior + +fn main() { +} diff --git a/src/test/ui/const-eval/ub-uninhabit.stderr b/src/test/ui/const-eval/ub-uninhabit.stderr new file mode 100644 index 00000000000..623b98dc453 --- /dev/null +++ b/src/test/ui/const-eval/ub-uninhabit.stderr @@ -0,0 +1,11 @@ +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/ub-uninhabit.rs:19:1 + | +LL | const BAD_BAD_BAD: Bar = unsafe { Foo { a: 1 }.b}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-eval/ub-usize-in-ref.rs b/src/test/ui/const-eval/ub-usize-in-ref.rs new file mode 100644 index 00000000000..aaff2f23381 --- /dev/null +++ b/src/test/ui/const-eval/ub-usize-in-ref.rs @@ -0,0 +1,22 @@ +// 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/const-eval/union-const-eval-field.rs b/src/test/ui/const-eval/union-const-eval-field.rs new file mode 100644 index 00000000000..41981e12567 --- /dev/null +++ b/src/test/ui/const-eval/union-const-eval-field.rs @@ -0,0 +1,45 @@ +// 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_fn)] + +type Field1 = i32; +type Field2 = f32; +type Field3 = i64; + +union DummyUnion { + field1: Field1, + field2: Field2, + field3: Field3, +} + +const FLOAT1_AS_I32: i32 = 1065353216; +const UNION: DummyUnion = DummyUnion { field1: FLOAT1_AS_I32 }; + +const fn read_field1() -> Field1 { + const FIELD1: Field1 = unsafe { UNION.field1 }; + FIELD1 +} + +const fn read_field2() -> Field2 { + const FIELD2: Field2 = unsafe { UNION.field2 }; + FIELD2 +} + +const fn read_field3() -> Field3 { + const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR exhibits undefined behavior + FIELD3 +} + +fn main() { + assert_eq!(read_field1(), FLOAT1_AS_I32); + assert_eq!(read_field2(), 1.0); + assert_eq!(read_field3(), unsafe { UNION.field3 }); +} diff --git a/src/test/ui/const-eval/union-const-eval-field.stderr b/src/test/ui/const-eval/union-const-eval-field.stderr new file mode 100644 index 00000000000..94896d6c225 --- /dev/null +++ b/src/test/ui/const-eval/union-const-eval-field.stderr @@ -0,0 +1,11 @@ +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/union-const-eval-field.rs:37:5 + | +LL | const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR exhibits undefined behavior + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 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: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-eval/union-ice.rs b/src/test/ui/const-eval/union-ice.rs new file mode 100644 index 00000000000..426710389eb --- /dev/null +++ b/src/test/ui/const-eval/union-ice.rs @@ -0,0 +1,51 @@ +// 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_fn)] + +type Field1 = i32; +type Field3 = i64; + +union DummyUnion { + field1: Field1, + field3: Field3, +} + +const UNION: DummyUnion = DummyUnion { field1: 1065353216 }; + +const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR this constant likely exhibits undefined + +const FIELD_PATH: Struct = Struct { //~ ERROR this constant likely exhibits undefined behavior + a: 42, + b: unsafe { UNION.field3 }, +}; + +struct Struct { + a: u8, + b: Field3, +} + +const FIELD_PATH2: Struct2 = Struct2 { //~ ERROR this constant likely exhibits undefined behavior + b: [ + 21, + unsafe { UNION.field3 }, + 23, + 24, + ], + a: 42, +}; + +struct Struct2 { + b: [Field3; 4], + a: u8, +} + +fn main() { +} diff --git a/src/test/ui/const-eval/union-ice.stderr b/src/test/ui/const-eval/union-ice.stderr new file mode 100644 index 00000000000..58e9033a071 --- /dev/null +++ b/src/test/ui/const-eval/union-ice.stderr @@ -0,0 +1,36 @@ +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/union-ice.rs:23:1 + | +LL | const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR this constant likely exhibits undefined + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 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]: this constant likely exhibits undefined behavior + --> $DIR/union-ice.rs:25:1 + | +LL | / const FIELD_PATH: Struct = Struct { //~ ERROR this constant likely exhibits undefined behavior +LL | | a: 42, +LL | | b: unsafe { UNION.field3 }, +LL | | }; + | |__^ type validation failed: encountered undefined bytes at .b + | + = 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/union-ice.rs:35:1 + | +LL | / const FIELD_PATH2: Struct2 = Struct2 { //~ ERROR this constant likely exhibits undefined behavior +LL | | b: [ +LL | | 21, +LL | | unsafe { UNION.field3 }, +... | +LL | | a: 42, +LL | | }; + | |__^ type validation failed: encountered undefined bytes at .b[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/const-eval/union-ub-fat-ptr.rs b/src/test/ui/const-eval/union-ub-fat-ptr.rs new file mode 100644 index 00000000000..935e69d2e22 --- /dev/null +++ b/src/test/ui/const-eval/union-ub-fat-ptr.rs @@ -0,0 +1,89 @@ +// 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. + +#[repr(C)] +#[derive(Copy, Clone)] +struct SliceRepr { + ptr: *const u8, + len: usize, +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct BadSliceRepr { + ptr: *const u8, + len: &'static u8, +} + +union SliceTransmute { + repr: SliceRepr, + bad: BadSliceRepr, + slice: &'static [u8], + str: &'static str, +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct DynRepr { + ptr: *const u8, + vtable: *const u8, +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct DynRepr2 { + ptr: *const u8, + vtable: *const u64, +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct BadDynRepr { + ptr: *const u8, + vtable: usize, +} + +union DynTransmute { + repr: DynRepr, + repr2: DynRepr2, + bad: BadDynRepr, + rust: &'static Trait, +} + +trait Trait {} + +// OK +const A: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.str}; +// should lint +const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str}; +// bad +const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str}; +//~^ ERROR this constant likely exhibits undefined behavior + +// OK +const A2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.slice}; +// should lint +const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice}; +// bad +const C2: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice}; +//~^ ERROR this constant likely exhibits undefined behavior + +// bad +const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust}; +//~^ ERROR this constant likely exhibits undefined behavior +// bad +const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust}; +//~^ ERROR this constant likely exhibits undefined behavior +// bad +const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust}; +//~^ ERROR this constant likely exhibits undefined behavior + +fn main() { +} diff --git a/src/test/ui/const-eval/union-ub-fat-ptr.stderr b/src/test/ui/const-eval/union-ub-fat-ptr.stderr new file mode 100644 index 00000000000..3a52db3c1a3 --- /dev/null +++ b/src/test/ui/const-eval/union-ub-fat-ptr.stderr @@ -0,0 +1,43 @@ +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/union-ub-fat-ptr.rs:67:1 + | +LL | const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered length is not a valid integer + | + = 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/union-ub-fat-ptr.rs:75:1 + | +LL | const C2: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered length is not a valid integer + | + = 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/union-ub-fat-ptr.rs:79:1 + | +LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tried to access memory with alignment 1, but alignment 8 is required + | + = 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/union-ub-fat-ptr.rs:82:1 + | +LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset 16, outside bounds of allocation 40 which has size 8 + | + = 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/union-ub-fat-ptr.rs:85:1 + | +LL | const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered vtable address is not a pointer + | + = 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 5 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-eval/union-ub.rs b/src/test/ui/const-eval/union-ub.rs new file mode 100644 index 00000000000..db36764c4a3 --- /dev/null +++ b/src/test/ui/const-eval/union-ub.rs @@ -0,0 +1,45 @@ +// 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. + +union DummyUnion { + u8: u8, + bool: bool, +} + +#[repr(C)] +#[derive(Copy, Clone)] +enum Enum { + A, + B, + C, +} + +#[derive(Copy, Clone)] +union Foo { + a: bool, + b: Enum, +} + +union Bar { + foo: Foo, + u8: u8, +} + +// the value is not valid for bools +const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; +//~^ ERROR this constant likely exhibits undefined behavior + +// The value is not valid for any union variant, but that's fine +// unions are just a convenient way to transmute bits around +const BAD_UNION: Foo = unsafe { Bar { u8: 42 }.foo }; + + +fn main() { +} diff --git a/src/test/ui/const-eval/union-ub.stderr b/src/test/ui/const-eval/union-ub.stderr new file mode 100644 index 00000000000..2a04dae337b --- /dev/null +++ b/src/test/ui/const-eval/union-ub.stderr @@ -0,0 +1,11 @@ +error[E0080]: this constant likely exhibits undefined behavior + --> $DIR/union-ub.rs:36:1 + | +LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, 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: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/tools/miri b/src/tools/miri index 911aedf7369..062cf07d988 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 911aedf736992e907d11cb494167f41f28d02368 +Subproject commit 062cf07d98822beb4c5b1132f47b4397275f403a