]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_typeck/src/check/intrinsic.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / intrinsic.rs
1 //! Type-checking for the rust-intrinsic and platform-intrinsic
2 //! intrinsics that the compiler exposes.
3
4 use crate::errors::{
5 SimdShuffleMissingLength, UnrecognizedAtomicOperation, UnrecognizedIntrinsicFunction,
6 WrongNumberOfTypeArgumentsToInstrinsic,
7 };
8 use crate::require_same_types;
9
10 use rustc_errors::struct_span_err;
11 use rustc_hir as hir;
12 use rustc_hir::def_id::DefId;
13 use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
14 use rustc_middle::ty::subst::Subst;
15 use rustc_middle::ty::{self, Ty, TyCtxt};
16 use rustc_span::symbol::{kw, sym, Symbol};
17 use rustc_target::spec::abi::Abi;
18
19 use std::iter;
20
21 fn equate_intrinsic_type<'tcx>(
22 tcx: TyCtxt<'tcx>,
23 it: &hir::ForeignItem<'_>,
24 def_id: DefId,
25 n_tps: usize,
26 abi: Abi,
27 safety: hir::Unsafety,
28 inputs: Vec<Ty<'tcx>>,
29 output: Ty<'tcx>,
30 ) {
31 match it.kind {
32 hir::ForeignItemKind::Fn(..) => {}
33 _ => {
34 struct_span_err!(tcx.sess, it.span, E0622, "intrinsic must be a function")
35 .span_label(it.span, "expected a function")
36 .emit();
37 return;
38 }
39 }
40
41 let i_n_tps = tcx.generics_of(def_id).own_counts().types;
42 if i_n_tps != n_tps {
43 let span = match it.kind {
44 hir::ForeignItemKind::Fn(_, _, ref generics) => generics.span,
45 _ => bug!(),
46 };
47
48 tcx.sess.emit_err(WrongNumberOfTypeArgumentsToInstrinsic {
49 span,
50 found: i_n_tps,
51 expected: n_tps,
52 });
53 return;
54 }
55
56 let fty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
57 inputs.into_iter(),
58 output,
59 false,
60 safety,
61 abi,
62 )));
63 let cause = ObligationCause::new(it.span, it.hir_id, ObligationCauseCode::IntrinsicType);
64 require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(def_id)), fty);
65 }
66
67 /// Returns `true` if the given intrinsic is unsafe to call or not.
68 pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
69 match intrinsic {
70 sym::abort
71 | sym::size_of
72 | sym::min_align_of
73 | sym::needs_drop
74 | sym::caller_location
75 | sym::size_of_val
76 | sym::min_align_of_val
77 | sym::add_with_overflow
78 | sym::sub_with_overflow
79 | sym::mul_with_overflow
80 | sym::wrapping_add
81 | sym::wrapping_sub
82 | sym::wrapping_mul
83 | sym::saturating_add
84 | sym::saturating_sub
85 | sym::rotate_left
86 | sym::rotate_right
87 | sym::ctpop
88 | sym::ctlz
89 | sym::cttz
90 | sym::bswap
91 | sym::bitreverse
92 | sym::discriminant_value
93 | sym::type_id
94 | sym::likely
95 | sym::unlikely
96 | sym::ptr_guaranteed_eq
97 | sym::ptr_guaranteed_ne
98 | sym::minnumf32
99 | sym::minnumf64
100 | sym::maxnumf32
101 | sym::rustc_peek
102 | sym::maxnumf64
103 | sym::type_name
104 | sym::variant_count => hir::Unsafety::Normal,
105 _ => hir::Unsafety::Unsafe,
106 }
107 }
108
109 /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
110 /// and in `library/core/src/intrinsics.rs`.
111 pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
112 let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n)));
113 let def_id = tcx.hir().local_def_id(it.hir_id).to_def_id();
114 let intrinsic_name = tcx.item_name(def_id);
115 let name_str = intrinsic_name.as_str();
116
117 let mk_va_list_ty = |mutbl| {
118 tcx.lang_items().va_list().map(|did| {
119 let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0)));
120 let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
121 let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
122 (
123 tcx.mk_ref(tcx.mk_region(env_region), ty::TypeAndMut { ty: va_list_ty, mutbl }),
124 va_list_ty,
125 )
126 })
127 };
128
129 let (n_tps, inputs, output, unsafety) = if name_str.starts_with("atomic_") {
130 let split: Vec<&str> = name_str.split('_').collect();
131 assert!(split.len() >= 2, "Atomic intrinsic in an incorrect format");
132
133 //We only care about the operation here
134 let (n_tps, inputs, output) = match split[1] {
135 "cxchg" | "cxchgweak" => (
136 1,
137 vec![tcx.mk_mut_ptr(param(0)), param(0), param(0)],
138 tcx.intern_tup(&[param(0), tcx.types.bool]),
139 ),
140 "load" => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)),
141 "store" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
142
143 "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax"
144 | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)),
145 "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()),
146 op => {
147 tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op });
148 return;
149 }
150 };
151 (n_tps, inputs, output, hir::Unsafety::Unsafe)
152 } else {
153 let unsafety = intrinsic_operation_unsafety(intrinsic_name);
154 let (n_tps, inputs, output) = match intrinsic_name {
155 sym::abort => (0, Vec::new(), tcx.types.never),
156 sym::unreachable => (0, Vec::new(), tcx.types.never),
157 sym::breakpoint => (0, Vec::new(), tcx.mk_unit()),
158 sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => {
159 (1, Vec::new(), tcx.types.usize)
160 }
161 sym::size_of_val | sym::min_align_of_val => {
162 (1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize)
163 }
164 sym::rustc_peek => (1, vec![param(0)], param(0)),
165 sym::caller_location => (0, vec![], tcx.caller_location_ty()),
166 sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => {
167 (1, Vec::new(), tcx.mk_unit())
168 }
169 sym::forget => (1, vec![param(0)], tcx.mk_unit()),
170 sym::transmute => (2, vec![param(0)], param(1)),
171 sym::move_val_init => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
172 sym::prefetch_read_data
173 | sym::prefetch_write_data
174 | sym::prefetch_read_instruction
175 | sym::prefetch_write_instruction => (
176 1,
177 vec![
178 tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
179 tcx.types.i32,
180 ],
181 tcx.mk_unit(),
182 ),
183 sym::drop_in_place => (1, vec![tcx.mk_mut_ptr(param(0))], tcx.mk_unit()),
184 sym::needs_drop => (1, Vec::new(), tcx.types.bool),
185
186 sym::type_name => (1, Vec::new(), tcx.mk_static_str()),
187 sym::type_id => (1, Vec::new(), tcx.types.u64),
188 sym::offset | sym::arith_offset => (
189 1,
190 vec![
191 tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
192 tcx.types.isize,
193 ],
194 tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
195 ),
196 sym::copy | sym::copy_nonoverlapping => (
197 1,
198 vec![
199 tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
200 tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
201 tcx.types.usize,
202 ],
203 tcx.mk_unit(),
204 ),
205 sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => (
206 1,
207 vec![
208 tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
209 tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
210 tcx.types.usize,
211 ],
212 tcx.mk_unit(),
213 ),
214 sym::write_bytes | sym::volatile_set_memory => (
215 1,
216 vec![
217 tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
218 tcx.types.u8,
219 tcx.types.usize,
220 ],
221 tcx.mk_unit(),
222 ),
223 sym::sqrtf32 => (0, vec![tcx.types.f32], tcx.types.f32),
224 sym::sqrtf64 => (0, vec![tcx.types.f64], tcx.types.f64),
225 sym::powif32 => (0, vec![tcx.types.f32, tcx.types.i32], tcx.types.f32),
226 sym::powif64 => (0, vec![tcx.types.f64, tcx.types.i32], tcx.types.f64),
227 sym::sinf32 => (0, vec![tcx.types.f32], tcx.types.f32),
228 sym::sinf64 => (0, vec![tcx.types.f64], tcx.types.f64),
229 sym::cosf32 => (0, vec![tcx.types.f32], tcx.types.f32),
230 sym::cosf64 => (0, vec![tcx.types.f64], tcx.types.f64),
231 sym::powf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
232 sym::powf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
233 sym::expf32 => (0, vec![tcx.types.f32], tcx.types.f32),
234 sym::expf64 => (0, vec![tcx.types.f64], tcx.types.f64),
235 sym::exp2f32 => (0, vec![tcx.types.f32], tcx.types.f32),
236 sym::exp2f64 => (0, vec![tcx.types.f64], tcx.types.f64),
237 sym::logf32 => (0, vec![tcx.types.f32], tcx.types.f32),
238 sym::logf64 => (0, vec![tcx.types.f64], tcx.types.f64),
239 sym::log10f32 => (0, vec![tcx.types.f32], tcx.types.f32),
240 sym::log10f64 => (0, vec![tcx.types.f64], tcx.types.f64),
241 sym::log2f32 => (0, vec![tcx.types.f32], tcx.types.f32),
242 sym::log2f64 => (0, vec![tcx.types.f64], tcx.types.f64),
243 sym::fmaf32 => (0, vec![tcx.types.f32, tcx.types.f32, tcx.types.f32], tcx.types.f32),
244 sym::fmaf64 => (0, vec![tcx.types.f64, tcx.types.f64, tcx.types.f64], tcx.types.f64),
245 sym::fabsf32 => (0, vec![tcx.types.f32], tcx.types.f32),
246 sym::fabsf64 => (0, vec![tcx.types.f64], tcx.types.f64),
247 sym::minnumf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
248 sym::minnumf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
249 sym::maxnumf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
250 sym::maxnumf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
251 sym::copysignf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
252 sym::copysignf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
253 sym::floorf32 => (0, vec![tcx.types.f32], tcx.types.f32),
254 sym::floorf64 => (0, vec![tcx.types.f64], tcx.types.f64),
255 sym::ceilf32 => (0, vec![tcx.types.f32], tcx.types.f32),
256 sym::ceilf64 => (0, vec![tcx.types.f64], tcx.types.f64),
257 sym::truncf32 => (0, vec![tcx.types.f32], tcx.types.f32),
258 sym::truncf64 => (0, vec![tcx.types.f64], tcx.types.f64),
259 sym::rintf32 => (0, vec![tcx.types.f32], tcx.types.f32),
260 sym::rintf64 => (0, vec![tcx.types.f64], tcx.types.f64),
261 sym::nearbyintf32 => (0, vec![tcx.types.f32], tcx.types.f32),
262 sym::nearbyintf64 => (0, vec![tcx.types.f64], tcx.types.f64),
263 sym::roundf32 => (0, vec![tcx.types.f32], tcx.types.f32),
264 sym::roundf64 => (0, vec![tcx.types.f64], tcx.types.f64),
265
266 sym::volatile_load | sym::unaligned_volatile_load => {
267 (1, vec![tcx.mk_imm_ptr(param(0))], param(0))
268 }
269 sym::volatile_store | sym::unaligned_volatile_store => {
270 (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit())
271 }
272
273 sym::ctpop
274 | sym::ctlz
275 | sym::ctlz_nonzero
276 | sym::cttz
277 | sym::cttz_nonzero
278 | sym::bswap
279 | sym::bitreverse => (1, vec![param(0)], param(0)),
280
281 sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
282 (1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool]))
283 }
284
285 sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
286 (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool)
287 }
288
289 sym::ptr_offset_from => {
290 (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize)
291 }
292 sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
293 (1, vec![param(0), param(0)], param(0))
294 }
295 sym::unchecked_shl | sym::unchecked_shr | sym::rotate_left | sym::rotate_right => {
296 (1, vec![param(0), param(0)], param(0))
297 }
298 sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => {
299 (1, vec![param(0), param(0)], param(0))
300 }
301 sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
302 (1, vec![param(0), param(0)], param(0))
303 }
304 sym::saturating_add | sym::saturating_sub => (1, vec![param(0), param(0)], param(0)),
305 sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
306 (1, vec![param(0), param(0)], param(0))
307 }
308 sym::float_to_int_unchecked => (2, vec![param(0)], param(1)),
309
310 sym::assume => (0, vec![tcx.types.bool], tcx.mk_unit()),
311 sym::likely => (0, vec![tcx.types.bool], tcx.types.bool),
312 sym::unlikely => (0, vec![tcx.types.bool], tcx.types.bool),
313
314 sym::discriminant_value => {
315 let assoc_items =
316 tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap());
317 let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id;
318
319 (
320 1,
321 vec![tcx.mk_imm_ref(
322 tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))),
323 param(0),
324 )],
325 tcx.mk_projection(discriminant_def_id, tcx.mk_substs([param(0).into()].iter())),
326 )
327 }
328
329 kw::Try => {
330 let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8);
331 let try_fn_ty = ty::Binder::bind(tcx.mk_fn_sig(
332 iter::once(mut_u8),
333 tcx.mk_unit(),
334 false,
335 hir::Unsafety::Normal,
336 Abi::Rust,
337 ));
338 let catch_fn_ty = ty::Binder::bind(tcx.mk_fn_sig(
339 [mut_u8, mut_u8].iter().cloned(),
340 tcx.mk_unit(),
341 false,
342 hir::Unsafety::Normal,
343 Abi::Rust,
344 ));
345 (
346 0,
347 vec![tcx.mk_fn_ptr(try_fn_ty), mut_u8, tcx.mk_fn_ptr(catch_fn_ty)],
348 tcx.types.i32,
349 )
350 }
351
352 sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) {
353 Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], tcx.mk_unit()),
354 None => bug!("`va_list` language item needed for C-variadic intrinsics"),
355 },
356
357 sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) {
358 Some((va_list_ref_ty, va_list_ty)) => {
359 let va_list_ptr_ty = tcx.mk_mut_ptr(va_list_ty);
360 (0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.mk_unit())
361 }
362 None => bug!("`va_list` language item needed for C-variadic intrinsics"),
363 },
364
365 sym::va_arg => match mk_va_list_ty(hir::Mutability::Mut) {
366 Some((va_list_ref_ty, _)) => (1, vec![va_list_ref_ty], param(0)),
367 None => bug!("`va_list` language item needed for C-variadic intrinsics"),
368 },
369
370 sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
371
372 other => {
373 tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other });
374 return;
375 }
376 };
377 (n_tps, inputs, output, unsafety)
378 };
379 equate_intrinsic_type(tcx, it, def_id, n_tps, Abi::RustIntrinsic, unsafety, inputs, output)
380 }
381
382 /// Type-check `extern "platform-intrinsic" { ... }` functions.
383 pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
384 let param = |n| {
385 let name = Symbol::intern(&format!("P{}", n));
386 tcx.mk_ty_param(n, name)
387 };
388
389 let def_id = tcx.hir().local_def_id(it.hir_id).to_def_id();
390 let name = it.ident.name;
391
392 let (n_tps, inputs, output) = match name {
393 sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => {
394 (2, vec![param(0), param(0)], param(1))
395 }
396 sym::simd_add
397 | sym::simd_sub
398 | sym::simd_mul
399 | sym::simd_rem
400 | sym::simd_div
401 | sym::simd_shl
402 | sym::simd_shr
403 | sym::simd_and
404 | sym::simd_or
405 | sym::simd_xor
406 | sym::simd_fmin
407 | sym::simd_fmax
408 | sym::simd_fpow
409 | sym::simd_saturating_add
410 | sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)),
411 sym::simd_fsqrt
412 | sym::simd_fsin
413 | sym::simd_fcos
414 | sym::simd_fexp
415 | sym::simd_fexp2
416 | sym::simd_flog2
417 | sym::simd_flog10
418 | sym::simd_flog
419 | sym::simd_fabs
420 | sym::simd_floor
421 | sym::simd_ceil => (1, vec![param(0)], param(0)),
422 sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)),
423 sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)),
424 sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)),
425 sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()),
426 sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
427 sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)),
428 sym::simd_cast => (2, vec![param(0)], param(1)),
429 sym::simd_bitmask => (2, vec![param(0)], param(1)),
430 sym::simd_select | sym::simd_select_bitmask => {
431 (2, vec![param(0), param(1), param(1)], param(1))
432 }
433 sym::simd_reduce_all | sym::simd_reduce_any => (1, vec![param(0)], tcx.types.bool),
434 sym::simd_reduce_add_ordered | sym::simd_reduce_mul_ordered => {
435 (2, vec![param(0), param(1)], param(1))
436 }
437 sym::simd_reduce_add_unordered
438 | sym::simd_reduce_mul_unordered
439 | sym::simd_reduce_and
440 | sym::simd_reduce_or
441 | sym::simd_reduce_xor
442 | sym::simd_reduce_min
443 | sym::simd_reduce_max
444 | sym::simd_reduce_min_nanless
445 | sym::simd_reduce_max_nanless => (2, vec![param(0)], param(1)),
446 name if name.as_str().starts_with("simd_shuffle") => {
447 match name.as_str()["simd_shuffle".len()..].parse() {
448 Ok(n) => {
449 let params = vec![param(0), param(0), tcx.mk_array(tcx.types.u32, n)];
450 (2, params, param(1))
451 }
452 Err(_) => {
453 tcx.sess.emit_err(SimdShuffleMissingLength { span: it.span, name });
454 return;
455 }
456 }
457 }
458 _ => {
459 let msg = format!("unrecognized platform-specific intrinsic function: `{}`", name);
460 tcx.sess.struct_span_err(it.span, &msg).emit();
461 return;
462 }
463 };
464
465 equate_intrinsic_type(
466 tcx,
467 it,
468 def_id,
469 n_tps,
470 Abi::PlatformIntrinsic,
471 hir::Unsafety::Unsafe,
472 inputs,
473 output,
474 )
475 }