Skip to content

Commit 25216b6

Browse files
Rollup merge of #118909 - Urgau:cleanup-improvement-invalid_ref_casting, r=est31
Some cleanup and improvement for invalid ref casting impl This PR makes some cleanups and improvements to the `invalid_reference_casting` implementation in preparation for linting on new patterns, while reusing most of the logic. r? `@est31` (feel free to re-assign)
2 parents d3d0287 + 4839cca commit 25216b6

File tree

3 files changed

+116
-65
lines changed

3 files changed

+116
-65
lines changed

compiler/rustc_lint/src/reference_casting.rs

+81-57
Original file line numberDiff line numberDiff line change
@@ -37,59 +37,73 @@ declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
3737

3838
impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
3939
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
40-
let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
41-
return;
42-
};
43-
44-
let init = cx.expr_or_init(e);
45-
46-
let Some(ty_has_interior_mutability) = is_cast_from_const_to_mut(cx, init) else {
47-
return;
48-
};
49-
let orig_cast = if init.span != e.span { Some(init.span) } else { None };
50-
let ty_has_interior_mutability = ty_has_interior_mutability.then_some(());
51-
52-
cx.emit_spanned_lint(
53-
INVALID_REFERENCE_CASTING,
54-
expr.span,
55-
if is_assignment {
56-
InvalidReferenceCastingDiag::AssignToRef { orig_cast, ty_has_interior_mutability }
57-
} else {
58-
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast, ty_has_interior_mutability }
59-
},
60-
);
40+
if let Some((e, pat)) = borrow_or_assign(cx, expr) {
41+
if matches!(pat, PatternKind::Borrow { mutbl: Mutability::Mut } | PatternKind::Assign) {
42+
let init = cx.expr_or_init(e);
43+
44+
let Some(ty_has_interior_mutability) = is_cast_from_ref_to_mut_ptr(cx, init) else {
45+
return;
46+
};
47+
let orig_cast = if init.span != e.span { Some(init.span) } else { None };
48+
let ty_has_interior_mutability = ty_has_interior_mutability.then_some(());
49+
50+
cx.emit_spanned_lint(
51+
INVALID_REFERENCE_CASTING,
52+
expr.span,
53+
if pat == PatternKind::Assign {
54+
InvalidReferenceCastingDiag::AssignToRef {
55+
orig_cast,
56+
ty_has_interior_mutability,
57+
}
58+
} else {
59+
InvalidReferenceCastingDiag::BorrowAsMut {
60+
orig_cast,
61+
ty_has_interior_mutability,
62+
}
63+
},
64+
);
65+
}
66+
}
6167
}
6268
}
6369

64-
fn is_operation_we_care_about<'tcx>(
70+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71+
enum PatternKind {
72+
Borrow { mutbl: Mutability },
73+
Assign,
74+
}
75+
76+
fn borrow_or_assign<'tcx>(
6577
cx: &LateContext<'tcx>,
6678
e: &'tcx Expr<'tcx>,
67-
) -> Option<(bool, &'tcx Expr<'tcx>)> {
68-
fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> {
69-
// &mut <expr>
70-
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
71-
expr
79+
) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
80+
fn deref_assign_or_addr_of<'tcx>(
81+
expr: &'tcx Expr<'tcx>,
82+
) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
83+
// &(mut) <expr>
84+
let (inner, pat) = if let ExprKind::AddrOf(_, mutbl, expr) = expr.kind {
85+
(expr, PatternKind::Borrow { mutbl })
7286
// <expr> = ...
7387
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
74-
expr
88+
(expr, PatternKind::Assign)
7589
// <expr> += ...
7690
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
77-
expr
91+
(expr, PatternKind::Assign)
7892
} else {
7993
return None;
8094
};
8195

82-
if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind {
83-
Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e))
84-
} else {
85-
None
86-
}
96+
// *<inner>
97+
let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else {
98+
return None;
99+
};
100+
Some((e, pat))
87101
}
88102

89103
fn ptr_write<'tcx>(
90104
cx: &LateContext<'tcx>,
91105
e: &'tcx Expr<'tcx>,
92-
) -> Option<(bool, &'tcx Expr<'tcx>)> {
106+
) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
93107
if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind
94108
&& let ExprKind::Path(ref qpath) = path.kind
95109
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
@@ -98,7 +112,7 @@ fn is_operation_we_care_about<'tcx>(
98112
Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned)
99113
)
100114
{
101-
Some((true, arg_ptr))
115+
Some((arg_ptr, PatternKind::Assign))
102116
} else {
103117
None
104118
}
@@ -107,20 +121,39 @@ fn is_operation_we_care_about<'tcx>(
107121
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
108122
}
109123

110-
fn is_cast_from_const_to_mut<'tcx>(
124+
fn is_cast_from_ref_to_mut_ptr<'tcx>(
111125
cx: &LateContext<'tcx>,
112126
orig_expr: &'tcx Expr<'tcx>,
113127
) -> Option<bool> {
114-
let mut need_check_freeze = false;
115-
let mut e = orig_expr;
116-
117128
let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);
118129

119130
// Bail out early if the end type is **not** a mutable pointer.
120131
if !matches!(end_ty.kind(), ty::RawPtr(TypeAndMut { ty: _, mutbl: Mutability::Mut })) {
121132
return None;
122133
}
123134

