Skip to content

Commit 2c088f9

Browse files
committed
Disallow reference to static mut for expressions
Add `E0796` error code. Add `static_mut_ref` lint. This is the idea for the 2024 edition.
1 parent 595bc6f commit 2c088f9

File tree

9 files changed

+261
-0
lines changed

9 files changed

+261
-0
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3877,6 +3877,7 @@ dependencies = [
38773877
"rustc_feature",
38783878
"rustc_fluent_macro",
38793879
"rustc_hir",
3880+
"rustc_hir_pretty",
38803881
"rustc_index",
38813882
"rustc_infer",
38823883
"rustc_lint_defs",

compiler/rustc_error_codes/src/error_codes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ E0792: include_str!("./error_codes/E0792.md"),
515515
E0793: include_str!("./error_codes/E0793.md"),
516516
E0794: include_str!("./error_codes/E0794.md"),
517517
E0795: include_str!("./error_codes/E0795.md"),
518+
E0796: include_str!("./error_codes/E0796.md"),
518519
}
519520

520521
// Undocumented removed error codes. Note that many removed error codes are kept in the list above
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Reference of mutable static.
2+
3+
Erroneous code example:
4+
5+
```compile_fail,edition2024,E0796
6+
static mut X: i32 = 23;
7+
static mut Y: i32 = 24;
8+
9+
unsafe {
10+
let y = &X;
11+
let ref x = X;
12+
let (x, y) = (&X, &Y);
13+
foo(&X);
14+
}
15+
16+
fn foo<'a>(_x: &'a i32) {}
17+
```
18+
19+
Mutable statics can be written to by multiple threads: aliasing violations or
20+
data races will cause undefined behavior.
21+
22+
Reference of mutable static is a hard error from 2024 edition.

compiler/rustc_hir_analysis/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" }
1717
rustc_feature = { path = "../rustc_feature" }
1818
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
1919
rustc_hir = { path = "../rustc_hir" }
20+
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
2021
rustc_index = { path = "../rustc_index" }
2122
rustc_infer = { path = "../rustc_infer" }
2223
rustc_lint_defs = { path = "../rustc_lint_defs" }

compiler/rustc_hir_analysis/messages.ftl

+14
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,20 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha
346346
hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]`
347347
.label = `#[start]` function is not allowed to be `#[track_caller]`
348348
349+
hir_analysis_static_mut_ref = reference of mutable static is disallowed
350+
.label = reference of mutable static
351+
.note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
352+
.suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
353+
.suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
354+
355+
hir_analysis_static_mut_ref_lint = {$shared}reference of mutable static is discouraged
356+
.label = shared reference of mutable static
357+
.label_mut = mutable reference of mutable static
358+
.suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
359+
.suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
360+
.note = reference of mutable static is a hard error from 2024 edition
361+
.why_note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
362+
349363
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
350364
351365
hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use rustc_hir as hir;
2+
use rustc_hir_pretty::qpath_to_string;
3+
use rustc_lint_defs::builtin::STATIC_MUT_REF;
4+
use rustc_middle::ty::TyCtxt;
5+
use rustc_span::Span;
6+
use rustc_type_ir::Mutability;
7+
8+
use crate::errors;
9+
10+
/// Check for shared or mutable references of `static mut` inside expression
11+
pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
12+
let span = expr.span;
13+
let hir_id = expr.hir_id;
14+
if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind
15+
&& matches!(borrow_kind, hir::BorrowKind::Ref)
16+
&& let Some(var) = is_path_static_mut(*expr)
17+
{
18+
handle_static_mut_ref(
19+
tcx,
20+
span,
21+
var,
22+
span.edition().at_least_rust_2024(),
23+
matches!(m, Mutability::Mut),
24+
hir_id,
25+
);
26+
}
27+
}
28+
29+
fn is_path_static_mut(expr: hir::Expr<'_>) -> Option<String> {
30+
if let hir::ExprKind::Path(qpath) = expr.kind
31+
&& let hir::QPath::Resolved(_, path) = qpath
32+
&& let hir::def::Res::Def(def_kind, _) = path.res
33+
&& let hir::def::DefKind::Static(mt) = def_kind
34+
&& matches!(mt, Mutability::Mut)
35+
{
36+
return Some(qpath_to_string(&qpath));
37+
}
38+
None
39+
}
40+
41+
fn handle_static_mut_ref(
42+
tcx: TyCtxt<'_>,
43+
span: Span,
44+
var: String,
45+
e2024: bool,
46+
mutable: bool,
47+
hir_id: hir::HirId,
48+
) {
49+
if e2024 {
50+
let sugg = if mutable {
51+
errors::StaticMutRefSugg::Mut { span, var }
52+
} else {
53+
errors::StaticMutRefSugg::Shared { span, var }
54+
};
55+
tcx.sess.parse_sess.dcx.emit_err(errors::StaticMutRef { span, sugg });
56+
return;
57+
}
58+
59+
let (label, sugg, shared) = if mutable {
60+
(
61+
errors::RefOfMutStaticLabel::Mut { span },
62+
errors::RefOfMutStaticSugg::Mut { span, var },
63+
"mutable ",
64+
)
65+
} else {
66+
(
67+
errors::RefOfMutStaticLabel::Shared { span },
68+
errors::RefOfMutStaticSugg::Shared { span, var },
69+
"shared ",
70+
)
71+
};
72+
tcx.emit_spanned_lint(
73+
STATIC_MUT_REF,
74+
hir_id,
75+
span,
76+
errors::RefOfMutStatic { shared, why_note: (), label, sugg },
77+
);
78+
}

