Skip to content

Commit 90631e2

Browse files
committed
Add interior_mutable_consts lint
1 parent 428326e commit 90631e2

8 files changed

+636
-3
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,14 @@ lint_incomplete_include =
420420
421421
lint_inner_macro_attribute_unstable = inner macro attributes are unstable
422422
423+
lint_interior_mutable_consts = interior mutability in `const` item have no effect to the `const` item itself
424+
.label = `{$ty_name}` is an interior mutable type
425+
.temporary = each usage of a `const` item creates a new temporary
426+
.never_original = only the temporaries and never the original `const` item `{$const_name}` will be modified
427+
.suggestion_inline_const = for use as an initializer, consider using an inline-const `const {"{"} /* ... */ {"}"}` at the usage site instead
428+
.suggestion_static = for a shared instance of `{$const_name}`, consider using a `static` item instead
429+
.suggestion_allow = alternatively consider allowing the lint
430+
423431
lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly
424432
.label = use a different label that doesn't start with `0` or `1`
425433
.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
@@ -1809,6 +1809,55 @@ pub(crate) enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
18091809
},
18101810
}
18111811

1812+
#[derive(LintDiagnostic)]
1813+
#[diag(lint_interior_mutable_consts)]
1814+
#[note(lint_temporary)]
1815+
#[note(lint_never_original)]
1816+
#[help(lint_suggestion_inline_const)]
1817+
pub(crate) struct InteriorMutableConstsDiag {
1818+
pub ty_name: Ident,
1819+
pub const_name: Ident,
1820+
#[label]
1821+
pub ty_span: Span,
1822+
#[subdiagnostic]
1823+
pub sugg_static: InteriorMutableConstsSuggestionStatic,
1824+
#[subdiagnostic]
1825+
pub sugg_allow: InteriorMutableConstsSuggestionAllow,
1826+
}
1827+
1828+
#[derive(Subdiagnostic)]
1829+
pub(crate) enum InteriorMutableConstsSuggestionStatic {
1830+
#[suggestion(
1831+
lint_suggestion_static,
1832+
code = "{before}static ",
1833+
style = "verbose",
1834+
applicability = "maybe-incorrect"
1835+
)]
1836+
Spanful {
1837+
#[primary_span]
1838+
const_: Span,
1839+
before: &'static str,
1840+
},
1841+
#[help(lint_suggestion_static)]
1842+
Spanless,
1843+
}
1844+
1845+
#[derive(Subdiagnostic)]
1846+
pub(crate) enum InteriorMutableConstsSuggestionAllow {
1847+
#[suggestion(
1848+
lint_suggestion_allow,
1849+
code = "#[allow(interior_mutable_consts)] ",
1850+
style = "verbose",
1851+
applicability = "maybe-incorrect"
1852+
)]
1853+
Spanful {
1854+
#[primary_span]
1855+
span: Span,
1856+
},
1857+
#[help(lint_suggestion_allow)]
1858+
Spanless,
1859+
}
1860+
18121861
pub(crate) struct ImproperCTypes<'a> {
18131862
pub ty: Ty<'a>,
18141863
pub desc: &'a str,

compiler/rustc_lint/src/types.rs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ mod improper_ctypes;
2222
use crate::lints::{
2323
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
2424
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
25-
AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
26-
InvalidNanComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag,
25+
AtomicOrderingStore, ImproperCTypes, InteriorMutableConstsDiag,
26+
InteriorMutableConstsSuggestionAllow, InteriorMutableConstsSuggestionStatic,
27+
InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
28+
UnusedComparisons, VariantSizeDifferencesDiag,
2729
};
2830
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
2931

@@ -166,6 +168,47 @@ declare_lint! {
166168
"detects ambiguous wide pointer comparisons"
167169
}
168170

