Skip to content

Commit 1b03eec

Browse files
authored
Rollup merge of #159960 - Urgau:invalid_ref-safe-interior-mut, r=mejrs
Allow `UnsafeCell` content access without `get` in `invalid_reference_casting` lint #159730 relaxed the rules so that it's no longer necessary to use `UnsafeCell::raw_get` or `UnsafeCell::get` to access the content of an unsafe cell. As a consequence this means that code this like is no longer invalid: ```rust use std::cell::UnsafeCell; unsafe fn get_mut_unchecked<T>(ptr: &UnsafeCell<T>) -> &mut T { let t = ptr as *const UnsafeCell<T> as *mut T; unsafe { &mut *t } } ``` Fixes #159915 cc @RalfJung
2 parents f8ee207 + 6b0ea30 commit 1b03eec

8 files changed

Lines changed: 100 additions & 121 deletions

File tree

compiler/rustc_lint/src/dangling.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ fn lint_addr_of_local<'a>(
179179
expr: &'a Expr<'a>,
180180
) {
181181
// peel casts as they do not interest us here, we want the inner expression.
182-
let (inner, _) = super::utils::peel_casts(cx, expr);
182+
let inner = super::utils::peel_casts(cx, expr);
183183

184184
if let ExprKind::AddrOf(_, _, inner_of) = inner.kind
185185
&& let ExprKind::Path(ref qpath) = inner_of.peel_blocks().kind

compiler/rustc_lint/src/lints.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -993,10 +993,6 @@ pub(crate) enum InvalidReferenceCastingDiag<'tcx> {
993993
BorrowAsMut {
994994
#[label("casting happened here")]
995995
orig_cast: Option<Span>,
996-
#[note(
997-
"even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`"
998-
)]
999-
ty_has_interior_mutability: bool,
1000996
},
1001997
#[diag("assigning to `&T` is undefined behavior, consider using an `UnsafeCell`")]
1002998
#[note(
@@ -1005,10 +1001,6 @@ pub(crate) enum InvalidReferenceCastingDiag<'tcx> {
10051001
AssignToRef {
10061002
#[label("casting happened here")]
10071003
orig_cast: Option<Span>,
1008-
#[note(
1009-
"even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`"
1010-
)]
1011-
ty_has_interior_mutability: bool,
10121004
},
10131005
#[diag(
10141006
"casting references to a bigger memory layout than the backing allocation is undefined behavior, even if the reference is unused"

compiler/rustc_lint/src/ptr_nulls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ fn useless_check<'a, 'tcx: 'a>(
112112

113113
/// Checks if the given expression is a null pointer (modulo casting)
114114
fn is_null_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Span> {
115-
let (expr, _) = peel_casts(cx, expr);
115+
let expr = peel_casts(cx, expr);
116116

117117
if let ExprKind::Call(path, []) = expr.kind
118118
&& let ExprKind::Path(ref qpath) = path.kind

compiler/rustc_lint/src/reference_casting.rs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
5252
};
5353

5454
if matches!(pat, PatternKind::Borrow { mutbl: Mutability::Mut } | PatternKind::Assign)
55-
&& let Some(ty_has_interior_mutability) =
56-
is_cast_from_ref_to_mut_ptr(cx, init, &mut peel_casts)
55+
&& is_invalid_cast_from_ref_to_mut_ptr(cx, init, &mut peel_casts)
5756
{
5857
cx.emit_span_lint(
5958
INVALID_REFERENCE_CASTING,
6059
expr.span,
6160
if pat == PatternKind::Assign {
62-
InvalidReferenceCastingDiag::AssignToRef {
63-
orig_cast,
64-
ty_has_interior_mutability,
65-
}
61+
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
6662
} else {
67-
InvalidReferenceCastingDiag::BorrowAsMut {
68-
orig_cast,
69-
ty_has_interior_mutability,
70-
}
63+
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
7164
},
7265
);
7366
}
@@ -146,49 +139,51 @@ fn borrow_or_assign<'tcx>(
146139
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
147140
}
148141

149-
fn is_cast_from_ref_to_mut_ptr<'tcx>(
142+
fn is_invalid_cast_from_ref_to_mut_ptr<'tcx>(
150143
cx: &LateContext<'tcx>,
151144
orig_expr: &'tcx Expr<'tcx>,
152-
mut peel_casts: impl FnMut() -> (&'tcx Expr<'tcx>, bool),
153-
) -> Option<bool> {
145+
mut peel_casts: impl FnMut() -> &'tcx Expr<'tcx>,
146+
) -> bool {
154147
let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);
155148

156149
// Bail out early if the end type is **not** a mutable pointer.
157150
if !matches!(end_ty.kind(), ty::RawPtr(_, Mutability::Mut)) {
158-
return None;
151+
return false;
159152
}
160153

