Skip to content

Commit c10072f

Browse files
committed
Auto merge of rust-lang#124895 - obeis:static-mut-hidden-ref, r=michaelwoerister
Disallow hidden references to mutable static Closes rust-lang#123060 Tracking: - rust-lang#123758
2 parents 2ccafed + fa621b7 commit c10072f

File tree

103 files changed

+1333
-538
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+1333
-538
lines changed

compiler/rustc_driver_impl/src/signal_handler.rs

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ macro raw_errln($tokens:tt) {
3434
}
3535

3636
/// Signal handler installed for SIGSEGV
37+
// FIXME(obeis): Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
38+
#[allow(static_mut_refs)]
3739
extern "C" fn print_stack_trace(_: libc::c_int) {
3840
const MAX_FRAMES: usize = 256;
3941
// Reserve data segment so we don't have to malloc in a signal handler, which might fail

compiler/rustc_hir_analysis/messages.ftl

+8-12
Original file line numberDiff line numberDiff line change
@@ -463,24 +463,20 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha
463463
hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]`
464464
.label = `#[start]` function is not allowed to be `#[track_caller]`
465465
466-
hir_analysis_static_mut_ref = creating a {$shared} reference to a mutable static
467-
.label = {$shared} reference to mutable static
468-
.note = {$shared ->
469-
[shared] this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior
470-
*[mutable] this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
471-
}
466+
hir_analysis_static_mut_ref = creating a {$shared_label}reference to a mutable static
467+
.label = {$shared_label}reference to mutable static
468+
.shared_note = shared references to mutable statics are dangerous since if there's any kind of mutation of, or mutable reference created for, that static while the reference lives, that's undefined behavior
469+
.mut_note = mutable references to mutable statics are dangerous since if there's any other pointer used or reference created for that static while the reference lives, that's undefined behavior
472470
.suggestion = use `addr_of!` instead to create a raw pointer
473471
.suggestion_mut = use `addr_of_mut!` instead to create a raw pointer
474472
475-
hir_analysis_static_mut_refs_lint = creating a {$shared} reference to mutable static is discouraged
476-
.label = {$shared} reference to mutable static
473+
hir_analysis_static_mut_refs_lint = creating a {$shared_label}reference to mutable static is discouraged
474+
.label = {$shared_label}reference to mutable static
477475
.suggestion = use `addr_of!` instead to create a raw pointer
478476
.suggestion_mut = use `addr_of_mut!` instead to create a raw pointer
479477
.note = this will be a hard error in the 2024 edition
480-
.why_note = {$shared ->
481-
[shared] this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior
482-
*[mutable] this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
483-
}
478+
.shared_note = shared references to mutable statics are dangerous since if there's any kind of mutation of, or mutable reference created for, that static while the reference lives, that's undefined behavior
479+
.mut_note = mutable references to mutable statics are dangerous since if there's any other pointer used or reference created for that static while the reference lives, that's undefined behavior
484480
485481
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
486482
+104-41
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,113 @@
11
use rustc_hir as hir;
22
use rustc_lint_defs::builtin::STATIC_MUT_REFS;
3-
use rustc_middle::ty::{Mutability, TyCtxt};
3+
use rustc_middle::ty::{Mutability, TyCtxt, TyKind};
44
use rustc_span::Span;
55

66
use crate::errors;
77

