Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f27dba0

Browse files
committedJul 29, 2024
Lint against &T to &mut T and &T to &UnsafeCell<T> transmutes
This adds the lint against `&T`->`&UnsafeCell<T>` transmutes, and also check in struct fields, and reference casts (`&*(&a as *const u8 as *const UnsafeCell<u8>)`). The code is quite complex; I've tried my best to simplify and comment it. This is missing one parts: array transmutes. When transmuting an array, this only consider the first element. The reason for that is that the code is already quite complex, and I didn't want to complicate it more. This catches the most common pattern of transmuting an array into an array of the same length with type of the same size; more complex cases are likely not properly handled. We could take a bigger sample, for example the first and last elements to increase the chance that the lint will catch mistakes, but then the runtime complexity becomes exponential with the nesting of the arrays (`[[[[[T; 2]; 2]; 2]; 2]; 2]` has complexity of O(2**5), for instance).
1 parent 78c8573 commit f27dba0

33 files changed

+1062
-73
lines changed
 

‎compiler/rustc_lint/messages.ftl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ lint_builtin_missing_doc = missing documentation for {$article} {$desc}
119119
120120
lint_builtin_mutable_transmutes =
121121
transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
122+
.note = transmute from `{$from}` to `{$to}`
123+
124+
lint_unsafe_cell_reference_casting =
125+
casting `&T` to `&UnsafeCell<T>` is undefined behavior, even if the reference is unused, consider using an `UnsafeCell` on the original data
126+
.label = casting happend here
127+
.note = cast from `{$from}` to `{$to}`
122128
123129
lint_builtin_no_mangle_fn = declaration of a `no_mangle` function
124130
lint_builtin_no_mangle_generic = functions generic over types or consts must be mangled
@@ -169,6 +175,10 @@ lint_builtin_unreachable_pub = unreachable `pub` {$what}
169175
170176
lint_builtin_unsafe_block = usage of an `unsafe` block
171177
178+
lint_builtin_unsafe_cell_transmutes =
179+
transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
180+
.note = transmute from `{$from}` to `{$to}`
181+
172182
lint_builtin_unsafe_extern_block = usage of an `unsafe extern` block
173183
174184
lint_builtin_unsafe_impl = implementation of an `unsafe` trait

‎compiler/rustc_lint/src/builtin.rs

Lines changed: 3 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ use crate::{
2929
BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
3030
BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
3131
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
32-
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
33-
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
34-
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
35-
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
32+
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinNoMangleGeneric,
33+
BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds,
34+
BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
3635
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
3736
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
3837
BuiltinWhileTrue, InvalidAsmLabel,
@@ -1103,72 +1102,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
11031102
}
11041103
}
11051104

