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.
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.
11 //! Type-checking for the rust-intrinsic and platform-intrinsic
12 //! intrinsics that the compiler exposes.
16 use rustc
::ty
::subst
::{self, Substs}
;
18 use rustc
::ty
::{self, Ty, TyCtxt}
;
19 use rustc
::ty
::fold
::TypeFolder
;
20 use {CrateCtxt, require_same_types}
;
22 use std
::collections
::{HashMap}
;
25 use syntax
::attr
::AttrMetaMethods
;
26 use syntax
::codemap
::Span
;
27 use syntax
::parse
::token
;
31 fn equate_intrinsic_type
<'a
, 'tcx
>(tcx
: &TyCtxt
<'tcx
>, it
: &hir
::ForeignItem
,
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
);
39 let mut substs
= Substs
::empty();
40 substs
.types
= i_ty
.generics
.types
.map(|def
| tcx
.mk_param_from_def(def
));
42 let fty
= tcx
.mk_fn_def(def_id
, tcx
.mk_substs(substs
), ty
::BareFnTy
{
43 unsafety
: hir
::Unsafety
::Unsafe
,
45 sig
: ty
::Binder(FnSig
{
51 let i_n_tps
= i_ty
.generics
.types
.len(subst
::FnSpace
);
53 span_err
!(tcx
.sess
, it
.span
, E0094
,
54 "intrinsic has wrong number of type \
55 parameters: found {}, expected {}",
58 require_same_types(tcx
,
65 format
!("intrinsic has wrong type: expected `{}`",
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
)
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");
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)),
90 tcx
.mk_tup(vec
!(param(ccx
, 0), tcx
.types
.bool
))),
91 "load" => (1, vec
!(tcx
.mk_imm_ptr(param(ccx
, 0))),
93 "store" => (1, vec
!(tcx
.mk_mut_ptr(param(ccx
, 0)), param(ccx
, 0)),
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)),
101 "fence" | "singlethreadfence" => {
102 (0, Vec
::new(), tcx
.mk_nil())
105 span_err
!(tcx
.sess
, it
.span
, E0092
,
106 "unrecognized atomic operation function: `{}`", op
);
110 (n_tps
, inputs
, ty
::FnConverging(output
))
111 } else if &name
[..] == "abort" || &name
[..] == "unreachable" {
112 (0, Vec
::new(), ty
::FnDiverging
)
114 let (n_tps
, inputs
, output
) = match &name
[..] {
115 "breakpoint" => (0, Vec
::new(), tcx
.mk_nil()),
117 "pref_align_of" | "min_align_of" => (1, Vec
::new(), ccx
.tcx
.types
.usize),
118 "size_of_val" | "min_align_of_val" => {
120 tcx
.mk_imm_ref(tcx
.mk_region(ty
::ReLateBound(ty
::DebruijnIndex
::new(1),
123 ], ccx
.tcx
.types
.usize)
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)),
132 tcx
.mk_mut_ptr(param(ccx
, 0)),
138 (1, vec
![tcx
.mk_mut_ptr(param(ccx
, 0))], tcx
.mk_nil())
140 "needs_drop" => (1, Vec
::new(), ccx
.tcx
.types
.bool
),
142 "type_name" => (1, Vec
::new(), tcx
.mk_static_str()),
143 "type_id" => (1, Vec
::new(), ccx
.tcx
.types
.u64),
144 "offset" | "arith_offset" => {
147 tcx
.mk_ptr(ty
::TypeAndMut
{
149 mutbl
: hir
::MutImmutable
153 tcx
.mk_ptr(ty
::TypeAndMut
{
155 mutbl
: hir
::MutImmutable
158 "copy" | "copy_nonoverlapping" => {
161 tcx
.mk_ptr(ty
::TypeAndMut
{
163 mutbl
: hir
::MutImmutable
165 tcx
.mk_ptr(ty
::TypeAndMut
{
167 mutbl
: hir
::MutMutable
173 "volatile_copy_memory" | "volatile_copy_nonoverlapping_memory" => {
176 tcx
.mk_ptr(ty
::TypeAndMut
{
178 mutbl
: hir
::MutMutable
180 tcx
.mk_ptr(ty
::TypeAndMut
{
182 mutbl
: hir
::MutImmutable
188 "write_bytes" | "volatile_set_memory" => {
191 tcx
.mk_ptr(ty
::TypeAndMut
{
193 mutbl
: hir
::MutMutable
200 "sqrtf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
201 "sqrtf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
204 vec
!( tcx
.types
.f32, tcx
.types
.i32 ),
209 vec
!( tcx
.types
.f64, tcx
.types
.i32 ),
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),
218 vec
!( tcx
.types
.f32, tcx
.types
.f32 ),
223 vec
!( tcx
.types
.f64, tcx
.types
.f64 ),
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),
238 vec
!( tcx
.types
.f32, tcx
.types
.f32, tcx
.types
.f32 ),
243 vec
!( tcx
.types
.f64, tcx
.types
.f64, tcx
.types
.f64 ),
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),
264 (1, vec
!( tcx
.mk_imm_ptr(param(ccx
, 0)) ), param(ccx
, 0)),
266 (1, vec
!( tcx
.mk_mut_ptr(param(ccx
, 0)), param(ccx
, 0) ), tcx
.mk_nil()),
268 "ctpop" | "ctlz" | "cttz" | "bswap" => (1, vec
!(param(ccx
, 0)), param(ccx
, 0)),
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
))),
274 "unchecked_div" | "unchecked_rem" =>
275 (1, vec
![param(ccx
, 0), param(ccx
, 0)], param(ccx
, 0)),
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)),
282 "return_address" => (0, vec
![], tcx
.mk_imm_ptr(tcx
.types
.u8)),
284 "assume" => (0, vec
![tcx
.types
.bool
], tcx
.mk_nil()),
286 "discriminant_value" => (1, vec
![
287 tcx
.mk_imm_ref(tcx
.mk_region(ty
::ReLateBound(ty
::DebruijnIndex
::new(1),
289 param(ccx
, 0))], tcx
.types
.u64),
292 let mut_u8
= tcx
.mk_mut_ptr(tcx
.types
.u8);
293 let fn_ty
= ty
::BareFnTy
{
294 unsafety
: hir
::Unsafety
::Normal
,
296 sig
: ty
::Binder(FnSig
{
297 inputs
: vec
![mut_u8
],
298 output
: ty
::FnOutput
::FnConverging(tcx
.mk_nil()),
302 (0, vec
![tcx
.mk_fn_ptr(fn_ty
), mut_u8
, mut_u8
], tcx
.types
.i32)
306 span_err
!(tcx
.sess
, it
.span
, E0093
,
307 "unrecognized intrinsic function: `{}`", *other
);
311 (n_tps
, inputs
, ty
::FnConverging(output
))
313 equate_intrinsic_type(
323 /// Type-check `extern "platform-intrinsic" { ... }` functions.
324 pub fn check_platform_intrinsic_type(ccx
: &CrateCtxt
,
325 it
: &hir
::ForeignItem
) {
327 let name
= token
::intern(&format
!("P{}", n
));
328 ccx
.tcx
.mk_param(subst
::FnSpace
, n
, name
)
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();
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))
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))
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() {
351 let params
= vec
![param(0), param(0),
352 tcx
.mk_ty(ty
::TyArray(tcx
.types
.u32, n
))];
353 (2, params
, param(1))
356 span_err
!(tcx
.sess
, it
.span
, E0439
,
357 "invalid `simd_shuffle`, needs length: `{}`", name
);
363 match intrinsics
::Intrinsic
::find(&name
) {
365 // this function is a platform specific intrinsic
367 span_err
!(tcx
.sess
, it
.span
, E0440
,
368 "platform-specific intrinsic has wrong number of type \
369 parameters: found {}, expected 0",
374 let mut structural_to_nomimal
= HashMap
::new();
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());
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
);
389 match_intrinsic_type_to_type(tcx
, "return value", it
.span
,
390 &mut structural_to_nomimal
,
391 &intr
.output
, sig
.output
.unwrap());
395 span_err
!(tcx
.sess
, it
.span
, E0441
,
396 "unrecognized platform-specific intrinsic function: `{}`", name
);
403 equate_intrinsic_type(
407 Abi
::PlatformIntrinsic
,
409 ty
::FnConverging(output
)
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
>(
420 structural_to_nominal
: &mut HashMap
<&'a intrinsics
::Type
, ty
::Ty
<'tcx
>>,
421 expected
: &'a intrinsics
::Type
, t
: ty
::Ty
<'tcx
>)
423 use intrinsics
::Type
::*;
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
)
432 Void
=> match t
.sty
{
433 ty
::TyTuple(ref v
) if v
.is_empty() => {}
,
434 _
=> simple_error(&format
!("`{}`", t
), "()"),
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
),
448 if signed {"i"}
else {"u"}
,
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
)),
457 Pointer(ref inner_expected
, ref _llvm_type
, const_
) => {
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"}
)
464 match_intrinsic_type_to_type(tcx
, position
, span
, structural_to_nominal
,
467 _
=> simple_error(&format
!("`{}`", t
), "raw pointer"),
470 Vector(ref inner_expected
, ref _llvm_type
, len
) => {
472 simple_error(&format
!("non-simd type `{}`", t
), "simd type");
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
));
481 let t_ty
= t
.simd_type(tcx
);
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
);
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",
496 match_intrinsic_type_to_type(tcx
,
499 structural_to_nominal
,
503 Aggregate(_flatten
, ref expected_contents
) => {
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()));
511 for (e
, c
) in expected_contents
.iter().zip(contents
) {
512 match_intrinsic_type_to_type(tcx
, position
, span
, structural_to_nominal
,
516 _
=> simple_error(&format
!("`{}`", t
),