diff --git a/Cargo.lock b/Cargo.lock index b8192e333fe..1d6f7ea1e27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3877,6 +3877,7 @@ dependencies = [ "rustc_feature", "rustc_fluent_macro", "rustc_hir", + "rustc_hir_pretty", "rustc_index", "rustc_infer", "rustc_lint_defs", diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 1028d43f9c5..a1391cceb71 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -515,6 +515,7 @@ E0793: include_str!("./error_codes/E0793.md"), E0794: include_str!("./error_codes/E0794.md"), E0795: include_str!("./error_codes/E0795.md"), +E0796: include_str!("./error_codes/E0796.md"), } // Undocumented removed error codes. Note that many removed error codes are kept in the list above diff --git a/compiler/rustc_error_codes/src/error_codes/E0796.md b/compiler/rustc_error_codes/src/error_codes/E0796.md new file mode 100644 index 00000000000..cea18f8db85 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0796.md @@ -0,0 +1,22 @@ +Reference of mutable static. + +Erroneous code example: + +```compile_fail,edition2024,E0796 +static mut X: i32 = 23; +static mut Y: i32 = 24; + +unsafe { + let y = &X; + let ref x = X; + let (x, y) = (&X, &Y); + foo(&X); +} + +fn foo<'a>(_x: &'a i32) {} +``` + +Mutable statics can be written to by multiple threads: aliasing violations or +data races will cause undefined behavior. + +Reference of mutable static is a hard error from 2024 edition. diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index b671bebeb05..b5ebc1fab76 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } rustc_lint_defs = { path = "../rustc_lint_defs" } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d8b6b9a1272..6a17668ad17 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -346,6 +346,20 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]` .label = `#[start]` function is not allowed to be `#[track_caller]` +hir_analysis_static_mut_ref = reference of mutable static is disallowed + .label = reference of mutable static + .note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + .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 + .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 + +hir_analysis_static_mut_ref_lint = {$shared}reference of mutable static is discouraged + .label = shared reference of mutable static + .label_mut = mutable reference of mutable static + .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 + .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 + .note = reference of mutable static is a hard error from 2024 edition + .why_note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior + hir_analysis_static_specialize = cannot specialize on `'static` lifetime hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs new file mode 100644 index 00000000000..5862643c42f --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/errs.rs @@ -0,0 +1,78 @@ +use rustc_hir as hir; +use rustc_hir_pretty::qpath_to_string; +use rustc_lint_defs::builtin::STATIC_MUT_REF; +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; +use rustc_type_ir::Mutability; + +use crate::errors; + +/// Check for shared or mutable references of `static mut` inside expression +pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) { + let span = expr.span; + let hir_id = expr.hir_id; + if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind + && matches!(borrow_kind, hir::BorrowKind::Ref) + && let Some(var) = is_path_static_mut(*expr) + { + handle_static_mut_ref( + tcx, + span, + var, + span.edition().at_least_rust_2024(), + matches!(m, Mutability::Mut), + hir_id, + ); + } +} + +fn is_path_static_mut(expr: hir::Expr<'_>) -> Option { + if let hir::ExprKind::Path(qpath) = expr.kind + && let hir::QPath::Resolved(_, path) = qpath + && let hir::def::Res::Def(def_kind, _) = path.res + && let hir::def::DefKind::Static(mt) = def_kind + && matches!(mt, Mutability::Mut) + { + return Some(qpath_to_string(&qpath)); + } + None +} + +fn handle_static_mut_ref( + tcx: TyCtxt<'_>, + span: Span, + var: String, + e2024: bool, + mutable: bool, + hir_id: hir::HirId, +) { + if e2024 { + let sugg = if mutable { + errors::StaticMutRefSugg::Mut { span, var } + } else { + errors::StaticMutRefSugg::Shared { span, var } + }; + tcx.sess.parse_sess.dcx.emit_err(errors::StaticMutRef { span, sugg }); + return; + } + + let (label, sugg, shared) = if mutable { + ( + errors::RefOfMutStaticLabel::Mut { span }, + errors::RefOfMutStaticSugg::Mut { span, var }, + "mutable ", + ) + } else { + ( + errors::RefOfMutStaticLabel::Shared { span }, + errors::RefOfMutStaticSugg::Shared { span, var }, + "shared ", + ) + }; + tcx.emit_spanned_lint( + STATIC_MUT_REF, + hir_id, + span, + errors::RefOfMutStatic { shared, why_note: (), label, sugg }, + ); +} diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index f60d6950670..ac0c715c6b3 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -66,6 +66,7 @@ mod compare_impl_item; pub mod dropck; mod entry; +mod errs; pub mod intrinsic; pub mod intrinsicck; mod region; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 9124d502110..4f22da4ba3b 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1410,3 +1410,94 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> { pub mut_key: &'a str, pub ptr_ty: Ty<'a>, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_static_mut_ref, code = "E0796")] +#[note] +pub struct StaticMutRef { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub sugg: StaticMutRefSugg, +} + +#[derive(Subdiagnostic)] +pub enum StaticMutRefSugg { + #[suggestion( + hir_analysis_suggestion, + style = "verbose", + code = "addr_of!({var})", + applicability = "maybe-incorrect" + )] + Shared { + #[primary_span] + span: Span, + var: String, + }, + #[suggestion( + hir_analysis_suggestion_mut, + style = "verbose", + code = "addr_of_mut!({var})", + applicability = "maybe-incorrect" + )] + Mut { + #[primary_span] + span: Span, + var: String, + }, +} + +// STATIC_MUT_REF lint +#[derive(LintDiagnostic)] +#[diag(hir_analysis_static_mut_ref_lint)] +#[note] +pub struct RefOfMutStatic<'a> { + pub shared: &'a str, + #[note(hir_analysis_why_note)] + pub why_note: (), + #[subdiagnostic] + pub label: RefOfMutStaticLabel, + #[subdiagnostic] + pub sugg: RefOfMutStaticSugg, +} + +#[derive(Subdiagnostic)] +pub enum RefOfMutStaticLabel { + #[label(hir_analysis_label)] + Shared { + #[primary_span] + span: Span, + }, + #[label(hir_analysis_label_mut)] + Mut { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +pub enum RefOfMutStaticSugg { + #[suggestion( + hir_analysis_suggestion, + style = "verbose", + code = "addr_of!({var})", + applicability = "maybe-incorrect" + )] + Shared { + #[primary_span] + span: Span, + var: String, + }, + #[suggestion( + hir_analysis_suggestion_mut, + style = "verbose", + code = "addr_of_mut!({var})", + applicability = "maybe-incorrect" + )] + Mut { + #[primary_span] + span: Span, + var: String, + }, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 19da51b7dcf..cfba6780a66 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -89,6 +89,7 @@ SINGLE_USE_LIFETIMES, SOFT_UNSTABLE, STABLE_FEATURES, + STATIC_MUT_REF, SUSPICIOUS_AUTO_TRAIT_IMPLS, TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, @@ -1767,6 +1768,57 @@ }; } +declare_lint! { + /// The `static_mut_ref` lint checks for shared or mutable references + /// of mutable static inside `unsafe` blocks and `unsafe` functions. + /// + /// ### Example + /// + /// ```rust,edition2021 + /// fn main() { + /// static mut X: i32 = 23; + /// static mut Y: i32 = 24; + /// + /// unsafe { + /// let y = &X; + /// let ref x = X; + /// let (x, y) = (&X, &Y); + /// foo(&X); + /// } + /// } + /// + /// unsafe fn _foo() { + /// static mut X: i32 = 23; + /// static mut Y: i32 = 24; + /// + /// let y = &X; + /// let ref x = X; + /// let (x, y) = (&X, &Y); + /// foo(&X); + /// } + /// + /// fn foo<'a>(_x: &'a i32) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Shared or mutable references of mutable static are almost always a mistake and + /// can lead to undefined behavior and various other problems in your code. + /// + /// This lint is "warn" by default on editions up to 2021, from 2024 there is + /// a hard error instead. + pub STATIC_MUT_REF, + Warn, + "shared references or mutable references of mutable static is discouraged", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #114447 ", + explain_reason: false, + }; +} + declare_lint! { /// The `absolute_paths_not_starting_with_crate` lint detects fully /// qualified paths that start with a module name instead of `crate`,