Skip to content

Commit 267d8a3

Browse files
committed
Add interior_mutable_consts lint
1 parent 7ab3d96 commit 267d8a3

8 files changed

+660
-5
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,14 @@ lint_incomplete_include =
430430
431431
lint_inner_macro_attribute_unstable = inner macro attributes are unstable
432432
433+
lint_interior_mutable_consts = interior mutability in `const` item have no effect to the `const` item itself
434+
.label = `{$ty_name}` is an interior mutable type
435+
.temporary = each usage of a `const` item creates a new temporary
436+
.never_original = only the temporaries and never the original `const` item `{$const_name}` will be modified
437+
.suggestion_inline_const = for use as an initializer, consider using an inline-const `const {"{"} /* ... */ {"}"}` at the usage site instead
438+
.suggestion_static = for a shared instance of `{$const_name}`, consider using a `static` item instead
439+
.suggestion_allow = alternatively consider allowing the lint
440+
433441
lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly
434442
.label = use a different label that doesn't start with `0` or `1`
435443
.help = start numbering with `2` instead

compiler/rustc_lint/src/lints.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,6 +1887,55 @@ pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> {
18871887
},
18881888
}
18891889

1890+
#[derive(LintDiagnostic)]
1891+
#[diag(lint_interior_mutable_consts)]
1892+
#[note(lint_temporary)]
1893+
#[note(lint_never_original)]
1894+
#[help(lint_suggestion_inline_const)]
1895+
pub(crate) struct InteriorMutableConstsDiag {
1896+
pub ty_name: Ident,
1897+
pub const_name: Ident,
1898+
#[label]
1899+
pub ty_span: Span,
1900+
#[subdiagnostic]
1901+
pub sugg_static: InteriorMutableConstsSuggestionStatic,
1902+
#[subdiagnostic]
1903+
pub sugg_allow: InteriorMutableConstsSuggestionAllow,
1904+
}
1905+
1906+
#[derive(Subdiagnostic)]
1907+
pub(crate) enum InteriorMutableConstsSuggestionStatic {
1908+
#[suggestion(
1909+
lint_suggestion_static,
1910+
code = "{before}static ",
1911+
style = "verbose",
1912+
applicability = "maybe-incorrect"
1913+
)]
1914+
Spanful {
1915+
#[primary_span]
1916+
const_: Span,
1917+
before: &'static str,
1918+
},
1919+
#[help(lint_suggestion_static)]
1920+
Spanless,
1921+
}
1922+
1923+
#[derive(Subdiagnostic)]
1924+
pub(crate) enum InteriorMutableConstsSuggestionAllow {
1925+
#[suggestion(
1926+
lint_suggestion_allow,
1927+
code = "#[allow(interior_mutable_consts)] ",
1928+
style = "verbose",
1929+
applicability = "maybe-incorrect"
1930+
)]
1931+
Spanful {
1932+
#[primary_span]
1933+
span: Span,
1934+
},
1935+
#[help(lint_suggestion_allow)]
1936+
Spanless,
1937+
}
1938+
18901939
pub(crate) struct ImproperCTypes<'a> {
18911940
pub ty: Ty<'a>,
18921941
pub desc: &'a str,

compiler/rustc_lint/src/types.rs

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ mod improper_ctypes;
2525
use crate::lints::{
2626
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
2727
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
28-
AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
29-
InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
30-
UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment,
31-
VariantSizeDifferencesDiag,
28+
AtomicOrderingStore, ImproperCTypes, InteriorMutableConstsDiag,
29+
InteriorMutableConstsSuggestionAllow, InteriorMutableConstsSuggestionStatic,
30+
InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
31+
UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion,
32+
UnusedComparisons, UsesPowerAlignment, VariantSizeDifferencesDiag,
3233
};
3334
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
3435

@@ -200,6 +201,47 @@ declare_lint! {
200201
"detects unpredictable function pointer comparisons"
201202
}
202203

