Skip to content
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Simplify implementation.
  • Loading branch information
cjgillot committed Aug 22, 2025
commit 9fe0b28db2439efa1a8147870c3e50f26f426ab5
90 changes: 46 additions & 44 deletions compiler/rustc_hir_typeck/src/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_hir as hir;
use rustc_index::Idx;
use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::LocalDefId;
use tracing::trace;

Expand Down Expand Up @@ -38,58 +38,86 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
ty
}

/// Try to display a sensible error with as much information as possible.
fn skeleton_string<'tcx>(
ty: Ty<'tcx>,
sk: Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>>,
) -> String {
match sk {
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
Ok(SizeSkeleton::Known(size, _)) => {
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
format!("{v} bits")
} else {
// `u128` should definitely be able to hold the size of different architectures
// larger sizes should be reported as error `are too big for the target architecture`
// otherwise we have a bug somewhere
bug!("{:?} overflow for u128", size)
}
}
Ok(SizeSkeleton::Generic(size)) => {
format!("generic size {size}")
}
Err(LayoutError::TooGeneric(bad)) => {
if *bad == ty {
"this type does not have a fixed size".to_owned()
} else {
format!("size can vary because of {bad}")
}
}
Err(err) => err.to_string(),
}
}

fn check_transmute<'tcx>(
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
from: Ty<'tcx>,
to: Ty<'tcx>,
hir_id: HirId,
) {
let dl = &tcx.data_layout;
let span = tcx.hir_span(hir_id);
let span = || tcx.hir_span(hir_id);
let normalize = |ty| {
if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) {
ty
} else {
Ty::new_error_with_message(
tcx,
span,
span(),
format!("tried to normalize non-wf type {ty:#?} in check_transmute"),
)
}
};

let from = normalize(from);
let to = normalize(to);
trace!(?from, ?to);
if from.has_non_region_infer() || to.has_non_region_infer() {
// Note: this path is currently not reached in any test, so any
// example that triggers this would be worth minimizing and
// converting into a test.
tcx.sess.dcx().span_bug(span, "argument to transmute has inference variables");
}

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

let skel = |ty| SizeSkeleton::compute(ty, tcx, typing_env);
let sk_from = skel(from);
let sk_to = skel(to);
let sk_from = SizeSkeleton::compute(from, tcx, typing_env);
let sk_to = SizeSkeleton::compute(to, tcx, typing_env);
trace!(?sk_from, ?sk_to);

// Check for same size using the skeletons.
if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
if let Ok(sk_from) = sk_from
&& let Ok(sk_to) = sk_to
{
if sk_from.same_size(sk_to) {
return;
}

// Special-case transmuting from `typeof(function)` and
// `Option<typeof(function)>` to present a clearer error.
let from = unpack_option_like(tcx, from);
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to)
&& size_to == Pointer(dl.instruction_address_space).size(&tcx)
if let ty::FnDef(..) = from.kind()
&& 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")
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")
Expand All @@ -98,35 +126,9 @@ fn check_transmute<'tcx>(
}
}

// Try to display a sensible error with as much information as possible.
let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
Ok(SizeSkeleton::Known(size, _)) => {
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
format!("{v} bits")
} else {
// `u128` should definitely be able to hold the size of different architectures
// larger sizes should be reported as error `are too big for the target architecture`
// otherwise we have a bug somewhere
bug!("{:?} overflow for u128", size)
}
}
Ok(SizeSkeleton::Generic(size)) => {
format!("generic size {size}")
}
Err(LayoutError::TooGeneric(bad)) => {
if *bad == ty {
"this type does not have a fixed size".to_owned()
} else {
format!("size can vary because of {bad}")
}
}
Err(err) => err.to_string(),
};

let mut err = struct_span_code_err!(
tcx.sess.dcx(),
span,
span(),
E0512,
"cannot transmute between types of different sizes, or dependently-sized types"
);
Expand Down