]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/intrinsic.rs
60067e6a6ec0238e9c8da62e73ec9f60ed71e31c
[rust.git] / src / librustc_typeck / check / intrinsic.rs
1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Type-checking for the rust-intrinsic and platform-intrinsic
12 //! intrinsics that the compiler exposes.
13
14 use intrinsics;
15 use rustc::traits::{ObligationCause, ObligationCauseCode};
16 use rustc::ty::subst::Substs;
17 use rustc::ty::{self, TyCtxt, Ty};
18 use rustc::util::nodemap::FxHashMap;
19 use require_same_types;
20
21 use syntax::abi::Abi;
22 use syntax::ast;
23 use syntax::symbol::Symbol;
24 use syntax_pos::Span;
25
26 use rustc::hir;
27
28 use std::iter;
29
30 fn equate_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
31                                    it: &hir::ForeignItem,
32                                    n_tps: usize,
33                                    abi: Abi,
34                                    inputs: Vec<Ty<'tcx>>,
35                                    output: Ty<'tcx>) {
36     let def_id = tcx.hir.local_def_id(it.id);
37
38     let substs = Substs::for_item(tcx, def_id,
39                                   |_, _| tcx.types.re_erased,
40                                   |def, _| tcx.mk_param_from_def(def));
41
42     let fty = tcx.mk_fn_def(def_id, substs, ty::Binder(tcx.mk_fn_sig(
43         inputs.into_iter(),
44         output,
45         false,
46         hir::Unsafety::Unsafe,
47         abi
48     )));
49     let i_n_tps = tcx.generics_of(def_id).types.len();
50     if i_n_tps != n_tps {
51         let span = match it.node {
52             hir::ForeignItemFn(_, _, ref generics) => generics.span,
53             hir::ForeignItemStatic(..) => it.span
54         };
55
56         struct_span_err!(tcx.sess, span, E0094,
57                         "intrinsic has wrong number of type \
58                         parameters: found {}, expected {}",
59                         i_n_tps, n_tps)
60             .span_label(span, format!("expected {} type parameter", n_tps))
61             .emit();
62     } else {
63         require_same_types(tcx,
64                            &ObligationCause::new(it.span,
65                                                  it.id,
66                                                  ObligationCauseCode::IntrinsicType),
67                            tcx.type_of(def_id),
68                            fty);
69     }
70 }
71
72 /// Remember to add all intrinsics here, in librustc_trans/trans/intrinsic.rs,
73 /// and in libcore/intrinsics.rs
74 pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
75                                       it: &hir::ForeignItem) {
76     let param = |n| tcx.mk_param(n, Symbol::intern(&format!("P{}", n)));
77     let name = it.name.as_str();
78     let (n_tps, inputs, output) = if name.starts_with("atomic_") {
79         let split : Vec<&str> = name.split('_').collect();
80         assert!(split.len() >= 2, "Atomic intrinsic not correct format");
81
82         //We only care about the operation here
83         let (n_tps, inputs, output) = match split[1] {
84             "cxchg" | "cxchgweak" => (1, vec![tcx.mk_mut_ptr(param(0)),
85                                               param(0),
86                                               param(0)],
87                                       tcx.intern_tup(&[param(0), tcx.types.bool], false)),
88             "load" => (1, vec![tcx.mk_imm_ptr(param(0))],
89                        param(0)),
90             "store" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)],
91                         tcx.mk_nil()),
92
93             "xchg" | "xadd" | "xsub" | "and"  | "nand" | "or" | "xor" | "max" |
94             "min"  | "umax" | "umin" => {
95                 (1, vec![tcx.mk_mut_ptr(param(0)), param(0)],
96                  param(0))
97             }
98             "fence" | "singlethreadfence" => {
99                 (0, Vec::new(), tcx.mk_nil())
100             }
101             op => {
102                 struct_span_err!(tcx.sess, it.span, E0092,
103                       "unrecognized atomic operation function: `{}`", op)
104                   .span_label(it.span, "unrecognized atomic operation")
105                   .emit();
106                 return;
107             }
108         };
109         (n_tps, inputs, output)
110     } else if &name[..] == "abort" || &name[..] == "unreachable" {
111         (0, Vec::new(), tcx.types.never)
112     } else {
113         let (n_tps, inputs, output) = match &name[..] {
114             "breakpoint" => (0, Vec::new(), tcx.mk_nil()),
115             "size_of" |
116             "pref_align_of" | "min_align_of" => (1, Vec::new(), tcx.types.usize),
117             "size_of_val" |  "min_align_of_val" => {
118                 (1, vec![
119                     tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
120                                                                   ty::BrAnon(0))),
121                                     param(0))
122                  ], tcx.types.usize)
123             }
124             "rustc_peek" => (1, vec![param(0)], param(0)),
125             "init" => (1, Vec::new(), param(0)),
126             "uninit" => (1, Vec::new(), param(0)),
127             "transmute" => (2, vec![ param(0) ], param(1)),
128             "move_val_init" => {
129                 (1,
130                  vec![
131                     tcx.mk_mut_ptr(param(0)),
132                     param(0)
133                   ],
134                tcx.mk_nil())
135             }
136             "drop_in_place" => {
137                 (1, vec![tcx.mk_mut_ptr(param(0))], tcx.mk_nil())
138             }
139             "needs_drop" => (1, Vec::new(), tcx.types.bool),
140
141             "type_name" => (1, Vec::new(), tcx.mk_static_str()),
142             "type_id" => (1, Vec::new(), tcx.types.u64),
143             "offset" | "arith_offset" => {
144               (1,
145                vec![
146                   tcx.mk_ptr(ty::TypeAndMut {
147                       ty: param(0),
148                       mutbl: hir::MutImmutable
149                   }),
150                   tcx.types.isize
151                ],
152                tcx.mk_ptr(ty::TypeAndMut {
153                    ty: param(0),
154                    mutbl: hir::MutImmutable
155                }))
156             }
157             "copy" | "copy_nonoverlapping" => {
158               (1,
159                vec![
160                   tcx.mk_ptr(ty::TypeAndMut {
161                       ty: param(0),
162                       mutbl: hir::MutImmutable
163                   }),
164                   tcx.mk_ptr(ty::TypeAndMut {
165                       ty: param(0),
166                       mutbl: hir::MutMutable
167                   }),
168                   tcx.types.usize,
169                ],
170                tcx.mk_nil())
171             }
172             "volatile_copy_memory" | "volatile_copy_nonoverlapping_memory" => {
173               (1,
174                vec![
175                   tcx.mk_ptr(ty::TypeAndMut {
176                       ty: param(0),
177                       mutbl: hir::MutMutable
178                   }),
179                   tcx.mk_ptr(ty::TypeAndMut {
180                       ty: param(0),
181                       mutbl: hir::MutImmutable
182                   }),
183                   tcx.types.usize,
184                ],
185                tcx.mk_nil())
186             }
187             "write_bytes" | "volatile_set_memory" => {
188               (1,
189                vec![
190                   tcx.mk_ptr(ty::TypeAndMut {
191                       ty: param(0),
192                       mutbl: hir::MutMutable
193                   }),
194                   tcx.types.u8,
195                   tcx.types.usize,
196                ],
197                tcx.mk_nil())
198             }
199             "sqrtf32" => (0, vec![ tcx.types.f32 ], tcx.types.f32),
200             "sqrtf64" => (0, vec![ tcx.types.f64 ], tcx.types.f64),
201             "powif32" => {
202                (0,
203                 vec![ tcx.types.f32, tcx.types.i32 ],
204                 tcx.types.f32)
205             }
206             "powif64" => {
207                (0,
208                 vec![ tcx.types.f64, tcx.types.i32 ],
209                 tcx.types.f64)
210             }
211             "sinf32" => (0, vec![ tcx.types.f32 ], tcx.types.f32),
212             "sinf64" => (0, vec![ tcx.types.f64 ], tcx.types.f64),
213             "cosf32" => (0, vec![ tcx.types.f32 ], tcx.types.f32),
214             "cosf64" => (0, vec![ tcx.types.f64 ], tcx.types.f64),
215             "powf32" => {
216                (0,
217                 vec![ tcx.types.f32, tcx.types.f32 ],
218                 tcx.types.f32)
219             }
220             "powf64" => {
221                (0,
222                 vec![ tcx.types.f64, tcx.types.f64 ],
223                 tcx.types.f64)
224             }
225             "expf32"   => (0, vec![ tcx.types.f32 ], tcx.types.f32),
226             "expf64"   => (0, vec![ tcx.types.f64 ], tcx.types.f64),
227             "exp2f32"  => (0, vec![ tcx.types.f32 ], tcx.types.f32),
228             "exp2f64"  => (0, vec![ tcx.types.f64 ], tcx.types.f64),
229             "logf32"   => (0, vec![ tcx.types.f32 ], tcx.types.f32),
230             "logf64"   => (0, vec![ tcx.types.f64 ], tcx.types.f64),
231             "log10f32" => (0, vec![ tcx.types.f32 ], tcx.types.f32),
232             "log10f64" => (0, vec![ tcx.types.f64 ], tcx.types.f64),
233             "log2f32"  => (0, vec![ tcx.types.f32 ], tcx.types.f32),
234             "log2f64"  => (0, vec![ tcx.types.f64 ], tcx.types.f64),
235             "fmaf32" => {
236                 (0,
237                  vec![ tcx.types.f32, tcx.types.f32, tcx.types.f32 ],
238                  tcx.types.f32)
239             }
240             "fmaf64" => {
241                 (0,
242                  vec![ tcx.types.f64, tcx.types.f64, tcx.types.f64 ],
243                  tcx.types.f64)
244             }
245             "fabsf32"      => (0, vec![ tcx.types.f32 ], tcx.types.f32),
246             "fabsf64"      => (0, vec![ tcx.types.f64 ], tcx.types.f64),
247             "copysignf32"  => (0, vec![ tcx.types.f32, tcx.types.f32 ], tcx.types.f32),
248             "copysignf64"  => (0, vec![ tcx.types.f64, tcx.types.f64 ], tcx.types.f64),
249             "floorf32"     => (0, vec![ tcx.types.f32 ], tcx.types.f32),
250             "floorf64"     => (0, vec![ tcx.types.f64 ], tcx.types.f64),
251             "ceilf32"      => (0, vec![ tcx.types.f32 ], tcx.types.f32),
252             "ceilf64"      => (0, vec![ tcx.types.f64 ], tcx.types.f64),
253             "truncf32"     => (0, vec![ tcx.types.f32 ], tcx.types.f32),
254             "truncf64"     => (0, vec![ tcx.types.f64 ], tcx.types.f64),
255             "rintf32"      => (0, vec![ tcx.types.f32 ], tcx.types.f32),
256             "rintf64"      => (0, vec![ tcx.types.f64 ], tcx.types.f64),
257             "nearbyintf32" => (0, vec![ tcx.types.f32 ], tcx.types.f32),
258             "nearbyintf64" => (0, vec![ tcx.types.f64 ], tcx.types.f64),
259             "roundf32"     => (0, vec![ tcx.types.f32 ], tcx.types.f32),
260             "roundf64"     => (0, vec![ tcx.types.f64 ], tcx.types.f64),
261
262             "volatile_load" =>
263                 (1, vec![ tcx.mk_imm_ptr(param(0)) ], param(0)),
264             "volatile_store" =>
265                 (1, vec![ tcx.mk_mut_ptr(param(0)), param(0) ], tcx.mk_nil()),
266
267             "ctpop" | "ctlz" | "cttz" | "bswap" => (1, vec![param(0)], param(0)),
268
269             "add_with_overflow" | "sub_with_overflow"  | "mul_with_overflow" =>
270                 (1, vec![param(0), param(0)],
271                 tcx.intern_tup(&[param(0), tcx.types.bool], false)),
272
273             "unchecked_div" | "unchecked_rem" =>
274                 (1, vec![param(0), param(0)], param(0)),
275             "unchecked_shl" | "unchecked_shr" =>
276                 (1, vec![param(0), param(0)], param(0)),
277
278             "overflowing_add" | "overflowing_sub" | "overflowing_mul" =>
279                 (1, vec![param(0), param(0)], param(0)),
280             "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" =>
281                 (1, vec![param(0), param(0)], param(0)),
282
283             "assume" => (0, vec![tcx.types.bool], tcx.mk_nil()),
284             "likely" => (0, vec![tcx.types.bool], tcx.types.bool),
285             "unlikely" => (0, vec![tcx.types.bool], tcx.types.bool),
286
287             "discriminant_value" => (1, vec![
288                     tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
289                                                                   ty::BrAnon(0))),
290                                    param(0))], tcx.types.u64),
291
292             "try" => {
293                 let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8);
294                 let fn_ty = ty::Binder(tcx.mk_fn_sig(
295                     iter::once(mut_u8),
296                     tcx.mk_nil(),
297                     false,
298                     hir::Unsafety::Normal,
299                     Abi::Rust,
300                 ));
301                 (0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32)
302             }
303
304             ref other => {
305                 struct_span_err!(tcx.sess, it.span, E0093,
306                                 "unrecognized intrinsic function: `{}`",
307                                 *other)
308                                 .span_label(it.span, "unrecognized intrinsic")
309                                 .emit();
310                 return;
311             }
312         };
313         (n_tps, inputs, output)
314     };
315     equate_intrinsic_type(tcx, it, n_tps, Abi::RustIntrinsic, inputs, output)
316 }
317
318 /// Type-check `extern "platform-intrinsic" { ... }` functions.
319 pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
320                                                it: &hir::ForeignItem) {
321     let param = |n| {
322         let name = Symbol::intern(&format!("P{}", n));
323         tcx.mk_param(n, name)
324     };
325
326     let def_id = tcx.hir.local_def_id(it.id);
327     let i_n_tps = tcx.generics_of(def_id).types.len();
328     let name = it.name.as_str();
329
330     let (n_tps, inputs, output) = match &*name {
331         "simd_eq" | "simd_ne" | "simd_lt" | "simd_le" | "simd_gt" | "simd_ge" => {
332             (2, vec![param(0), param(0)], param(1))
333         }
334         "simd_add" | "simd_sub" | "simd_mul" |
335         "simd_div" | "simd_shl" | "simd_shr" |
336         "simd_and" | "simd_or" | "simd_xor" => {
337             (1, vec![param(0), param(0)], param(0))
338         }
339         "simd_insert" => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
340         "simd_extract" => (2, vec![param(0), tcx.types.u32], param(1)),
341         "simd_cast" => (2, vec![param(0)], param(1)),
342         name if name.starts_with("simd_shuffle") => {
343             match name["simd_shuffle".len()..].parse() {
344                 Ok(n) => {
345                     let params = vec![param(0), param(0),
346                                       tcx.mk_ty(ty::TyArray(tcx.types.u32, n))];
347                     (2, params, param(1))
348                 }
349                 Err(_) => {
350                     span_err!(tcx.sess, it.span, E0439,
351                               "invalid `simd_shuffle`, needs length: `{}`", name);
352                     return
353                 }
354             }
355         }
356         _ => {
357             match intrinsics::Intrinsic::find(&name) {
358                 Some(intr) => {
359                     // this function is a platform specific intrinsic
360                     if i_n_tps != 0 {
361                         span_err!(tcx.sess, it.span, E0440,
362                                   "platform-specific intrinsic has wrong number of type \
363                                    parameters: found {}, expected 0",
364                                   i_n_tps);
365                         return
366                     }
367
368                     let mut structural_to_nomimal = FxHashMap();
369
370                     let sig = tcx.type_of(def_id).fn_sig();
371                     let sig = tcx.no_late_bound_regions(&sig).unwrap();
372                     if intr.inputs.len() != sig.inputs().len() {
373                         span_err!(tcx.sess, it.span, E0444,
374                                   "platform-specific intrinsic has invalid number of \
375                                    arguments: found {}, expected {}",
376                                   sig.inputs().len(), intr.inputs.len());
377                         return
378                     }
379                     let input_pairs = intr.inputs.iter().zip(sig.inputs());
380                     for (i, (expected_arg, arg)) in input_pairs.enumerate() {
381                         match_intrinsic_type_to_type(tcx, &format!("argument {}", i + 1), it.span,
382                                                      &mut structural_to_nomimal, expected_arg, arg);
383                     }
384                     match_intrinsic_type_to_type(tcx, "return value", it.span,
385                                                  &mut structural_to_nomimal,
386                                                  &intr.output, sig.output());
387                     return
388                 }
389                 None => {
390                     span_err!(tcx.sess, it.span, E0441,
391                               "unrecognized platform-specific intrinsic function: `{}`", name);
392                     return;
393                 }
394             }
395         }
396     };
397
398     equate_intrinsic_type(tcx, it, n_tps, Abi::PlatformIntrinsic,
399                           inputs, output)
400 }
401
402 // walk the expected type and the actual type in lock step, checking they're
403 // the same, in a kinda-structural way, i.e. `Vector`s have to be simd structs with
404 // exactly the right element type
405 fn match_intrinsic_type_to_type<'a, 'tcx>(
406         tcx: TyCtxt<'a, 'tcx, 'tcx>,
407         position: &str,
408         span: Span,
409         structural_to_nominal: &mut FxHashMap<&'a intrinsics::Type, ty::Ty<'tcx>>,
410         expected: &'a intrinsics::Type, t: ty::Ty<'tcx>)
411 {
412     use intrinsics::Type::*;
413
414     let simple_error = |real: &str, expected: &str| {
415         span_err!(tcx.sess, span, E0442,
416                   "intrinsic {} has wrong type: found {}, expected {}",
417                   position, real, expected)
418     };
419
420     match *expected {
421         Void => match t.sty {
422             ty::TyTuple(ref v, _) if v.is_empty() => {},
423             _ => simple_error(&format!("`{}`", t), "()"),
424         },
425         // (The width we pass to LLVM doesn't concern the type checker.)
426         Integer(signed, bits, _llvm_width) => match (signed, bits, &t.sty) {
427             (true,  8,  &ty::TyInt(ast::IntTy::I8)) |
428             (false, 8,  &ty::TyUint(ast::UintTy::U8)) |
429             (true,  16, &ty::TyInt(ast::IntTy::I16)) |
430             (false, 16, &ty::TyUint(ast::UintTy::U16)) |
431             (true,  32, &ty::TyInt(ast::IntTy::I32)) |
432             (false, 32, &ty::TyUint(ast::UintTy::U32)) |
433             (true,  64, &ty::TyInt(ast::IntTy::I64)) |
434             (false, 64, &ty::TyUint(ast::UintTy::U64)) |
435             (true,  128, &ty::TyInt(ast::IntTy::I128)) |
436             (false, 128, &ty::TyUint(ast::UintTy::U128)) => {},
437             _ => simple_error(&format!("`{}`", t),
438                               &format!("`{}{n}`",
439                                        if signed {"i"} else {"u"},
440                                        n = bits)),
441         },
442         Float(bits) => match (bits, &t.sty) {
443             (32, &ty::TyFloat(ast::FloatTy::F32)) |
444             (64, &ty::TyFloat(ast::FloatTy::F64)) => {},
445             _ => simple_error(&format!("`{}`", t),
446                               &format!("`f{n}`", n = bits)),
447         },
448         Pointer(ref inner_expected, ref _llvm_type, const_) => {
449             match t.sty {
450                 ty::TyRawPtr(ty::TypeAndMut { ty, mutbl }) => {
451                     if (mutbl == hir::MutImmutable) != const_ {
452                         simple_error(&format!("`{}`", t),
453                                      if const_ {"const pointer"} else {"mut pointer"})
454                     }
455                     match_intrinsic_type_to_type(tcx, position, span, structural_to_nominal,
456                                                  inner_expected, ty)
457                 }
458                 _ => simple_error(&format!("`{}`", t), "raw pointer"),
459             }
460         }
461         Vector(ref inner_expected, ref _llvm_type, len) => {
462             if !t.is_simd() {
463                 simple_error(&format!("non-simd type `{}`", t), "simd type");
464                 return;
465             }
466             let t_len = t.simd_size(tcx);
467             if len as usize != t_len {
468                 simple_error(&format!("vector with length {}", t_len),
469                              &format!("length {}", len));
470                 return;
471             }
472             let t_ty = t.simd_type(tcx);
473             {
474                 // check that a given structural type always has the same an intrinsic definition
475                 let previous = structural_to_nominal.entry(expected).or_insert(t);
476                 if *previous != t {
477                     // this gets its own error code because it is non-trivial
478                     span_err!(tcx.sess, span, E0443,
479                               "intrinsic {} has wrong type: found `{}`, expected `{}` which \
480                                was used for this vector type previously in this signature",
481                               position,
482                               t,
483                               *previous);
484                     return;
485                 }
486             }
487             match_intrinsic_type_to_type(tcx,
488                                          position,
489                                          span,
490                                          structural_to_nominal,
491                                          inner_expected,
492                                          t_ty)
493         }
494         Aggregate(_flatten, ref expected_contents) => {
495             match t.sty {
496                 ty::TyTuple(contents, _) => {
497                     if contents.len() != expected_contents.len() {
498                         simple_error(&format!("tuple with length {}", contents.len()),
499                                      &format!("tuple with length {}", expected_contents.len()));
500                         return
501                     }
502                     for (e, c) in expected_contents.iter().zip(contents) {
503                         match_intrinsic_type_to_type(tcx, position, span, structural_to_nominal,
504                                                      e, c)
505                     }
506                 }
507                 _ => simple_error(&format!("`{}`", t),
508                                   "tuple"),
509             }
510         }
511     }
512 }