Skip to content
Open
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
38 changes: 24 additions & 14 deletions compiler/rustc_hir_typeck/src/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_index::Idx;
use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
use rustc_middle::ty::{self, Ty, TyCtxt, Unnormalized};
use rustc_span::ErrorGuaranteed;
use rustc_span::def_id::LocalDefId;
use tracing::trace;

Expand Down Expand Up @@ -72,7 +73,7 @@ fn check_transmute<'tcx>(
from: Ty<'tcx>,
to: Ty<'tcx>,
hir_id: HirId,
) {
) -> Result<(), ErrorGuaranteed> {
let span = || tcx.hir_span(hir_id);
let normalize = |ty| {
if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) {
Expand All @@ -92,7 +93,7 @@ fn check_transmute<'tcx>(

// Transmutes that are only changing lifetimes are always ok.
if from == to {
return;
return Ok(());
}

let sk_from = SizeSkeleton::compute(from, tcx, typing_env);
Expand All @@ -104,7 +105,7 @@ fn check_transmute<'tcx>(
&& let Ok(sk_to) = sk_to
{
if sk_from.same_size(sk_to) {
return;
return Ok(());
}

// Special-case transmuting from `typeof(function)` and
Expand All @@ -114,12 +115,17 @@ fn check_transmute<'tcx>(
&& let SizeSkeleton::Known(size_to, _) = sk_to
&& size_to == Pointer(tcx.data_layout.instruction_address_space).size(&tcx)
{
struct_span_code_err!(tcx.sess.dcx(), span(), E0591, "can't transmute zero-sized type")
.with_note(format!("source type: {from}"))
.with_note(format!("target type: {to}"))
.with_help("cast with `as` to a pointer instead")
.emit();
return;
let err = struct_span_code_err!(
tcx.sess.dcx(),
span(),
E0591,
"can't transmute zero-sized type"
)
.with_note(format!("source type: {from}"))
.with_note(format!("target type: {to}"))
.with_help("cast with `as` to a pointer instead")
.emit();
return Err(err);
}
}

Expand All @@ -131,21 +137,25 @@ fn check_transmute<'tcx>(
);
if from == to {
err.note(format!("`{from}` does not have a fixed size"));
err.emit();
Err(err.emit())
} else {
err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)));
err.note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
err.emit();
Err(err.emit())
}
}