171+
declare_lint! {
172+
/// The `interior_mutable_consts` lint detects instance where
173+
/// const-items have a interior mutable type, which silently does nothing.
174+
///
175+
/// ### Example
176+
///
177+
/// ```rust
178+
/// use std::sync::Once;
179+
///
180+
/// // SAFETY: should only be call once
181+
/// unsafe extern "C" fn ffi_init() { /* ... */ }
182+
///
183+
/// const A: Once = Once::new(); // using `B` will always creates temporaries and
184+
/// // never modify it-self on use, should be a
185+
/// // static-item instead
186+
///
187+
/// fn init() {
188+
/// A.call_once(|| unsafe {
189+
/// ffi_init(); // unsound, as the `call_once` is on a temporary
190+
/// // and not on a shared variable
191+
/// })
192+
/// }
193+
/// ```
194+
///
195+
/// {{produces}}
196+
///
197+
/// ### Explanation
198+
///
199+
/// Using a const-item with an interior mutable type has no effect as const-item
200+
/// are essentially inlined wherever they are used, meaning that they are copied
201+
/// directly into the relevant context when used rendering modification through
202+
/// interior mutability ineffective across usage of that const-item.
203+
///
204+
/// The current implementation of this lint only warns on significant `std` and
205+
/// `core` interior mutable types, like `Once`, `AtomicI32`, ... this is done out
206+
/// of prudence and may be extended in the future.
207+
INTERIOR_MUTABLE_CONSTS,
208+
Warn,
209+
"detects const items with interior mutable type",
210+
}
211+
169212
#[derive(Copy, Clone, Default)]
170213
pub(crate) struct TypeLimits {
171214
/// Id of the last visited negated expression
@@ -178,7 +221,8 @@ impl_lint_pass!(TypeLimits => [
178221
UNUSED_COMPARISONS,
179222
OVERFLOWING_LITERALS,
180223
INVALID_NAN_COMPARISONS,
181-
AMBIGUOUS_WIDE_POINTER_COMPARISONS
224+
AMBIGUOUS_WIDE_POINTER_COMPARISONS,
225+
INTERIOR_MUTABLE_CONSTS,
182226
]);
183227

184228
impl TypeLimits {
@@ -515,6 +559,44 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
515559
})
516560
}
517561
}
562+
563+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
564+
if let hir::ItemKind::Const(ty, _generics, _body_id) = item.kind
565+
&& let hir::TyKind::Path(ref qpath) = ty.kind
566+
&& let Some(def_id) = cx.qpath_res(qpath, ty.hir_id).opt_def_id()
567+
&& cx.tcx.has_attr(def_id, sym::rustc_significant_interior_mutable_type)
568+
&& let Some(ty_name) = cx.tcx.opt_item_ident(def_id)
569+
{
570+
let (sugg_static, sugg_allow) = if let Some(vis_span) =
571+
item.vis_span.find_ancestor_inside(item.span)
572+
&& item.span.can_be_used_for_suggestions()
573+
&& vis_span.can_be_used_for_suggestions()
574+
{
575+
(
576+
InteriorMutableConstsSuggestionStatic::Spanful {
577+
const_: item.vis_span.between(item.ident.span),
578+
before: if !vis_span.is_empty() { " " } else { "" },
579+
},
580+
InteriorMutableConstsSuggestionAllow::Spanful {
581+
span: item.span.shrink_to_lo(),
582+
},
583+
)
584+
} else {
585+
(
586+
InteriorMutableConstsSuggestionStatic::Spanless,
587+
InteriorMutableConstsSuggestionAllow::Spanless,
588+
)
589+
};
590+
591+
cx.emit_span_lint(INTERIOR_MUTABLE_CONSTS, item.span, InteriorMutableConstsDiag {
592+
ty_name,
593+
ty_span: ty.span,
594+
const_name: item.ident,
595+
sugg_static,
596+
sugg_allow,
597+
});
598+
}
599+
}
518600
}
519601

