Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow hidden references to mutable static #124895

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_driver_impl/src/signal_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ macro raw_errln($tokens:tt) {
}

/// Signal handler installed for SIGSEGV
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
#[allow(static_mut_refs)]
extern "C" fn print_stack_trace(_: libc::c_int) {
const MAX_FRAMES: usize = 256;
// Reserve data segment so we don't have to malloc in a signal handler, which might fail
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_error_codes/src/error_codes/E0796.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#### Note: this error code is no longer emitted by the compiler.

You have created a reference to a mutable static.

Erroneous code example:

```compile_fail,edition2024,E0796
```
static mut X: i32 = 23;

fn work() {
let _val = unsafe { X };
}

let x_ref = unsafe { &mut X };
work();
// The next line has Undefined Behavior!
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,3 +679,4 @@ E0798: 0798,
// E0723, // unstable feature in `const` context
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
// E0744, // merged into E0728
// E0796, // unused error code. We use `static_mut_refs` lint instead.
19 changes: 0 additions & 19 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -467,25 +467,6 @@ 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 = creating a {$shared} reference to a mutable static
.label = {$shared} reference to mutable static
.note = {$shared ->
[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
*[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
}
.suggestion = use `addr_of!` instead to create a raw pointer
.suggestion_mut = use `addr_of_mut!` instead to create a raw pointer

hir_analysis_static_mut_refs_lint = creating a {$shared} reference to mutable static is discouraged
.label = {$shared} reference to mutable static
.suggestion = use `addr_of!` instead to create a raw pointer
.suggestion_mut = use `addr_of_mut!` instead to create a raw pointer
.note = this will be a hard error in the 2024 edition
.why_note = {$shared ->
[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
*[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
}

hir_analysis_static_specialize = cannot specialize on `'static` lifetime

hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ mod check;
mod compare_impl_item;
pub mod dropck;
mod entry;
mod errs;
pub mod intrinsic;
pub mod intrinsicck;
mod region;
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_hir_analysis/src/check/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ use rustc_middle::ty::TyCtxt;
use rustc_span::source_map;
use tracing::debug;

use super::errs::{maybe_expr_static_mut, maybe_stmt_static_mut};

#[derive(Debug, Copy, Clone)]
struct Context {
/// The scope that contains any new variables declared, plus its depth in
Expand Down Expand Up @@ -229,8 +227,6 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
let stmt_id = stmt.hir_id.local_id;
debug!("resolve_stmt(stmt.id={:?})", stmt_id);

maybe_stmt_static_mut(visitor.tcx, *stmt);

// Every statement will clean up the temporaries created during
// execution of that statement. Therefore each statement has an
// associated destruction scope that represents the scope of the
Expand All @@ -249,8 +245,6 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr);

maybe_expr_static_mut(visitor.tcx, *expr);

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

Expand Down
51 changes: 0 additions & 51 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1522,57 +1522,6 @@ pub(crate) struct OnlyCurrentTraitsPointerSugg<'a> {
pub ptr_ty: Ty<'a>,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_static_mut_ref, code = E0796)]
#[note]
pub(crate) struct StaticMutRef<'a> {
#[primary_span]
#[label]
pub span: Span,
#[subdiagnostic]
pub sugg: MutRefSugg,
pub shared: &'a str,
}

#[derive(Subdiagnostic)]
pub(crate) enum MutRefSugg {
#[multipart_suggestion(
hir_analysis_suggestion,
style = "verbose",
applicability = "maybe-incorrect"
)]
Shared {
#[suggestion_part(code = "addr_of!(")]
lo: Span,
#[suggestion_part(code = ")")]
hi: Span,
},
#[multipart_suggestion(
hir_analysis_suggestion_mut,
style = "verbose",
applicability = "maybe-incorrect"
)]
Mut {
#[suggestion_part(code = "addr_of_mut!(")]
lo: Span,
#[suggestion_part(code = ")")]
hi: Span,
},
}

// STATIC_MUT_REF lint
#[derive(LintDiagnostic)]
#[diag(hir_analysis_static_mut_refs_lint)]
#[note]
#[note(hir_analysis_why_note)]
pub(crate) struct RefOfMutStatic<'a> {
#[label]
pub span: Span,
#[subdiagnostic]
pub sugg: MutRefSugg,
pub shared: &'a str,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_not_supported_delegation)]
pub(crate) struct UnsupportedDelegation<'a> {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,13 @@ lint_single_use_lifetime = lifetime parameter `{$ident}` only used once

lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`

lint_static_mut_refs_lint = creating a {$shared_label}reference to mutable static is discouraged
.label = {$shared_label}reference to mutable static
.suggestion = use `&raw const` instead to create a raw pointer
.suggestion_mut = use `&raw mut` instead to create a raw pointer
.shared_note = shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives
.mut_note = mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives

lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion
.label = `{$self_ty}` implements `Deref<Target = dyn {$target_principal}>` which conflicts with supertrait `{$supertrait_principal}`
.label2 = target type is a supertrait of `{$self_ty}`
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ mod ptr_nulls;
mod redundant_semicolon;
mod reference_casting;
mod shadowed_into_iter;
mod static_mut_refs;
mod tail_expr_drop_order;
mod traits;
mod types;
Expand Down Expand Up @@ -120,6 +121,7 @@ use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use shadowed_into_iter::ShadowedIntoIter;
pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
use static_mut_refs::*;
use tail_expr_drop_order::TailExprDropOrder;
use traits::*;
use types::*;
Expand Down Expand Up @@ -246,6 +248,7 @@ late_lint_methods!(
ImplTraitOvercaptures: ImplTraitOvercaptures,
TailExprDropOrder: TailExprDropOrder,
IfLetRescope: IfLetRescope::default(),
StaticMutRefs: StaticMutRefs,
]
]
);
Expand Down
32 changes: 32 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3060,3 +3060,35 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
pub(crate) struct OutOfScopeMacroCalls {
pub path: String,
}

#[derive(LintDiagnostic)]
#[diag(lint_static_mut_refs_lint)]
pub(crate) struct RefOfMutStatic<'a> {
#[label]
pub span: Span,
#[subdiagnostic]
pub sugg: Option<MutRefSugg>,
pub shared_label: &'a str,
#[note(lint_shared_note)]
pub shared_note: bool,
#[note(lint_mut_note)]
pub mut_note: bool,
}

#[derive(Subdiagnostic)]
pub(crate) enum MutRefSugg {
#[multipart_suggestion(lint_suggestion, style = "verbose", applicability = "maybe-incorrect")]
Shared {
#[suggestion_part(code = "&raw const ")]
span: Span,
},
#[multipart_suggestion(
lint_suggestion_mut,
style = "verbose",
applicability = "maybe-incorrect"
)]
Mut {
#[suggestion_part(code = "&raw mut ")]
span: Span,
},
}
154 changes: 154 additions & 0 deletions compiler/rustc_lint/src/static_mut_refs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use rustc_hir as hir;
use rustc_hir::{Expr, Stmt};
use rustc_middle::ty::{Mutability, TyKind};
use rustc_session::lint::FutureIncompatibilityReason;
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::edition::Edition;
use rustc_span::Span;

use crate::lints::{MutRefSugg, RefOfMutStatic};
use crate::{LateContext, LateLintPass, LintContext};

declare_lint! {
/// The `static_mut_refs` 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, in 2024 is "deny".
pub STATIC_MUT_REFS,
Warn,
"shared references or mutable references of mutable static is discouraged",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>",
explain_reason: false,
};
@edition Edition2024 => Deny;
}

declare_lint_pass!(StaticMutRefs => [STATIC_MUT_REFS]);

impl<'tcx> LateLintPass<'tcx> for StaticMutRefs {
#[allow(rustc::usage_of_ty_tykind)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
let err_span = expr.span;
match expr.kind {
hir::ExprKind::AddrOf(borrow_kind, m, ex)
if matches!(borrow_kind, hir::BorrowKind::Ref)
&& let Some(err_span) = path_is_static_mut(ex, err_span) =>
{
emit_static_mut_refs(
cx,
err_span,
err_span.with_hi(ex.span.lo()),
m,
!expr.span.from_expansion(),
);
}
hir::ExprKind::MethodCall(_, e, _, _)
if let Some(err_span) = path_is_static_mut(e, expr.span)
&& let typeck = cx.typeck_results()
&& let Some(method_def_id) = typeck.type_dependent_def_id(expr.hir_id)
&& let inputs =
cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()
&& let Some(receiver) = inputs.get(0)
&& let TyKind::Ref(_, _, m) = receiver.kind() =>
{
emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), *m, false);
}
_ => {}
}
}

fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) {
if let hir::StmtKind::Let(loc) = stmt.kind
&& let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
&& let hir::ByRef::Yes(m) = ba.0
&& let Some(init) = loc.init
&& let Some(err_span) = path_is_static_mut(init, init.span)
{
emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), m, false);
}
}
}

fn path_is_static_mut(mut expr: &hir::Expr<'_>, mut err_span: Span) -> Option<Span> {
if err_span.from_expansion() {
err_span = expr.span;
}

while let hir::ExprKind::Field(e, _) = expr.kind {
expr = e;
}

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 { safety: _, mutability: Mutability::Mut, nested: false } =
def_kind
{
return Some(err_span);
}
None
}

fn emit_static_mut_refs(
cx: &LateContext<'_>,
span: Span,
sugg_span: Span,
mutable: Mutability,
suggest_addr_of: bool,
) {
let (shared_label, shared_note, mut_note, sugg) = match mutable {
Mutability::Mut => {
let sugg =
if suggest_addr_of { Some(MutRefSugg::Mut { span: sugg_span }) } else { None };
("mutable ", false, true, sugg)
}
Mutability::Not => {
let sugg =
if suggest_addr_of { Some(MutRefSugg::Shared { span: sugg_span }) } else { None };
("shared ", true, false, sugg)
}
};

cx.emit_span_lint(
STATIC_MUT_REFS,
span,
RefOfMutStatic { span, sugg, shared_label, shared_note, mut_note },
);
}
Loading
Loading