Skip to content

Commit 5370c57

Browse files
Make PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS into a HIR lint
1 parent a8ae2af commit 5370c57

File tree

10 files changed

+136
-165
lines changed

10 files changed

+136
-165
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,11 @@ lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're
805805
lint_type_ir_trait_usage = do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver
806806
.note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler
807807
808+
lint_undefined_transmute = pointers cannot be transmuted to integers during const eval
809+
.note = at compile-time, pointers do not have an integer value
810+
.note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior
811+
.help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html
812+
808813
lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing
809814
.label = argument has type `{$arg_ty}`
810815
.suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ mod reference_casting;
7575
mod shadowed_into_iter;
7676
mod static_mut_refs;
7777
mod traits;
78+
mod transmute;
7879
mod types;
7980
mod unit_bindings;
8081
mod unqualified_local_imports;
@@ -118,6 +119,7 @@ use shadowed_into_iter::ShadowedIntoIter;
118119
pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
119120
use static_mut_refs::*;
120121
use traits::*;
122+
use transmute::CheckTransmutes;
121123
use types::*;
122124
use unit_bindings::*;
123125
use unqualified_local_imports::*;
@@ -245,6 +247,7 @@ late_lint_methods!(
245247
IfLetRescope: IfLetRescope::default(),
246248
StaticMutRefs: StaticMutRefs,
247249
UnqualifiedLocalImports: UnqualifiedLocalImports,
250+
CheckTransmutes: CheckTransmutes,
248251
]
249252
]
250253
);

