1 //! Type-checking for the rust-intrinsic and platform-intrinsic
2 //! intrinsics that the compiler exposes.
5 SimdShuffleMissingLength
, UnrecognizedAtomicOperation
, UnrecognizedIntrinsicFunction
,
6 WrongNumberOfGenericArgumentsToIntrinsic
,
8 use crate::require_same_types
;
10 use rustc_errors
::{pluralize, struct_span_err}
;
12 use rustc_middle
::traits
::{ObligationCause, ObligationCauseCode}
;
13 use rustc_middle
::ty
::subst
::Subst
;
14 use rustc_middle
::ty
::{self, TyCtxt}
;
15 use rustc_span
::symbol
::{kw, sym, Symbol}
;
16 use rustc_target
::spec
::abi
::Abi
;
20 fn equate_intrinsic_type
<'tcx
>(
22 it
: &hir
::ForeignItem
<'_
>,
25 sig
: ty
::PolyFnSig
<'tcx
>,
27 let (own_counts
, span
) = match &it
.kind
{
28 hir
::ForeignItemKind
::Fn(.., generics
) => {
29 let own_counts
= tcx
.generics_of(it
.def_id
.to_def_id()).own_counts();
30 (own_counts
, generics
.span
)
33 struct_span_err
!(tcx
.sess
, it
.span
, E0622
, "intrinsic must be a function")
34 .span_label(it
.span
, "expected a function")
40 let gen_count_ok
= |found
: usize, expected
: usize, descr
: &str| -> bool
{
41 if found
!= expected
{
42 tcx
.sess
.emit_err(WrongNumberOfGenericArgumentsToIntrinsic
{
46 expected_pluralize
: pluralize
!(expected
),
55 if gen_count_ok(own_counts
.lifetimes
, n_lts
, "lifetime")
56 && gen_count_ok(own_counts
.types
, n_tps
, "type")
57 && gen_count_ok(own_counts
.consts
, 0, "const")
59 let fty
= tcx
.mk_fn_ptr(sig
);
60 let cause
= ObligationCause
::new(it
.span
, it
.hir_id(), ObligationCauseCode
::IntrinsicType
);
61 require_same_types(tcx
, &cause
, tcx
.mk_fn_ptr(tcx
.fn_sig(it
.def_id
)), fty
);
65 /// Returns the unsafety of the given intrinsic.
66 pub fn intrinsic_operation_unsafety(intrinsic
: Symbol
) -> hir
::Unsafety
{
68 // When adding a new intrinsic to this list,
69 // it's usually worth updating that intrinsic's documentation
70 // to note that it's safe to call, since
71 // safe extern fns are otherwise unprecedented.
76 | sym
::caller_location
77 | sym
::add_with_overflow
78 | sym
::sub_with_overflow
79 | sym
::mul_with_overflow
92 | sym
::discriminant_value
96 | sym
::ptr_guaranteed_eq
97 | sym
::ptr_guaranteed_ne
106 | sym
::variant_count
=> hir
::Unsafety
::Normal
,
107 _
=> hir
::Unsafety
::Unsafe
,
111 /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
112 /// and in `library/core/src/intrinsics.rs`.
113 pub fn check_intrinsic_type(tcx
: TyCtxt
<'_
>, it
: &hir
::ForeignItem
<'_
>) {
114 let param
= |n
| tcx
.mk_ty_param(n
, Symbol
::intern(&format
!("P{}", n
)));
115 let intrinsic_name
= tcx
.item_name(it
.def_id
.to_def_id());
116 let name_str
= intrinsic_name
.as_str();
118 let bound_vars
= tcx
.mk_bound_variable_kinds(
119 [ty
::BoundVariableKind
::Region(ty
::BrAnon(0)), ty
::BoundVariableKind
::Region(ty
::BrEnv
)]
123 let mk_va_list_ty
= |mutbl
| {
124 tcx
.lang_items().va_list().map(|did
| {
125 let region
= tcx
.mk_region(ty
::ReLateBound(
127 ty
::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }
,
129 let env_region
= tcx
.mk_region(ty
::ReLateBound(
131 ty
::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv }
,
133 let va_list_ty
= tcx
.type_of(did
).subst(tcx
, &[region
.into()]);
134 (tcx
.mk_ref(env_region
, ty
::TypeAndMut { ty: va_list_ty, mutbl }
), va_list_ty
)
138 let (n_tps
, n_lts
, inputs
, output
, unsafety
) = if name_str
.starts_with("atomic_") {
139 let split
: Vec
<&str> = name_str
.split('_'
).collect();
140 assert
!(split
.len() >= 2, "Atomic intrinsic in an incorrect format");
142 //We only care about the operation here
143 let (n_tps
, inputs
, output
) = match split
[1] {
144 "cxchg" | "cxchgweak" => (
146 vec
![tcx
.mk_mut_ptr(param(0)), param(0), param(0)],
147 tcx
.intern_tup(&[param(0), tcx
.types
.bool
]),
149 "load" => (1, vec
![tcx
.mk_imm_ptr(param(0))], param(0)),
150 "store" => (1, vec
![tcx
.mk_mut_ptr(param(0)), param(0)], tcx
.mk_unit()),
152 "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax"
153 | "umin" => (1, vec
![tcx
.mk_mut_ptr(param(0)), param(0)], param(0)),
154 "fence" | "singlethreadfence" => (0, Vec
::new(), tcx
.mk_unit()),
156 tcx
.sess
.emit_err(UnrecognizedAtomicOperation { span: it.span, op }
);
160 (n_tps
, 0, inputs
, output
, hir
::Unsafety
::Unsafe
)
162 let unsafety
= intrinsic_operation_unsafety(intrinsic_name
);
163 let (n_tps
, inputs
, output
) = match intrinsic_name
{
164 sym
::abort
=> (0, Vec
::new(), tcx
.types
.never
),
165 sym
::unreachable
=> (0, Vec
::new(), tcx
.types
.never
),
166 sym
::breakpoint
=> (0, Vec
::new(), tcx
.mk_unit()),
167 sym
::size_of
| sym
::pref_align_of
| sym
::min_align_of
| sym
::variant_count
=> {
168 (1, Vec
::new(), tcx
.types
.usize)
170 sym
::size_of_val
| sym
::min_align_of_val
=> {
171 (1, vec
![tcx
.mk_imm_ptr(param(0))], tcx
.types
.usize)
173 sym
::rustc_peek
=> (1, vec
![param(0)], param(0)),
174 sym
::caller_location
=> (0, vec
![], tcx
.caller_location_ty()),
175 sym
::assert_inhabited
| sym
::assert_zero_valid
| sym
::assert_uninit_valid
=> {
176 (1, Vec
::new(), tcx
.mk_unit())
178 sym
::forget
=> (1, vec
![param(0)], tcx
.mk_unit()),
179 sym
::transmute
=> (2, vec
![param(0)], param(1)),
180 sym
::prefetch_read_data
181 | sym
::prefetch_write_data
182 | sym
::prefetch_read_instruction
183 | sym
::prefetch_write_instruction
=> (
186 tcx
.mk_ptr(ty
::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }
),
191 sym
::drop_in_place
=> (1, vec
![tcx
.mk_mut_ptr(param(0))], tcx
.mk_unit()),
192 sym
::needs_drop
=> (1, Vec
::new(), tcx
.types
.bool
),
194 sym
::type_name
=> (1, Vec
::new(), tcx
.mk_static_str()),
195 sym
::type_id
=> (1, Vec
::new(), tcx
.types
.u64),
196 sym
::offset
| sym
::arith_offset
=> (
199 tcx
.mk_ptr(ty
::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }
),
202 tcx
.mk_ptr(ty
::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }
),
204 sym
::copy
| sym
::copy_nonoverlapping
=> (
207 tcx
.mk_ptr(ty
::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }
),
208 tcx
.mk_ptr(ty
::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }
),
213 sym
::volatile_copy_memory
| sym
::volatile_copy_nonoverlapping_memory
=> (
216 tcx
.mk_ptr(ty
::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }
),
217 tcx
.mk_ptr(ty
::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }
),
222 sym
::write_bytes
| sym
::volatile_set_memory
=> (
225 tcx
.mk_ptr(ty
::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }
),
231 sym
::sqrtf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
232 sym
::sqrtf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
233 sym
::powif32
=> (0, vec
![tcx
.types
.f32, tcx
.types
.i32], tcx
.types
.f32),
234 sym
::powif64
=> (0, vec
![tcx
.types
.f64, tcx
.types
.i32], tcx
.types
.f64),
235 sym
::sinf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
236 sym
::sinf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
237 sym
::cosf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
238 sym
::cosf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
239 sym
::powf32
=> (0, vec
![tcx
.types
.f32, tcx
.types
.f32], tcx
.types
.f32),
240 sym
::powf64
=> (0, vec
![tcx
.types
.f64, tcx
.types
.f64], tcx
.types
.f64),
241 sym
::expf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
242 sym
::expf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
243 sym
::exp2f32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
244 sym
::exp2f64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
245 sym
::logf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
246 sym
::logf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
247 sym
::log10f32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
248 sym
::log10f64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
249 sym
::log2f32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
250 sym
::log2f64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
251 sym
::fmaf32
=> (0, vec
![tcx
.types
.f32, tcx
.types
.f32, tcx
.types
.f32], tcx
.types
.f32),
252 sym
::fmaf64
=> (0, vec
![tcx
.types
.f64, tcx
.types
.f64, tcx
.types
.f64], tcx
.types
.f64),
253 sym
::fabsf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
254 sym
::fabsf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
255 sym
::minnumf32
=> (0, vec
![tcx
.types
.f32, tcx
.types
.f32], tcx
.types
.f32),
256 sym
::minnumf64
=> (0, vec
![tcx
.types
.f64, tcx
.types
.f64], tcx
.types
.f64),
257 sym
::maxnumf32
=> (0, vec
![tcx
.types
.f32, tcx
.types
.f32], tcx
.types
.f32),
258 sym
::maxnumf64
=> (0, vec
![tcx
.types
.f64, tcx
.types
.f64], tcx
.types
.f64),
259 sym
::copysignf32
=> (0, vec
![tcx
.types
.f32, tcx
.types
.f32], tcx
.types
.f32),
260 sym
::copysignf64
=> (0, vec
![tcx
.types
.f64, tcx
.types
.f64], tcx
.types
.f64),
261 sym
::floorf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
262 sym
::floorf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
263 sym
::ceilf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
264 sym
::ceilf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
265 sym
::truncf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
266 sym
::truncf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
267 sym
::rintf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
268 sym
::rintf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
269 sym
::nearbyintf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
270 sym
::nearbyintf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
271 sym
::roundf32
=> (0, vec
![tcx
.types
.f32], tcx
.types
.f32),
272 sym
::roundf64
=> (0, vec
![tcx
.types
.f64], tcx
.types
.f64),
274 sym
::volatile_load
| sym
::unaligned_volatile_load
=> {
275 (1, vec
![tcx
.mk_imm_ptr(param(0))], param(0))
277 sym
::volatile_store
| sym
::unaligned_volatile_store
=> {
278 (1, vec
![tcx
.mk_mut_ptr(param(0)), param(0)], tcx
.mk_unit())
287 | sym
::bitreverse
=> (1, vec
![param(0)], param(0)),
289 sym
::add_with_overflow
| sym
::sub_with_overflow
| sym
::mul_with_overflow
=> {
290 (1, vec
![param(0), param(0)], tcx
.intern_tup(&[param(0), tcx
.types
.bool
]))
293 sym
::ptr_guaranteed_eq
| sym
::ptr_guaranteed_ne
=> {
294 (1, vec
![tcx
.mk_imm_ptr(param(0)), tcx
.mk_imm_ptr(param(0))], tcx
.types
.bool
)
297 sym
::const_allocate
=> {
298 (0, vec
![tcx
.types
.usize, tcx
.types
.usize], tcx
.mk_mut_ptr(tcx
.types
.u8))
301 sym
::ptr_offset_from
=> {
302 (1, vec
![tcx
.mk_imm_ptr(param(0)), tcx
.mk_imm_ptr(param(0))], tcx
.types
.isize)
304 sym
::unchecked_div
| sym
::unchecked_rem
| sym
::exact_div
=> {
305 (1, vec
![param(0), param(0)], param(0))
307 sym
::unchecked_shl
| sym
::unchecked_shr
| sym
::rotate_left
| sym
::rotate_right
=> {
308 (1, vec
![param(0), param(0)], param(0))
310 sym
::unchecked_add
| sym
::unchecked_sub
| sym
::unchecked_mul
=> {
311 (1, vec
![param(0), param(0)], param(0))
313 sym
::wrapping_add
| sym
::wrapping_sub
| sym
::wrapping_mul
=> {
314 (1, vec
![param(0), param(0)], param(0))
316 sym
::saturating_add
| sym
::saturating_sub
=> (1, vec
![param(0), param(0)], param(0)),
317 sym
::fadd_fast
| sym
::fsub_fast
| sym
::fmul_fast
| sym
::fdiv_fast
| sym
::frem_fast
=> {
318 (1, vec
![param(0), param(0)], param(0))
320 sym
::float_to_int_unchecked
=> (2, vec
![param(0)], param(1)),
322 sym
::assume
=> (0, vec
![tcx
.types
.bool
], tcx
.mk_unit()),
323 sym
::likely
=> (0, vec
![tcx
.types
.bool
], tcx
.types
.bool
),
324 sym
::unlikely
=> (0, vec
![tcx
.types
.bool
], tcx
.types
.bool
),
326 sym
::discriminant_value
=> {
328 tcx
.associated_items(tcx
.lang_items().discriminant_kind_trait().unwrap());
329 let discriminant_def_id
= assoc_items
.in_definition_order().next().unwrap().def_id
;
331 let br
= ty
::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }
;
335 tcx
.mk_imm_ref(tcx
.mk_region(ty
::ReLateBound(ty
::INNERMOST
, br
)), param(0)),
337 tcx
.mk_projection(discriminant_def_id
, tcx
.mk_substs([param(0).into()].iter())),
342 let mut_u8
= tcx
.mk_mut_ptr(tcx
.types
.u8);
343 let try_fn_ty
= ty
::Binder
::dummy(tcx
.mk_fn_sig(
347 hir
::Unsafety
::Normal
,
350 let catch_fn_ty
= ty
::Binder
::dummy(tcx
.mk_fn_sig(
351 [mut_u8
, mut_u8
].iter().cloned(),
354 hir
::Unsafety
::Normal
,
359 vec
![tcx
.mk_fn_ptr(try_fn_ty
), mut_u8
, tcx
.mk_fn_ptr(catch_fn_ty
)],
364 sym
::va_start
| sym
::va_end
=> match mk_va_list_ty(hir
::Mutability
::Mut
) {
365 Some((va_list_ref_ty
, _
)) => (0, vec
![va_list_ref_ty
], tcx
.mk_unit()),
366 None
=> bug
!("`va_list` language item needed for C-variadic intrinsics"),
369 sym
::va_copy
=> match mk_va_list_ty(hir
::Mutability
::Not
) {
370 Some((va_list_ref_ty
, va_list_ty
)) => {
371 let va_list_ptr_ty
= tcx
.mk_mut_ptr(va_list_ty
);
372 (0, vec
![va_list_ptr_ty
, va_list_ref_ty
], tcx
.mk_unit())
374 None
=> bug
!("`va_list` language item needed for C-variadic intrinsics"),
377 sym
::va_arg
=> match mk_va_list_ty(hir
::Mutability
::Mut
) {
378 Some((va_list_ref_ty
, _
)) => (1, vec
![va_list_ref_ty
], param(0)),
379 None
=> bug
!("`va_list` language item needed for C-variadic intrinsics"),
382 sym
::nontemporal_store
=> (1, vec
![tcx
.mk_mut_ptr(param(0)), param(0)], tcx
.mk_unit()),
385 let br
= ty
::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }
;
387 tcx
.mk_imm_ref(tcx
.mk_region(ty
::ReLateBound(ty
::INNERMOST
, br
)), param(0));
388 (1, vec
![param_ty
; 2], tcx
.types
.bool
)
391 sym
::black_box
=> (1, vec
![param(0)], param(0)),
394 tcx
.sess
.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }
);
398 (n_tps
, 0, inputs
, output
, unsafety
)
400 let sig
= tcx
.mk_fn_sig(inputs
.into_iter(), output
, false, unsafety
, Abi
::RustIntrinsic
);
401 let sig
= ty
::Binder
::bind_with_vars(sig
, bound_vars
);
402 equate_intrinsic_type(tcx
, it
, n_tps
, n_lts
, sig
)
405 /// Type-check `extern "platform-intrinsic" { ... }` functions.
406 pub fn check_platform_intrinsic_type(tcx
: TyCtxt
<'_
>, it
: &hir
::ForeignItem
<'_
>) {
408 let name
= Symbol
::intern(&format
!("P{}", n
));
409 tcx
.mk_ty_param(n
, name
)
412 let name
= it
.ident
.name
;
414 let (n_tps
, inputs
, output
) = match name
{
415 sym
::simd_eq
| sym
::simd_ne
| sym
::simd_lt
| sym
::simd_le
| sym
::simd_gt
| sym
::simd_ge
=> {
416 (2, vec
![param(0), param(0)], param(1))
431 | sym
::simd_saturating_add
432 | sym
::simd_saturating_sub
=> (1, vec
![param(0), param(0)], param(0)),
446 | sym
::simd_trunc
=> (1, vec
![param(0)], param(0)),
447 sym
::simd_fpowi
=> (1, vec
![param(0), tcx
.types
.i32], param(0)),
448 sym
::simd_fma
=> (1, vec
![param(0), param(0), param(0)], param(0)),
449 sym
::simd_gather
=> (3, vec
![param(0), param(1), param(2)], param(0)),
450 sym
::simd_scatter
=> (3, vec
![param(0), param(1), param(2)], tcx
.mk_unit()),
451 sym
::simd_insert
=> (2, vec
![param(0), tcx
.types
.u32, param(1)], param(0)),
452 sym
::simd_extract
=> (2, vec
![param(0), tcx
.types
.u32], param(1)),
453 sym
::simd_cast
=> (2, vec
![param(0)], param(1)),
454 sym
::simd_bitmask
=> (2, vec
![param(0)], param(1)),
455 sym
::simd_select
| sym
::simd_select_bitmask
=> {
456 (2, vec
![param(0), param(1), param(1)], param(1))
458 sym
::simd_reduce_all
| sym
::simd_reduce_any
=> (1, vec
![param(0)], tcx
.types
.bool
),
459 sym
::simd_reduce_add_ordered
| sym
::simd_reduce_mul_ordered
=> {
460 (2, vec
![param(0), param(1)], param(1))
462 sym
::simd_reduce_add_unordered
463 | sym
::simd_reduce_mul_unordered
464 | sym
::simd_reduce_and
465 | sym
::simd_reduce_or
466 | sym
::simd_reduce_xor
467 | sym
::simd_reduce_min
468 | sym
::simd_reduce_max
469 | sym
::simd_reduce_min_nanless
470 | sym
::simd_reduce_max_nanless
=> (2, vec
![param(0)], param(1)),
471 name
if name
.as_str().starts_with("simd_shuffle") => {
472 match name
.as_str()["simd_shuffle".len()..].parse() {
474 let params
= vec
![param(0), param(0), tcx
.mk_array(tcx
.types
.u32, n
)];
475 (2, params
, param(1))
478 tcx
.sess
.emit_err(SimdShuffleMissingLength { span: it.span, name }
);
484 let msg
= format
!("unrecognized platform-specific intrinsic function: `{}`", name
);
485 tcx
.sess
.struct_span_err(it
.span
, &msg
).emit();
490 let sig
= tcx
.mk_fn_sig(
494 hir
::Unsafety
::Unsafe
,
495 Abi
::PlatformIntrinsic
,
497 let sig
= ty
::Binder
::dummy(sig
);
498 equate_intrinsic_type(tcx
, it
, n_tps
, 0, sig
)