diff --git a/src/Cargo.lock b/src/Cargo.lock index e626906ca5ef7..89daa8e09c7d1 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -355,7 +355,7 @@ dependencies = [ "cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "clippy-mini-macro-test 0.2.0", "clippy_lints 0.0.212", - "compiletest_rs 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "derive-new 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -461,7 +461,7 @@ dependencies = [ [[package]] name = "compiletest_rs" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1319,7 +1319,7 @@ dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "cargo_metadata 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3118,7 +3118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" "checksum commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" "checksum commoncrypto-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2" -"checksum compiletest_rs 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "04cea0fe8b8aaca8143af607ad69076866c9f08b83c4b7faca0e993e5486831b" +"checksum compiletest_rs 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "d3064bc712922596dd5ab449fca9261d411893356581fe5297b96aa8f53bb1b8" "checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" "checksum core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cc3532ec724375c7cb7ff0a097b714fde180bb1f6ed2ab27cfcd99ffca873cd2" "checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index b6cb336648bc7..8e71df3c34b0b 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 9125597a7273e..1e9584bc55bdf 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 faad32a5d994e..81cc897232ab0 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 8df6df155112e..7cb4f7d386098 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 3c92f3f623581..873fef75bb9eb 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 10d3af85337e8..c6c1a1d1ebb22 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 } } - pub fn validate_ptr_target( + fn validate_scalar( &self, - ptr: Pointer, - ptr_align: Align, - ty: Ty<'tcx> + 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::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)), + assert_eq!(size.bytes(), 4); + if ::std::char::from_u32(bits as u32).is_none() { + return err!(InvalidChar(bits)); } } + _ => {}, + } - ty::TyFnPtr(_) => { - self.memory.read_ptr_sized(ptr, ptr_align)?; - }, - ty::TyRef(_, rty, _) | - ty::TyRawPtr(ty::TypeAndMut { ty: rty, .. }) => { - self.read_ptr(ptr, ptr_align, rty)?; + 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) + } + } + } - ty::TyAdt(def, _) => { - if def.is_box() { - self.read_ptr(ptr, ptr_align, ty.boxed_ty())?; - return Ok(()); - } + /// 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, + mut layout: TyLayout<'tcx>, + path: String, + seen: &mut FxHashSet<(Pointer, Ty<'tcx>)>, + todo: &mut Vec<(Pointer, Ty<'tcx>, String)>, + ) -> EvalResult<'tcx> { + self.memory.dump_alloc(ptr.alloc_id); + trace!("validate_ptr_target: {:?}, {:#?}", ptr, layout); - 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)?; + 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); + }, + 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 ac4d1c74b8cc1..2765feb23f1b9 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 3afcd6f2d9bb5..0c921f66198eb 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 28373741c2f58..59bf2ae6c0fe7 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 e281ba7963979..56dd3f603b692 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 c8d4ce88f27c1..9902fe98cc011 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 df09be00c342f..dac4738e2b4bf 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/codegen/function-arguments.rs b/src/test/codegen/function-arguments.rs index c027dece01414..09031508da1f7 100644 --- a/src/test/codegen/function-arguments.rs +++ b/src/test/codegen/function-arguments.rs @@ -120,13 +120,13 @@ pub fn unsafe_slice(_: &[UnsafeInner]) { pub fn str(_: &[u8]) { } -// CHECK: @trait_borrow({}* nonnull %arg0.0, {}* noalias nonnull readonly %arg0.1) +// CHECK: @trait_borrow({}* nonnull %arg0.0, [4 x [[USIZE]]]* noalias readonly dereferenceable({{.*}}) %arg0.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn trait_borrow(_: &Drop) { } -// CHECK: @trait_box({}* noalias nonnull, {}* noalias nonnull readonly) +// CHECK: @trait_box({}* noalias nonnull, [4 x [[USIZE]]]* noalias readonly dereferenceable({{.*}})) #[no_mangle] pub fn trait_box(_: Box) { } diff --git a/src/test/compile-fail/issue-26548.rs b/src/test/compile-fail/issue-26548.rs index fc4f3d1fb53de..85ddf8d9493d5 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/compile-fail/union-ub-fat-ptr.rs b/src/test/compile-fail/union-ub-fat-ptr.rs new file mode 100644 index 0000000000000..935e69d2e2230 --- /dev/null +++ b/src/test/compile-fail/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/double_check.rs b/src/test/ui/const-eval/double_check.rs new file mode 100644 index 0000000000000..81f6e7ddd2de2 --- /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 0000000000000..b661ee92475e6 --- /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 0000000000000..2a0a674e237fe --- /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 f3578bcef6e41..e7ffbe81b9ae7 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 464ba8ff92723..a08d405d4494d 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 0000000000000..7a7e2ef0b6b55 --- /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 0000000000000..97badc19c9498 --- /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 0000000000000..8538dd14afed1 --- /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 0000000000000..4b7ccc25c6c01 --- /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 0000000000000..b405f766f9132 --- /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 0000000000000..a5e341524bc73 --- /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 0000000000000..623b98dc4531b --- /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 0000000000000..aaff2f233815b --- /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 0000000000000..41981e1256791 --- /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 0000000000000..94896d6c22577 --- /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 0000000000000..426710389eb29 --- /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 0000000000000..58e9033a071a1 --- /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.rs b/src/test/ui/const-eval/union-ub.rs new file mode 100644 index 0000000000000..db36764c4a306 --- /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 0000000000000..2a04dae337b27 --- /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 911aedf736992..e6f1e15676c26 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 911aedf736992e907d11cb494167f41f28d02368 +Subproject commit e6f1e15676c26fdc7c4713647fe007b26f361a8e