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