520602
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: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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 | static A: Cell<i32> = Cell::new(0);
16+
| ~~~~~~
17+
help: alternatively consider allowing the lint
18+
|
19+
LL | #[allow(interior_mutable_consts)] const A: Cell<i32> = Cell::new(0);
20+
| +++++++++++++++++++++++++++++++++
21+
22+
warning: interior mutability in `const` item have no effect to the `const` item itself
23+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:7:1
24+
|
25+
LL | const B: RefCell<i32> = RefCell::new(0);
26+
| ^^^^^^^^^------------^^^^^^^^^^^^^^^^^^^
27+
| |
28+
| `RefCell` is an interior mutable type
29+
|
30+
= note: each usage of a `const` item creates a new temporary
31+
= note: only the temporaries and never the original `const` item `B` will be modified
32+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
33+
help: for a shared instance of `B`, consider using a `static` item instead
34+
|
35+
LL | static B: RefCell<i32> = RefCell::new(0);
36+
| ~~~~~~
37+
help: alternatively consider allowing the lint
38+
|
39+
LL | #[allow(interior_mutable_consts)] const B: RefCell<i32> = RefCell::new(0);
40+
| +++++++++++++++++++++++++++++++++
41+
42+
warning: interior mutability in `const` item have no effect to the `const` item itself
43+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:9:1
44+
|
45+
LL | const C: UnsafeCell<i32> = UnsafeCell::new(0);
46+
| ^^^^^^^^^---------------^^^^^^^^^^^^^^^^^^^^^^
47+
| |
48+
| `UnsafeCell` is an interior mutable type
49+
|
50+
= note: each usage of a `const` item creates a new temporary
51+
= note: only the temporaries and never the original `const` item `C` will be modified
52+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
53+
help: for a shared instance of `C`, consider using a `static` item instead
54+
|
55+
LL | static C: UnsafeCell<i32> = UnsafeCell::new(0);
56+
| ~~~~~~
57+
help: alternatively consider allowing the lint
58+
|
59+
LL | #[allow(interior_mutable_consts)] const C: UnsafeCell<i32> = UnsafeCell::new(0);
60+
| +++++++++++++++++++++++++++++++++
61+
62+
warning: interior mutability in `const` item have no effect to the `const` item itself
63+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:11:1
64+
|
65+
LL | const D: LazyCell<i32> = LazyCell::new(|| 0);
66+
| ^^^^^^^^^-------------^^^^^^^^^^^^^^^^^^^^^^^
67+
| |
68+
| `LazyCell` is an interior mutable type
69+
|
70+
= note: each usage of a `const` item creates a new temporary
71+
= note: only the temporaries and never the original `const` item `D` will be modified
72+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
73+
help: for a shared instance of `D`, consider using a `static` item instead
74+
|
75+
LL | static D: LazyCell<i32> = LazyCell::new(|| 0);
76+
| ~~~~~~
77+
help: alternatively consider allowing the lint
78+
|
79+
LL | #[allow(interior_mutable_consts)] const D: LazyCell<i32> = LazyCell::new(|| 0);
80+
| +++++++++++++++++++++++++++++++++
81+
82+
warning: interior mutability in `const` item have no effect to the `const` item itself
83+
--> $DIR/interior-mutable-types-in-consts-not-sync.rs:13:1
84+
|
85+
LL | const E: OnceCell<i32> = OnceCell::new();
86+
| ^^^^^^^^^-------------^^^^^^^^^^^^^^^^^^^
87+
| |
88+
| `OnceCell` is an interior mutable type
89+
|
90+
= note: each usage of a `const` item creates a new temporary
91+
= note: only the temporaries and never the original `const` item `E` will be modified
92+
= help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
93+
help: for a shared instance of `E`, consider using a `static` item instead
94+
|
95+
LL | static E: OnceCell<i32> = OnceCell::new();
96+
| ~~~~~~
97+
help: alternatively consider allowing the lint
98+
|
99+
LL | #[allow(interior_mutable_consts)] const E: OnceCell<i32> = OnceCell::new();
100+
| +++++++++++++++++++++++++++++++++
101+
102+
warning: 5 warnings emitted
103+
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)