Auto merge of #66950 - RalfJung:rollup-12d0zx8, r=RalfJung
Rollup of 5 pull requests Successful merges: - #66245 (Conditional compilation for sanitizers) - #66654 (Handle const-checks for `&mut` outside of `HasMutInterior`) - #66822 (libunwind_panic: adjust miri panic hack) - #66827 (handle diverging functions forwarding their return place) - #66834 (rustbuild fixes) Failed merges: r? @ghost
This commit is contained in:
commit
4af3ee8ee2
24 changed files with 293 additions and 262 deletions
|
@ -643,7 +643,9 @@ class RustBuild(object):
|
||||||
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
||||||
(os.pathsep + env["LIBRARY_PATH"]) \
|
(os.pathsep + env["LIBRARY_PATH"]) \
|
||||||
if "LIBRARY_PATH" in env else ""
|
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())
|
build_section = "target.{}".format(self.build_triple())
|
||||||
target_features = []
|
target_features = []
|
||||||
|
@ -652,13 +654,13 @@ class RustBuild(object):
|
||||||
elif self.get_toml("crt-static", build_section) == "false":
|
elif self.get_toml("crt-static", build_section) == "false":
|
||||||
target_features += ["-crt-static"]
|
target_features += ["-crt-static"]
|
||||||
if target_features:
|
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)
|
target_linker = self.get_toml("linker", build_section)
|
||||||
if target_linker is not None:
|
if target_linker is not None:
|
||||||
env["RUSTFLAGS"] += "-C linker=" + target_linker + " "
|
env["RUSTFLAGS"] += " -C linker=" + target_linker
|
||||||
env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes "
|
env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
|
||||||
if self.get_toml("deny-warnings", "rust") != "false":
|
if self.get_toml("deny-warnings", "rust") != "false":
|
||||||
env["RUSTFLAGS"] += "-Dwarnings "
|
env["RUSTFLAGS"] += " -Dwarnings"
|
||||||
|
|
||||||
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
|
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
|
||||||
os.pathsep + env["PATH"]
|
os.pathsep + env["PATH"]
|
||||||
|
|
|
@ -260,7 +260,7 @@ install!((self, builder, _config),
|
||||||
};
|
};
|
||||||
Rustc, "src/librustc", true, only_hosts: true, {
|
Rustc, "src/librustc", true, only_hosts: true, {
|
||||||
builder.ensure(dist::Rustc {
|
builder.ensure(dist::Rustc {
|
||||||
compiler: self.compiler,
|
compiler: builder.compiler(builder.top_stage, self.target),
|
||||||
});
|
});
|
||||||
install_rustc(builder, self.compiler.stage, self.target);
|
install_rustc(builder, self.compiler.stage, self.target);
|
||||||
};
|
};
|
||||||
|
|
36
src/doc/unstable-book/src/language-features/cfg-sanitize.md
Normal file
36
src/doc/unstable-book/src/language-features/cfg-sanitize.md
Normal file
|
@ -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 {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
|
@ -1348,9 +1348,11 @@ extern "rust-intrinsic" {
|
||||||
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
|
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
|
||||||
|
|
||||||
/// Internal hook used by Miri to implement unwinding.
|
/// Internal hook used by Miri to implement unwinding.
|
||||||
|
/// Compiles to a NOP during non-Miri codegen.
|
||||||
|
///
|
||||||
/// Perma-unstable: do not use
|
/// Perma-unstable: do not use
|
||||||
#[cfg(not(bootstrap))]
|
#[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
|
// Some functions are defined here because they accidentally got made
|
||||||
|
|
|
@ -36,10 +36,7 @@ use core::raw;
|
||||||
use core::panic::BoxMeUp;
|
use core::panic::BoxMeUp;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(miri)] {
|
if #[cfg(target_os = "emscripten")] {
|
||||||
#[path = "miri.rs"]
|
|
||||||
mod imp;
|
|
||||||
} else if #[cfg(target_os = "emscripten")] {
|
|
||||||
#[path = "emcc.rs"]
|
#[path = "emcc.rs"]
|
||||||
mod imp;
|
mod imp;
|
||||||
} else if #[cfg(target_arch = "wasm32")] {
|
} 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)]
|
#[unwind(allowed)]
|
||||||
pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
|
pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
|
||||||
let payload = payload as *mut &mut dyn BoxMeUp;
|
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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<dyn Any + Send>) -> ! {
|
|
||||||
core::intrinsics::miri_start_panic(Box::into_raw(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
|
|
||||||
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,
|
|
||||||
};
|
|
|
@ -47,6 +47,17 @@ pub enum Sanitizer {
|
||||||
Thread,
|
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 {
|
impl FromStr for Sanitizer {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
fn from_str(s: &str) -> Result<Sanitizer, ()> {
|
fn from_str(s: &str) -> Result<Sanitizer, ()> {
|
||||||
|
@ -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 {
|
if sess.opts.debug_assertions {
|
||||||
ret.insert((Symbol::intern("debug_assertions"), None));
|
ret.insert((Symbol::intern("debug_assertions"), None));
|
||||||
}
|
}
|
||||||
|
|
|
@ -528,18 +528,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
_ => FnAbi::new(&bx, sig, &extra_args)
|
_ => FnAbi::new(&bx, sig, &extra_args)
|
||||||
};
|
};
|
||||||
|
|
||||||
// This should never be reachable at runtime:
|
// For normal codegen, this Miri-specific intrinsic is just a NOP.
|
||||||
// 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.
|
|
||||||
if intrinsic == Some("miri_start_panic") {
|
if intrinsic == Some("miri_start_panic") {
|
||||||
bx.abort();
|
let target = destination.as_ref().unwrap().1;
|
||||||
bx.unreachable();
|
helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
|
||||||
|
helper.funclet_br(self, &mut bx, target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -524,6 +524,9 @@ declare_features! (
|
||||||
/// Allows the use of `if` and `match` in constants.
|
/// Allows the use of `if` and `match` in constants.
|
||||||
(active, const_if_match, "1.41.0", Some(49146), None),
|
(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
|
// feature-group-end: actual feature gates
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
|
@ -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_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, 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::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.
|
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
|
||||||
|
|
|
@ -651,20 +651,28 @@ where
|
||||||
use rustc::mir::PlaceBase;
|
use rustc::mir::PlaceBase;
|
||||||
|
|
||||||
let mut place_ty = match &place.base {
|
let mut place_ty = match &place.base {
|
||||||
PlaceBase::Local(mir::RETURN_PLACE) => match self.frame().return_place {
|
PlaceBase::Local(mir::RETURN_PLACE) => {
|
||||||
Some(return_place) => {
|
// `return_place` has the *caller* layout, but we want to use our
|
||||||
// We use our layout to verify our assumption; caller will validate
|
// `layout to verify our assumption. The caller will validate
|
||||||
// their layout on return.
|
// their layout on return.
|
||||||
PlaceTy {
|
PlaceTy {
|
||||||
place: *return_place,
|
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(
|
layout: self.layout_of(
|
||||||
self.subst_from_frame_and_normalize_erasing_regions(
|
self.subst_from_frame_and_normalize_erasing_regions(
|
||||||
self.frame().body.return_ty()
|
self.frame().body.return_ty()
|
||||||
)
|
)
|
||||||
)?,
|
)?,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => throw_unsup!(InvalidNullPointerUsage),
|
|
||||||
},
|
},
|
||||||
PlaceBase::Local(local) => PlaceTy {
|
PlaceBase::Local(local) => PlaceTy {
|
||||||
// This works even for dead/uninitialized locals; we check further when writing
|
// 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
|
// to handle padding properly, which is only correct if we never look at this data with the
|
||||||
// wrong type.
|
// wrong type.
|
||||||
|
|
||||||
let ptr = match self.check_mplace_access(dest, None)
|
// Invalid places are a thing: the return place of a diverging function
|
||||||
.expect("places should be checked on creation")
|
let ptr = match self.check_mplace_access(dest, None)?
|
||||||
{
|
{
|
||||||
Some(ptr) => ptr,
|
Some(ptr) => ptr,
|
||||||
None => return Ok(()), // zero-sized access
|
None => return Ok(()), // zero-sized access
|
||||||
|
|
|
@ -29,6 +29,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
|
||||||
#![feature(stmt_expr_attributes)]
|
#![feature(stmt_expr_attributes)]
|
||||||
#![feature(bool_to_option)]
|
#![feature(bool_to_option)]
|
||||||
#![feature(trait_alias)]
|
#![feature(trait_alias)]
|
||||||
|
#![feature(matches_macro)]
|
||||||
|
|
||||||
#![recursion_limit="256"]
|
#![recursion_limit="256"]
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rustc::ty::{self, Ty};
|
||||||
use rustc::hir::def_id::DefId;
|
use rustc::hir::def_id::DefId;
|
||||||
use syntax_pos::DUMMY_SP;
|
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 {
|
pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
|
||||||
ConstQualifs {
|
ConstQualifs {
|
||||||
|
@ -33,9 +33,10 @@ pub trait Qualif {
|
||||||
/// of the type.
|
/// of the type.
|
||||||
fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
|
fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
|
||||||
|
|
||||||
fn in_static(_cx: &ConstCx<'_, 'tcx>, _def_id: DefId) -> bool {
|
fn in_static(cx: &ConstCx<'_, 'tcx>, def_id: DefId) -> bool {
|
||||||
// FIXME(eddyb) should we do anything here for value properties?
|
// `mir_const_qualif` does return the qualifs in the final value of a `static`, so we could
|
||||||
false
|
// 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(
|
fn in_projection_structurally(
|
||||||
|
@ -217,34 +218,6 @@ impl Qualif for HasMutInterior {
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match *rvalue {
|
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, _) => {
|
Rvalue::Aggregate(ref kind, _) => {
|
||||||
if let AggregateKind::Adt(def, ..) = **kind {
|
if let AggregateKind::Adt(def, ..) = **kind {
|
||||||
if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
|
if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
|
||||||
|
|
|
@ -23,13 +23,6 @@ use super::qualifs::{self, HasMutInterior, NeedsDrop};
|
||||||
use super::resolver::FlowSensitiveAnalysis;
|
use super::resolver::FlowSensitiveAnalysis;
|
||||||
use super::{ConstKind, Item, Qualif, is_lang_panic_fn};
|
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> =
|
pub type IndirectlyMutableResults<'mir, 'tcx> =
|
||||||
old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'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.
|
/// The span of the current statement.
|
||||||
span: Span,
|
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<Local>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Validator<'_, 'mir, 'tcx> {
|
impl Deref for Validator<'_, 'mir, 'tcx> {
|
||||||
|
@ -213,7 +195,6 @@ impl Validator<'a, 'mir, 'tcx> {
|
||||||
span: item.body.span,
|
span: item.body.span,
|
||||||
item,
|
item,
|
||||||
qualifs,
|
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
|
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
|
||||||
/// context. Returns `Forbidden` if an error was emitted.
|
/// context.
|
||||||
pub fn check_op_spanned<O>(&mut self, op: O, span: Span) -> CheckOpResult
|
pub fn check_op_spanned<O>(&mut self, op: O, span: Span)
|
||||||
where
|
where
|
||||||
O: NonConstOp
|
O: NonConstOp
|
||||||
{
|
{
|
||||||
trace!("check_op: op={:?}", op);
|
trace!("check_op: op={:?}", op);
|
||||||
|
|
||||||
if op.is_allowed_in_item(self) {
|
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
|
// 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 {
|
if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
|
||||||
self.tcx.sess.span_warn(span, "skipping const checks");
|
self.tcx.sess.span_warn(span, "skipping const checks");
|
||||||
return CheckOpResult::Unleashed;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
op.emit_error(self, span);
|
op.emit_error(self, span);
|
||||||
CheckOpResult::Forbidden
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits an error if an expression cannot be evaluated in the current context.
|
/// 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;
|
let span = self.span;
|
||||||
self.check_op_spanned(op, 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);
|
let is_thread_local = self.tcx.has_attr(def_id, sym::thread_local);
|
||||||
if is_thread_local {
|
if is_thread_local {
|
||||||
self.check_op_spanned(ops::ThreadLocalAccess, span)
|
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) {
|
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||||
trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
|
trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
|
||||||
|
|
||||||
// Check nested operands and places.
|
|
||||||
if let Rvalue::Ref(_, kind, ref place) = *rvalue {
|
|
||||||
// Special-case reborrows to be more like a copy of a reference.
|
// Special-case reborrows to be more like a copy of a reference.
|
||||||
let mut reborrow_place = None;
|
if let Rvalue::Ref(_, kind, ref place) = *rvalue {
|
||||||
if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
|
if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) {
|
||||||
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 {
|
|
||||||
let ctx = match kind {
|
let ctx = match kind {
|
||||||
BorrowKind::Shared => PlaceContext::NonMutatingUse(
|
BorrowKind::Shared => PlaceContext::NonMutatingUse(
|
||||||
NonMutatingUseContext::SharedBorrow,
|
NonMutatingUseContext::SharedBorrow,
|
||||||
|
@ -351,14 +320,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
self.visit_place_base(&place.base, ctx, location);
|
self.visit_place_base(&place.base, ctx, location);
|
||||||
self.visit_projection(&place.base, proj, ctx, location);
|
self.visit_projection(&place.base, reborrowed_proj, ctx, location);
|
||||||
} else {
|
return;
|
||||||
self.super_rvalue(rvalue, location);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.super_rvalue(rvalue, location);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.super_rvalue(rvalue, location);
|
||||||
|
|
||||||
match *rvalue {
|
match *rvalue {
|
||||||
Rvalue::Use(_) |
|
Rvalue::Use(_) |
|
||||||
Rvalue::Repeat(..) |
|
Rvalue::Repeat(..) |
|
||||||
|
@ -369,9 +337,58 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
|
||||||
Rvalue::Cast(CastKind::Pointer(_), ..) |
|
Rvalue::Cast(CastKind::Pointer(_), ..) |
|
||||||
Rvalue::Discriminant(..) |
|
Rvalue::Discriminant(..) |
|
||||||
Rvalue::Len(_) |
|
Rvalue::Len(_) |
|
||||||
Rvalue::Ref(..) |
|
|
||||||
Rvalue::Aggregate(..) => {}
|
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) => {
|
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
|
||||||
let operand_ty = operand.ty(self.body, self.tcx);
|
let operand_ty = operand.ty(self.body, self.tcx);
|
||||||
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
|
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(
|
fn visit_projection_elem(
|
||||||
&mut self,
|
&mut self,
|
||||||
place_base: &PlaceBase<'tcx>,
|
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,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -177,6 +177,7 @@ symbols! {
|
||||||
cfg_attr,
|
cfg_attr,
|
||||||
cfg_attr_multi,
|
cfg_attr_multi,
|
||||||
cfg_doctest,
|
cfg_doctest,
|
||||||
|
cfg_sanitize,
|
||||||
cfg_target_feature,
|
cfg_target_feature,
|
||||||
cfg_target_has_atomic,
|
cfg_target_has_atomic,
|
||||||
cfg_target_thread_local,
|
cfg_target_thread_local,
|
||||||
|
@ -634,6 +635,7 @@ symbols! {
|
||||||
rust_eh_unwind_resume,
|
rust_eh_unwind_resume,
|
||||||
rust_oom,
|
rust_oom,
|
||||||
rvalue_static_promotion,
|
rvalue_static_promotion,
|
||||||
|
sanitize,
|
||||||
sanitizer_runtime,
|
sanitizer_runtime,
|
||||||
_Self,
|
_Self,
|
||||||
self_in_typedefs,
|
self_in_typedefs,
|
||||||
|
|
|
@ -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 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 pp = &reborrow;
|
||||||
let ppp = &pp;
|
let ppp = &pp;
|
||||||
***ppp
|
***ppp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _: std::cell::Cell<i32> = {
|
||||||
|
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() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
error[E0017]: references in constants may only refer to immutable values
|
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;
|
LL | let p = &mut a;
|
||||||
| ^^^^^^ constants require immutable values
|
| ^^^^^^ constants require immutable values
|
||||||
|
|
||||||
error[E0017]: references in constants may only refer to immutable values
|
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
|
||||||
--> $DIR/const-multi-ref.rs:5:21
|
--> $DIR/const-multi-ref.rs:16:13
|
||||||
|
|
|
|
||||||
LL | let reborrow = {p};
|
LL | let p = &a;
|
||||||
| ^ constants require immutable values
|
| ^^
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
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`.
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
static X: i32 = 1;
|
static X: i32 = 1;
|
||||||
const C: i32 = 2;
|
const C: i32 = 2;
|
||||||
|
static mut M: i32 = 3;
|
||||||
|
|
||||||
const CR: &'static mut i32 = &mut C; //~ ERROR E0017
|
const CR: &'static mut i32 = &mut C; //~ ERROR E0017
|
||||||
static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
|
static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
|
||||||
|
//~| ERROR E0019
|
||||||
//~| ERROR cannot borrow
|
//~| ERROR cannot borrow
|
||||||
static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017
|
static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017
|
||||||
|
static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR E0017
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,28 +1,40 @@
|
||||||
error[E0017]: references in constants may only refer to immutable values
|
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;
|
LL | const CR: &'static mut i32 = &mut C;
|
||||||
| ^^^^^^ constants require immutable values
|
| ^^^^^^ 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
|
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;
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
|
||||||
| ^^^^^^ statics require immutable values
|
| ^^^^^^ statics require immutable values
|
||||||
|
|
||||||
error[E0596]: cannot borrow immutable static item `X` as mutable
|
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;
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
|
||||||
| ^^^^^^ cannot borrow as mutable
|
| ^^^^^^ cannot borrow as mutable
|
||||||
|
|
||||||
error[E0017]: references in statics may only refer to immutable values
|
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;
|
LL | static CONST_REF: &'static mut i32 = &mut C;
|
||||||
| ^^^^^^ statics require immutable values
|
| ^^^^^^ 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
|
||||||
|
|
||||||
Some errors have detailed explanations: E0017, E0596.
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0017, E0019, E0596.
|
||||||
For more information about an error, try `rustc --explain E0017`.
|
For more information about an error, try `rustc --explain E0017`.
|
||||||
|
|
|
@ -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() {}
|
|
|
@ -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`.
|
|
3
src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs
Normal file
3
src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#[cfg(not(sanitize = "thread"))]
|
||||||
|
//~^ `cfg(sanitize)` is experimental
|
||||||
|
fn main() {}
|
12
src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr
Normal file
12
src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr
Normal file
|
@ -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`.
|
26
src/test/ui/sanitize-cfg.rs
Normal file
26
src/test/ui/sanitize-cfg.rs
Normal file
|
@ -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() {}
|
Loading…
Add table
Add a link
Reference in a new issue