Skip to content

Commit 4a42356

Browse files
committed
Add interior_mutable_consts lint
1 parent c44f879 commit 4a42356

8 files changed

+660
-5
lines changed

compiler/rustc_lint/messages.ftl

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

compiler/rustc_lint/src/lints.rs

+49
Original file line numberDiff line numberDiff line change
@@ -1874,6 +1874,55 @@ pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> {
18741874
},
18751875
}
18761876

1877+
#[derive(LintDiagnostic)]
1878+
#[diag(lint_interior_mutable_consts)]
1879+
#[note(lint_temporary)]
1880+
#[note(lint_never_original)]
1881+
#[help(lint_suggestion_inline_const)]
1882+
pub(crate) struct InteriorMutableConstsDiag {
1883+
pub ty_name: Ident,
1884+
pub const_name: Ident,
1885+
#[label]
1886+
pub ty_span: Span,
1887+
#[subdiagnostic]
1888+
pub sugg_static: InteriorMutableConstsSuggestionStatic,
1889+
#[subdiagnostic]
1890+
pub sugg_allow: InteriorMutableConstsSuggestionAllow,
1891+
}
1892+
1893+
#[derive(Subdiagnostic)]
1894+
pub(crate) enum InteriorMutableConstsSuggestionStatic {
1895+
#[suggestion(
1896+
lint_suggestion_static,
1897+
code = "{before}static ",
1898+
style = "verbose",
1899+
applicability = "maybe-incorrect"
1900+
)]
1901+
Spanful {
1902+
#[primary_span]
1903+
const_: Span,
1904+
before: &'static str,
1905+
},
1906+
#[help(lint_suggestion_static)]
1907+
Spanless,
1908+
}
1909+
1910+
#[derive(Subdiagnostic)]
1911+
pub(crate) enum InteriorMutableConstsSuggestionAllow {
1912+
#[suggestion(
1913+
lint_suggestion_allow,
1914+
code = "#[allow(interior_mutable_consts)] ",
1915+
style = "verbose",
1916+
applicability = "maybe-incorrect"
1917+
)]
1918+
Spanful {
1919+
#[primary_span]
1920+
span: Span,
1921+
},
1922+
#[help(lint_suggestion_allow)]
1923+
Spanless,
1924+
}
1925+
18771926
pub(crate) struct ImproperCTypes<'a> {
18781927
pub ty: Ty<'a>,
18791928
pub desc: &'a str,

compiler/rustc_lint/src/types.rs

+90-5
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ mod improper_ctypes;
2323
use crate::lints::{
2424
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
2525
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
26-
AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
27-
InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
28-
UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment,
29-
VariantSizeDifferencesDiag,
26+
AtomicOrderingStore, ImproperCTypes, InteriorMutableConstsDiag,
27+
InteriorMutableConstsSuggestionAllow, InteriorMutableConstsSuggestionStatic,
28+
InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
29+
UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion,
30+
UnusedComparisons, UsesPowerAlignment, VariantSizeDifferencesDiag,
3031
};
3132
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
3233

@@ -198,6 +199,47 @@ declare_lint! {
198199
"detects unpredictable function pointer comparisons"
199200
}
200201

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

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

692777
declare_lint! {
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() {}
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+
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)