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