Auto merge of #31710 - eddyb:reify, r=nikomatsakis

Distinguish fn item types to allow reification from nothing to fn pointers.

The first commit is a rebase of #26284, except for files that have moved since.

This is a [breaking-change], due to:
* each FFI function has a distinct type, like all other functions currently do
* all generic parameters on functions are recorded in their item types, e.g.:
`size_of::<u8>` & `size_of::<i8>`'s types differ despite their identical signature.
* function items are zero-sized, which will stop transmutes from working on them

The first two cases are handled in most cases with the new coerce-unify logic,
which will combine incompatible function item types into function pointers,
at the outer-most level of if-else chains, match arms and array literals.

The last case is specially handled during type-checking such that transmutes
from a function item type to a pointer or integer type will continue to work for
another release cycle, but are being linted against. To get rid of warnings and
ensure your code will continue to compile, cast to a pointer before transmuting.
This commit is contained in:
bors 2016-03-09 20:16:20 -08:00
commit bcda58f491
109 changed files with 2044 additions and 2230 deletions

View file

@ -14,26 +14,26 @@ pub mod testtypes {
use std::any::TypeId;
pub fn type_ids() -> Vec<TypeId> {
let mut ids = vec!();
ids.push(TypeId::of::<FooNil>());
ids.push(TypeId::of::<FooBool>());
ids.push(TypeId::of::<FooInt>());
ids.push(TypeId::of::<FooUint>());
ids.push(TypeId::of::<FooFloat>());
ids.push(TypeId::of::<FooEnum>());
ids.push(TypeId::of::<FooUniq>());
ids.push(TypeId::of::<FooPtr>());
ids.push(TypeId::of::<&'static FooTrait>());
ids.push(TypeId::of::<FooStruct>());
ids.push(TypeId::of::<FooTuple>());
ids
vec![
TypeId::of::<FooBool>(),
TypeId::of::<FooInt>(),
TypeId::of::<FooUint>(),
TypeId::of::<FooFloat>(),
TypeId::of::<FooStr>(),
TypeId::of::<FooArray>(),
TypeId::of::<FooSlice>(),
TypeId::of::<FooBox>(),
TypeId::of::<FooPtr>(),
TypeId::of::<FooRef>(),
TypeId::of::<FooFnPtr>(),
TypeId::of::<FooNil>(),
TypeId::of::<FooTuple>(),
TypeId::of::<FooTrait>(),
TypeId::of::<FooStruct>(),
TypeId::of::<FooEnum>()
]
}
// Tests ty_nil
pub type FooNil = ();
// Skipping ty_bot
// Tests TyBool
pub type FooBool = bool;
@ -49,25 +49,26 @@ pub mod testtypes {
// Tests TyFloat (does not test all variants of FloatTy)
pub type FooFloat = f64;
// For TyStr, what kind of string should I use? &'static str? String? Raw str?
// Tests TyStr
pub type FooStr = str;
// Tests TyEnum
pub enum FooEnum {
VarA(usize),
VarB(usize, usize)
}
// Tests TyArray
pub type FooArray = [u8; 1];
// Tests TySlice
pub type FooSlice = [u8];
// Tests TyBox (of u8)
pub type FooUniq = Box<u8>;
// As with TyStr, what type should be used for TyArray?
pub type FooBox = Box<u8>;
// Tests TyRawPtr
pub type FooPtr = *const u8;
// Skipping TyRef
// Tests TyRef
pub type FooRef = &'static u8;
// Skipping TyBareFn (how do you get a bare function type, rather than proc or closure?)
// Tests TyFnPtr
pub type FooFnPtr = fn(u8) -> bool;
// Tests TyTrait
pub trait FooTrait {
@ -80,14 +81,17 @@ pub mod testtypes {
foo_field: usize
}
// Tests TyEnum
pub enum FooEnum {
VarA(usize),
VarB(usize, usize)
}
// Tests TyTuple
pub type FooNil = ();
pub type FooTuple = (u8, i8, bool);
// Skipping ty_param
// Skipping ty_self
// Skipping ty_self
// Skipping TyParam
// Skipping TyInfer

View file

@ -11,23 +11,37 @@
// Test that the types of distinct fn items are not compatible by
// default. See also `run-pass/fn-item-type-*.rs`.
fn foo(x: isize) -> isize { x * 2 }
fn bar(x: isize) -> isize { x * 4 }
fn foo<T>(x: isize) -> isize { x * 2 }
fn bar<T>(x: isize) -> isize { x * 4 }
fn eq<T>(x: T, y: T) { }
fn main() {
let f = if true { foo } else { bar };
//~^ ERROR if and else have incompatible types
//~| expected `fn(isize) -> isize {foo}`
//~| found `fn(isize) -> isize {bar}`
//~| expected fn item,
//~| found a different fn item
trait Foo { fn foo() { /* this is a default fn */ } }
impl<T> Foo for T { /* `foo` is still default here */ }
eq(foo, bar);
fn main() {
eq(foo::<u8>, bar::<u8>);
//~^ ERROR mismatched types
//~| expected `fn(isize) -> isize {foo}`
//~| found `fn(isize) -> isize {bar}`
//~| expected `fn(isize) -> isize {foo::<u8>}`
//~| found `fn(isize) -> isize {bar::<u8>}`
//~| expected fn item
//~| found a different fn item
eq(foo::<u8>, foo::<i8>);
//~^ ERROR mismatched types
//~| expected `fn(isize) -> isize {foo::<u8>}`
//~| found `fn(isize) -> isize {foo::<i8>}`
eq(bar::<String>, bar::<Vec<u8>>);
//~^ ERROR mismatched types
//~| expected `fn(isize) -> isize {bar::<collections::string::String>}`
//~| found `fn(isize) -> isize {bar::<collections::vec::Vec<u8>>}`
//~| expected struct `collections::string::String`
//~| found struct `collections::vec::Vec`
// Make sure we distinguish between trait methods correctly.
eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
//~^ ERROR mismatched types
//~| expected `fn() {Foo::foo}`
//~| found `fn() {Foo::foo}`
}

View file

@ -0,0 +1,16 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(intrinsics)]
extern "rust-intrinsic" {
pub static breakpoint : unsafe extern "rust-intrinsic" fn();
//~^ ERROR intrinsic has wrong type
}
fn main() { unsafe { breakpoint(); } }

View file

@ -17,7 +17,7 @@ fn main() {
let y = match x {
[] => None,
//~^ ERROR mismatched types
//~| expected `[_#0i; 2]`
//~| expected `[_#1i; 2]`
//~| found `[_#7t; 0]`
//~| expected an array with a fixed size of 2 elements
//~| found one with 0 elements

View file

@ -107,7 +107,7 @@ impl Debug for Player {
}
fn str_to_direction(to_parse: &str) -> RoomDirection {
match to_parse {
match to_parse { //~ ERROR match arms have incompatible types
"w" | "west" => RoomDirection::West,
"e" | "east" => RoomDirection::East,
"n" | "north" => RoomDirection::North,
@ -116,7 +116,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
"out" => RoomDirection::Out,
"up" => RoomDirection::Up,
"down" => RoomDirection::Down,
_ => None //~ ERROR mismatched types
_ => None //~ NOTE match arm with an incompatible type
}
}

View file

@ -21,5 +21,5 @@ impl<A> vec_monad<A> for Vec<A> {
}
fn main() {
["hi"].bind(|x| [x] );
//~^ ERROR no method named `bind` found for type `[&str; 1]` in the current scope
//~^ ERROR no method named `bind` found for type `[&'static str; 1]` in the current scope
}

View file

@ -0,0 +1,51 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::mem;
unsafe fn foo() -> (isize, *const (), Option<fn()>) {
let i = mem::transmute(bar);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
let p = mem::transmute(foo);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
let of = mem::transmute(main);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
(i, p, of)
}
unsafe fn bar() {
mem::transmute::<_, *mut ()>(foo);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
mem::transmute::<_, fn()>(bar);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
// No error if a coercion would otherwise occur.
mem::transmute::<fn(), usize>(main);
// Error, still, if the resulting type is not pointer-sized.
mem::transmute::<_, u8>(main);
//~^ ERROR transmute called with differently sized types
}
fn main() {
unsafe {
foo();
bar();
}
}

View file

@ -22,7 +22,7 @@ fn main() {
let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
//~^ ERROR: mismatched types
//~| expected `unsafe extern "C" fn(isize, u8)`
//~| found `unsafe extern "C" fn(isize, u8, ...)`
//~| found `unsafe extern "C" fn(isize, u8, ...) {foo}`
//~| expected non-variadic fn
//~| found variadic function

View file

@ -86,8 +86,10 @@ pub fn id<T>(x: T) -> T { (x as T) }
pub fn use_id() {
let _ =
((id::<[i32; (3 as usize)]> as
fn([i32; 3]) -> [i32; 3] {id})(([(1 as i32), (2 as i32),
(3 as i32)] as [i32; 3])) as
fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32),
(2 as i32),
(3 as i32)] as
[i32; 3])) as
[i32; 3]);
}
fn main() { }

View file

@ -0,0 +1,77 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Check that coercions can unify if-else, match arms and array elements.
// Try to construct if-else chains, matches and arrays out of given expressions.
macro_rules! check {
($last:expr $(, $rest:expr)+) => {
// Last expression comes first because of whacky ifs and matches.
let _ = $(if false { $rest })else+ else { $last };
let _ = match 0 { $(_ if false => $rest,)+ _ => $last };
let _ = [$($rest,)+ $last];
}
}
// Check all non-uniform cases of 2 and 3 expressions of 2 types.
macro_rules! check2 {
($a:expr, $b:expr) => {
check!($a, $b);
check!($b, $a);
check!($a, $a, $b);
check!($a, $b, $a);
check!($a, $b, $b);
check!($b, $a, $a);
check!($b, $a, $b);
check!($b, $b, $a);
}
}
// Check all non-uniform cases of 2 and 3 expressions of 3 types.
macro_rules! check3 {
($a:expr, $b:expr, $c:expr) => {
// Delegate to check2 for cases where a type repeats.
check2!($a, $b);
check2!($b, $c);
check2!($a, $c);
// Check the remaining cases, i.e. permutations of ($a, $b, $c).
check!($a, $b, $c);
check!($a, $c, $b);
check!($b, $a, $c);
check!($b, $c, $a);
check!($c, $a, $b);
check!($c, $b, $a);
}
}
use std::mem::size_of;
fn foo() {}
fn bar() {}
pub fn main() {
check3!(foo, bar, foo as fn());
check3!(size_of::<u8>, size_of::<u16>, size_of::<usize> as fn() -> usize);
let s = String::from("bar");
check2!("foo", &s);
let a = [1, 2, 3];
let v = vec![1, 2, 3];
check2!(&a[..], &v);
// Make sure in-array coercion still works.
let _ = [("a", Default::default()), (Default::default(), "b"), (&s, &s)];
}

View file

@ -25,16 +25,17 @@
#[repr(u32)]
enum Foo {
A = 0,
B = 23
A = 0,
B = 23
}
#[inline(never)]
extern "C" fn foo(_x: usize) -> Foo { Foo::B }
pub fn main() {
unsafe {
let f: extern "C" fn(usize) -> u32 = ::std::mem::transmute(foo);
assert_eq!(f(0xDEADBEEF), Foo::B as u32);
}
unsafe {
let f: extern "C" fn(usize) -> u32 =
::std::mem::transmute(foo as extern "C" fn(usize) -> Foo);
assert_eq!(f(0xDEADBEEF), Foo::B as u32);
}
}

View file

@ -0,0 +1,22 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that fn item types are zero-sized.
use std::mem::{size_of, size_of_val};
fn main() {
assert_eq!(size_of_val(&main), 0);
let (a, b) = (size_of::<u8>, size_of::<u16>);
assert_eq!(size_of_val(&a), 0);
assert_eq!(size_of_val(&b), 0);
assert_eq!((a(), b()), (1, 2));
}

View file

@ -19,23 +19,29 @@ use issue13507::testtypes;
use std::any::TypeId;
pub fn type_ids() -> Vec<TypeId> {
let mut ids = vec!();
ids.push(TypeId::of::<testtypes::FooNil>());
ids.push(TypeId::of::<testtypes::FooBool>());
ids.push(TypeId::of::<testtypes::FooInt>());
ids.push(TypeId::of::<testtypes::FooUint>());
ids.push(TypeId::of::<testtypes::FooFloat>());
ids.push(TypeId::of::<testtypes::FooEnum>());
ids.push(TypeId::of::<testtypes::FooUniq>());
ids.push(TypeId::of::<testtypes::FooPtr>());
ids.push(TypeId::of::<&'static testtypes::FooTrait>());
ids.push(TypeId::of::<testtypes::FooStruct>());
ids.push(TypeId::of::<testtypes::FooTuple>());
ids
use issue13507::testtypes::*;
vec![
TypeId::of::<FooBool>(),
TypeId::of::<FooInt>(),
TypeId::of::<FooUint>(),
TypeId::of::<FooFloat>(),
TypeId::of::<FooStr>(),
TypeId::of::<FooArray>(),
TypeId::of::<FooSlice>(),
TypeId::of::<FooBox>(),
TypeId::of::<FooPtr>(),
TypeId::of::<FooRef>(),
TypeId::of::<FooFnPtr>(),
TypeId::of::<FooNil>(),
TypeId::of::<FooTuple>(),
TypeId::of::<FooTrait>(),
TypeId::of::<FooStruct>(),
TypeId::of::<FooEnum>()
]
}
pub fn main() {
let othercrate = testtypes::type_ids();
let othercrate = issue13507::testtypes::type_ids();
let thiscrate = type_ids();
assert_eq!(thiscrate, othercrate);
}

View file

@ -204,48 +204,41 @@ fn t24() -> fn(u8) -> S {
C4
}
fn main(){
unsafe {
assert_eq!(t1()(), regular());
fn main() {
assert_eq!(t1()(), regular());
assert!(::std::mem::transmute::<_, *mut ()>(t2()) ==
::std::mem::transmute::<_, *mut ()>(E::U));
assert!(::std::mem::transmute::<_, *mut ()>(t3()) ==
::std::mem::transmute::<_, *mut ()>(S));
assert_eq!(t2() as *mut (), E::U as *mut ());
assert_eq!(t3() as *mut (), S as *mut ());
assert_eq!(t4()(), S::hey());
let s = S(42);
assert_eq!(t5()(&s), <S as X>::hoy(&s));
assert_eq!(t4()(), S::hey());
let s = S(42);
assert_eq!(t5()(&s), <S as X>::hoy(&s));
assert_eq!(t6()(), ext::regular_fn());
assert!(::std::mem::transmute::<_, *mut ()>(t7()) ==
::std::mem::transmute::<_, *mut ()>(ext::E::U));
assert!(::std::mem::transmute::<_, *mut ()>(t8()) ==
::std::mem::transmute::<_, *mut ()>(ext::S));
assert_eq!(t6()(), ext::regular_fn());
assert_eq!(t7() as *mut (), ext::E::U as *mut ());
assert_eq!(t8() as *mut (), ext::S as *mut ());
assert_eq!(t9()(), ext::S::hey());
let sext = ext::S(6);
assert_eq!(t10()(&sext), <ext::S as ext::X>::hoy(&sext));
assert_eq!(t9()(), ext::S::hey());
let sext = ext::S(6);
assert_eq!(t10()(&sext), <ext::S as ext::X>::hoy(&sext));
let p = parametric::<u8>;
assert!(::std::mem::transmute::<_, *mut ()>(t11()) ==
::std::mem::transmute::<_, *mut ()>(p));
let p = parametric::<u8>;
assert_eq!(t11() as *mut (), p as *mut ());
assert_eq!(t12(), C);
assert_eq!(t13(), C2);
assert_eq!(t13_2(), C3);
assert_eq!(t12(), C);
assert_eq!(t13(), C2);
assert_eq!(t13_2(), C3);
assert_eq!(t14()(), <S as X>::hoy2());
assert_eq!(t15()(&s), S::hey2(&s));
assert_eq!(t16()(10u32, 20u32), F::f(10u32, 20u32));
assert_eq!(t17()(30u32, 10u64), F::f(30u32, 10u64));
assert_eq!(t18()(50u64, 5u64), F::f(50u64, 5u64));
assert_eq!(t19()(322u64, 2u32), F::f(322u64, 2u32));
assert_eq!(t20()(123u64, 38u32), <u32 as T<_, _>>::staticmeth(123, 38));
assert_eq!(t21(), Unit);
assert_eq!(t22(), None);
assert_eq!(t23(), (CEnum::A, CEnum::B));
assert_eq!(t24(), C4);
}
assert_eq!(t14()(), <S as X>::hoy2());
assert_eq!(t15()(&s), S::hey2(&s));
assert_eq!(t16()(10u32, 20u32), F::f(10u32, 20u32));
assert_eq!(t17()(30u32, 10u64), F::f(30u32, 10u64));
assert_eq!(t18()(50u64, 5u64), F::f(50u64, 5u64));
assert_eq!(t19()(322u64, 2u32), F::f(322u64, 2u32));
assert_eq!(t20()(123u64, 38u32), <u32 as T<_, _>>::staticmeth(123, 38));
assert_eq!(t21(), Unit);
assert_eq!(t22(), None);
assert_eq!(t23(), (CEnum::A, CEnum::B));
assert_eq!(t24(), C4);
}

View file

@ -24,13 +24,14 @@
use std::mem;
#[inline(never)]
extern "C" fn foo<'a>(x: &'a isize) -> Option<&'a isize> { Some(x) }
extern "C" fn foo(x: &isize) -> Option<&isize> { Some(x) }
static FOO: isize = 0xDEADBEE;
pub fn main() {
unsafe {
let f: for<'a> extern "C" fn(&'a isize) -> &'a isize = mem::transmute(foo);
let f: extern "C" fn(&isize) -> &isize =
mem::transmute(foo as extern "C" fn(&isize) -> Option<&isize>);
assert_eq!(*f(&FOO), FOO);
}
}

View file

@ -12,8 +12,6 @@
#![allow(unknown_features)]
#![feature(box_syntax)]
use std::{option, mem};
// Iota-reduction is a rule in the Calculus of (Co-)Inductive Constructions,
// which "says that a destructor applied to an object built from a constructor
// behaves as expected". -- http://coq.inria.fr/doc/Reference-Manual006.html
@ -43,9 +41,9 @@ macro_rules! check_option {
check_option!($e, $T, |ptr| assert_eq!(*ptr, $e));
}};
($e:expr, $T:ty, |$v:ident| $chk:expr) => {{
assert!(option::Option::None::<$T>.is_none());
assert!(None::<$T>.is_none());
let e = $e;
let s_ = option::Option::Some::<$T>(e);
let s_ = Some::<$T>(e);
let $v = s_.as_ref().unwrap();
$chk
}}
@ -78,9 +76,8 @@ pub fn main() {
check_type!(&17, &isize);
check_type!(box 18, Box<isize>);
check_type!("foo".to_string(), String);
check_type!(vec!(20, 22), Vec<isize> );
let mint: usize = unsafe { mem::transmute(main) };
check_type!(vec!(20, 22), Vec<isize>);
check_type!(main, fn(), |pthing| {
assert_eq!(mint, unsafe { mem::transmute(*pthing) })
assert_eq!(main as fn(), *pthing as fn())
});
}

View file

@ -0,0 +1,27 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(transmute_from_fn_item_types)]
use std::mem;
fn main() {
unsafe {
let u = mem::transmute(main);
let p = mem::transmute(main);
let f = mem::transmute(main);
let tuple: (usize, *mut (), fn()) = (u, p, f);
assert_eq!(mem::transmute::<_, [usize; 3]>(tuple), [main as usize; 3]);
mem::transmute::<_, usize>(main);
mem::transmute::<_, *mut ()>(main);
mem::transmute::<_, fn()>(main);
}
}