204+
declare_lint! {
205+
/// The `interior_mutable_consts` lint detects instance where
206+
/// const-items have a interior mutable type, which silently does nothing.
207+
///
208+
/// ### Example
209+
///
210+
/// ```rust
211+
/// use std::sync::Once;
212+
///
213+
/// // SAFETY: should only be call once
214+
/// unsafe extern "C" fn ffi_init() { /* ... */ }
215+
///
216+
/// const A: Once = Once::new(); // using `B` will always creates temporaries and
217+
/// // never modify it-self on use, should be a
218+
/// // static-item instead
219+
///
220+
/// fn init() {
221+
/// A.call_once(|| unsafe {
222+
/// ffi_init(); // unsound, as the `call_once` is on a temporary
223+
/// // and not on a shared variable
224+
/// })
225+
/// }
226+
/// ```
227+
///
228+
/// {{produces}}
229+
///
230+
/// ### Explanation
231+
///
232+
/// Using a const-item with an interior mutable type has no effect as const-item
233+
/// are essentially inlined wherever they are used, meaning that they are copied
234+
/// directly into the relevant context when used rendering modification through
235+
/// interior mutability ineffective across usage of that const-item.
236+
///
237+
/// The current implementation of this lint only warns on significant `std` and
238+
/// `core` interior mutable types, like `Once`, `AtomicI32`, ... this is done out
239+
/// of prudence and may be extended in the future.
240+
INTERIOR_MUTABLE_CONSTS,
241+
Warn,
242+
"detects const items with interior mutable type",
243+
}
244+
203245
#[derive(Copy, Clone, Default)]
204246
pub(crate) struct TypeLimits {
205247
/// Id of the last visited negated expression
@@ -213,7 +255,8 @@ impl_lint_pass!(TypeLimits => [
213255
OVERFLOWING_LITERALS,
214256
INVALID_NAN_COMPARISONS,
215257
AMBIGUOUS_WIDE_POINTER_COMPARISONS,
216-
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS
258+
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
259+
INTERIOR_MUTABLE_CONSTS,
217260
]);
218261

219262
impl TypeLimits {
@@ -686,6 +729,48 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
686729
})
687730
}
688731
}
732+
733+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
734+
if let hir::ItemKind::Const(ident, ty, _generics, _body_id) = item.kind
735+
&& let hir::TyKind::Path(ref qpath) = ty.kind
736+
&& let Some(def_id) = cx.qpath_res(qpath, ty.hir_id).opt_def_id()
737+
&& cx.tcx.has_attr(def_id, sym::rustc_significant_interior_mutable_type)
738+
&& let Some(ty_name) = cx.tcx.opt_item_ident(def_id)
739+
{
740+
let (sugg_static, sugg_allow) = if let Some(vis_span) =
741+
item.vis_span.find_ancestor_inside(item.span)
742+
&& item.span.can_be_used_for_suggestions()
743+
&& vis_span.can_be_used_for_suggestions()
744+
{
745+
(
746+
InteriorMutableConstsSuggestionStatic::Spanful {
747+
const_: item.vis_span.between(ident.span),
748+
before: if !vis_span.is_empty() { " " } else { "" },
749+
},
750+
InteriorMutableConstsSuggestionAllow::Spanful {
751+
span: item.span.shrink_to_lo(),
752+
},
753+
)
754+
} else {
755+
(
756+
InteriorMutableConstsSuggestionStatic::Spanless,
757+
InteriorMutableConstsSuggestionAllow::Spanless,
758+
)
759+
};
760+
761+
cx.emit_span_lint(
762+
INTERIOR_MUTABLE_CONSTS,
763+
item.span,
764+
InteriorMutableConstsDiag {
765+
ty_name,
766+
ty_span: ty.span,
767+
const_name: ident,
768+
sugg_static,
769+
sugg_allow,
770+
},
771+
);
772+
}
773+
}
689774
}
690775

691776
declare_lint! {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ check-pass
2+
3+
use std::cell::{Cell, RefCell, UnsafeCell, LazyCell, OnceCell};
4+
5+
const A: Cell<i32> = Cell::new(0);
6+
//~^ WARN interior mutability in `const` item
7+
const B: RefCell<i32> = RefCell::new(0);
8+
//~^ WARN interior mutability in `const` item
9+
const C: UnsafeCell<i32> = UnsafeCell::new(0);
10+
//~^ WARN interior mutability in `const` item
11+
const D: LazyCell<i32> = LazyCell::new(|| 0);
12+
//~^ WARN interior mutability in `const` item
13+
const E: OnceCell<i32> = OnceCell::new();
14+
//~^ WARN interior mutability in `const` item
15+
16+
fn main() {}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
warning: interior mutability in `const` item have no effect to the `const` item itself
2+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:5:1
3+
|
4+
LL | const A: Cell<i32> = Cell::new(0);
5+
| ^^^^^^^^^---------^^^^^^^^^^^^^^^^
6+
| |
7+
| `Cell` is an interior mutable type
8+
|
9+
= note: each usage of a `const` item creates a new temporary
10+
= note: only the temporaries and never the original `const` item `A` will be modified
11+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
12+
= note: `#[warn(interior_mutable_consts)]` on by default
13+
help: for a shared instance of `A`, consider using a `static` item instead
14+
|
15+
LL - const A: Cell<i32> = Cell::new(0);
16+
LL + static A: Cell<i32> = Cell::new(0);
17+
|
18+
help: alternatively consider allowing the lint
19+
|
20+
LL | #[allow(interior_mutable_consts)] const A: Cell<i32> = Cell::new(0);
21+
| +++++++++++++++++++++++++++++++++
22+
23+
warning: interior mutability in `const` item have no effect to the `const` item itself
24+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:7:1
25+
|
26+
LL | const B: RefCell<i32> = RefCell::new(0);
27+
| ^^^^^^^^^------------^^^^^^^^^^^^^^^^^^^
28+
| |
29+
| `RefCell` is an interior mutable type
30+
|
31+
= note: each usage of a `const` item creates a new temporary
32+
= note: only the temporaries and never the original `const` item `B` will be modified
33+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
34+
help: for a shared instance of `B`, consider using a `static` item instead
35+
|
36+
LL - const B: RefCell<i32> = RefCell::new(0);
37+
LL + static B: RefCell<i32> = RefCell::new(0);
38+
|
39+
help: alternatively consider allowing the lint
40+
|
41+
LL | #[allow(interior_mutable_consts)] const B: RefCell<i32> = RefCell::new(0);
42+
| +++++++++++++++++++++++++++++++++
43+
44+
warning: interior mutability in `const` item have no effect to the `const` item itself
45+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:9:1
46+
|
47+
LL | const C: UnsafeCell<i32> = UnsafeCell::new(0);
48+
| ^^^^^^^^^---------------^^^^^^^^^^^^^^^^^^^^^^
49+
| |
50+
| `UnsafeCell` is an interior mutable type
51+
|
52+
= note: each usage of a `const` item creates a new temporary
53+
= note: only the temporaries and never the original `const` item `C` will be modified
54+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
55+
help: for a shared instance of `C`, consider using a `static` item instead
56+
|
57+
LL - const C: UnsafeCell<i32> = UnsafeCell::new(0);
58+
LL + static C: UnsafeCell<i32> = UnsafeCell::new(0);
59+
|
60+
help: alternatively consider allowing the lint
61+
|
62+
LL | #[allow(interior_mutable_consts)] const C: UnsafeCell<i32> = UnsafeCell::new(0);
63+
| +++++++++++++++++++++++++++++++++
64+
65+
warning: interior mutability in `const` item have no effect to the `const` item itself
66+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:11:1
67+
|
68+
LL | const D: LazyCell<i32> = LazyCell::new(|| 0);
69+
| ^^^^^^^^^-------------^^^^^^^^^^^^^^^^^^^^^^^
70+
| |
71+
| `LazyCell` is an interior mutable type
72+
|
73+
= note: each usage of a `const` item creates a new temporary
74+
= note: only the temporaries and never the original `const` item `D` will be modified
75+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
76+
help: for a shared instance of `D`, consider using a `static` item instead
77+
|
78+
LL - const D: LazyCell<i32> = LazyCell::new(|| 0);
79+
LL + static D: LazyCell<i32> = LazyCell::new(|| 0);
80+
|
81+
help: alternatively consider allowing the lint
82+
|
83+
LL | #[allow(interior_mutable_consts)] const D: LazyCell<i32> = LazyCell::new(|| 0);
84+
| +++++++++++++++++++++++++++++++++
85+
86+
warning: interior mutability in `const` item have no effect to the `const` item itself
87+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:13:1
88+
|
89+
LL | const E: OnceCell<i32> = OnceCell::new();
90+
| ^^^^^^^^^-------------^^^^^^^^^^^^^^^^^^^
91+
| |
92+
| `OnceCell` is an interior mutable type
93+
|
94+
= note: each usage of a `const` item creates a new temporary
95+
= note: only the temporaries and never the original `const` item `E` will be modified
96+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
97+
help: for a shared instance of `E`, consider using a `static` item instead
98+
|
99+
LL - const E: OnceCell<i32> = OnceCell::new();
100+
LL + static E: OnceCell<i32> = OnceCell::new();
101+
|
102+
help: alternatively consider allowing the lint
103+
|
104+
LL | #[allow(interior_mutable_consts)] const E: OnceCell<i32> = OnceCell::new();
105+
| +++++++++++++++++++++++++++++++++
106+
107+
warning: 5 warnings emitted
108+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//@ check-pass
2+
//@ run-rustfix
3+
4+
#![allow(dead_code)] // for rustfix
5+
#![feature(reentrant_lock)]
6+
7+
use std::sync::{Once, Barrier, Condvar, LazyLock, Mutex, OnceLock, ReentrantLock, RwLock};
8+
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicI32, AtomicU32};
9+
10+
#[allow(interior_mutable_consts)] static A: Once = Once::new();
11+
//~^ WARN interior mutability in `const` item
12+
#[allow(interior_mutable_consts)] static B: Barrier = Barrier::new(0);
13+
//~^ WARN interior mutability in `const` item
14+
#[allow(interior_mutable_consts)] static C: Condvar = Condvar::new();
15+
//~^ WARN interior mutability in `const` item
16+
#[allow(interior_mutable_consts)] static D: LazyLock<i32> = LazyLock::new(|| 0);
17+
//~^ WARN interior mutability in `const` item
18+
#[allow(interior_mutable_consts)] static E: Mutex<i32> = Mutex::new(0);
19+
//~^ WARN interior mutability in `const` item
20+
#[allow(interior_mutable_consts)] static F: OnceLock<i32> = OnceLock::new();
21+
//~^ WARN interior mutability in `const` item
22+
#[allow(interior_mutable_consts)] static G: ReentrantLock<i32> = ReentrantLock::new(0);
23+
//~^ WARN interior mutability in `const` item
24+
#[allow(interior_mutable_consts)] static H: RwLock<i32> = RwLock::new(0);
25+
//~^ WARN interior mutability in `const` item
26+
#[allow(interior_mutable_consts)] static I: AtomicBool = AtomicBool::new(false);
27+
//~^ WARN interior mutability in `const` item
28+
#[allow(interior_mutable_consts)] static J: AtomicPtr<i32> = AtomicPtr::new(std::ptr::null_mut());
29+
//~^ WARN interior mutability in `const` item
30+
#[allow(interior_mutable_consts)] static K: AtomicI32 = AtomicI32::new(0);
31+
//~^ WARN interior mutability in `const` item
32+
#[allow(interior_mutable_consts)] static L: AtomicU32 = AtomicU32::new(0);
33+
//~^ WARN interior mutability in `const` item
34+
35+
#[allow(interior_mutable_consts)] pub(crate) static X: Once = Once::new();
36+
//~^ WARN interior mutability in `const` item
37+
38+
fn main() {
39+
#[allow(interior_mutable_consts)] static Z: Once = Once::new();
40+
//~^ WARN interior mutability in `const` item
41+
}
42+
43+
struct S;
44+
impl S {
45+
const Z: Once = Once::new(); // not a const-item
46+
}

0 commit comments

Comments
 (0)