Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify invalid_reference_casting lint around interior mutable types #116421

Merged
merged 1 commit into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ lint_invalid_reference_casting_borrow_as_mut = casting `&T` to `&mut T` is undef

lint_invalid_reference_casting_note_book = for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

lint_invalid_reference_casting_note_ty_has_interior_mutability = even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`

lint_lintpass_by_hand = implementing `LintPass` by hand
.help = try using `declare_lint_pass!` or `impl_lint_pass!` instead

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,12 +771,16 @@ pub enum InvalidReferenceCastingDiag {
BorrowAsMut {
#[label]
orig_cast: Option<Span>,
#[note(lint_invalid_reference_casting_note_ty_has_interior_mutability)]
ty_has_interior_mutability: Option<()>,
},
#[diag(lint_invalid_reference_casting_assign_to_ref)]
#[note(lint_invalid_reference_casting_note_book)]
AssignToRef {
#[label]
orig_cast: Option<Span>,
#[note(lint_invalid_reference_casting_note_ty_has_interior_mutability)]
ty_has_interior_mutability: Option<()>,
},
}

Expand Down
26 changes: 15 additions & 11 deletions compiler/rustc_lint/src/reference_casting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,19 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {

let init = cx.expr_or_init(e);

let orig_cast = if is_cast_from_const_to_mut(cx, init) {
if init.span != e.span { Some(init.span) } else { None }
} else {
let Some(ty_has_interior_mutability) = is_cast_from_const_to_mut(cx, init) else {
return;
};
let orig_cast = if init.span != e.span { Some(init.span) } else { None };
let ty_has_interior_mutability = ty_has_interior_mutability.then_some(());

cx.emit_spanned_lint(
INVALID_REFERENCE_CASTING,
expr.span,
if is_assignment {
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
InvalidReferenceCastingDiag::AssignToRef { orig_cast, ty_has_interior_mutability }
} else {
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast, ty_has_interior_mutability }
},
);
}
Expand Down Expand Up @@ -104,15 +104,18 @@ fn is_operation_we_care_about<'tcx>(
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
}

fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, orig_expr: &'tcx Expr<'tcx>) -> bool {
fn is_cast_from_const_to_mut<'tcx>(
cx: &LateContext<'tcx>,
orig_expr: &'tcx Expr<'tcx>,
) -> Option<bool> {
let mut need_check_freeze = false;
let mut e = orig_expr;

let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);

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

loop {
Expand Down Expand Up @@ -155,10 +158,11 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, orig_expr: &'tcx Expr
//
// We also consider non concrete skeleton types (ie generics)
// to be an issue since there is no way to make it safe for abitrary types.
!need_check_freeze
|| inner_ty.is_freeze(cx.tcx, cx.param_env)
|| !inner_ty.has_concrete_skeleton()
let inner_ty_has_interior_mutability =
!inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton();
(!need_check_freeze || !inner_ty_has_interior_mutability)
.then_some(inner_ty_has_interior_mutability)
} else {
false
None
}
}
8 changes: 8 additions & 0 deletions tests/ui/lint/reference_casting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ unsafe fn ref_to_mut() {
let _num = &mut *num;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior

let cell = &std::cell::UnsafeCell::new(0);
let _num = &mut *(cell as *const _ as *mut i32);
//~^ ERROR casting `&T` to `&mut T` is undefined behavior

unsafe fn generic_ref_cast_mut<T>(this: &T) -> &mut T {
&mut *((this as *const _) as *mut _)
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
Expand Down Expand Up @@ -106,6 +110,8 @@ unsafe fn assign_to_ref() {
std::mem::transmute::<*const i32, *mut i32>(num),
-1i32,
);
*((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5;
//~^ ERROR assigning to `&T` is undefined behavior

let value = num as *const i32 as *mut i32;
*value = 1;
Expand Down Expand Up @@ -148,6 +154,8 @@ unsafe fn no_warn() {
*RAW_PTR = 42; // RAW_PTR is defined outside the function body,
// make sure we don't ICE on it when trying to
// determine if we should lint on it or not.
let cell = &std::cell::UnsafeCell::new(0);
let _num = &mut *(cell.get() as *mut i32);

fn safe_as_mut<T>(x: &std::cell::UnsafeCell<T>) -> &mut T {
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
Expand Down
60 changes: 39 additions & 21 deletions tests/ui/lint/reference_casting.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -158,95 +158,104 @@ LL | let _num = &mut *num;
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:68:9
--> $DIR/reference_casting.rs:68:16
|
LL | let _num = &mut *(cell as *const _ as *mut i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
= note: even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`

error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:72:9
|
LL | &mut *((this as *const _) as *mut _)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:73:18
--> $DIR/reference_casting.rs:77:18
|
LL | unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:78:18
--> $DIR/reference_casting.rs:82:18
|
LL | unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:88:5
--> $DIR/reference_casting.rs:92:5
|
LL | *(a as *const _ as *mut _) = String::from("Replaced");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:90:5
--> $DIR/reference_casting.rs:94:5
|
LL | *(a as *const _ as *mut String) += " world";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:92:5
--> $DIR/reference_casting.rs:96:5
|
LL | *std::ptr::from_ref(num).cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:94:5
--> $DIR/reference_casting.rs:98:5
|
LL | *std::ptr::from_ref({ num }).cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:96:5
--> $DIR/reference_casting.rs:100:5
|
LL | *{ std::ptr::from_ref(num) }.cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:98:5
--> $DIR/reference_casting.rs:102:5
|
LL | *(std::ptr::from_ref({ num }) as *mut i32) += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:100:5
--> $DIR/reference_casting.rs:104:5
|
LL | *std::mem::transmute::<_, *mut i32>(num) += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:102:5
--> $DIR/reference_casting.rs:106:5
|
LL | *(std::mem::transmute::<_, *mut i32>(num) as *mut i32) += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:104:5
--> $DIR/reference_casting.rs:108:5
|
LL | / std::ptr::write(
LL | |
Expand All @@ -258,7 +267,16 @@ LL | | );
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:111:5
--> $DIR/reference_casting.rs:113:5
|
LL | *((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
= note: even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:117:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
Expand All @@ -268,7 +286,7 @@ LL | *value = 1;
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:114:5
--> $DIR/reference_casting.rs:120:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
Expand All @@ -279,23 +297,23 @@ LL | *value_rebind = 1;
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>

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

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

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

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

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

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

error: aborting due to 38 previous errors
error: aborting due to 40 previous errors

Loading