compiler/rustc_hir_analysis/src/check/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ mod check;
6666
mod compare_impl_item;
6767
pub mod dropck;
6868
mod entry;
69+
mod errs;
6970
pub mod intrinsic;
7071
pub mod intrinsicck;
7172
mod region;

compiler/rustc_hir_analysis/src/errors.rs

+91
Original file line numberDiff line numberDiff line change
@@ -1410,3 +1410,94 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> {
14101410
pub mut_key: &'a str,
14111411
pub ptr_ty: Ty<'a>,
14121412
}
1413+
1414+
#[derive(Diagnostic)]
1415+
#[diag(hir_analysis_static_mut_ref, code = "E0796")]
1416+
#[note]
1417+
pub struct StaticMutRef {
1418+
#[primary_span]
1419+
#[label]
1420+
pub span: Span,
1421+
#[subdiagnostic]
1422+
pub sugg: StaticMutRefSugg,
1423+
}
1424+
1425+
#[derive(Subdiagnostic)]
1426+
pub enum StaticMutRefSugg {
1427+
#[suggestion(
1428+
hir_analysis_suggestion,
1429+
style = "verbose",
1430+
code = "addr_of!({var})",
1431+
applicability = "maybe-incorrect"
1432+
)]
1433+
Shared {
1434+
#[primary_span]
1435+
span: Span,
1436+
var: String,
1437+
},
1438+
#[suggestion(
1439+
hir_analysis_suggestion_mut,
1440+
style = "verbose",
1441+
code = "addr_of_mut!({var})",
1442+
applicability = "maybe-incorrect"
1443+
)]
1444+
Mut {
1445+
#[primary_span]
1446+
span: Span,
1447+
var: String,
1448+
},
1449+
}
1450+
1451+
// STATIC_MUT_REF lint
1452+
#[derive(LintDiagnostic)]
1453+
#[diag(hir_analysis_static_mut_ref_lint)]
1454+
#[note]
1455+
pub struct RefOfMutStatic<'a> {
1456+
pub shared: &'a str,
1457+
#[note(hir_analysis_why_note)]
1458+
pub why_note: (),
1459+
#[subdiagnostic]
1460+
pub label: RefOfMutStaticLabel,
1461+
#[subdiagnostic]
1462+
pub sugg: RefOfMutStaticSugg,
1463+
}
1464+
1465+
#[derive(Subdiagnostic)]
1466+
pub enum RefOfMutStaticLabel {
1467+
#[label(hir_analysis_label)]
1468+
Shared {
1469+
#[primary_span]
1470+
span: Span,
1471+
},
1472+
#[label(hir_analysis_label_mut)]
1473+
Mut {
1474+
#[primary_span]
1475+
span: Span,
1476+
},
1477+
}
1478+
1479+
#[derive(Subdiagnostic)]
1480+
pub enum RefOfMutStaticSugg {
1481+
#[suggestion(
1482+
hir_analysis_suggestion,
1483+
style = "verbose",
1484+
code = "addr_of!({var})",
1485+
applicability = "maybe-incorrect"
1486+
)]
1487+
Shared {
1488+
#[primary_span]
1489+
span: Span,
1490+
var: String,
1491+
},
1492+
#[suggestion(
1493+
hir_analysis_suggestion_mut,
1494+
style = "verbose",
1495+
code = "addr_of_mut!({var})",
1496+
applicability = "maybe-incorrect"
1497+
)]
1498+
Mut {
1499+
#[primary_span]
1500+
span: Span,
1501+
var: String,
1502+
},
1503+
}

