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, Unnormalized};
11use rustc_span::ErrorGuaranteed;
12use rustc_span::def_id::LocalDefId;
13use tracing::trace;
14
15fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
18 let ty::Adt(def, args) = *ty.kind() else { return ty };
19
20 if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() {
21 let data_idx;
22
23 let one = VariantIdx::new(1);
24 let zero = VariantIdx::ZERO;
25
26 if def.variant(zero).fields.is_empty() {
27 data_idx = one;
28 } else if def.variant(one).fields.is_empty() {
29 data_idx = zero;
30 } else {
31 return ty;
32 }
33
34 if def.variant(data_idx).fields.len() == 1 {
35 return def.variant(data_idx).single_field().ty(tcx, args).skip_norm_wip();
36 }
37 }
38
39 ty
40}
41
42fn skeleton_string<'tcx>(
44 ty: Ty<'tcx>,
45 sk: Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>>,
46) -> String {
47 match sk {
48 Ok(SizeSkeleton::Pointer { tail, .. }) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("pointer to `{0}`", tail))
})format!("pointer to `{tail}`"),
49 Ok(SizeSkeleton::Known(size, _)) => {
50 if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
51 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} bits", v))
})format!("{v} bits")
52 } else {
53 ::rustc_middle::util::bug::bug_fmt(format_args!("{0:?} overflow for u128",
size))bug!("{:?} overflow for u128", size)
57 }
58 }
59 Err(LayoutError::TooGeneric(bad)) => {
60 if *bad == ty {
61 "this type does not have a fixed size".to_owned()
62 } else {
63 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("size can vary because of {0}",
bad))
})format!("size can vary because of {bad}")
64 }
65 }
66 Err(err) => err.to_string(),
67 }
68}
69
70fn check_transmute<'tcx>(
71 tcx: TyCtxt<'tcx>,
72 typing_env: ty::TypingEnv<'tcx>,
73 from: Unnormalized<'tcx, Ty<'tcx>>,
74 to: Unnormalized<'tcx, Ty<'tcx>>,
75 hir_id: HirId,
76) -> Result<(), ErrorGuaranteed> {
77 let span = tcx.hir_span(hir_id);
78 let normalize = |ty| {
79 if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) {
80 ty
81 } else {
82 Ty::new_error_with_message(
83 tcx,
84 span,
85 ::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"),
86 )
87 }
88 };
89
90 let from = normalize(from);
91 let to = normalize(to);
92 {
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:92",
"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(92u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::intrinsicck"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("from")
}> =
::tracing::__macro_support::FieldName::new("from");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("to")
}> =
::tracing::__macro_support::FieldName::new("to");
NAME.as_str()
}], ::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&from)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&to)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!(?from, ?to);
93
94 if from == to {
96 return Ok(());
97 }
98
99 let sk_from = SizeSkeleton::compute(from, tcx, typing_env, span);
100 let sk_to = SizeSkeleton::compute(to, tcx, typing_env, span);
101 {
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:101",
"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(101u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::intrinsicck"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("sk_from")
}> =
::tracing::__macro_support::FieldName::new("sk_from");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("sk_to")
}> =
::tracing::__macro_support::FieldName::new("sk_to");
NAME.as_str()
}], ::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&sk_from)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&sk_to)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!(?sk_from, ?sk_to);
102
103 if let Ok(sk_from) = sk_from
105 && let Ok(sk_to) = sk_to
106 {
107 if sk_from.same_size(sk_to) {
108 return Ok(());
109 }
110
111 let from = unpack_option_like(tcx, from);
114 if let ty::FnDef(..) = from.kind()
115 && let SizeSkeleton::Known(size_to, _) = sk_to
116 && size_to == Pointer(tcx.data_layout.instruction_address_space).size(&tcx)
117 {
118 {
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")
119 .with_note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("source type: {0}", from))
})format!("source type: {from}"))
120 .with_note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("target type: {0}", to))
})format!("target type: {to}"))
121 .with_help("cast with `as` to a pointer instead")
122 .emit();
123 return Ok(());
124 }
125 }
126
127 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!(
128 tcx.sess.dcx(),
129 span,
130 E0512,
131 "cannot transmute between types of different sizes, or dependently-sized types"
132 );
133 if from == to {
134 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"));
135 Err(err.emit())
136 } else {
137 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)));
138 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)));
139 Err(err.emit())
140 }
141}
142
143fn check_offload<'tcx>(
144 tcx: TyCtxt<'tcx>,
145 typing_env: ty::TypingEnv<'tcx>,
146 kernel_ty: Ty<'tcx>,
147 args_ty: Ty<'tcx>,
148 ret_ty: Ty<'tcx>,
149 hir_id: HirId,
150) -> Result<(), ErrorGuaranteed> {
151 let span = tcx.hir_span(hir_id);
152 let ty::FnDef(kernel_def_id, kernel_args) = *kernel_ty.kind() else {
153 let err = tcx
154 .sess
155 .dcx()
156 .struct_span_err(
157 span,
158 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected a function item for the offload kernel, found `{0}`",
kernel_ty))
})format!("expected a function item for the offload kernel, found `{}`", kernel_ty),
159 )
160 .emit();
161 return Err(err);
162 };
163
164 let kernel_sig =
165 tcx.fn_sig(kernel_def_id).instantiate(tcx, kernel_args.skip_binder()).skip_norm_wip();
166 let kernel_sig = tcx.instantiate_bound_regions_with_erased(kernel_sig);
167
168 let ty::Tuple(tuple_fields) = *args_ty.kind() else {
169 let err = tcx
170 .sess
171 .dcx()
172 .struct_span_err(
173 span,
174 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected a tuple for the offload arguments, found `{0}`",
args_ty))
})format!("expected a tuple for the offload arguments, found `{}`", args_ty),
175 )
176 .emit();
177 return Err(err);
178 };
179
180 if kernel_sig.inputs().len() != tuple_fields.len() {
181 let err = tcx
182 .sess
183 .dcx()
184 .struct_span_err(
185 span,
186 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("offload kernel expects {0} arguments, but {1} arguments were provided",
kernel_sig.inputs().len(), tuple_fields.len()))
})format!(
187 "offload kernel expects {} arguments, but {} arguments were provided",
188 kernel_sig.inputs().len(),
189 tuple_fields.len()
190 ),
191 )
192 .emit();
193 return Err(err);
194 }
195
196 let normalize = |ty| {
197 if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) {
198 ty
199 } else {
200 Ty::new_error_with_message(
201 tcx,
202 span,
203 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("tried to normalize non-wf type {0:#?} in check_offload",
ty))
})format!("tried to normalize non-wf type {ty:#?} in check_offload"),
204 )
205 }
206 };
207
208 let mut result = Ok(());
209
210 for (i, (&input_ty, arg_ty)) in kernel_sig.inputs().iter().zip(tuple_fields.iter()).enumerate()
211 {
212 let norm_input_ty = normalize(input_ty);
213 let norm_arg_ty = normalize(arg_ty);
214 if norm_input_ty != norm_arg_ty {
215 let err = tcx
216 .sess
217 .dcx()
218 .struct_span_err(
219 span,
220 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("type mismatch in offload kernel argument {0}: expected `{1}`, found `{2}`",
i, norm_input_ty, norm_arg_ty))
})format!(
221 "type mismatch in offload kernel argument {}: expected `{}`, found `{}`",
222 i, norm_input_ty, norm_arg_ty
223 ),
224 )
225 .emit();
226 result = Err(err);
227 }
228 }
229
230 let norm_kernel_ret = normalize(kernel_sig.output());
231 let norm_offload_ret = normalize(ret_ty);
232 if norm_kernel_ret != norm_offload_ret {
233 let err = tcx.sess.dcx().struct_span_err(
234 span,
235 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("offload kernel return type mismatch: kernel returns `{0}`, but offload call expects `{1}`",
norm_kernel_ret, norm_offload_ret))
})format!(
236 "offload kernel return type mismatch: kernel returns `{}`, but offload call expects `{}`",
237 norm_kernel_ret, norm_offload_ret
238 )
239 ).emit();
240 result = Err(err);
241 }
242
243 result
244}
245
246pub(crate) fn check_transmutes(tcx: TyCtxt<'_>, owner: LocalDefId) -> Result<(), ErrorGuaranteed> {
247 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()));
248 let typeck_results = tcx.typeck(owner);
249 if let Some(e) = typeck_results.tainted_by_errors {
250 return Err(e);
251 };
252
253 let typing_env = ty::TypingEnv::codegen(tcx, owner);
254 let mut result = Ok(());
255 for &(from, to, hir_id) in &typeck_results.transmutes_to_check {
256 let (to, from) = ty::set_aliases_to_non_rigid(tcx, (to, from)).unzip();
257 result = result.and(check_transmute(tcx, typing_env, from, to, hir_id));
258 }
259 result
260}
261
262pub(crate) fn check_offloads(tcx: TyCtxt<'_>, owner: LocalDefId) -> Result<(), ErrorGuaranteed> {
263 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()));
264 let typeck_results = tcx.typeck(owner);
265 if let Some(e) = typeck_results.tainted_by_errors {
266 return Err(e);
267 };
268
269 let typing_env = ty::TypingEnv::codegen(tcx, owner);
270 let mut result = Ok(());
271 for &(kernel_ty, args_ty, ret_ty, hir_id) in &typeck_results.offloads_to_check {
272 result = result.and(check_offload(tcx, typing_env, kernel_ty, args_ty, ret_ty, hir_id));
273 }
274 result
275}