compiler: treat &raw (const|mut) UNSAFE_STATIC
implied deref as safe
The implied deref to statics introduced by HIR->THIR lowering is only used to create place expressions, it lacks unsafe semantics. It is also confusing, as there is no visible `*ident` in the source. For both classes of "unsafe static" (extern static and static mut) allow this operation. We lack a clear story around `thread_local! { static mut }`, which is actually its own category of item that reuses the static syntax but has its own rules. It's possible they should be similarly included, but in the absence of a good reason one way or another, we do not bless it.
This commit is contained in:
parent
ada5e2c7b5
commit
3fdd8d5ef3
9 changed files with 123 additions and 9 deletions
|
@ -449,6 +449,24 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ExprKind::AddressOf { arg, .. } => {
|
||||||
|
if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
|
||||||
|
// THIR desugars UNSAFE_STATIC into *UNSAFE_STATIC_REF, where
|
||||||
|
// UNSAFE_STATIC_REF holds the addr of the UNSAFE_STATIC, so: take two steps
|
||||||
|
&& let ExprKind::Deref { arg } = self.thir[arg].kind
|
||||||
|
// FIXME(workingjubiee): we lack a clear reason to reject ThreadLocalRef here,
|
||||||
|
// but we also have no conclusive reason to allow it either!
|
||||||
|
&& let ExprKind::StaticRef { .. } = self.thir[arg].kind
|
||||||
|
{
|
||||||
|
// A raw ref to a place expr, even an "unsafe static", is okay!
|
||||||
|
// We short-circuit to not recursively traverse this expression.
|
||||||
|
return;
|
||||||
|
// note: const_mut_refs enables this code, and it currently remains unsafe:
|
||||||
|
// static mut BYTE: u8 = 0;
|
||||||
|
// static mut BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(BYTE) };
|
||||||
|
// static mut DEREF_BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(*BYTE_PTR) };
|
||||||
|
}
|
||||||
|
}
|
||||||
ExprKind::Deref { arg } => {
|
ExprKind::Deref { arg } => {
|
||||||
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
|
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
|
||||||
self.thir[arg].kind
|
self.thir[arg].kind
|
||||||
|
|
|
@ -939,9 +939,11 @@ impl<'tcx> Cx<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We encode uses of statics as a `*&STATIC` where the `&STATIC` part is
|
// A source Rust `path::to::STATIC` is a place expr like *&ident is.
|
||||||
// a constant reference (or constant raw pointer for `static mut`) in MIR
|
// In THIR, we make them exactly equivalent by inserting the implied *& or *&raw,
|
||||||
|
// but distinguish between &STATIC and &THREAD_LOCAL as they have different semantics
|
||||||
Res::Def(DefKind::Static { .. }, id) => {
|
Res::Def(DefKind::Static { .. }, id) => {
|
||||||
|
// this is &raw for extern static or static mut, and & for other statics
|
||||||
let ty = self.tcx.static_ptr_ty(id);
|
let ty = self.tcx.static_ptr_ty(id);
|
||||||
let temp_lifetime = self
|
let temp_lifetime = self
|
||||||
.rvalue_scopes
|
.rvalue_scopes
|
||||||
|
|
|
@ -9,7 +9,7 @@ const C1: &i32 = &S;
|
||||||
const C1_READ: () = {
|
const C1_READ: () = {
|
||||||
assert!(*C1 == 0);
|
assert!(*C1 == 0);
|
||||||
};
|
};
|
||||||
const C2: *const i32 = unsafe { std::ptr::addr_of!(S_MUT) };
|
const C2: *const i32 = std::ptr::addr_of!(S_MUT);
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
assert_eq!(*C1, 0);
|
assert_eq!(*C1, 0);
|
||||||
|
|
|
@ -16,12 +16,9 @@ static mut STATIC: u32 = 42;
|
||||||
static INTERIOR_MUTABLE_STATIC: SyncUnsafeCell<u32> = SyncUnsafeCell::new(42);
|
static INTERIOR_MUTABLE_STATIC: SyncUnsafeCell<u32> = SyncUnsafeCell::new(42);
|
||||||
|
|
||||||
// A static that mutably points to STATIC.
|
// A static that mutably points to STATIC.
|
||||||
static PTR: SyncPtr = SyncPtr {
|
static PTR: SyncPtr = SyncPtr { foo: ptr::addr_of_mut!(STATIC) };
|
||||||
foo: unsafe { ptr::addr_of_mut!(STATIC) },
|
static INTERIOR_MUTABLE_PTR: SyncPtr =
|
||||||
};
|
SyncPtr { foo: ptr::addr_of!(INTERIOR_MUTABLE_STATIC) as *mut u32 };
|
||||||
static INTERIOR_MUTABLE_PTR: SyncPtr = SyncPtr {
|
|
||||||
foo: ptr::addr_of!(INTERIOR_MUTABLE_STATIC) as *mut u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let ptr = PTR.foo;
|
let ptr = PTR.foo;
|
||||||
|
|
16
tests/ui/static/raw-ref-deref-with-unsafe.rs
Normal file
16
tests/ui/static/raw-ref-deref-with-unsafe.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//@ check-pass
|
||||||
|
#![feature(const_mut_refs)]
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
// This code should remain unsafe because of the two unsafe operations here,
|
||||||
|
// even if in a hypothetical future we deem all &raw (const|mut) *ptr exprs safe.
|
||||||
|
|
||||||
|
static mut BYTE: u8 = 0;
|
||||||
|
static mut BYTE_PTR: *mut u8 = ptr::addr_of_mut!(BYTE);
|
||||||
|
// An unsafe static's ident is a place expression in its own right, so despite the above being safe
|
||||||
|
// (it's fine to create raw refs to places!) the following derefs the ptr before creating its ref
|
||||||
|
static mut DEREF_BYTE_PTR: *mut u8 = unsafe { ptr::addr_of_mut!(*BYTE_PTR) };
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = unsafe { DEREF_BYTE_PTR };
|
||||||
|
}
|
18
tests/ui/static/raw-ref-deref-without-unsafe.rs
Normal file
18
tests/ui/static/raw-ref-deref-without-unsafe.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#![feature(const_mut_refs)]
|
||||||
|
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
// This code should remain unsafe because of the two unsafe operations here,
|
||||||
|
// even if in a hypothetical future we deem all &raw (const|mut) *ptr exprs safe.
|
||||||
|
|
||||||
|
static mut BYTE: u8 = 0;
|
||||||
|
static mut BYTE_PTR: *mut u8 = ptr::addr_of_mut!(BYTE);
|
||||||
|
// An unsafe static's ident is a place expression in its own right, so despite the above being safe
|
||||||
|
// (it's fine to create raw refs to places!) the following derefs the ptr before creating its ref!
|
||||||
|
static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);
|
||||||
|
//~^ ERROR: use of mutable static
|
||||||
|
//~| ERROR: dereference of raw pointer
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = unsafe { DEREF_BYTE_PTR };
|
||||||
|
}
|
19
tests/ui/static/raw-ref-deref-without-unsafe.stderr
Normal file
19
tests/ui/static/raw-ref-deref-without-unsafe.stderr
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
|
||||||
|
--> $DIR/raw-ref-deref-without-unsafe.rs:12:56
|
||||||
|
|
|
||||||
|
LL | static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);
|
||||||
|
| ^^^^^^^^^ dereference of raw pointer
|
||||||
|
|
|
||||||
|
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
|
||||||
|
|
||||||
|
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
|
||||||
|
--> $DIR/raw-ref-deref-without-unsafe.rs:12:57
|
||||||
|
|
|
||||||
|
LL | static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);
|
||||||
|
| ^^^^^^^^ use of mutable static
|
||||||
|
|
|
||||||
|
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0133`.
|
27
tests/ui/static/raw-ref-extern-static.rs
Normal file
27
tests/ui/static/raw-ref-extern-static.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
//@ check-pass
|
||||||
|
#![feature(raw_ref_op)]
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
// see https://github.com/rust-lang/rust/issues/125833
|
||||||
|
// notionally, taking the address of an extern static is a safe operation,
|
||||||
|
// as we only point at it instead of generating a true reference to it
|
||||||
|
|
||||||
|
// it may potentially induce linker errors, but the safety of that is not about taking addresses!
|
||||||
|
// any safety obligation of the extern static's correctness in declaration is on the extern itself,
|
||||||
|
// see RFC 3484 for more on that: https://rust-lang.github.io/rfcs/3484-unsafe-extern-blocks.html
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
static THERE: u8;
|
||||||
|
static mut SOMEWHERE: u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let ptr2there = ptr::addr_of!(THERE);
|
||||||
|
let ptr2somewhere = ptr::addr_of!(SOMEWHERE);
|
||||||
|
let ptr2somewhere = ptr::addr_of_mut!(SOMEWHERE);
|
||||||
|
|
||||||
|
// testing both addr_of and the expression it directly expands to
|
||||||
|
let raw2there = &raw const THERE;
|
||||||
|
let raw2somewhere = &raw const SOMEWHERE;
|
||||||
|
let raw2somewhere = &raw mut SOMEWHERE;
|
||||||
|
}
|
17
tests/ui/static/raw-ref-static-mut.rs
Normal file
17
tests/ui/static/raw-ref-static-mut.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
//@ check-pass
|
||||||
|
#![feature(raw_ref_op)]
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
// see https://github.com/rust-lang/rust/issues/125833
|
||||||
|
// notionally, taking the address of a static mut is a safe operation,
|
||||||
|
// as we only point at it instead of generating a true reference to it
|
||||||
|
static mut NOWHERE: usize = 0;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let p2nowhere = ptr::addr_of!(NOWHERE);
|
||||||
|
let p2nowhere = ptr::addr_of_mut!(NOWHERE);
|
||||||
|
|
||||||
|
// testing both addr_of and the expression it directly expands to
|
||||||
|
let raw2nowhere = &raw const NOWHERE;
|
||||||
|
let raw2nowhere = &raw mut NOWHERE;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue