Skip to content

Commit b01c95d

Browse files
committed
Auto merge of rust-lang#124895 - obeis:static-mut-hidden-ref, r=michaelwoerister,traviscross
Disallow hidden references to mutable static Closes rust-lang#123060 Tracking: - rust-lang#123758
2 parents 368e2fd + 74561dc commit b01c95d

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
@@ -35,6 +35,8 @@ macro raw_errln($tokens:tt) {
3535
}
3636

3737
/// Signal handler installed for SIGSEGV
38+
// FIXME(obeis): Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
39+
#[allow(static_mut_refs)]
3840
extern "C" fn print_stack_trace(_: libc::c_int) {
3941
const MAX_FRAMES: usize = 256;
4042
// 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
@@ -468,24 +468,20 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha
468468
hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]`
469469
.label = `#[start]` function is not allowed to be `#[track_caller]`
470470
471-
hir_analysis_static_mut_ref = creating a {$shared} reference to a mutable static
472-
.label = {$shared} reference to mutable static
473-
.note = {$shared ->
474-
[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
475-
*[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
476-
}
471+
hir_analysis_static_mut_ref = creating a {$shared_label}reference to a mutable static
472+
.label = {$shared_label}reference to mutable static
473+
.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
474+
.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
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
480-
hir_analysis_static_mut_refs_lint = creating a {$shared} reference to mutable static is discouraged
481-
.label = {$shared} reference to mutable static
478+
hir_analysis_static_mut_refs_lint = creating a {$shared_label}reference to mutable static is discouraged
479+
.label = {$shared_label}reference to mutable static
482480
.suggestion = use `addr_of!` instead to create a raw pointer
483481
.suggestion_mut = use `addr_of_mut!` instead to create a raw pointer
484482
.note = this will be a hard error in the 2024 edition
485-
.why_note = {$shared ->
486-
[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
487-
*[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
488-
}
483+
.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
484+
.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
489485
490486
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
491487
+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 rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamNa
4343
use rustc_trait_selection::infer::InferCtxtExt;
4444
use rustc_trait_selection::traits::ObligationCtxt;
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
@@ -1524,14 +1524,17 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> {
15241524

15251525
#[derive(Diagnostic)]
15261526
#[diag(hir_analysis_static_mut_ref, code = E0796)]
1527-
#[note]
15281527
pub struct StaticMutRef<'a> {
15291528
#[primary_span]
15301529
#[label]
15311530
pub span: Span,
15321531
#[subdiagnostic]
1533-
pub sugg: MutRefSugg,
1534-
pub shared: &'a str,
1532+
pub sugg: Option<MutRefSugg>,
1533+
pub shared_label: &'a str,
1534+
#[note(hir_analysis_shared_note)]
1535+
pub shared_note: bool,
1536+
#[note(hir_analysis_mut_note)]
1537+
pub mut_note: bool,
15351538
}
15361539

15371540
#[derive(Subdiagnostic)]
@@ -1560,17 +1563,20 @@ pub enum MutRefSugg {
15601563
},
15611564
}
15621565

1563-
// STATIC_MUT_REF lint
1566+
// `STATIC_MUT_REF` lint
15641567
#[derive(LintDiagnostic)]
15651568
#[diag(hir_analysis_static_mut_refs_lint)]
15661569
#[note]
1567-
#[note(hir_analysis_why_note)]
15681570
pub struct RefOfMutStatic<'a> {
15691571
#[label]
15701572
pub span: Span,
15711573
#[subdiagnostic]
1572-
pub sugg: MutRefSugg,
1573-
pub shared: &'a str,
1574+
pub sugg: Option<MutRefSugg>,
1575+
pub shared_label: &'a str,
1576+
#[note(hir_analysis_shared_note)]
1577+
pub shared_note: bool,
1578+
#[note(hir_analysis_mut_note)]
1579+
pub mut_note: bool,
15741580
}
15751581

15761582
#[derive(Diagnostic)]

library/alloc/tests/vec.rs

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

12851285
#[test]
12861286
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
1287+
// FIXME(obeis): Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
1288+
#[cfg_attr(not(bootstrap), allow(static_mut_refs))]
12871289
fn test_from_iter_specialization_panic_during_drop_doesnt_leak() {
12881290
static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5];
12891291
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)