1use hir::HirId;
2use rustc_abi::Primitive::Pointer;
3use rustc_abi::VariantIdx;
4use rustc_errors::codes::*;
5use rustc_errors::struct_span_code_err;
6use rustc_hir as hir;
7use rustc_index::Idx;
8use rustc_middle::bug;
9use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
10use rustc_middle::ty::{self, Ty, TyCtxt};
11use rustc_span::def_id::LocalDefId;
12use tracing::trace;
13
14fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
17 let ty::Adt(def, args) = *ty.kind() else { return ty };
18
19 if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() {
20 let data_idx;
21
22 let one = VariantIdx::new(1);
23 let zero = VariantIdx::ZERO;
24
25 if def.variant(zero).fields.is_empty() {
26 data_idx = one;
27 } else if def.variant(one).fields.is_empty() {
28 data_idx = zero;
29 } else {
30 return ty;
31 }
32
33 if def.variant(data_idx).fields.len() == 1 {
34 return def.variant(data_idx).single_field().ty(tcx, args);
35 }
36 }
37
38 ty
39}
40
41fn skeleton_string<'tcx>(
43 ty: Ty<'tcx>,
44 sk: Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>>,
45) -> String {
46 match sk {
47 Ok(SizeSkeleton::Pointer { tail, .. }) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("pointer to `{0}`", tail))
})format!("pointer to `{tail}`"),
48 Ok(SizeSkeleton::Known(size, _)) => {
49 if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
50 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} bits", v))
})format!("{v} bits")
51 } else {
52 ::rustc_middle::util::bug::bug_fmt(format_args!("{0:?} overflow for u128",
size))bug!("{:?} overflow for u128", size)
56 }
57 }
58 Ok(SizeSkeleton::Generic(size)) => {
59 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("generic size {0}", size))
})format!("generic size {size}")
60 }
61 Err(LayoutError::TooGeneric(bad)) => {
62 if *bad == ty {
63 "this type does not have a fixed size".to_owned()
64 } else {
65 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("size can vary because of {0}",
bad))
})format!("size can vary because of {bad}")
66 }
67 }
68 Err(err) => err.to_string(),
69 }
70}
71
72fn check_transmute<'tcx>(
73 tcx: TyCtxt<'tcx>,
74 typing_env: ty::TypingEnv<'tcx>,
75 from: Ty<'tcx>,
76 to: Ty<'tcx>,
77 hir_id: HirId,
78) {
79 let span = || tcx.hir_span(hir_id);
80 let normalize = |ty| {
81 if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) {
82 ty
83 } else {
84 Ty::new_error_with_message(
85 tcx,
86 span(),
87 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("tried to normalize non-wf type {0:#?} in check_transmute",
ty))
})format!("tried to normalize non-wf type {ty:#?} in check_transmute"),
88 )
89 }
90 };
91
92 let from = normalize(from);
93 let to = normalize(to);
94 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/intrinsicck.rs:94",
"rustc_hir_typeck::intrinsicck", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/intrinsicck.rs"),
::tracing_core::__macro_support::Option::Some(94u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::intrinsicck"),
::tracing_core::field::FieldSet::new(&["from", "to"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&from) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&to) as
&dyn Value))])
});
} else { ; }
};trace!(?from, ?to);
95
96 if from == to {
98 return;
99 }
100
101 let sk_from = SizeSkeleton::compute(from, tcx, typing_env);
102 let sk_to = SizeSkeleton::compute(to, tcx, typing_env);
103 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/intrinsicck.rs:103",
"rustc_hir_typeck::intrinsicck", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/intrinsicck.rs"),
::tracing_core::__macro_support::Option::Some(103u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::intrinsicck"),
::tracing_core::field::FieldSet::new(&["sk_from", "sk_to"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&sk_from) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&sk_to) as
&dyn Value))])
});
} else { ; }
};trace!(?sk_from, ?sk_to);
104
105 if let Ok(sk_from) = sk_from
107 && let Ok(sk_to) = sk_to
108 {
109 if sk_from.same_size(sk_to) {
110 return;
111 }
112
113 let from = unpack_option_like(tcx, from);
116 if let ty::FnDef(..) = from.kind()
117 && let SizeSkeleton::Known(size_to, _) = sk_to
118 && size_to == Pointer(tcx.data_layout.instruction_address_space).size(&tcx)
119 {
120 {
tcx.sess.dcx().struct_span_err(span(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("can\'t transmute zero-sized type"))
})).with_code(E0591)
}struct_span_code_err!(tcx.sess.dcx(), span(), E0591, "can't transmute zero-sized type")
121 .with_note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("source type: {0}", from))
})format!("source type: {from}"))
122 .with_note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("target type: {0}", to))
})format!("target type: {to}"))
123 .with_help("cast with `as` to a pointer instead")
124 .emit();
125 return;
126 }
127 }
128
129 let mut err = {
tcx.sess.dcx().struct_span_err(span(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot transmute between types of different sizes, or dependently-sized types"))
})).with_code(E0512)
}struct_span_code_err!(
130 tcx.sess.dcx(),
131 span(),
132 E0512,
133 "cannot transmute between types of different sizes, or dependently-sized types"
134 );
135 if from == to {
136 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` does not have a fixed size",
from))
})format!("`{from}` does not have a fixed size"));
137 err.emit();
138 } else {
139 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("source type: `{0}` ({1})", from,
skeleton_string(from, sk_from)))
})format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)));
140 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("target type: `{0}` ({1})", to,
skeleton_string(to, sk_to)))
})format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
141 err.emit();
142 }
143}
144
145pub(crate) fn check_transmutes(tcx: TyCtxt<'_>, owner: LocalDefId) {
146 if !!tcx.is_typeck_child(owner.to_def_id()) {
::core::panicking::panic("assertion failed: !tcx.is_typeck_child(owner.to_def_id())")
};assert!(!tcx.is_typeck_child(owner.to_def_id()));
147 let typeck_results = tcx.typeck(owner);
148 let None = typeck_results.tainted_by_errors else { return };
149
150 let typing_env = ty::TypingEnv::post_analysis(tcx, owner);
151 for &(from, to, hir_id) in &typeck_results.transmutes_to_check {
152 check_transmute(tcx, typing_env, from, to, hir_id);
153 }
154}