88
/// Check for shared or mutable references of `static mut` inside expression
9-
pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
10-
let span = expr.span;
11-
let hir_id = expr.hir_id;
12-
if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind
13-
&& matches!(borrow_kind, hir::BorrowKind::Ref)
14-
&& path_if_static_mut(expr)
15-
{
16-
handle_static_mut_ref(
17-
tcx,
18-
span,
19-
span.with_hi(expr.span.lo()),
20-
span.shrink_to_hi(),
21-
span.edition().at_least_rust_2024(),
22-
m,
23-
hir_id,
24-
);
9+
pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: &hir::Expr<'_>) {
10+
let err_span = expr.span;
11+
let lint_level_hir_id = expr.hir_id;
12+
match expr.kind {
13+
hir::ExprKind::AddrOf(borrow_kind, m, ex)
14+
if matches!(borrow_kind, hir::BorrowKind::Ref)
15+
&& let Some(err_span) = path_is_static_mut(ex, err_span) =>
16+
{
17+
handle_static_mut_ref(
18+
tcx,
19+
err_span,
20+
err_span.with_hi(ex.span.lo()),
21+
err_span.shrink_to_hi(),
22+
err_span.edition().at_least_rust_2024(),
23+
Some(m),
24+
lint_level_hir_id,
25+
!expr.span.from_expansion(),
26+
);
27+
}
28+
hir::ExprKind::Index(expr, _, _)
29+
if let Some(err_span) = path_is_static_mut(expr, err_span) =>
30+
{
31+
handle_static_mut_ref(
32+
tcx,
33+
err_span,
34+
err_span.with_hi(expr.span.lo()),
35+
err_span.shrink_to_hi(),
36+
err_span.edition().at_least_rust_2024(),
37+
None,
38+
lint_level_hir_id,
39+
false,
40+
);
41+
}
42+
_ => {}
2543
}
2644
}
2745

2846
/// Check for shared or mutable references of `static mut` inside statement
29-
pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
47+
pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: &hir::Stmt<'_>) {
3048
if let hir::StmtKind::Let(loc) = stmt.kind
3149
&& let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
3250
&& let hir::ByRef::Yes(rmutbl) = ba.0
3351
&& let Some(init) = loc.init
34-
&& path_if_static_mut(init)
52+
&& let Some(err_span) = path_is_static_mut(init, init.span)
3553
{
3654
handle_static_mut_ref(
3755
tcx,
38-
init.span,
39-
init.span.shrink_to_lo(),
40-
init.span.shrink_to_hi(),
56+
err_span,
57+
err_span.shrink_to_lo(),
58+
err_span.shrink_to_hi(),
4159
loc.span.edition().at_least_rust_2024(),
42-
rmutbl,
60+
Some(rmutbl),
4361
stmt.hir_id,
62+
false,
63+
);
64+
}
65+
}
66+
67+
/// Check if method call takes shared or mutable references of `static mut`
68+
#[allow(rustc::usage_of_ty_tykind)]
69+
pub fn maybe_method_static_mut(tcx: TyCtxt<'_>, expr: &hir::Expr<'_>) {
70+
if let hir::ExprKind::MethodCall(_, e, _, _) = expr.kind
71+
&& let Some(err_span) = path_is_static_mut(e, expr.span)
72+
&& let typeck = tcx.typeck(expr.hir_id.owner)
73+
&& let Some(method_def_id) = typeck.type_dependent_def_id(expr.hir_id)
74+
&& let inputs = tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()
75+
&& !inputs.is_empty()
76+
&& inputs[0].is_ref()
77+
{
78+
let m = if let TyKind::Ref(_, _, m) = inputs[0].kind() { *m } else { Mutability::Not };
79+
80+
handle_static_mut_ref(
81+
tcx,
82+
err_span,
83+
err_span.shrink_to_lo(),
84+
err_span.shrink_to_hi(),
85+
err_span.edition().at_least_rust_2024(),
86+
Some(m),
87+
expr.hir_id,
88+
false,
4489
);
4590
}
4691
}
4792

48-
fn path_if_static_mut(expr: &hir::Expr<'_>) -> bool {
93+
fn path_is_static_mut(mut expr: &hir::Expr<'_>, mut err_span: Span) -> Option<Span> {
94+
if err_span.from_expansion() {
95+
err_span = expr.span;
96+
}
97+
98+
while let hir::ExprKind::Field(e, _) = expr.kind {
99+
expr = e;
100+
}
101+
49102
if let hir::ExprKind::Path(qpath) = expr.kind
50103
&& let hir::QPath::Resolved(_, path) = qpath
51104
&& let hir::def::Res::Def(def_kind, _) = path.res
52105
&& let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } =
53106
def_kind
54107
{
55-
return true;
108+
return Some(err_span);
56109
}
57-
false
110+
None
58111
}
59112