1106-
declare_lint! {
1107-
/// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut
1108-
/// T` because it is [undefined behavior].
1109-
///
1110-
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
1111-
///
1112-
/// ### Example
1113-
///
1114-
/// ```rust,compile_fail
1115-
/// unsafe {
1116-
/// let y = std::mem::transmute::<&i32, &mut i32>(&5);
1117-
/// }
1118-
/// ```
1119-
///
1120-
/// {{produces}}
1121-
///
1122-
/// ### Explanation
1123-
///
1124-
/// Certain assumptions are made about aliasing of data, and this transmute
1125-
/// violates those assumptions. Consider using [`UnsafeCell`] instead.
1126-
///
1127-
/// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
1128-
MUTABLE_TRANSMUTES,
1129-
Deny,
1130-
"transmuting &T to &mut T is undefined behavior, even if the reference is unused"
1131-
}
1132-
1133-
declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]);
1134-
1135-
impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
1136-
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
1137-
if let Some((&ty::Ref(_, _, from_mutbl), &ty::Ref(_, _, to_mutbl))) =
1138-
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
1139-
{
1140-
if from_mutbl < to_mutbl {
1141-
cx.emit_span_lint(MUTABLE_TRANSMUTES, expr.span, BuiltinMutablesTransmutes);
1142-
}
1143-
}
1144-
1145-
fn get_transmute_from_to<'tcx>(
1146-
cx: &LateContext<'tcx>,
1147-
expr: &hir::Expr<'_>,
1148-
) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
1149-
let def = if let hir::ExprKind::Path(ref qpath) = expr.kind {
1150-
cx.qpath_res(qpath, expr.hir_id)
1151-
} else {
1152-
return None;
1153-
};
1154-
if let Res::Def(DefKind::Fn, did) = def {
1155-
if !def_id_is_transmute(cx, did) {
1156-
return None;
1157-
}
1158-
let sig = cx.typeck_results().node_type(expr.hir_id).fn_sig(cx.tcx);
1159-
let from = sig.inputs().skip_binder()[0];
1160-
let to = sig.output().skip_binder();
1161-
return Some((from, to));
1162-
}
1163-
None
1164-
}
1165-
1166-
fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool {
1167-
cx.tcx.is_intrinsic(def_id, sym::transmute)
1168-
}
1169-
}
1170-
}
1171-
11721105
declare_lint! {
11731106
/// The `unstable_features` lint detects uses of `#![feature]`.
11741107
///
@@ -1615,7 +1548,6 @@ declare_lint_pass!(
16151548
UNUSED_DOC_COMMENTS,
16161549
NO_MANGLE_CONST_ITEMS,
16171550
NO_MANGLE_GENERIC_ITEMS,
1618-
MUTABLE_TRANSMUTES,
16191551
UNSTABLE_FEATURES,
16201552
UNREACHABLE_PUB,
16211553
TYPE_ALIAS_BOUNDS,

‎compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ mod macro_expr_fragment_specifier_2024_migration;
6565
mod map_unit_fn;
6666
mod methods;
6767
mod multiple_supertrait_upcastable;
68+
mod mutable_transmutes;
6869
mod non_ascii_idents;
6970
mod non_fmt_panic;
7071
mod non_local_def;
@@ -105,6 +106,7 @@ use macro_expr_fragment_specifier_2024_migration::*;
105106
use map_unit_fn::*;
106107
use methods::*;
107108
use multiple_supertrait_upcastable::*;
109+
use mutable_transmutes::*;
108110
use non_ascii_idents::*;
109111
use non_fmt_panic::NonPanicFmt;
110112
use non_local_def::*;
@@ -209,6 +211,7 @@ late_lint_methods!(
209211
// Depends on referenced function signatures in expressions
210212
PtrNullChecks: PtrNullChecks,
211213
MutableTransmutes: MutableTransmutes,
214+
UnsafeCellReferenceCasting: UnsafeCellReferenceCasting,
212215
TypeAliasBounds: TypeAliasBounds,
213216
TrivialConstraints: TrivialConstraints,
214217
TypeLimits: TypeLimits::new(),

‎compiler/rustc_lint/src/lints.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,34 @@ pub struct BuiltinConstNoMangle {
225225
pub suggestion: Span,
226226
}
227227

228+
// mutable_transmutes.rs
228229
#[derive(LintDiagnostic)]
229230
#[diag(lint_builtin_mutable_transmutes)]
230-
pub struct BuiltinMutablesTransmutes;
231+
#[note]
232+
pub struct BuiltinMutablesTransmutes {
233+
pub from: String,
234+
pub to: String,
235+
}
236+
237+
// mutable_transmutes.rs
238+
#[derive(LintDiagnostic)]
239+
#[diag(lint_builtin_unsafe_cell_transmutes)]
240+
#[note]
241+
pub struct BuiltinUnsafeCellTransmutes {
242+
pub from: String,
243+
pub to: String,
244+
}
245+
246+
// mutable_transmutes.rs
247+
#[derive(LintDiagnostic)]
248+
#[diag(lint_unsafe_cell_reference_casting)]
249+
#[note]
250+
pub struct UnsafeCellReferenceCastingDiag {
251+
#[label]
252+
pub orig_cast: Option<Span>,
253+
pub from: String,
254+
pub to: String,
255+
}
231256

232257
#[derive(LintDiagnostic)]
233258
#[diag(lint_builtin_unstable_features)]

‎compiler/rustc_lint/src/mutable_transmutes.rs

Lines changed: 715 additions & 0 deletions
Large diffs are not rendered by default.

‎src/tools/miri/tests/fail/concurrency/read_only_atomic_cmpxchg.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Should not rely on the aliasing model for its failure.
22
//@compile-flags: -Zmiri-disable-stacked-borrows
3+
#![allow(unsafe_cell_reference_casting)]
34

45
use std::sync::atomic::{AtomicI32, Ordering};
56

‎src/tools/miri/tests/fail/concurrency/read_only_atomic_load_acquire.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Should not rely on the aliasing model for its failure.
22
//@compile-flags: -Zmiri-disable-stacked-borrows
3+
#![allow(unsafe_cell_reference_casting)]
34

45
use std::sync::atomic::{AtomicI32, Ordering};
56

‎src/tools/miri/tests/pass/atomic-readonly-load.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Stacked Borrows doesn't like this.
22
//@compile-flags: -Zmiri-tree-borrows
3+
#![allow(unsafe_cell_reference_casting)]
34

45
use std::sync::atomic::*;
56

‎src/tools/miri/tests/pass/tree_borrows/transmute-unsafecell.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@compile-flags: -Zmiri-tree-borrows
2+
#![allow(unsafe_cell_transmutes)]
23

34
//! Testing `mem::transmute` between types with and without interior mutability.
45
//! All transmutations should work, as long as we don't do any actual accesses

‎tests/ui/lint/force-warn/allowed-cli-deny-by-default-lint.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ warning: transmuting &T to &mut T is undefined behavior, even if the reference i
44
LL | let y = std::mem::transmute::<&i32, &mut i32>(&5);
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7+
= note: transmute from `&i32` to `&mut i32`
78
= note: requested on the command line with `--force-warn mutable-transmutes`
89

910
warning: 1 warning emitted

‎tests/ui/lint/force-warn/allowed-deny-by-default-lint.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ warning: transmuting &T to &mut T is undefined behavior, even if the reference i
44
LL | let y = std::mem::transmute::<&i32, &mut i32>(&5);
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7+
= note: transmute from `&i32` to `&mut i32`
78
= note: requested on the command line with `--force-warn mutable-transmutes`
89

910
warning: 1 warning emitted

‎tests/ui/lint/force-warn/deny-by-default-lint.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ warning: transmuting &T to &mut T is undefined behavior, even if the reference i
44
LL | let y = std::mem::transmute::<&i32, &mut i32>(&5);
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7+
= note: transmute from `&i32` to `&mut i32`
78
= note: requested on the command line with `--force-warn mutable-transmutes`
89

910
warning: 1 warning emitted
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//@ check-pass
2+
3+
use std::mem::transmute;
4+
5+
fn main() {
6+
let _a: &mut u8 = unsafe { transmute(&mut 0u8) };
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ check-pass
2+
3+
use std::cell::UnsafeCell;
4+
use std::mem::transmute;
5+
6+
fn main() {
7+
let _a: &mut UnsafeCell<u8> = unsafe { transmute(&mut 0u8) };
8+
let _a: &UnsafeCell<u8> = unsafe { transmute(&mut 0u8) };
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ check-pass
2+
3+
use std::cell::UnsafeCell;
4+
use std::mem::transmute;
5+
6+
fn main() {
7+
let _a: &UnsafeCell<u8> = unsafe { transmute(&UnsafeCell::new(0u8)) };
8+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use std::mem::transmute;
2+
3+
fn main() {
4+
let _a: [&mut u8; 2] = unsafe { transmute([&1u8; 2]) };
5+
//~^ ERROR transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
6+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
2+
--> $DIR/array.rs:4:37
3+
|
4+
LL | let _a: [&mut u8; 2] = unsafe { transmute([&1u8; 2]) };
5+
| ^^^^^^^^^
6+
|
7+
= note: transmute from `[&u8; 2][0]` to `[&mut u8; 2][0]`
8+
= note: `#[deny(mutable_transmutes)]` on by default
9+
10+
error: aborting due to 1 previous error
11+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::cell::UnsafeCell;
2+
use std::mem::transmute;
3+
4+
#[repr(transparent)]
5+
struct A {
6+
a: B,
7+
}
8+
#[repr(transparent)]
9+
struct B {
10+
b: C,
11+
}
12+
#[repr(transparent)]
13+
struct C {
14+
c: &'static D,
15+
}
16+
#[repr(transparent)]
17+
struct D {
18+
d: UnsafeCell<u8>,
19+
}
20+
21+
#[repr(transparent)]
22+
struct E {
23+
e: F,
24+
}
25+
#[repr(transparent)]
26+
struct F {
27+
f: &'static G,
28+
}
29+
#[repr(transparent)]
30+
struct G {
31+
g: H,
32+
}
33+
#[repr(transparent)]
34+
struct H {
35+
h: u8,
36+
}
37+
38+
fn main() {
39+
let _: A = unsafe { transmute(&1u8) };
40+
//~^ ERROR transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
41+
let _: A = unsafe { transmute(E { e: F { f: &G { g: H { h: 0 } } } }) };
42+
//~^ ERROR transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
43+
let _: &'static UnsafeCell<u8> = unsafe { transmute(E { e: F { f: &G { g: H { h: 0 } } } }) };
44+
//~^ ERROR transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
45+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
2+
--> $DIR/diag_includes_field_names.rs:39:25
3+
|
4+
LL | let _: A = unsafe { transmute(&1u8) };
5+
| ^^^^^^^^^
6+
|
7+
= note: transmute from `*&u8` to `(*A.a.b.c).d`
8+
= note: `#[deny(unsafe_cell_transmutes)]` on by default
9+
10+
error: transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
11+
--> $DIR/diag_includes_field_names.rs:41:25
12+
|
13+
LL | let _: A = unsafe { transmute(E { e: F { f: &G { g: H { h: 0 } } } }) };
14+
| ^^^^^^^^^
15+
|
16+
= note: transmute from `(*E.e.f).g.h` to `(*A.a.b.c).d`
17+
18+
error: transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
19+
--> $DIR/diag_includes_field_names.rs:43:47
20+
|
21+
LL | let _: &'static UnsafeCell<u8> = unsafe { transmute(E { e: F { f: &G { g: H { h: 0 } } } }) };
22+
| ^^^^^^^^^
23+
|
24+
= note: transmute from `(*E.e.f).g.h` to `*&UnsafeCell<u8>`
25+
26+
error: aborting due to 3 previous errors
27+

‎tests/ui/transmute/transmute-imut-to-mut.stderr renamed to ‎tests/ui/lint/mutable_transmutes/imm_to_mut.stderr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
error: transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
2-
--> $DIR/transmute-imut-to-mut.rs:6:32
2+
--> $DIR/imm_to_mut.rs:6:32
33
|
44
LL | let _a: &mut u8 = unsafe { transmute(&1u8) };
55
| ^^^^^^^^^
66
|
7+
= note: transmute from `&u8` to `&mut u8`
78
= note: `#[deny(mutable_transmutes)]` on by default
89

910
error: aborting due to 1 previous error
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use std::cell::UnsafeCell;
2+
use std::mem::transmute;
3+
4+
fn main() {
5+
let _a: &UnsafeCell<u8> = unsafe { transmute(&1u8) };
6+
//~^ ERROR transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
2+
--> $DIR/imm_to_unsafecelll.rs:5:40
3+
|
4+
LL | let _a: &UnsafeCell<u8> = unsafe { transmute(&1u8) };
5+
| ^^^^^^^^^
6+
|
7+
= note: transmute from `*&u8` to `*&UnsafeCell<u8>`
8+
= note: `#[deny(unsafe_cell_transmutes)]` on by default
9+
10+
error: aborting due to 1 previous error
11+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use std::cell::UnsafeCell;
2+
use std::mem::transmute;
3+
4+
#[repr(C)]
5+
struct Foo<T> {
6+
a: u32,
7+
b: Bar<T>,
8+
}
9+
#[repr(C)]
10+
struct Bar<T>(Baz<T>);
11+
#[repr(C)]
12+
struct Baz<T>(T);
13+
14+
#[repr(C)]
15+
struct Other(&'static u8, &'static u8);
16+
17+
fn main() {
18+
let _: Foo<&'static mut u8> = unsafe { transmute(Other(&1u8, &1u8)) };
19+
//~^ ERROR transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
20+
let _: Foo<&'static UnsafeCell<u8>> = unsafe { transmute(Other(&1u8, &1u8)) };
21+
//~^ ERROR transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
2+
--> $DIR/nested_field.rs:18:44
3+
|
4+
LL | let _: Foo<&'static mut u8> = unsafe { transmute(Other(&1u8, &1u8)) };
5+
| ^^^^^^^^^
6+
|
7+
= note: transmute from `Other.1` to `Foo<&mut u8>.b.0.0`
8+
= note: `#[deny(mutable_transmutes)]` on by default
9+
10+
error: transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
11+
--> $DIR/nested_field.rs:20:52
12+
|
13+
LL | let _: Foo<&'static UnsafeCell<u8>> = unsafe { transmute(Other(&1u8, &1u8)) };
14+
| ^^^^^^^^^
15+
|
16+
= note: transmute from `*Other.1` to `*Foo<&UnsafeCell<u8>>.b.0.0`
17+
= note: `#[deny(unsafe_cell_transmutes)]` on by default
18+
19+
error: aborting due to 2 previous errors
20+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This test checks that transmuting part of `&T` to part of `&mut T` errors.
2+
// I don't think this is necessary or common, but this is what the current code does.
3+
4+
use std::cell::UnsafeCell;
5+
use std::mem::transmute;
6+
7+
#[repr(C, packed)]
8+
struct Foo<T>(u8, T);
9+
10+
#[repr(C, packed)]
11+
struct Bar(&'static u8, u8);
12+
13+
fn main() {
14+
let _: Foo<&'static mut u8> = unsafe { transmute(Bar(&1u8, 0)) };
15+
//~^ ERROR transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
16+
let _: Foo<&'static UnsafeCell<u8>> = unsafe { transmute(Bar(&1u8, 0)) };
17+
//~^ ERROR transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell
2+
--> $DIR/partially_overlapping.rs:14:44
3+
|
4+
LL | let _: Foo<&'static mut u8> = unsafe { transmute(Bar(&1u8, 0)) };
5+
| ^^^^^^^^^
6+
|
7+
= note: transmute from `Bar.0` to `Foo<&mut u8>.1`
8+
= note: `#[deny(mutable_transmutes)]` on by default
9+
10+
error: transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
11+
--> $DIR/partially_overlapping.rs:16:52
12+
|
13+
LL | let _: Foo<&'static UnsafeCell<u8>> = unsafe { transmute(Bar(&1u8, 0)) };
14+
| ^^^^^^^^^
15+
|
16+
= note: transmute from `*Bar.0` to `*Foo<&UnsafeCell<u8>>.1`
17+
= note: `#[deny(unsafe_cell_transmutes)]` on by default
18+
19+
error: aborting due to 2 previous errors
20+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use std::cell::UnsafeCell;
2+
use std::mem::transmute;
3+
4+
#[repr(C)]
5+
struct Foo(&'static u8, &'static u8);
6+
#[repr(C)]
7+
struct Bar(&'static UnsafeCell<u8>, &'static mut u8);
8+
9+
fn main() {
10+
let _a: Bar = unsafe { transmute(Foo(&0, &0)) };
11+
//~^ ERROR transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: transmuting &T to &UnsafeCell<T> is undefined behavior, even if the reference is unused, consider using UnsafeCell on the original data
2+
--> $DIR/report_only_first_error.rs:10:28
3+
|
4+
LL | let _a: Bar = unsafe { transmute(Foo(&0, &0)) };
5+
| ^^^^^^^^^
6+
|
7+
= note: transmute from `*Foo.0` to `*Bar.0`
8+
= note: `#[deny(unsafe_cell_transmutes)]` on by default
9+
10+
error: aborting due to 1 previous error
11+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use std::cell::UnsafeCell;
2+
3+
#[repr(C)]
4+
struct A {
5+
a: u64,
6+
b: u32,
7+
c: u32,
8+
}
9+
10+
#[repr(C)]
11+
struct B {
12+
a: u64,
13+
b: UnsafeCell<u32>,
14+
c: u32,
15+
}
16+
17+
fn main() {
18+
let a = A { a: 0, b: 0, c: 0 };
19+
let _b = unsafe { &*(&a as *const A as *const B) };
20+
//~^ ERROR casting `&T` to `&UnsafeCell<T>` is undefined behavior, even if the reference is unused, consider using an `UnsafeCell` on the original data
21+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: casting `&T` to `&UnsafeCell<T>` is undefined behavior, even if the reference is unused, consider using an `UnsafeCell` on the original data
2+
--> $DIR/unsafe_cell_reference_casting.rs:19:23
3+
|
4+
LL | let _b = unsafe { &*(&a as *const A as *const B) };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: cast from `A.b` to `B.b`
8+
= note: `#[deny(unsafe_cell_reference_casting)]` on by default
9+
10+
error: aborting due to 1 previous error
11+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use std::cell::UnsafeCell;
2+
3+
#[repr(C)]
4+
struct A<T: ?Sized> {
5+
a: u32,
6+
b: T,
7+
}
8+
9+
#[repr(C)]
10+
struct B {
11+
a: UnsafeCell<u32>,
12+
b: [u32],
13+
}
14+
15+
fn main() {
16+
let a = &A { a: 0, b: [0_u32, 0] } as &A<[u32]>;
17+
let _b = unsafe { &*(a as *const A<[u32]> as *const B) };
18+
//~^ ERROR casting `&T` to `&UnsafeCell<T>` is undefined behavior, even if the reference is unused, consider using an `UnsafeCell` on the original data
19+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: casting `&T` to `&UnsafeCell<T>` is undefined behavior, even if the reference is unused, consider using an `UnsafeCell` on the original data
2+
--> $DIR/unsized.rs:17:23
3+
|
4+
LL | let _b = unsafe { &*(a as *const A<[u32]> as *const B) };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: cast from `A<[u32]>.a` to `B.a`
8+
= note: `#[deny(unsafe_cell_reference_casting)]` on by default
9+
10+
error: aborting due to 1 previous error
11+

0 commit comments

Comments
 (0)
Please sign in to comment.