compiler/rustc_lint/src/transmute.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use rustc_hir::def::{DefKind, Res};
2+
use rustc_hir::{self as hir};
3+
use rustc_macros::LintDiagnostic;
4+
use rustc_session::{declare_lint, impl_lint_pass};
5+
use rustc_span::sym;
6+
7+
use crate::{LateContext, LateLintPass};
8+
9+
declare_lint! {
10+
/// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer
11+
/// transmute in const functions and associated constants.
12+
///
13+
/// ### Example
14+
///
15+
/// ```rust
16+
/// const fn foo(ptr: *const u8) -> usize {
17+
/// unsafe {
18+
/// std::mem::transmute::<*const u8, usize>(ptr)
19+
/// }
20+
/// }
21+
/// ```
22+
///
23+
/// {{produces}}
24+
///
25+
/// ### Explanation
26+
///
27+
/// Transmuting pointers to integers in a `const` context is undefined behavior.
28+
/// Any attempt to use the resulting integer will abort const-evaluation.
29+
///
30+
/// But sometimes the compiler might not emit an error for pointer to integer transmutes
31+
/// inside const functions and associated consts because they are evaluated only when referenced.
32+
/// Therefore, this lint serves as an extra layer of defense to prevent any undefined behavior
33+
/// from compiling without any warnings or errors.
34+
///
35+
/// See [std::mem::transmute] in the reference for more details.
36+
///
37+
/// [std::mem::transmute]: https://doc.rust-lang.org/std/mem/fn.transmute.html
38+
pub PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
39+
Warn,
40+
"detects pointer to integer transmutes in const functions and associated constants",
41+
}
42+
43+
pub(crate) struct CheckTransmutes;
44+
45+
impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS]);
46+
47+
impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
48+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
49+
let hir::ExprKind::Call(callee, _) = expr.kind else {
50+
return;
51+
};
52+
let hir::ExprKind::Path(qpath) = callee.kind else {
53+
return;
54+
};
55+
let Res::Def(DefKind::Fn, def_id) = cx.qpath_res(&qpath, callee.hir_id) else {
56+
return;
57+
};
58+
if !cx.tcx.is_intrinsic(def_id, sym::transmute) {
59+
return;
60+
};
61+
let body_owner_def_id = cx.tcx.hir_enclosing_body_owner(expr.hir_id);
62+
let Some(context) = cx.tcx.hir_body_const_context(body_owner_def_id) else {
63+
return;
64+
};
65+
let args = cx.typeck_results().node_args(callee.hir_id);
66+
67+
let src = args.type_at(0);
68+
let dst = args.type_at(1);
69+
70+
// Check for transmutes that exhibit undefined behavior.
71+
// For example, transmuting pointers to integers in a const context.
72+
//
73+
// Why do we consider const functions and associated constants only?
74+
//
75+
// Generally, undefined behavior in const items are handled by the evaluator.
76+
// But, const functions and associated constants are evaluated only when referenced.
77+
// This can result in undefined behavior in a library going unnoticed until
78+
// the function or constant is actually used.
79+
//
80+
// Therefore, we only consider const functions and associated constants here and leave
81+
// other const items to be handled by the evaluator.
82+
if matches!(context, hir::ConstContext::ConstFn)
83+
|| matches!(cx.tcx.def_kind(body_owner_def_id), DefKind::AssocConst)
84+
{
85+
if src.is_raw_ptr() && dst.is_integral() {
86+
cx.tcx.emit_node_span_lint(
87+
PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
88+
expr.hir_id,
89+
expr.span,
90+
UndefinedTransmuteLint,
91+
);
92+
}
93+
}
94+
}
95+
}
96+
97+
#[derive(LintDiagnostic)]
98+
#[diag(lint_undefined_transmute)]
99+
#[note]
100+
#[note(lint_note2)]
101+
#[help]
102+
pub(crate) struct UndefinedTransmuteLint;

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ declare_lint_pass! {
7979
PRIVATE_BOUNDS,
8080
PRIVATE_INTERFACES,
8181
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
82-
PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
8382
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
8483
REDUNDANT_IMPORTS,
8584
REDUNDANT_LIFETIMES,
@@ -4851,40 +4850,6 @@ declare_lint! {
48514850
@feature_gate = supertrait_item_shadowing;
48524851
}
48534852

4854-
declare_lint! {
4855-
/// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer
4856-
/// transmute in const functions and associated constants.
4857-
///
4858-
/// ### Example
4859-
///
4860-
/// ```rust
4861-
/// const fn foo(ptr: *const u8) -> usize {
4862-
/// unsafe {
4863-
/// std::mem::transmute::<*const u8, usize>(ptr)
4864-
/// }
4865-
/// }
4866-
/// ```
4867-
///
4868-
/// {{produces}}
4869-
///
4870-
/// ### Explanation
4871-
///
4872-
/// Transmuting pointers to integers in a `const` context is undefined behavior.
4873-
/// Any attempt to use the resulting integer will abort const-evaluation.
4874-
///
4875-
/// But sometimes the compiler might not emit an error for pointer to integer transmutes
4876-
/// inside const functions and associated consts because they are evaluated only when referenced.
4877-
/// Therefore, this lint serves as an extra layer of defense to prevent any undefined behavior
4878-
/// from compiling without any warnings or errors.
4879-
///
4880-
/// See [std::mem::transmute] in the reference for more details.
4881-
///
4882-
/// [std::mem::transmute]: https://doc.rust-lang.org/std/mem/fn.transmute.html
4883-
pub PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
4884-
Warn,
4885-
"detects pointer to integer transmutes in const functions and associated constants",
4886-
}
4887-
48884853
declare_lint! {
48894854
/// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives.
48904855
///

compiler/rustc_mir_transform/messages.ftl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,5 @@ mir_transform_unconditional_recursion = function cannot return without recursing
7878
7979
mir_transform_unconditional_recursion_call_site_label = recursive call site
8080
81-
mir_transform_undefined_transmute = pointers cannot be transmuted to integers during const eval
82-
.note = at compile-time, pointers do not have an integer value
83-
.note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior
84-
.help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html
85-
8681
mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored
8782
mir_transform_unnecessary_transmute = unnecessary transmute

compiler/rustc_mir_transform/src/check_undefined_transmutes.rs

Lines changed: 0 additions & 77 deletions
This file was deleted.

compiler/rustc_mir_transform/src/errors.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,6 @@ impl<'a> LintDiagnostic<'a, ()> for UnnecessaryTransmute {
178178
}
179179
}
180180

181-
#[derive(LintDiagnostic)]
182-
#[diag(mir_transform_undefined_transmute)]
183-
#[note]
184-
#[note(mir_transform_note2)]
185-
#[help]
186-
pub(crate) struct UndefinedTransmute;
187-
188181
#[derive(Diagnostic)]
189182
#[diag(mir_transform_force_inline)]
190183
#[note]

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ declare_passes! {
123123
mod check_const_item_mutation : CheckConstItemMutation;
124124
mod check_null : CheckNull;
125125
mod check_packed_ref : CheckPackedRef;
126-
mod check_undefined_transmutes : CheckUndefinedTransmutes;
127126
mod check_unnecessary_transmutes: CheckUnnecessaryTransmutes;
128127
// This pass is public to allow external drivers to perform MIR cleanup
129128
pub mod cleanup_post_borrowck : CleanupPostBorrowck;
@@ -390,7 +389,6 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
390389
&Lint(check_packed_ref::CheckPackedRef),
391390
&Lint(check_const_item_mutation::CheckConstItemMutation),
392391
&Lint(function_item_references::FunctionItemReferences),
393-
&Lint(check_undefined_transmutes::CheckUndefinedTransmutes),
394392
&Lint(check_unnecessary_transmutes::CheckUnnecessaryTransmutes),
395393
// What we need to do constant evaluation.
396394
&simplify::SimplifyCfg::Initial,
Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
#![deny(ptr_to_integer_transmute_in_consts)]
2+
13
const fn foo(ptr: *const u8) -> usize {
24
unsafe {
35
std::mem::transmute(ptr)
4-
//~^ WARN pointers cannot be transmuted to integers
6+
//~^ ERROR pointers cannot be transmuted to integers
57
}
68
}
79

@@ -11,7 +13,7 @@ trait Human {
1113
let ptr: *const usize = &value;
1214
unsafe {
1315
std::mem::transmute(ptr)
14-
//~^ WARN pointers cannot be transmuted to integers
16+
//~^ ERROR pointers cannot be transmuted to integers
1517
}
1618
};
1719

@@ -28,7 +30,7 @@ impl<T> Type<T> {
2830
let ptr: *const usize = &value;
2931
unsafe {
3032
std::mem::transmute(ptr)
31-
//~^ WARN pointers cannot be transmuted to integers
33+
//~^ ERROR pointers cannot be transmuted to integers
3234
}
3335
};
3436

@@ -38,9 +40,7 @@ impl<T> Type<T> {
3840
}
3941

4042
fn control(ptr: *const u8) -> usize {
41-
unsafe {
42-
std::mem::transmute(ptr)
43-
}
43+
unsafe { std::mem::transmute(ptr) }
4444
}
4545

4646
struct ControlStruct;
@@ -49,22 +49,15 @@ impl ControlStruct {
4949
fn new() -> usize {
5050
let value = 10;
5151
let ptr: *const i32 = &value;
52-
unsafe {
53-
std::mem::transmute(ptr)
54-
}
52+
unsafe { std::mem::transmute(ptr) }
5553
}
5654
}
5755

58-
5956
const fn zoom(ptr: *const u8) -> usize {
6057
unsafe {
6158
std::mem::transmute(ptr)
62-
//~^ WARN pointers cannot be transmuted to integers
59+
//~^ ERROR pointers cannot be transmuted to integers
6360
}
6461
}
6562

66-
fn main() {
67-
const a: u8 = 10;
68-
const value: usize = zoom(&a);
69-
//~^ ERROR evaluation of constant value failed
70-
}
63+
fn main() {}

0 commit comments

Comments
 (0)