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.
17 use middle
::ty
::FnSig
;
18 use middle
::ty
::{self, Ty}
;
19 use middle
::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
: &ty
::ctxt
<'tcx
>, it
: &hir
::ForeignItem
,
34 inputs
: Vec
<ty
::Ty
<'tcx
>>,
35 output
: ty
::FnOutput
<'tcx
>) {
36 let fty
= tcx
.mk_fn(None
, tcx
.mk_bare_fn(ty
::BareFnTy
{
37 unsafety
: hir
::Unsafety
::Unsafe
,
39 sig
: ty
::Binder(FnSig
{
45 let i_ty
= tcx
.lookup_item_type(tcx
.map
.local_def_id(it
.id
));
46 let i_n_tps
= i_ty
.generics
.types
.len(subst
::FnSpace
);
48 span_err
!(tcx
.sess
, it
.span
, E0094
,
49 "intrinsic has wrong number of type \
50 parameters: found {}, expected {}",
53 require_same_types(tcx
,
60 format
!("intrinsic has wrong type: expected `{}`",
66 /// Remember to add all intrinsics here, in librustc_trans/trans/intrinsic.rs,
67 /// and in libcore/intrinsics.rs
68 pub fn check_intrinsic_type(ccx
: &CrateCtxt
, it
: &hir
::ForeignItem
) {
69 fn param
<'a
, 'tcx
>(ccx
: &CrateCtxt
<'a
, 'tcx
>, n
: u32) -> Ty
<'tcx
> {
70 let name
= token
::intern(&format
!("P{}", n
));
71 ccx
.tcx
.mk_param(subst
::FnSpace
, n
, name
)
75 let name
= it
.name
.as_str();
76 let (n_tps
, inputs
, output
) = if name
.starts_with("atomic_") {
77 let split
: Vec
<&str> = name
.split('_'
).collect();
78 assert
!(split
.len() >= 2, "Atomic intrinsic not correct format");
80 //We only care about the operation here
81 let (n_tps
, inputs
, output
) = match split
[1] {
82 "cxchg" => (1, vec
!(tcx
.mk_mut_ptr(param(ccx
, 0)),
86 "load" => (1, vec
!(tcx
.mk_imm_ptr(param(ccx
, 0))),
88 "store" => (1, vec
!(tcx
.mk_mut_ptr(param(ccx
, 0)), param(ccx
, 0)),
91 "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" |
92 "min" | "umax" | "umin" => {
93 (1, vec
!(tcx
.mk_mut_ptr(param(ccx
, 0)), param(ccx
, 0)),
96 "fence" | "singlethreadfence" => {
97 (0, Vec
::new(), tcx
.mk_nil())
100 span_err
!(tcx
.sess
, it
.span
, E0092
,
101 "unrecognized atomic operation function: `{}`", op
);
105 (n_tps
, inputs
, ty
::FnConverging(output
))
106 } else if &name
[..] == "abort" || &name
[..] == "unreachable" {
107 (0, Vec
::new(), ty
::FnDiverging
)
109 let (n_tps
, inputs
, output
) = match &name
[..] {
110 "breakpoint" => (0, Vec
::new(), tcx
.mk_nil()),
112 "pref_align_of" | "min_align_of" => (1, Vec
::new(), ccx
.tcx
.types
.usize),
113 "size_of_val" | "min_align_of_val" => {
115 tcx
.mk_imm_ref(tcx
.mk_region(ty
::ReLateBound(ty
::DebruijnIndex
::new(1),
118 ], ccx
.tcx
.types
.usize)
120 "init" | "init_dropped" => (1, Vec
::new(), param(ccx
, 0)),
121 "uninit" => (1, Vec
::new(), param(ccx
, 0)),
122 "forget" => (1, vec
!( param(ccx
, 0) ), tcx
.mk_nil()),
123 "transmute" => (2, vec
!( param(ccx
, 0) ), param(ccx
, 1)),
127 tcx
.mk_mut_ptr(param(ccx
, 0)),
133 (1, vec
![tcx
.mk_mut_ptr(param(ccx
, 0))], tcx
.mk_nil())
135 "needs_drop" => (1, Vec
::new(), ccx
.tcx
.types
.bool
),
137 "type_name" => (1, Vec
::new(), tcx
.mk_static_str()),
138 "type_id" => (1, Vec
::new(), ccx
.tcx
.types
.u64),
139 "offset" | "arith_offset" => {
142 tcx
.mk_ptr(ty
::TypeAndMut
{
144 mutbl
: hir
::MutImmutable
148 tcx
.mk_ptr(ty
::TypeAndMut
{
150 mutbl
: hir
::MutImmutable
153 "copy" | "copy_nonoverlapping" => {
156 tcx
.mk_ptr(ty
::TypeAndMut
{
158 mutbl
: hir
::MutImmutable
160 tcx
.mk_ptr(ty
::TypeAndMut
{
162 mutbl
: hir
::MutMutable
168 "volatile_copy_memory" | "volatile_copy_nonoverlapping_memory" => {
171 tcx
.mk_ptr(ty
::TypeAndMut
{
173 mutbl
: hir
::MutMutable
175 tcx
.mk_ptr(ty
::TypeAndMut
{
177 mutbl
: hir
::MutImmutable
183 "write_bytes" | "volatile_set_memory" => {
186 tcx
.mk_ptr(ty
::TypeAndMut
{
188 mutbl
: hir
::MutMutable
195 "sqrtf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
196 "sqrtf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
199 vec
!( tcx
.types
.f32, tcx
.types
.i32 ),
204 vec
!( tcx
.types
.f64, tcx
.types
.i32 ),
207 "sinf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
208 "sinf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
209 "cosf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
210 "cosf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
213 vec
!( tcx
.types
.f32, tcx
.types
.f32 ),
218 vec
!( tcx
.types
.f64, tcx
.types
.f64 ),
221 "expf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
222 "expf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
223 "exp2f32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
224 "exp2f64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
225 "logf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
226 "logf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
227 "log10f32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
228 "log10f64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
229 "log2f32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
230 "log2f64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
233 vec
!( tcx
.types
.f32, tcx
.types
.f32, tcx
.types
.f32 ),
238 vec
!( tcx
.types
.f64, tcx
.types
.f64, tcx
.types
.f64 ),
241 "fabsf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
242 "fabsf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
243 "copysignf32" => (0, vec
!( tcx
.types
.f32, tcx
.types
.f32 ), tcx
.types
.f32),
244 "copysignf64" => (0, vec
!( tcx
.types
.f64, tcx
.types
.f64 ), tcx
.types
.f64),
245 "floorf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
246 "floorf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
247 "ceilf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
248 "ceilf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
249 "truncf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
250 "truncf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
251 "rintf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
252 "rintf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
253 "nearbyintf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
254 "nearbyintf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
255 "roundf32" => (0, vec
!( tcx
.types
.f32 ), tcx
.types
.f32),
256 "roundf64" => (0, vec
!( tcx
.types
.f64 ), tcx
.types
.f64),
259 (1, vec
!( tcx
.mk_imm_ptr(param(ccx
, 0)) ), param(ccx
, 0)),
261 (1, vec
!( tcx
.mk_mut_ptr(param(ccx
, 0)), param(ccx
, 0) ), tcx
.mk_nil()),
263 "ctpop" | "ctlz" | "cttz" | "bswap" => (1, vec
!(param(ccx
, 0)), param(ccx
, 0)),
265 "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" =>
266 (1, vec
!(param(ccx
, 0), param(ccx
, 0)),
267 tcx
.mk_tup(vec
!(param(ccx
, 0), tcx
.types
.bool
))),
269 "unchecked_div" | "unchecked_rem" =>
270 (1, vec
![param(ccx
, 0), param(ccx
, 0)], param(ccx
, 0)),
272 "overflowing_add" | "overflowing_sub" | "overflowing_mul" =>
273 (1, vec
![param(ccx
, 0), param(ccx
, 0)], param(ccx
, 0)),
275 "return_address" => (0, vec
![], tcx
.mk_imm_ptr(tcx
.types
.u8)),
277 "assume" => (0, vec
![tcx
.types
.bool
], tcx
.mk_nil()),
279 "discriminant_value" => (1, vec
![
280 tcx
.mk_imm_ref(tcx
.mk_region(ty
::ReLateBound(ty
::DebruijnIndex
::new(1),
282 param(ccx
, 0))], tcx
.types
.u64),
285 let mut_u8
= tcx
.mk_mut_ptr(tcx
.types
.u8);
286 let fn_ty
= ty
::BareFnTy
{
287 unsafety
: hir
::Unsafety
::Normal
,
289 sig
: ty
::Binder(FnSig
{
290 inputs
: vec
![mut_u8
],
291 output
: ty
::FnOutput
::FnConverging(tcx
.mk_nil()),
295 let fn_ty
= tcx
.mk_bare_fn(fn_ty
);
296 (0, vec
![tcx
.mk_fn(None
, fn_ty
), mut_u8
], mut_u8
)
300 span_err
!(tcx
.sess
, it
.span
, E0093
,
301 "unrecognized intrinsic function: `{}`", *other
);
305 (n_tps
, inputs
, ty
::FnConverging(output
))
307 equate_intrinsic_type(
317 /// Type-check `extern "platform-intrinsic" { ... }` functions.
318 pub fn check_platform_intrinsic_type(ccx
: &CrateCtxt
,
319 it
: &hir
::ForeignItem
) {
321 let name
= token
::intern(&format
!("P{}", n
));
322 ccx
.tcx
.mk_param(subst
::FnSpace
, n
, name
)
326 let i_ty
= tcx
.lookup_item_type(tcx
.map
.local_def_id(it
.id
));
327 let i_n_tps
= i_ty
.generics
.types
.len(subst
::FnSpace
);
328 let name
= it
.name
.as_str();
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))
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))
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() {
345 let params
= vec
![param(0), param(0),
346 tcx
.mk_ty(ty
::TyArray(tcx
.types
.u32, n
))];
347 (2, params
, param(1))
350 span_err
!(tcx
.sess
, it
.span
, E0439
,
351 "invalid `simd_shuffle`, needs length: `{}`", name
);
357 match intrinsics
::Intrinsic
::find(tcx
, &name
) {
359 // this function is a platform specific intrinsic
361 span_err
!(tcx
.sess
, it
.span
, E0440
,
362 "platform-specific intrinsic has wrong number of type \
363 parameters: found {}, expected 0",
368 let mut structural_to_nomimal
= HashMap
::new();
370 let sig
= tcx
.no_late_bound_regions(i_ty
.ty
.fn_sig()).unwrap();
371 if intr
.inputs
.len() != sig
.inputs
.len() {
372 span_err
!(tcx
.sess
, it
.span
, E0444
,
373 "platform-specific intrinsic has invalid number of \
374 arguments: found {}, expected {}",
375 intr
.inputs
.len(), sig
.inputs
.len());
378 let input_pairs
= intr
.inputs
.iter().zip(&sig
.inputs
);
379 for (i
, (expected_arg
, arg
)) in input_pairs
.enumerate() {
380 match_intrinsic_type_to_type(tcx
, &format
!("argument {}", i
+ 1), it
.span
,
381 &mut structural_to_nomimal
, expected_arg
, arg
);
383 match_intrinsic_type_to_type(tcx
, "return value", it
.span
,
384 &mut structural_to_nomimal
,
385 &intr
.output
, sig
.output
.unwrap());
389 span_err
!(tcx
.sess
, it
.span
, E0441
,
390 "unrecognized platform-specific intrinsic function: `{}`", name
);
397 equate_intrinsic_type(
401 abi
::PlatformIntrinsic
,
403 ty
::FnConverging(output
)
407 // walk the expected type and the actual type in lock step, checking they're
408 // the same, in a kinda-structural way, i.e. `Vector`s have to be simd structs with
409 // exactly the right element type
410 fn match_intrinsic_type_to_type
<'tcx
, 'a
>(
411 tcx
: &ty
::ctxt
<'tcx
>,
414 structural_to_nominal
: &mut HashMap
<&'a intrinsics
::Type
, ty
::Ty
<'tcx
>>,
415 expected
: &'a intrinsics
::Type
, t
: ty
::Ty
<'tcx
>)
417 use intrinsics
::Type
::*;
419 let simple_error
= |real
: &str, expected
: &str| {
420 span_err
!(tcx
.sess
, span
, E0442
,
421 "intrinsic {} has wrong type: found {}, expected {}",
422 position
, real
, expected
)
426 Void
=> match t
.sty
{
427 ty
::TyTuple(ref v
) if v
.is_empty() => {}
,
428 _
=> simple_error(&format
!("`{}`", t
), "()"),
430 // (The width we pass to LLVM doesn't concern the type checker.)
431 Integer(signed
, bits
, _llvm_width
) => match (signed
, bits
, &t
.sty
) {
432 (true, 8, &ty
::TyInt(ast
::IntTy
::TyI8
)) |
433 (false, 8, &ty
::TyUint(ast
::UintTy
::TyU8
)) |
434 (true, 16, &ty
::TyInt(ast
::IntTy
::TyI16
)) |
435 (false, 16, &ty
::TyUint(ast
::UintTy
::TyU16
)) |
436 (true, 32, &ty
::TyInt(ast
::IntTy
::TyI32
)) |
437 (false, 32, &ty
::TyUint(ast
::UintTy
::TyU32
)) |
438 (true, 64, &ty
::TyInt(ast
::IntTy
::TyI64
)) |
439 (false, 64, &ty
::TyUint(ast
::UintTy
::TyU64
)) => {}
,
440 _
=> simple_error(&format
!("`{}`", t
),
442 if signed {"i"}
else {"u"}
,
445 Float(bits
) => match (bits
, &t
.sty
) {
446 (32, &ty
::TyFloat(ast
::FloatTy
::TyF32
)) |
447 (64, &ty
::TyFloat(ast
::FloatTy
::TyF64
)) => {}
,
448 _
=> simple_error(&format
!("`{}`", t
),
449 &format
!("`f{n}`", n
= bits
)),
451 Pointer(ref inner_expected
, ref _llvm_type
, const_
) => {
453 ty
::TyRawPtr(ty
::TypeAndMut { ty, mutbl }
) => {
454 if (mutbl
== hir
::MutImmutable
) != const_
{
455 simple_error(&format
!("`{}`", t
),
456 if const_ {"const pointer"}
else {"mut pointer"}
)
458 match_intrinsic_type_to_type(tcx
, position
, span
, structural_to_nominal
,
461 _
=> simple_error(&format
!("`{}`", t
), "raw pointer"),
464 Vector(ref inner_expected
, ref _llvm_type
, len
) => {
466 simple_error(&format
!("non-simd type `{}`", t
), "simd type");
469 let t_len
= t
.simd_size(tcx
);
470 if len
as usize != t_len
{
471 simple_error(&format
!("vector with length {}", t_len
),
472 &format
!("length {}", len
));
475 let t_ty
= t
.simd_type(tcx
);
477 // check that a given structural type always has the same an intrinsic definition
478 let previous
= structural_to_nominal
.entry(expected
).or_insert(t
);
480 // this gets its own error code because it is non-trivial
481 span_err
!(tcx
.sess
, span
, E0443
,
482 "intrinsic {} has wrong type: found `{}`, expected `{}` which \
483 was used for this vector type previously in this signature",
490 match_intrinsic_type_to_type(tcx
,
493 structural_to_nominal
,
497 Aggregate(_flatten
, ref expected_contents
) => {
499 ty
::TyTuple(ref contents
) => {
500 if contents
.len() != expected_contents
.len() {
501 simple_error(&format
!("tuple with length {}", contents
.len()),
502 &format
!("tuple with length {}", expected_contents
.len()));
505 for (e
, c
) in expected_contents
.iter().zip(contents
) {
506 match_intrinsic_type_to_type(tcx
, position
, span
, structural_to_nominal
,
510 _
=> simple_error(&format
!("`{}`", t
),