161-
let (e, need_check_freeze) = peel_casts();
162-
154+
let e = peel_casts();
163155
let start_ty = cx.typeck_results().node_type(e.hir_id);
156+
164157
if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
165-
// If an UnsafeCell method is involved, we need to additionally check the
166-
// inner type for the presence of the Freeze trait (ie does NOT contain
167-
// an UnsafeCell), since in that case we would incorrectly lint on valid casts.
158+
// We need to additionally check the inner type for the presence of the Freeze trait
159+
// (ie does NOT contain an UnsafeCell), since in that case we would incorrectly lint
160+
// on valid casts (see https://github.com/rust-lang/unsafe-code-guidelines/issues/281).
168161
//
169162
// Except on the presence of non concrete skeleton types (ie generics)
170163
// since there is no way to make it safe for arbitrary types.
164+
//
165+
// However this does mean we miss out on some cases where the user doesn't go
166+
// through an UnsafeCell but there's an UnsafeCell somewhere else in the type.
171167
let inner_ty_has_interior_mutability =
172168
!inner_ty.is_freeze(cx.tcx, cx.typing_env()) && inner_ty.has_concrete_skeleton();
173-
(!need_check_freeze || !inner_ty_has_interior_mutability)
174-
.then_some(inner_ty_has_interior_mutability)
169+
!inner_ty_has_interior_mutability
175170
} else {
176-
None
171+
false
177172
}
178173
}
179174

180175
fn is_cast_to_bigger_memory_layout<'tcx>(
181176
cx: &LateContext<'tcx>,
182177
orig_expr: &'tcx Expr<'tcx>,
183-
mut peel_casts: impl FnMut() -> (&'tcx Expr<'tcx>, bool),
178+
mut peel_casts: impl FnMut() -> &'tcx Expr<'tcx>,
184179
) -> Option<(TyAndLayout<'tcx>, TyAndLayout<'tcx>, Expr<'tcx>)> {
185180
let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);
186181

187182
let ty::RawPtr(inner_end_ty, _) = end_ty.kind() else {
188183
return None;
189184
};
190185

191-
let (e, _) = peel_casts();
186+
let e = peel_casts();
192187
let start_ty = cx.typeck_results().node_type(e.hir_id);
193188

194189
let ty::Ref(_, inner_start_ty, _) = start_ty.kind() else {

compiler/rustc_lint/src/utils.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,11 @@ use crate::LateContext;
66
/// Given an expression, peel all of casts (`<expr> as ...`, `<expr>.cast{,_mut,_const}()`,
77
/// `ptr::from_ref(<expr>)`, ...) and init expressions.
88
///
9-
/// Returns the innermost expression and a boolean representing if one of the casts was
10-
/// `UnsafeCell::raw_get(<expr>)`
9+
/// Returns the innermost expression.
1110
pub(crate) fn peel_casts<'tcx>(
1211
cx: &LateContext<'tcx>,
1312
mut e: &'tcx Expr<'tcx>,
14-
) -> (&'tcx Expr<'tcx>, bool) {
15-
let mut gone_trough_unsafe_cell_raw_get = false;
16-
13+
) -> &'tcx Expr<'tcx> {
1714
loop {
1815
e = e.peel_blocks();
1916
// <expr> as ...
@@ -37,9 +34,6 @@ pub(crate) fn peel_casts<'tcx>(
3734
Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get | sym::transmute)
3835
)
3936
{
40-
if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
41-
gone_trough_unsafe_cell_raw_get = true;
42-
}
4337
arg
4438
} else {
4539
let init = cx.expr_or_init(e);
@@ -51,5 +45,5 @@ pub(crate) fn peel_casts<'tcx>(
5145
};
5246
}
5347

54-
(e, gone_trough_unsafe_cell_raw_get)
48+
e
5549
}

library/core/src/cell.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2236,7 +2236,6 @@ impl<T: ?Sized + fmt::Display> fmt::Display for RefMut<'_, T> {
22362236
/// /// # Safety
22372237
/// /// The caller must not call `get_mut_unchecked` again (on any alias of `ptr`) for the duration
22382238
/// /// of the lifetime of the returned reference.
2239-
/// # #[allow(invalid_reference_casting)] // FIXME should the lint really fire here?
22402239
/// unsafe fn get_mut_unchecked<T>(ptr: &UnsafeCell<T>) -> &mut T {
22412240
/// let t = ptr as *const UnsafeCell<T> as *mut T;
22422241
/// unsafe { &mut *t }

