diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 730e8cf05d41d..bb169414886de 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -643,7 +643,9 @@ def build_bootstrap(self): env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ (os.pathsep + env["LIBRARY_PATH"]) \ if "LIBRARY_PATH" in env else "" - env["RUSTFLAGS"] = "-Cdebuginfo=2 " + # preserve existing RUSTFLAGS + env.setdefault("RUSTFLAGS", "") + env["RUSTFLAGS"] += " -Cdebuginfo=2" build_section = "target.{}".format(self.build_triple()) target_features = [] @@ -652,13 +654,13 @@ def build_bootstrap(self): elif self.get_toml("crt-static", build_section) == "false": target_features += ["-crt-static"] if target_features: - env["RUSTFLAGS"] += "-C target-feature=" + (",".join(target_features)) + " " + env["RUSTFLAGS"] += " -C target-feature=" + (",".join(target_features)) target_linker = self.get_toml("linker", build_section) if target_linker is not None: - env["RUSTFLAGS"] += "-C linker=" + target_linker + " " - env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes " + env["RUSTFLAGS"] += " -C linker=" + target_linker + env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes" if self.get_toml("deny-warnings", "rust") != "false": - env["RUSTFLAGS"] += "-Dwarnings " + env["RUSTFLAGS"] += " -Dwarnings" env["PATH"] = os.path.join(self.bin_root(), "bin") + \ os.pathsep + env["PATH"] diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 384219c38fd04..f8734ebdf4254 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -260,7 +260,7 @@ install!((self, builder, _config), }; Rustc, "src/librustc", true, only_hosts: true, { builder.ensure(dist::Rustc { - compiler: self.compiler, + compiler: builder.compiler(builder.top_stage, self.target), }); install_rustc(builder, self.compiler.stage, self.target); }; diff --git a/src/doc/unstable-book/src/language-features/cfg-sanitize.md b/src/doc/unstable-book/src/language-features/cfg-sanitize.md new file mode 100644 index 0000000000000..949f24ab9c11e --- /dev/null +++ b/src/doc/unstable-book/src/language-features/cfg-sanitize.md @@ -0,0 +1,36 @@ +# `cfg_sanitize` + +The tracking issue for this feature is: [#39699] + +[#39699]: https://github.com/rust-lang/rust/issues/39699 + +------------------------ + +The `cfg_sanitize` feature makes it possible to execute different code +depending on whether a particular sanitizer is enabled or not. + +## Examples + +``` rust +#![feature(cfg_sanitize)] + +#[cfg(sanitize = "thread")] +fn a() { + // ... +} + +#[cfg(not(sanitize = "thread"))] +fn a() { + // ... +} + +fn b() { + if cfg!(sanitize = "leak") { + // ... + } else { + // ... + } +} + +``` + diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index e3dc5630c94b4..d4952f53bf7fe 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1348,9 +1348,11 @@ extern "rust-intrinsic" { pub fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; /// Internal hook used by Miri to implement unwinding. + /// Compiles to a NOP during non-Miri codegen. + /// /// Perma-unstable: do not use #[cfg(not(bootstrap))] - pub fn miri_start_panic(data: *mut (dyn crate::any::Any + crate::marker::Send)) -> !; + pub fn miri_start_panic(data: *mut (dyn crate::any::Any + crate::marker::Send)) -> (); } // Some functions are defined here because they accidentally got made diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 0c834e5c2a05c..3a14197c77bec 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -36,10 +36,7 @@ use core::raw; use core::panic::BoxMeUp; cfg_if::cfg_if! { - if #[cfg(miri)] { - #[path = "miri.rs"] - mod imp; - } else if #[cfg(target_os = "emscripten")] { + if #[cfg(target_os = "emscripten")] { #[path = "emcc.rs"] mod imp; } else if #[cfg(target_arch = "wasm32")] { @@ -94,5 +91,14 @@ pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8), #[unwind(allowed)] pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 { let payload = payload as *mut &mut dyn BoxMeUp; - imp::panic(Box::from_raw((*payload).take_box())) + let payload = (*payload).take_box(); + + // Miri panic support: cfg'd out of normal builds just to be sure. + // When going through normal codegen, `miri_start_panic` is a NOP, so the + // Miri-enabled sysroot still supports normal unwinding. But when executed in + // Miri, this line initiates unwinding. + #[cfg(miri)] + core::intrinsics::miri_start_panic(payload); + + imp::panic(Box::from_raw(payload)) } diff --git a/src/libpanic_unwind/miri.rs b/src/libpanic_unwind/miri.rs deleted file mode 100644 index f26c42fd4bcba..0000000000000 --- a/src/libpanic_unwind/miri.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![allow(nonstandard_style)] - -use core::any::Any; -use alloc::boxed::Box; - -pub fn payload() -> *mut u8 { - core::ptr::null_mut() -} - -pub unsafe fn panic(data: Box) -> ! { - core::intrinsics::miri_start_panic(Box::into_raw(data)) -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box { - Box::from_raw(ptr) -} - -// This is required by the compiler to exist (e.g., it's a lang item), -// but is never used by Miri. Therefore, we just use a stub here -#[lang = "eh_personality"] -#[cfg(not(test))] -fn rust_eh_personality() { - unsafe { core::intrinsics::abort() } -} - -// The rest is required on *some* targets to exist (specifically, MSVC targets that use SEH). -// We just add it on all targets. Copied from `seh.rs`. -#[repr(C)] -pub struct _TypeDescriptor { - pub pVFTable: *const u8, - pub spare: *mut u8, - pub name: [u8; 11], -} - -const TYPE_NAME: [u8; 11] = *b"rust_panic\0"; - -#[cfg_attr(not(test), lang = "eh_catch_typeinfo")] -static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { - pVFTable: core::ptr::null(), - spare: core::ptr::null_mut(), - name: TYPE_NAME, -}; diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index d2ac5436cc802..9a242d9d0769b 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -47,6 +47,17 @@ pub enum Sanitizer { Thread, } +impl fmt::Display for Sanitizer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Sanitizer::Address => "address".fmt(f), + Sanitizer::Leak => "leak".fmt(f), + Sanitizer::Memory => "memory".fmt(f), + Sanitizer::Thread => "thread".fmt(f), + } + } +} + impl FromStr for Sanitizer { type Err = (); fn from_str(s: &str) -> Result { @@ -1580,6 +1591,10 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { } } } + if let Some(s) = &sess.opts.debugging_opts.sanitizer { + let symbol = Symbol::intern(&s.to_string()); + ret.insert((sym::sanitize, Some(symbol))); + } if sess.opts.debug_assertions { ret.insert((Symbol::intern("debug_assertions"), None)); } diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 14be0e80fb482..d76392f7570b4 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -528,18 +528,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => FnAbi::new(&bx, sig, &extra_args) }; - // This should never be reachable at runtime: - // We should only emit a call to this intrinsic in #[cfg(miri)] mode, - // which means that we will never actually use the generate object files - // (we will just be interpreting the MIR) - // - // Note that we still need to be able to codegen *something* for this intrisnic: - // Miri currently uses Xargo to build a special libstd. As a side effect, - // we generate normal object files for libstd - while these are never used, - // we still need to be able to build them. + // For normal codegen, this Miri-specific intrinsic is just a NOP. if intrinsic == Some("miri_start_panic") { - bx.abort(); - bx.unreachable(); + let target = destination.as_ref().unwrap().1; + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.funclet_br(self, &mut bx, target); return; } diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index 7c0d39965fc00..16d8ada9f24c0 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -524,6 +524,9 @@ declare_features! ( /// Allows the use of `if` and `match` in constants. (active, const_if_match, "1.41.0", Some(49146), None), + /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. + (active, cfg_sanitize, "1.41.0", Some(39699), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs index 9c936492cbded..4fa0198d8716d 100644 --- a/src/librustc_feature/builtin_attrs.rs +++ b/src/librustc_feature/builtin_attrs.rs @@ -25,6 +25,7 @@ const GATED_CFGS: &[GatedCfg] = &[ (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)), (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), + (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 5b263f7680131..70d9836b6ff0f 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -651,20 +651,28 @@ where use rustc::mir::PlaceBase; let mut place_ty = match &place.base { - PlaceBase::Local(mir::RETURN_PLACE) => match self.frame().return_place { - Some(return_place) => { - // We use our layout to verify our assumption; caller will validate - // their layout on return. - PlaceTy { - place: *return_place, - layout: self.layout_of( - self.subst_from_frame_and_normalize_erasing_regions( - self.frame().body.return_ty() - ) - )?, - } + PlaceBase::Local(mir::RETURN_PLACE) => { + // `return_place` has the *caller* layout, but we want to use our + // `layout to verify our assumption. The caller will validate + // their layout on return. + PlaceTy { + place: match self.frame().return_place { + Some(p) => *p, + // Even if we don't have a return place, we sometimes need to + // create this place, but any attempt to read from / write to it + // (even a ZST read/write) needs to error, so let us make this + // a NULL place. + // + // FIXME: Ideally we'd make sure that the place projections also + // bail out. + None => Place::null(&*self), + }, + layout: self.layout_of( + self.subst_from_frame_and_normalize_erasing_regions( + self.frame().body.return_ty() + ) + )?, } - None => throw_unsup!(InvalidNullPointerUsage), }, PlaceBase::Local(local) => PlaceTy { // This works even for dead/uninitialized locals; we check further when writing @@ -791,8 +799,8 @@ where // to handle padding properly, which is only correct if we never look at this data with the // wrong type. - let ptr = match self.check_mplace_access(dest, None) - .expect("places should be checked on creation") + // Invalid places are a thing: the return place of a diverging function + let ptr = match self.check_mplace_access(dest, None)? { Some(ptr) => ptr, None => return Ok(()), // zero-sized access diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index cca76700ed101..f4fb9a5e4f2a2 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -29,6 +29,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(stmt_expr_attributes)] #![feature(bool_to_option)] #![feature(trait_alias)] +#![feature(matches_macro)] #![recursion_limit="256"] diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index 9ed1ca740b8e7..2d5a0a2afcd01 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -5,7 +5,7 @@ use rustc::ty::{self, Ty}; use rustc::hir::def_id::DefId; use syntax_pos::DUMMY_SP; -use super::{ConstKind, Item as ConstCx}; +use super::Item as ConstCx; pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs { ConstQualifs { @@ -33,9 +33,10 @@ pub trait Qualif { /// of the type. fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool; - fn in_static(_cx: &ConstCx<'_, 'tcx>, _def_id: DefId) -> bool { - // FIXME(eddyb) should we do anything here for value properties? - false + fn in_static(cx: &ConstCx<'_, 'tcx>, def_id: DefId) -> bool { + // `mir_const_qualif` does return the qualifs in the final value of a `static`, so we could + // use value-based qualification here, but we shouldn't do this without a good reason. + Self::in_any_value_of_ty(cx, cx.tcx.type_of(def_id)) } fn in_projection_structurally( @@ -217,34 +218,6 @@ impl Qualif for HasMutInterior { rvalue: &Rvalue<'tcx>, ) -> bool { match *rvalue { - // Returning `true` for `Rvalue::Ref` indicates the borrow isn't - // allowed in constants (and the `Checker` will error), and/or it - // won't be promoted, due to `&mut ...` or interior mutability. - Rvalue::Ref(_, kind, ref place) => { - let ty = place.ty(cx.body, cx.tcx).ty; - - if let BorrowKind::Mut { .. } = kind { - // In theory, any zero-sized value could be borrowed - // mutably without consequences. - match ty.kind { - // Inside a `static mut`, &mut [...] is also allowed. - | ty::Array(..) - | ty::Slice(_) - if cx.const_kind == Some(ConstKind::StaticMut) - => {}, - - // FIXME(eddyb): We only return false for `&mut []` outside a const - // context which seems unnecessary given that this is merely a ZST. - | ty::Array(_, len) - if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0) - && cx.const_kind == None - => {}, - - _ => return true, - } - } - } - Rvalue::Aggregate(ref kind, _) => { if let AggregateKind::Adt(def, ..) = **kind { if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() { diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 829d9ee6fafd7..783c64ece7371 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -23,13 +23,6 @@ use super::qualifs::{self, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{ConstKind, Item, Qualif, is_lang_panic_fn}; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum CheckOpResult { - Forbidden, - Unleashed, - Allowed, -} - pub type IndirectlyMutableResults<'mir, 'tcx> = old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>; @@ -149,17 +142,6 @@ pub struct Validator<'a, 'mir, 'tcx> { /// The span of the current statement. span: Span, - - /// True if the local was assigned the result of an illegal borrow (`ops::MutBorrow`). - /// - /// This is used to hide errors from {re,}borrowing the newly-assigned local, instead pointing - /// the user to the place where the illegal borrow occurred. This set is only populated once an - /// error has been emitted, so it will never cause an erroneous `mir::Body` to pass validation. - /// - /// FIXME(ecstaticmorse): assert at the end of checking that if `tcx.has_errors() == false`, - /// this set is empty. Note that if we start removing locals from - /// `derived_from_illegal_borrow`, just checking at the end won't be enough. - derived_from_illegal_borrow: BitSet, } impl Deref for Validator<'_, 'mir, 'tcx> { @@ -213,7 +195,6 @@ impl Validator<'a, 'mir, 'tcx> { span: item.body.span, item, qualifs, - derived_from_illegal_borrow: BitSet::new_empty(item.body.local_decls.len()), } } @@ -258,15 +239,15 @@ impl Validator<'a, 'mir, 'tcx> { } /// Emits an error at the given `span` if an expression cannot be evaluated in the current - /// context. Returns `Forbidden` if an error was emitted. - pub fn check_op_spanned(&mut self, op: O, span: Span) -> CheckOpResult + /// context. + pub fn check_op_spanned(&mut self, op: O, span: Span) where O: NonConstOp { trace!("check_op: op={:?}", op); if op.is_allowed_in_item(self) { - return CheckOpResult::Allowed; + return; } // If an operation is supported in miri (and is not already controlled by a feature gate) it @@ -276,20 +257,19 @@ impl Validator<'a, 'mir, 'tcx> { if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { self.tcx.sess.span_warn(span, "skipping const checks"); - return CheckOpResult::Unleashed; + return; } op.emit_error(self, span); - CheckOpResult::Forbidden } /// Emits an error if an expression cannot be evaluated in the current context. - pub fn check_op(&mut self, op: impl NonConstOp) -> CheckOpResult { + pub fn check_op(&mut self, op: impl NonConstOp) { let span = self.span; self.check_op_spanned(op, span) } - fn check_static(&mut self, def_id: DefId, span: Span) -> CheckOpResult { + fn check_static(&mut self, def_id: DefId, span: Span) { let is_thread_local = self.tcx.has_attr(def_id, sym::thread_local); if is_thread_local { self.check_op_spanned(ops::ThreadLocalAccess, span) @@ -322,20 +302,9 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location); - // Check nested operands and places. + // Special-case reborrows to be more like a copy of a reference. if let Rvalue::Ref(_, kind, ref place) = *rvalue { - // Special-case reborrows to be more like a copy of a reference. - let mut reborrow_place = None; - if let &[ref proj_base @ .., elem] = place.projection.as_ref() { - if elem == ProjectionElem::Deref { - let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty; - if let ty::Ref(..) = base_ty.kind { - reborrow_place = Some(proj_base); - } - } - } - - if let Some(proj) = reborrow_place { + if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) { let ctx = match kind { BorrowKind::Shared => PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow, @@ -351,14 +320,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { ), }; self.visit_place_base(&place.base, ctx, location); - self.visit_projection(&place.base, proj, ctx, location); - } else { - self.super_rvalue(rvalue, location); + self.visit_projection(&place.base, reborrowed_proj, ctx, location); + return; } - } else { - self.super_rvalue(rvalue, location); } + self.super_rvalue(rvalue, location); + match *rvalue { Rvalue::Use(_) | Rvalue::Repeat(..) | @@ -369,9 +337,58 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { Rvalue::Cast(CastKind::Pointer(_), ..) | Rvalue::Discriminant(..) | Rvalue::Len(_) | - Rvalue::Ref(..) | Rvalue::Aggregate(..) => {} + | Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place) + | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) + => { + let ty = place.ty(self.body, self.tcx).ty; + let is_allowed = match ty.kind { + // Inside a `static mut`, `&mut [...]` is allowed. + ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut + => true, + + // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given + // that this is merely a ZST and it is already eligible for promotion. + // This may require an RFC? + /* + ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0) + => true, + */ + + _ => false, + }; + + if !is_allowed { + self.check_op(ops::MutBorrow(kind)); + } + } + + // At the moment, `PlaceBase::Static` is only used for promoted MIR. + | Rvalue::Ref(_, BorrowKind::Shared, ref place) + | Rvalue::Ref(_, BorrowKind::Shallow, ref place) + if matches!(place.base, PlaceBase::Static(_)) + => bug!("Saw a promoted during const-checking, which must run before promotion"), + + | Rvalue::Ref(_, kind @ BorrowKind::Shared, ref place) + | Rvalue::Ref(_, kind @ BorrowKind::Shallow, ref place) + => { + // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually + // seek the cursors beforehand. + self.qualifs.has_mut_interior.cursor.seek_before(location); + self.qualifs.indirectly_mutable.seek(location); + + let borrowed_place_has_mut_interior = HasMutInterior::in_place( + &self.item, + &|local| self.qualifs.has_mut_interior_eager_seek(local), + place.as_ref(), + ); + + if borrowed_place_has_mut_interior { + self.check_op(ops::MutBorrow(kind)); + } + } + Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { let operand_ty = operand.ty(self.body, self.tcx); let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); @@ -436,58 +453,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { } } - fn visit_assign(&mut self, dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { - trace!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location); - - // Error on mutable borrows or shared borrows of values with interior mutability. - // - // This replicates the logic at the start of `assign` in the old const checker. Note that - // it depends on `HasMutInterior` being set for mutable borrows as well as values with - // interior mutability. - if let Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue { - // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually seek - // the cursors beforehand. - self.qualifs.has_mut_interior.cursor.seek_before(location); - self.qualifs.indirectly_mutable.seek(location); - - let rvalue_has_mut_interior = HasMutInterior::in_rvalue( - &self.item, - &|local| self.qualifs.has_mut_interior_eager_seek(local), - rvalue, - ); - - if rvalue_has_mut_interior { - let is_derived_from_illegal_borrow = match borrowed_place.as_local() { - // If an unprojected local was borrowed and its value was the result of an - // illegal borrow, suppress this error and mark the result of this borrow as - // illegal as well. - Some(borrowed_local) - if self.derived_from_illegal_borrow.contains(borrowed_local) => - { - true - } - - // Otherwise proceed normally: check the legality of a mutable borrow in this - // context. - _ => self.check_op(ops::MutBorrow(kind)) == CheckOpResult::Forbidden, - }; - - // When the target of the assignment is a local with no projections, mark it as - // derived from an illegal borrow if necessary. - // - // FIXME: should we also clear `derived_from_illegal_borrow` when a local is - // assigned a new value? - if is_derived_from_illegal_borrow { - if let Some(dest) = dest.as_local() { - self.derived_from_illegal_borrow.insert(dest); - } - } - } - } - - self.super_assign(dest, rvalue, location); - } - fn visit_projection_elem( &mut self, place_base: &PlaceBase<'tcx>, @@ -724,3 +689,36 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) } }); } + +fn place_as_reborrow( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + place: &'a Place<'tcx>, +) -> Option<&'a [PlaceElem<'tcx>]> { + place + .projection + .split_last() + .and_then(|(outermost, inner)| { + if outermost != &ProjectionElem::Deref { + return None; + } + + // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` + // that points to the allocation for the static. Don't treat these as reborrows. + if let PlaceBase::Local(local) = place.base { + if body.local_decls[local].is_ref_to_static() { + return None; + } + } + + // Ensure the type being derefed is a reference and not a raw pointer. + // + // This is sufficient to prevent an access to a `static mut` from being marked as a + // reborrow, even if the check above were to disappear. + let inner_ty = Place::ty_from(&place.base, inner, body, tcx).ty; + match inner_ty.kind { + ty::Ref(..) => Some(inner), + _ => None, + } + }) +} diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 7d43c3c8d076e..3059b05969106 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -177,6 +177,7 @@ symbols! { cfg_attr, cfg_attr_multi, cfg_doctest, + cfg_sanitize, cfg_target_feature, cfg_target_has_atomic, cfg_target_thread_local, @@ -634,6 +635,7 @@ symbols! { rust_eh_unwind_resume, rust_oom, rvalue_static_promotion, + sanitize, sanitizer_runtime, _Self, self_in_typedefs, diff --git a/src/test/ui/consts/const-multi-ref.rs b/src/test/ui/consts/const-multi-ref.rs index 498e99e668b43..5e2be0d4f3f02 100644 --- a/src/test/ui/consts/const-multi-ref.rs +++ b/src/test/ui/consts/const-multi-ref.rs @@ -1,11 +1,24 @@ -const _X: i32 = { +// Ensure that we point the user to the erroneous borrow but not to any subsequent borrows of that +// initial one. + +const _: i32 = { let mut a = 5; - let p = &mut a; //~ ERROR references in constants may only refer to immutable values + let p = &mut a; //~ ERROR references in constants may only refer to immutable values - let reborrow = {p}; //~ ERROR references in constants may only refer to immutable values + let reborrow = {p}; let pp = &reborrow; let ppp = &pp; ***ppp }; +const _: std::cell::Cell = { + let mut a = std::cell::Cell::new(5); + let p = &a; //~ ERROR cannot borrow a constant which may contain interior mutability + + let reborrow = {p}; + let pp = &reborrow; + let ppp = &pp; + a +}; + fn main() {} diff --git a/src/test/ui/consts/const-multi-ref.stderr b/src/test/ui/consts/const-multi-ref.stderr index 9e525ef9aac75..ed3837e9c9ee5 100644 --- a/src/test/ui/consts/const-multi-ref.stderr +++ b/src/test/ui/consts/const-multi-ref.stderr @@ -1,15 +1,16 @@ error[E0017]: references in constants may only refer to immutable values - --> $DIR/const-multi-ref.rs:3:13 + --> $DIR/const-multi-ref.rs:6:13 | LL | let p = &mut a; | ^^^^^^ constants require immutable values -error[E0017]: references in constants may only refer to immutable values - --> $DIR/const-multi-ref.rs:5:21 +error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead + --> $DIR/const-multi-ref.rs:16:13 | -LL | let reborrow = {p}; - | ^ constants require immutable values +LL | let p = &a; + | ^^ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0017`. +Some errors have detailed explanations: E0017, E0492. +For more information about an error, try `rustc --explain E0017`. diff --git a/src/test/ui/error-codes/E0017.rs b/src/test/ui/error-codes/E0017.rs index 94b6587eb815a..3bc518c2c2b71 100644 --- a/src/test/ui/error-codes/E0017.rs +++ b/src/test/ui/error-codes/E0017.rs @@ -1,8 +1,11 @@ static X: i32 = 1; const C: i32 = 2; +static mut M: i32 = 3; const CR: &'static mut i32 = &mut C; //~ ERROR E0017 static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017 + //~| ERROR E0019 //~| ERROR cannot borrow static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017 +static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR E0017 fn main() {} diff --git a/src/test/ui/error-codes/E0017.stderr b/src/test/ui/error-codes/E0017.stderr index 47863f0221405..8c8660adceb7a 100644 --- a/src/test/ui/error-codes/E0017.stderr +++ b/src/test/ui/error-codes/E0017.stderr @@ -1,28 +1,40 @@ error[E0017]: references in constants may only refer to immutable values - --> $DIR/E0017.rs:4:30 + --> $DIR/E0017.rs:5:30 | LL | const CR: &'static mut i32 = &mut C; | ^^^^^^ constants require immutable values +error[E0019]: static contains unimplemented expression type + --> $DIR/E0017.rs:6:39 + | +LL | static STATIC_REF: &'static mut i32 = &mut X; + | ^^^^^^ + error[E0017]: references in statics may only refer to immutable values - --> $DIR/E0017.rs:5:39 + --> $DIR/E0017.rs:6:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ statics require immutable values error[E0596]: cannot borrow immutable static item `X` as mutable - --> $DIR/E0017.rs:5:39 + --> $DIR/E0017.rs:6:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ cannot borrow as mutable error[E0017]: references in statics may only refer to immutable values - --> $DIR/E0017.rs:7:38 + --> $DIR/E0017.rs:9:38 | LL | static CONST_REF: &'static mut i32 = &mut C; | ^^^^^^ statics require immutable values -error: aborting due to 4 previous errors +error[E0017]: references in statics may only refer to immutable values + --> $DIR/E0017.rs:10:52 + | +LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; + | ^^^^^^ statics require immutable values + +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0017, E0596. +Some errors have detailed explanations: E0017, E0019, E0596. For more information about an error, try `rustc --explain E0017`. diff --git a/src/test/ui/error-codes/E0388.rs b/src/test/ui/error-codes/E0388.rs deleted file mode 100644 index 3aa4ac9655cc9..0000000000000 --- a/src/test/ui/error-codes/E0388.rs +++ /dev/null @@ -1,9 +0,0 @@ -static X: i32 = 1; -const C: i32 = 2; - -const CR: &'static mut i32 = &mut C; //~ ERROR E0017 -static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017 - //~| ERROR cannot borrow -static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017 - -fn main() {} diff --git a/src/test/ui/error-codes/E0388.stderr b/src/test/ui/error-codes/E0388.stderr deleted file mode 100644 index b52d5260b13c8..0000000000000 --- a/src/test/ui/error-codes/E0388.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error[E0017]: references in constants may only refer to immutable values - --> $DIR/E0388.rs:4:30 - | -LL | const CR: &'static mut i32 = &mut C; - | ^^^^^^ constants require immutable values - -error[E0017]: references in statics may only refer to immutable values - --> $DIR/E0388.rs:5:39 - | -LL | static STATIC_REF: &'static mut i32 = &mut X; - | ^^^^^^ statics require immutable values - -error[E0596]: cannot borrow immutable static item `X` as mutable - --> $DIR/E0388.rs:5:39 - | -LL | static STATIC_REF: &'static mut i32 = &mut X; - | ^^^^^^ cannot borrow as mutable - -error[E0017]: references in statics may only refer to immutable values - --> $DIR/E0388.rs:7:38 - | -LL | static CONST_REF: &'static mut i32 = &mut C; - | ^^^^^^ statics require immutable values - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0017, E0596. -For more information about an error, try `rustc --explain E0017`. diff --git a/src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs b/src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs new file mode 100644 index 0000000000000..c3e7cc9ed8a9b --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs @@ -0,0 +1,3 @@ +#[cfg(not(sanitize = "thread"))] +//~^ `cfg(sanitize)` is experimental +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr b/src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr new file mode 100644 index 0000000000000..f67a0d83bdd3a --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr @@ -0,0 +1,12 @@ +error[E0658]: `cfg(sanitize)` is experimental and subject to change + --> $DIR/feature-gate-cfg_sanitize.rs:1:11 + | +LL | #[cfg(not(sanitize = "thread"))] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/39699 + = help: add `#![feature(cfg_sanitize)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/sanitize-cfg.rs b/src/test/ui/sanitize-cfg.rs new file mode 100644 index 0000000000000..9c198543a8664 --- /dev/null +++ b/src/test/ui/sanitize-cfg.rs @@ -0,0 +1,26 @@ +// Verifies that when compiling with -Zsanitizer=option, +// the `#[cfg(sanitize = "option")]` attribute is configured. + +// needs-sanitizer-support +// only-linux +// only-x86_64 +// check-pass +// revisions: address leak memory thread +//[address]compile-flags: -Zsanitizer=address --cfg address +//[leak]compile-flags: -Zsanitizer=leak --cfg leak +//[memory]compile-flags: -Zsanitizer=memory --cfg memory +//[thread]compile-flags: -Zsanitizer=thread --cfg thread + +#![feature(cfg_sanitize)] + +#[cfg(all(sanitize = "address", address))] +fn main() {} + +#[cfg(all(sanitize = "leak", leak))] +fn main() {} + +#[cfg(all(sanitize = "memory", memory))] +fn main() {} + +#[cfg(all(sanitize = "thread", thread))] +fn main() {}