pub(crate) fn check_transmutes(tcx: TyCtxt<'_>, owner: LocalDefId) {
pub(crate) fn check_transmutes(tcx: TyCtxt<'_>, owner: LocalDefId) -> Result<(), ErrorGuaranteed> {
assert!(!tcx.is_typeck_child(owner.to_def_id()));
let typeck_results = tcx.typeck(owner);
let None = typeck_results.tainted_by_errors else { return };
if let Some(e) = typeck_results.tainted_by_errors {
return Err(e);
};

let typing_env = ty::TypingEnv::post_analysis(tcx, owner);
let mut result = Ok(());
for &(from, to, hir_id) in &typeck_results.transmutes_to_check {
check_transmute(tcx, typing_env, from, to, hir_id);
result = result.and(check_transmute(tcx, typing_env, from, to, hir_id));
}
result
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ rustc_queries! {
}

/// Unsafety-check this `LocalDefId`.
query check_transmutes(key: LocalDefId) {
query check_transmutes(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
desc { "check transmute calls inside `{}`", tcx.def_path_str(key) }
}

Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,15 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
body.tainted_by_errors = Some(error_reported);
}

let root = tcx.typeck_root_def_id_local(def);
if let Err(e) = tcx.check_transmutes(root) {
body.tainted_by_errors = Some(e);
}

// Also taint the body if it's within a top-level item that is not well formed.
//
// We do this check here and not during `mir_promoted` because that may result
// in borrowck cycles if WF requires looking into an opaque hidden type.
let root = tcx.typeck_root_def_id_local(def);
match tcx.def_kind(root) {
DefKind::Fn
| DefKind::AssocFn
Expand Down
2 changes: 1 addition & 1 deletion tests/rustdoc-ui/issues/issue-79494.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//@ only-64bit

pub const ZST: &[u8] = unsafe { std::mem::transmute(1usize) };
//~^ ERROR transmuting from 8-byte type to 16-byte type
//~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types [E0512]
9 changes: 6 additions & 3 deletions tests/rustdoc-ui/issues/issue-79494.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
error[E0080]: transmuting from 8-byte type to 16-byte type: `usize` -> `&[u8]`
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> $DIR/issue-79494.rs:3:33
|
LL | pub const ZST: &[u8] = unsafe { std::mem::transmute(1usize) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `ZST` failed here
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `usize` (64 bits)
= note: target type: `&[u8]` (128 bits)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.
For more information about this error, try `rustc --explain E0512`.
3 changes: 1 addition & 2 deletions tests/ui/consts/transmute-size-mismatch-before-typeck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ fn main() {
}

const ZST: &[u8] = unsafe { std::mem::transmute(1usize) };
//~^ ERROR transmuting from
//~| ERROR cannot transmute between types of different sizes
//~^ ERROR cannot transmute between types of different sizes
11 changes: 2 additions & 9 deletions tests/ui/consts/transmute-size-mismatch-before-typeck.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
error[E0080]: transmuting from word size type to 2 * word size type: `usize` -> `&[u8]`
--> $DIR/transmute-size-mismatch-before-typeck.rs:16:29
|
LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `ZST` failed here

error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> $DIR/transmute-size-mismatch-before-typeck.rs:16:29
|
Expand All @@ -13,7 +7,6 @@ LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) };
= note: source type: `usize` (word size)
= note: target type: `&[u8]` (2 * word size)

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

Some errors have detailed explanations: E0080, E0512.
For more information about an error, try `rustc --explain E0080`.
For more information about this error, try `rustc --explain E0512`.
3 changes: 1 addition & 2 deletions tests/ui/layout/base-layout-is-sized-ice-123078.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ struct S {
}

const C: S = unsafe { std::mem::transmute(()) };
//~^ ERROR the type `S` has an unknown layout
//~| ERROR cannot transmute between types of different sizes, or dependently-sized types
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types

const _: [(); {
C;
Expand Down
12 changes: 3 additions & 9 deletions tests/ui/layout/base-layout-is-sized-ice-123078.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ help: the `Box` type always has a statically known size and allocates its conten
LL | a: Box<[u8]>,
| ++++ +

error[E0080]: the type `S` has an unknown layout
--> $DIR/base-layout-is-sized-ice-123078.rs:10:1
|
LL | const C: S = unsafe { std::mem::transmute(()) };
| ^^^^^^^^^^ evaluation of `C` failed here

error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> $DIR/base-layout-is-sized-ice-123078.rs:10:23
|
Expand All @@ -31,7 +25,7 @@ LL | const C: S = unsafe { std::mem::transmute(()) };
= note: source type: `()` (0 bits)
= note: target type: `S` (the type `S` has an unknown layout)

error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

Some errors have detailed explanations: E0080, E0277, E0512.
For more information about an error, try `rustc --explain E0080`.
Some errors have detailed explanations: E0277, E0512.
For more information about an error, try `rustc --explain E0277`.
12 changes: 12 additions & 0 deletions tests/ui/transmute/transmute-in-async-fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@compile-flags: -Zmir-enable-passes=+DataflowConstProp --crate-type lib
//@ edition:2021
pub async fn a() -> u32 {
unsafe { std::mem::transmute(1u64) }
//~^error: cannot transmute between types of different sizes, or dependently-sized types
}

pub async fn b() -> u32 {
let closure = || unsafe { std::mem::transmute(1u64) };
//~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types [E0512]
closure()
}
21 changes: 21 additions & 0 deletions tests/ui/transmute/transmute-in-async-fn.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error is emitted in typeck iirc. It should be tainting the body. If it isnt, it's likely that tcx.dcx() is used instead of infcx.dcx()

Please check if we can avoid changing anything in const prop by properly tainting wherever this error is emitted

Copy link
Copy Markdown
Contributor Author

@Human9000-bit Human9000-bit Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first glance there isn't infcx where the error is emitted, and no obvious way to get one

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is it being emitted? If it is within the typeck query, we can probably change something further up the call stack ( you can try using -Ztreat-err-as-bug to find out where the error is emitted and what the call stack is while emitting it

Copy link
Copy Markdown
Contributor Author

@Human9000-bit Human9000-bit Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is emitted in check_transmutes ran by run_required_analyses:

if not_typeck_child {
tcx.ensure_ok().mir_borrowck(def_id);
tcx.ensure_ok().check_transmutes(def_id);
}

and the very error emission happens here:

} else {
err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)));
err.note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
err.emit();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow. Didn't know that was changed (#145469)

I'll need to think on it. Unsure why it was necessary to live in a separate query

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that would fix this optimizer ICE, but i didn't suggest that because i assumed we'd get the same ICE in CTFE, and the only fix for that is to run the query earlier, where it would likely cycle again

Copy link
Copy Markdown
Contributor Author

@Human9000-bit Human9000-bit Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could check at the beginning of optimized_mir that check_transmutes does not emit an error.

I tried to do that at the start of run_pass of dataflow const prop. It ICEd for def_id being a typeck child.
So we can't just slap check_transmutes for every def_id (at least in mir opts)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can invoke is_typeck_child to invoke check_transmutes on the parent (there's also a method for getting the typeck parent).

This will fix the ICE (if you also make the check_transmute query return a Result<(), ErrorReported> and don't do anything if it's an error).

Tho at that point it may be better to do what cjgillot suggested and check it at the start of thr optimized_mir query and just return a dummy body or the unoptimized body.

I'm just sceptical it fixed the ICE in general as it should be possibe to produce the ICE in const eval by evaluating code that has the same transmute problem

Copy link
Copy Markdown
Contributor

@oli-obk oli-obk Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm apparently not: https://play.rust-lang.org/?version=nightly&mode=release&edition=2024&gist=31291deac2b05c040502d266b33e2f69

It seems very weird that we report an error here, but that's probably an oversight. The ctfe error should be an ICE

Copy link
Copy Markdown
Contributor Author

@Human9000-bit Human9000-bit Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently your example is getting caught in well-formedness checking during HIR analysis, which uses different methods of interpreter compared to dataflow const prop opt

--> $DIR/transmute-in-async-fn.rs:4:14
|
LL | unsafe { std::mem::transmute(1u64) }
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `u64` (64 bits)
= note: target type: `u32` (32 bits)

error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> $DIR/transmute-in-async-fn.rs:9:31
|
LL | let closure = || unsafe { std::mem::transmute(1u64) };
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `u64` (64 bits)
= note: target type: `u32` (32 bits)

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0512`.
Loading