tests/ui/lint/reference_casting.rs

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ unsafe fn ref_to_mut() {
3737
let _num = &mut *(std::mem::transmute::<_, *mut i32>(num) as *mut i32);
3838
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
3939
let _num = &mut *std::cell::UnsafeCell::raw_get(
40-
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
41-
num as *const i32 as *const std::cell::UnsafeCell<i32>
40+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
41+
num as *const i32 as *const std::cell::UnsafeCell<i32>,
4242
);
4343

4444
let deferred = num as *const i32 as *mut i32;
@@ -62,10 +62,6 @@ unsafe fn ref_to_mut() {
6262
let _num = &mut *num;
6363
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
6464

65-
let cell = &std::cell::UnsafeCell::new(0);
66-
let _num = &mut *(cell as *const _ as *mut i32);
67-
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
68-
6965
unsafe fn generic_ref_cast_mut<T>(this: &T) -> &mut T {
7066
&mut *((this as *const _) as *mut _)
7167
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
@@ -104,12 +100,10 @@ unsafe fn assign_to_ref() {
104100
*(std::mem::transmute::<_, *mut i32>(num) as *mut i32) += 1;
105101
//~^ ERROR assigning to `&T` is undefined behavior
106102
std::ptr::write(
107-
//~^ ERROR assigning to `&T` is undefined behavior
103+
//~^ ERROR assigning to `&T` is undefined behavior
108104
std::mem::transmute::<*const i32, *mut i32>(num),
109105
-1i32,
110106
);
111-
*((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5;
112-
//~^ ERROR assigning to `&T` is undefined behavior
113107

114108
let value = num as *const i32 as *mut i32;
115109
*value = 1;
@@ -207,14 +201,16 @@ unsafe fn bigger_layout() {
207201
}
208202

209203
{
210-
let mut l: [u8; 2] = [0,1];
204+
let mut l: [u8; 2] = [0, 1];
211205
let w: *mut [u16; 2] = &mut l as *mut [u8; 2] as *mut _;
212-
let w: *mut [u16] = unsafe {&mut *w};
206+
let w: *mut [u16] = unsafe { &mut *w };
213207
//~^ ERROR casting references to a bigger memory layout
214208
}
215209

216210
{
217-
fn foo() -> [i32; 1] { todo!() }
211+
fn foo() -> [i32; 1] {
212+
todo!()
213+
}
218214

219215
let num = foo();
220216
let _num = &*(&num as *const i32 as *const i64);
@@ -224,15 +220,19 @@ unsafe fn bigger_layout() {
224220
}
225221

226222
{
227-
fn bar(_a: &[i32; 2]) -> &[i32; 1] { todo!() }
223+
fn bar(_a: &[i32; 2]) -> &[i32; 1] {
224+
todo!()
225+
}
228226

229227
let num = bar(&[0, 0]);
230228
let _num = &*(num as *const i32 as *const i64);
231229
let _num = &*(bar(&[0, 0]) as *const i32 as *const i64);
232230
}
233231

234232
{
235-
fn foi<T>() -> T { todo!() }
233+
fn foi<T>() -> T {
234+
todo!()
235+
}
236236

237237
let num = foi::<i32>();
238238
let _num = &*(&num as *const i32 as *const i64);
@@ -286,24 +286,41 @@ unsafe fn no_warn() {
286286
let value: *const i32 = &mut value;
287287
*(value as *const i16 as *mut i16) = 42;
288288
*RAW_PTR = 42; // RAW_PTR is defined outside the function body,
289-
// make sure we don't ICE on it when trying to
290-
// determine if we should lint on it or not.
289+
// make sure we don't ICE on it when trying to
290+
// determine if we should lint on it or not.
291+
*((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5;
292+
291293
let cell = &std::cell::UnsafeCell::new(0);
292294
let _num = &mut *(cell.get() as *mut i32);
295+
let _num = &mut *(cell as *const _ as *mut i32);
293296

294-
fn safe_as_mut<T>(x: &std::cell::UnsafeCell<T>) -> &mut T {
297+
unsafe fn get_mut_unchecked<T>(x: &std::cell::UnsafeCell<T>) -> &mut T {
295298
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
296299
}
297300

298-
fn cell_as_mut(x: &std::cell::Cell<i32>) -> &mut i32 {
301+
unsafe fn get_mut_unchecked2<T>(ptr: &std::cell::UnsafeCell<T>) -> &mut T {
302+
let t = ptr as *const std::cell::UnsafeCell<T> as *mut T;
303+
unsafe { &mut *t }
304+
}
305+
306+
unsafe fn cell_as_mut(x: &std::cell::Cell<i32>) -> &mut i32 {
299307
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
300308
}
301309

310+
unsafe fn cell_as_mut2(x: &std::cell::Cell<i32>) -> &mut i32 {
311+
unsafe { &mut *(x as *const std::cell::Cell<i32> as *mut i32) }
312+
}
313+
302314
#[repr(transparent)]
303315
struct DoesContainUnsafeCell(std::cell::UnsafeCell<i32>);
304-
fn safe_as_mut2(x: &DoesContainUnsafeCell) -> &mut DoesContainUnsafeCell {
316+
317+
unsafe fn get_mut_unchecked3(x: &DoesContainUnsafeCell) -> &mut DoesContainUnsafeCell {
305318
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
306319
}
320+
321+
unsafe fn get_mut_unchecked4(x: &DoesContainUnsafeCell) -> &mut DoesContainUnsafeCell {
322+
unsafe { &mut *(x as *const DoesContainUnsafeCell as *mut _) }
323+
}
307324
}
308325

309326
fn main() {}

0 commit comments

Comments
 (0)