60113
fn handle_static_mut_ref(
@@ -63,27 +116,37 @@ fn handle_static_mut_ref(
63116
lo: Span,
64117
hi: Span,
65118
e2024: bool,
66-
mutable: Mutability,
67-
hir_id: hir::HirId,
119+
mutable: Option<Mutability>,
120+
lint_level_hir_id: hir::HirId,
121+
suggest_addr_of: bool,
68122
) {
123+
let (shared_label, shared_note, mut_note, sugg) = match mutable {
124+
Some(Mutability::Mut) => {
125+
let sugg =
126+
if suggest_addr_of { Some(errors::MutRefSugg::Mut { lo, hi }) } else { None };
127+
("mutable ", false, true, sugg)
128+
}
129+
Some(Mutability::Not) => {
130+
let sugg =
131+
if suggest_addr_of { Some(errors::MutRefSugg::Shared { lo, hi }) } else { None };
132+
("shared ", true, false, sugg)
133+
}
134+
None => ("", true, true, None),
135+
};
69136
if e2024 {
70-
let (sugg, shared) = if mutable == Mutability::Mut {
71-
(errors::MutRefSugg::Mut { lo, hi }, "mutable")
72-
} else {
73-
(errors::MutRefSugg::Shared { lo, hi }, "shared")
74-
};
75-
tcx.dcx().emit_err(errors::StaticMutRef { span, sugg, shared });
137+
tcx.dcx().emit_err(errors::StaticMutRef {
138+
span,
139+
sugg,
140+
shared_label,
141+
shared_note,
142+
mut_note,
143+
});
76144
} else {
77-
let (sugg, shared) = if mutable == Mutability::Mut {
78-
(errors::MutRefSugg::Mut { lo, hi }, "mutable")
79-
} else {
80-
(errors::MutRefSugg::Shared { lo, hi }, "shared")
81-
};
82145
tcx.emit_node_span_lint(
83146
STATIC_MUT_REFS,
84-
hir_id,
147+
lint_level_hir_id,
85148
span,
86-
errors::RefOfMutStatic { span, sugg, shared },
149+
errors::RefOfMutStatic { span, sugg, shared_label, shared_note, mut_note },
87150
);
88151
}
89152
}

compiler/rustc_hir_analysis/src/check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ mod check;
6666
mod compare_impl_item;
6767
pub mod dropck;
6868
mod entry;
69-
mod errs;
69+
pub mod errs;
7070
pub mod intrinsic;
7171
pub mod intrinsicck;
7272
mod region;

compiler/rustc_hir_analysis/src/check/region.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
228228
let stmt_id = stmt.hir_id.local_id;
229229
debug!("resolve_stmt(stmt.id={:?})", stmt_id);
230230

231-
maybe_stmt_static_mut(visitor.tcx, *stmt);
231+
maybe_stmt_static_mut(visitor.tcx, stmt);
232232

233233
// Every statement will clean up the temporaries created during
234234
// execution of that statement. Therefore each statement has an
@@ -248,7 +248,7 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
248248
fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
249249
debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr);
250250

251-
maybe_expr_static_mut(visitor.tcx, *expr);
251+
maybe_expr_static_mut(visitor.tcx, expr);
252252

253253
let prev_cx = visitor.cx;
254254
visitor.enter_node_scope_with_dtor(expr.hir_id.local_id);

compiler/rustc_hir_analysis/src/collect.rs

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use std::cell::Cell;
4343
use std::iter;
4444
use std::ops::Bound;
4545

46+
use crate::check::errs::maybe_method_static_mut;
4647
use crate::check::intrinsic::intrinsic_operation_unsafety;
4748
use crate::errors;
4849
use crate::hir_ty_lowering::{HirTyLowerer, RegionInferReason};
@@ -324,6 +325,7 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
324325
// depends on typecheck and would therefore hide
325326
// any further errors in case one typeck fails.
326327
}
328+
maybe_method_static_mut(self.tcx, expr);
327329
intravisit::walk_expr(self, expr);
328330
}
329331