135+
let (e, need_check_freeze) = peel_casts(cx, orig_expr);
136+
137+
let start_ty = cx.typeck_results().node_type(e.hir_id);
138+
if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
139+
// If an UnsafeCell method is involved, we need to additionally check the
140+
// inner type for the presence of the Freeze trait (ie does NOT contain
141+
// an UnsafeCell), since in that case we would incorrectly lint on valid casts.
142+
//
143+
// Except on the presence of non concrete skeleton types (ie generics)
144+
// since there is no way to make it safe for arbitrary types.
145+
let inner_ty_has_interior_mutability =
146+
!inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton();
147+
(!need_check_freeze || !inner_ty_has_interior_mutability)
148+
.then_some(inner_ty_has_interior_mutability)
149+
} else {
150+
None
151+
}
152+
}
153+
154+
fn peel_casts<'tcx>(cx: &LateContext<'tcx>, mut e: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
155+
let mut gone_trough_unsafe_cell_raw_get = false;
156+
124157
loop {
125158
e = e.peel_blocks();
126159
// <expr> as ...
@@ -145,27 +178,18 @@ fn is_cast_from_const_to_mut<'tcx>(
145178
)
146179
{
147180
if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
148-
need_check_freeze = true;
181+
gone_trough_unsafe_cell_raw_get = true;
149182
}
150183
arg
151184
} else {
152-
break;
185+
let init = cx.expr_or_init(e);
186+
if init.hir_id != e.hir_id {
187+
init
188+
} else {
189+
break;
190+
}
153191
};
154192
}
155193

156-
let start_ty = cx.typeck_results().node_type(e.hir_id);
157-
if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
158-
// If an UnsafeCell method is involved we need to additionally check the
159-
// inner type for the presence of the Freeze trait (ie does NOT contain
160-
// an UnsafeCell), since in that case we would incorrectly lint on valid casts.
161-
//
162-
// We also consider non concrete skeleton types (ie generics)
163-
// to be an issue since there is no way to make it safe for abitrary types.
164-
let inner_ty_has_interior_mutability =
165-
!inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton();
166-
(!need_check_freeze || !inner_ty_has_interior_mutability)
167-
.then_some(inner_ty_has_interior_mutability)
168-
} else {
169-
None
170-
}
194+
(e, gone_trough_unsafe_cell_raw_get)
171195
}

tests/ui/lint/reference_casting.rs

+7
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ unsafe fn assign_to_ref() {
113113
*((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5;
114114
//~^ ERROR assigning to `&T` is undefined behavior
115115

116+
let value = num as *const i32 as *mut i32;
117+
*value = 1;
118+
//~^ ERROR assigning to `&T` is undefined behavior
119+
let value = num as *const i32;
120+
let value = value as *mut i32;
121+
*value = 1;
122+
//~^ ERROR assigning to `&T` is undefined behavior
116123
let value = num as *const i32 as *mut i32;
117124
*value = 1;
118125
//~^ ERROR assigning to `&T` is undefined behavior

tests/ui/lint/reference_casting.stderr

+28-8
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,27 @@ LL | *value = 1;
286286
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
287287

288288
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
289-
--> $DIR/reference_casting.rs:120:5
289+
--> $DIR/reference_casting.rs:121:5
290+
|
291+
LL | let value = value as *mut i32;
292+
| ----------------- casting happend here
293+
LL | *value = 1;
294+
| ^^^^^^^^^^
295+
|
296+
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
297+
298+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
299+
--> $DIR/reference_casting.rs:124:5
300+
|
301+
LL | let value = num as *const i32 as *mut i32;
302+
| ----------------------------- casting happend here
303+
LL | *value = 1;
304+
| ^^^^^^^^^^
305+
|
306+
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
307+
308+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
309+
--> $DIR/reference_casting.rs:127:5
290310
|
291311
LL | let value = num as *const i32 as *mut i32;
292312
| ----------------------------- casting happend here
@@ -297,23 +317,23 @@ LL | *value_rebind = 1;
297317
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
298318

299319
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
300-
--> $DIR/reference_casting.rs:122:5
320+
--> $DIR/reference_casting.rs:129:5
301321
|
302322
LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
303323
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
304324
|
305325
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
306326

307327
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
308-
--> $DIR/reference_casting.rs:124:5
328+
--> $DIR/reference_casting.rs:131:5
309329
|
310330
LL | *(num as *const _ as usize as *mut i32) = 2;
311331
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
312332
|
313333
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
314334

315335
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
316-
--> $DIR/reference_casting.rs:126:5
336+
--> $DIR/reference_casting.rs:133:5
317337
|
318338
LL | let value = num as *const i32 as *mut i32;
319339
| ----------------------------- casting happend here
@@ -324,7 +344,7 @@ LL | std::ptr::write(value, 2);
324344
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
325345

326346
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
327-
--> $DIR/reference_casting.rs:128:5
347+
--> $DIR/reference_casting.rs:135:5
328348
|
329349
LL | let value = num as *const i32 as *mut i32;
330350
| ----------------------------- casting happend here
@@ -335,7 +355,7 @@ LL | std::ptr::write_unaligned(value, 2);
335355
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
336356

337357
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
338-
--> $DIR/reference_casting.rs:130:5
358+
--> $DIR/reference_casting.rs:137:5
339359
|
340360
LL | let value = num as *const i32 as *mut i32;
341361
| ----------------------------- casting happend here
@@ -346,12 +366,12 @@ LL | std::ptr::write_volatile(value, 2);
346366
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
347367

348368
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
349-
--> $DIR/reference_casting.rs:134:9
369+
--> $DIR/reference_casting.rs:141:9
350370
|
351371
LL | *(this as *const _ as *mut _) = a;
352372
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
353373
|
354374
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
355375

356-
error: aborting due to 40 previous errors
376+
error: aborting due to 42 previous errors
357377

0 commit comments

Comments
 (0)