compiler/rustc_lint_defs/src/builtin.rs

+52
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ declare_lint_pass! {
8989
SINGLE_USE_LIFETIMES,
9090
SOFT_UNSTABLE,
9191
STABLE_FEATURES,
92+
STATIC_MUT_REF,
9293
SUSPICIOUS_AUTO_TRAIT_IMPLS,
9394
TEST_UNSTABLE_LINT,
9495
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
@@ -1767,6 +1768,57 @@ declare_lint! {
17671768
};
17681769
}
17691770

1771+
declare_lint! {
1772+
/// The `static_mut_ref` lint checks for shared or mutable references
1773+
/// of mutable static inside `unsafe` blocks and `unsafe` functions.
1774+
///
1775+
/// ### Example
1776+
///
1777+
/// ```rust,edition2021
1778+
/// fn main() {
1779+
/// static mut X: i32 = 23;
1780+
/// static mut Y: i32 = 24;
1781+
///
1782+
/// unsafe {
1783+
/// let y = &X;
1784+
/// let ref x = X;
1785+
/// let (x, y) = (&X, &Y);
1786+
/// foo(&X);
1787+
/// }
1788+
/// }
1789+
///
1790+
/// unsafe fn _foo() {
1791+
/// static mut X: i32 = 23;
1792+
/// static mut Y: i32 = 24;
1793+
///
1794+
/// let y = &X;
1795+
/// let ref x = X;
1796+
/// let (x, y) = (&X, &Y);
1797+
/// foo(&X);
1798+
/// }
1799+
///
1800+
/// fn foo<'a>(_x: &'a i32) {}
1801+
/// ```
1802+
///
1803+
/// {{produces}}
1804+
///
1805+
/// ### Explanation
1806+
///
1807+
/// Shared or mutable references of mutable static are almost always a mistake and
1808+
/// can lead to undefined behavior and various other problems in your code.
1809+
///
1810+
/// This lint is "warn" by default on editions up to 2021, from 2024 there is
1811+
/// a hard error instead.
1812+
pub STATIC_MUT_REF,
1813+
Warn,
1814+
"shared references or mutable references of mutable static is discouraged",
1815+
@future_incompatible = FutureIncompatibleInfo {
1816+
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
1817+
reference: "issue #114447 <https://github.com/rust-lang/rust/issues/114447>",
1818+
explain_reason: false,
1819+
};
1820+
}
1821+
17701822
declare_lint! {
17711823
/// The `absolute_paths_not_starting_with_crate` lint detects fully
17721824
/// qualified paths that start with a module name instead of `crate`,

0 commit comments

Comments
 (0)