compiler/rustc_hir_analysis/src/errors.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -1502,14 +1502,17 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> {
15021502

15031503
#[derive(Diagnostic)]
15041504
#[diag(hir_analysis_static_mut_ref, code = E0796)]
1505-
#[note]
15061505
pub struct StaticMutRef<'a> {
15071506
#[primary_span]
15081507
#[label]
15091508
pub span: Span,
15101509
#[subdiagnostic]
1511-
pub sugg: MutRefSugg,
1512-
pub shared: &'a str,
1510+
pub sugg: Option<MutRefSugg>,
1511+
pub shared_label: &'a str,
1512+
#[note(hir_analysis_shared_note)]
1513+
pub shared_note: bool,
1514+
#[note(hir_analysis_mut_note)]
1515+
pub mut_note: bool,
15131516
}
15141517

15151518
#[derive(Subdiagnostic)]
@@ -1538,17 +1541,20 @@ pub enum MutRefSugg {
15381541
},
15391542
}
15401543

1541-
// STATIC_MUT_REF lint
1544+
// `STATIC_MUT_REF` lint
15421545
#[derive(LintDiagnostic)]
15431546
#[diag(hir_analysis_static_mut_refs_lint)]
15441547
#[note]
1545-
#[note(hir_analysis_why_note)]
15461548
pub struct RefOfMutStatic<'a> {
15471549
#[label]
15481550
pub span: Span,
15491551
#[subdiagnostic]
1550-
pub sugg: MutRefSugg,
1551-
pub shared: &'a str,
1552+
pub sugg: Option<MutRefSugg>,
1553+
pub shared_label: &'a str,
1554+
#[note(hir_analysis_shared_note)]
1555+
pub shared_note: bool,
1556+
#[note(hir_analysis_mut_note)]
1557+
pub mut_note: bool,
15521558
}
15531559

15541560
#[derive(Diagnostic)]

library/alloc/tests/vec.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,8 @@ fn test_from_iter_specialization_panic_during_iteration_drops() {
12851285

12861286
#[test]
12871287
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
1288+
// FIXME(obeis): Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
1289+
#[cfg_attr(not(bootstrap), allow(static_mut_refs))]
12881290
fn test_from_iter_specialization_panic_during_drop_doesnt_leak() {
12891291
static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5];
12901292
static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2];

library/core/tests/atomic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ fn static_init() {
228228
}
229229

230230
#[test]
231+
// FIXME(obeis): Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
232+
#[cfg_attr(not(bootstrap), allow(static_mut_refs))]
231233
fn atomic_access_bool() {
232234
static mut ATOMIC: AtomicBool = AtomicBool::new(false);
233235

library/panic_unwind/src/seh.rs

+2
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ cfg_if::cfg_if! {
297297
}
298298
}
299299

300+
// FIXME(obeis): Do not allow `static_mut_refs` lint
301+
#[allow(static_mut_refs)]
300302
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
301303
use core::intrinsics::atomic_store_seqcst;
302304

library/std/src/sync/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
//! Consider the following code, operating on some global static variables:
1010
//!
1111
//! ```rust
12+
//! // FIXME(obeis): Do not allow `static_mut_refs` lint
13+
//! #![allow(static_mut_refs)]
14+
//!
1215
//! static mut A: u32 = 0;
1316
//! static mut B: u32 = 0;
1417
//! static mut C: u32 = 0;

library/std/src/sys/pal/wasm/alloc.rs

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
//! The crate itself provides a global allocator which on wasm has no
1717
//! synchronization as there are no threads!
1818
19+
// FIXME(obeis): Do not allow `static_mut_refs` lint
20+
#![allow(static_mut_refs)]
21+
1922
use crate::alloc::{GlobalAlloc, Layout, System};
2023

2124
static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new();

library/std/src/thread/local/tests.rs

+3
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ fn smoke_dtor() {
103103

104104
#[test]
105105
fn circular() {
106+
// FIXME(obeis): Do not allow `static_mut_refs` lint
107+
#![allow(static_mut_refs)]
108+
106109
struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>);
107110
struct S2(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>);
108111
thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));

src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ fn issue11371() {
172172
static mut X: Option<i32> = Some(123);
173173
unsafe {
174174
if X.is_some() {
175+
//~^ ERROR: creating a shared reference to mutable static is discouraged
175176
X = None;
176177
X.unwrap();
177178
}

0 commit comments

Comments
 (0)