about summary refs log tree commit diff
diff options
context:
space:
mode:
authorObei Sideg <obei.sideg@gmail.com>2023-12-22 14:56:20 +0300
committerObei Sideg <obei.sideg@gmail.com>2024-01-06 06:31:35 +0300
commit2c088f95202400084987ed874822b4969f678d20 (patch)
tree10a2427767bbfa1f717ea2f946484436895e4ff6
parent595bc6f00369475047538fdae1ff8cea692ac385 (diff)
downloadrust-2c088f95202400084987ed874822b4969f678d20.tar.gz
rust-2c088f95202400084987ed874822b4969f678d20.zip
Disallow reference to `static mut` for expressions
Add `E0796` error code.
Add `static_mut_ref` lint.

This is the idea for the 2024 edition.
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs1
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0796.md22
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml1
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl14
-rw-r--r--compiler/rustc_hir_analysis/src/check/errs.rs78
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs91
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs52
9 files changed, 261 insertions, 0 deletions
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 @@ E0792: include_str!("./error_codes/E0792.md"),
 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<String> {
+    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 check;
 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 @@ declare_lint_pass! {
         SINGLE_USE_LIFETIMES,
         SOFT_UNSTABLE,
         STABLE_FEATURES,
+        STATIC_MUT_REF,
         SUSPICIOUS_AUTO_TRAIT_IMPLS,
         TEST_UNSTABLE_LINT,
         TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
@@ -1768,6 +1769,57 @@ declare_lint! {
 }
 
 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 <https://github.com/rust-lang/rust/issues/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`,
     /// `self`, or an extern crate name