rollup merge of #23282: nikomatsakis/fn-trait-inheritance

The primary motivation here is to sidestep #19032 -- for a time, I thought that we should improve coherence or otherwise extend the language, but I now think that any such changes will require more time to bake. In the meantime, inheritance amongst the fn traits is both logically correct *and* a simple solution to that obstacle. This change introduces inheritance and modifies the compiler so that it can properly generate impls for closures and fns.

Things enabled by this PR (but not included in this PR):

1. An impl of `FnMut` for `&mut F` where `F : FnMut` (https://github.com/rust-lang/rust/issues/23015).
2. A better version of `Thunk` I've been calling `FnBox`.

I did not include either of these in the PR because:

1. Adding the impls in 1 currently induces a coherence conflict with the pattern trait. This is interesting and merits some discussion.
2. `FnBox` deserves to be a PR of its own.

The main downside to this design is (a) the need to write impls by hand; (b) the possibility of implementing `FnMut` with different semantics from `Fn`, etc. Point (a) is minor -- in particular, it does not affect normal closure usage -- and could be addressed in the future in many ways (better defaults; convenient macros; specialization; etc). Point (b) is unfortunate but "just a bug" from my POV, and certainly not unique to these traits (c.f. Copy/Clone, PartialEq/Eq, etc). (Until we lift the feature-gate on implementing the Fn traits, in any case, there is room to correct both of these if we find a nice way.)

Note that I believe this change is reversible in the future if we decide on another course of action, due to the feature gate on implementing the `Fn` traits, though I do not (currently) think we should reverse it.

Fixes #18835.

r? @nrc
This commit is contained in:
Alex Crichton 2015-03-24 14:50:44 -07:00
commit 8f6c879d2a
40 changed files with 713 additions and 263 deletions

View file

@ -18,26 +18,36 @@ struct SFn {
}
impl Fn<(isize,)> for SFn {
type Output = isize;
extern "rust-call" fn call(&self, (z,): (isize,)) -> isize {
self.x * self.y * z
}
}
impl FnMut<(isize,)> for SFn {
extern "rust-call" fn call_mut(&mut self, args: (isize,)) -> isize { self.call(args) }
}
impl FnOnce<(isize,)> for SFn {
type Output = isize;
extern "rust-call" fn call_once(self, args: (isize,)) -> isize { self.call(args) }
}
struct SFnMut {
x: isize,
y: isize,
}
impl FnMut<(isize,)> for SFnMut {
type Output = isize;
extern "rust-call" fn call_mut(&mut self, (z,): (isize,)) -> isize {
self.x * self.y * z
}
}
impl FnOnce<(isize,)> for SFnMut {
type Output = isize;
extern "rust-call" fn call_once(mut self, args: (isize,)) -> isize { self.call_mut(args) }
}
struct SFnOnce {
x: String,
}

View file

@ -10,5 +10,6 @@
fn main() {
let x: Option<&[u8]> = Some("foo").map(std::mem::transmute);
//~^ ERROR: is not implemented for the type
//~^ ERROR E0277
//~| ERROR E0277
}

View file

@ -18,5 +18,5 @@ fn main() {
let _x: extern "C" fn() = f; // OK
is_fn(f);
//~^ ERROR the trait `core::ops::Fn<()>` is not implemented for the type `extern "C" fn()
//~| ERROR the trait `core::ops::Fn<()>` is not implemented for the type `extern "C" fn()
//~| ERROR the trait `core::ops::FnOnce<()>` is not implemented for the type `extern "C" fn()
}

View file

@ -18,28 +18,21 @@
struct Foo;
impl Fn<()> for Foo {
//~^ ERROR angle-bracket notation is not stable when used with the `Fn` family of traits
type Output = ();
extern "rust-call" fn call(&self, args: ()) -> () {}
extern "rust-call" fn call(self, args: ()) -> () {}
}
struct Foo1;
impl Fn() for Foo1 {
impl FnOnce() for Foo1 {
//~^ ERROR associated type bindings are not allowed here
extern "rust-call" fn call(&self, args: ()) -> () {}
extern "rust-call" fn call_once(self, args: ()) -> () {}
}
struct Bar;
impl FnMut<()> for Bar {
//~^ ERROR angle-bracket notation is not stable when used with the `Fn` family of traits
type Output = ();
extern "rust-call" fn call_mut(&self, args: ()) -> () {}
}
struct Baz;
impl FnOnce<()> for Baz {
//~^ ERROR angle-bracket notation is not stable when used with the `Fn` family of traits
type Output = ();
extern "rust-call" fn call_once(&self, args: ()) -> () {}
}

View file

@ -35,5 +35,5 @@ fn main() {
needs_fn(1);
//~^ ERROR `core::ops::Fn<(isize,)>`
//~| ERROR `core::ops::Fn<(isize,)>`
//~| ERROR `core::ops::FnOnce<(isize,)>`
}

View file

@ -17,9 +17,13 @@ fn apply<T, F>(t: T, f: F) where F: FnOnce(T) {
}
fn main() {
apply(&3, takes_mut); //~ ERROR (values differ in mutability)
apply(&3, takes_imm);
apply(&3, takes_mut);
//~^ ERROR (values differ in mutability)
//~| ERROR (values differ in mutability)
apply(&mut 3, takes_mut);
apply(&mut 3, takes_imm); //~ ERROR (values differ in mutability)
apply(&mut 3, takes_imm);
//~^ ERROR (values differ in mutability)
//~| ERROR (values differ in mutability)
}

View file

@ -16,11 +16,10 @@ struct Debuger<T> {
x: T
}
impl<T: fmt::Debug> ops::Fn<(),> for Debuger<T> {
impl<T: fmt::Debug> ops::FnOnce<(),> for Debuger<T> {
type Output = ();
fn call(&self, _args: ()) {
//~^ ERROR `call` has an incompatible type for trait: expected "rust-call" fn, found "Rust" fn
fn call_once(self, _args: ()) {
//~^ ERROR `call_once` has an incompatible type for trait: expected "rust-call" fn, found "Rust" fn
println!("{:?}", self.x);
}
}

View file

@ -13,10 +13,20 @@
struct Foo;
impl<'a, T> Fn<(&'a T,)> for Foo {
type Output = ();
extern "rust-call" fn call(&self, (_,): (T,)) {}
//~^ ERROR: has an incompatible type for trait: expected &-ptr
}
impl<'a, T> FnMut<(&'a T,)> for Foo {
extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {}
//~^ ERROR: has an incompatible type for trait: expected &-ptr
}
impl<'a, T> FnOnce<(&'a T,)> for Foo {
type Output = ();
extern "rust-call" fn call_once(self, (_,): (T,)) {}
//~^ ERROR: has an incompatible type for trait: expected &-ptr
}
fn main() {}

View file

@ -18,13 +18,18 @@ struct S {
}
impl FnMut<(isize,)> for S {
type Output = isize;
extern "rust-call" fn call_mut(&mut self, (z,): (isize,)) -> isize {
self.x * self.y * z
}
}
impl FnOnce<(isize,)> for S {
type Output = isize;
extern "rust-call" fn call_once(mut self, (z,): (isize,)) -> isize {
self.call_mut((z,))
}
}
fn main() {
let mut s = S {
x: 3,

View file

@ -18,12 +18,16 @@ struct S {
}
impl FnMut<isize> for S {
type Output = isize;
extern "rust-call" fn call_mut(&mut self, z: isize) -> isize {
self.x + self.y + z
}
}
impl FnOnce<isize> for S {
type Output = isize;
extern "rust-call" fn call_once(mut self, z: isize) -> isize { self.call_mut(z) }
}
fn main() {
let mut s = S {
x: 1,

View file

@ -19,13 +19,17 @@ use std::ops::{Fn,FnMut,FnOnce};
struct S;
impl FnMut<(isize,)> for S {
type Output = isize;
extern "rust-call" fn call_mut(&mut self, (x,): (isize,)) -> isize {
x * x
}
}
impl FnOnce<(isize,)> for S {
type Output = isize;
extern "rust-call" fn call_once(mut self, args: (isize,)) -> isize { self.call_mut(args) }
}
fn call_it<F:Fn(isize)->isize>(f: &F, x: isize) -> isize {
f.call((x,))
}
@ -33,5 +37,4 @@ fn call_it<F:Fn(isize)->isize>(f: &F, x: isize) -> isize {
fn main() {
let x = call_it(&S, 22);
//~^ ERROR not implemented
//~| ERROR not implemented
}

View file

@ -28,14 +28,19 @@ impl<F,A,R> YCombinator<F,A,R> {
}
impl<A,R,F : FnMut(&mut FnMut(A) -> R, A) -> R> FnMut<(A,)> for YCombinator<F,A,R> {
type Output = R;
extern "rust-call" fn call_mut(&mut self, (arg,): (A,)) -> R {
(self.func)(self, arg)
//~^ ERROR cannot borrow `*self` as mutable more than once at a time
}
}
impl<A,R,F : FnMut(&mut FnMut(A) -> R, A) -> R> FnOnce<(A,)> for YCombinator<F,A,R> {
type Output = R;
extern "rust-call" fn call_once(mut self, args: (A,)) -> R {
self.call_mut(args)
}
}
fn main() {
let mut counter = 0;
let factorial = |recur: &mut FnMut(u32) -> u32, arg: u32| -> u32 {

View file

@ -27,11 +27,15 @@ fn a() {
}
fn b() {
let y = call_it_mut(&mut square, 22); //~ ERROR not implemented
let y = call_it_mut(&mut square, 22);
//~^ ERROR not implemented
//~| ERROR not implemented
}
fn c() {
let z = call_it_once(square, 22); //~ ERROR not implemented
let z = call_it_once(square, 22);
//~^ ERROR not implemented
//~| ERROR not implemented
}
fn main() { }

View file

@ -27,11 +27,15 @@ fn a() {
}
fn b() {
let y = call_it_mut(&mut square, 22); //~ ERROR not implemented
let y = call_it_mut(&mut square, 22);
//~^ ERROR not implemented
//~| ERROR not implemented
}
fn c() {
let z = call_it_once(square, 22); //~ ERROR not implemented
let z = call_it_once(square, 22);
//~^ ERROR not implemented
//~| ERROR not implemented
}
fn main() { }

View file

@ -28,11 +28,15 @@ fn a() {
}
fn b() {
let y = call_it_mut(&mut square, 22); //~ ERROR not implemented
let y = call_it_mut(&mut square, 22);
//~^ ERROR not implemented
//~| ERROR not implemented
}
fn c() {
let z = call_it_once(square, 22); //~ ERROR not implemented
let z = call_it_once(square, 22);
//~^ ERROR not implemented
//~| ERROR not implemented
}
fn main() { }

View file

@ -14,7 +14,6 @@ use std::ops::Fn;
struct Foo<T>(T);
impl<T: Copy> Fn<()> for Foo<T> {
type Output = T;
extern "rust-call" fn call(&self, _: ()) -> T {
match *self {
Foo(t) => t
@ -22,6 +21,20 @@ impl<T: Copy> Fn<()> for Foo<T> {
}
}
impl<T: Copy> FnMut<()> for Foo<T> {
extern "rust-call" fn call_mut(&mut self, _: ()) -> T {
self.call(())
}
}
impl<T: Copy> FnOnce<()> for Foo<T> {
type Output = T;
extern "rust-call" fn call_once(self, _: ()) -> T {
self.call(())
}
}
fn main() {
let t: u8 = 1;
println!("{}", Foo(t)());

View file

@ -17,10 +17,18 @@ trait Foo { fn dummy(&self) { }}
struct Bar;
impl<'a> std::ops::Fn<(&'a (Foo+'a),)> for Bar {
type Output = ();
extern "rust-call" fn call(&self, _: (&'a Foo,)) {}
}
impl<'a> std::ops::FnMut<(&'a (Foo+'a),)> for Bar {
extern "rust-call" fn call_mut(&mut self, a: (&'a Foo,)) { self.call(a) }
}
impl<'a> std::ops::FnOnce<(&'a (Foo+'a),)> for Bar {
type Output = ();
extern "rust-call" fn call_once(self, a: (&'a Foo,)) { self.call(a) }
}
struct Baz;
impl Foo for Baz {}

View file

@ -36,9 +36,21 @@ impl Alloy {
}
impl<'b> Fn<(&'b mut (Response+'b),)> for SendFile {
extern "rust-call" fn call(&self, (_res,): (&'b mut (Response+'b),)) {}
}
impl<'b> FnMut<(&'b mut (Response+'b),)> for SendFile {
extern "rust-call" fn call_mut(&mut self, (_res,): (&'b mut (Response+'b),)) {
self.call((_res,))
}
}
impl<'b> FnOnce<(&'b mut (Response+'b),)> for SendFile {
type Output = ();
extern "rust-call" fn call(&self, (_res,): (&'b mut (Response+'b),)) {}
extern "rust-call" fn call_once(self, (_res,): (&'b mut (Response+'b),)) {
self.call((_res,))
}
}
impl<Rq: Request, Rs: Response> Ingot<Rq, Rs> for HelloWorld {

View file

@ -20,20 +20,36 @@
struct Foo { foo: u32 }
impl FnMut<()> for Foo {
type Output = u32;
extern "rust-call" fn call_mut(&mut self, _: ()) -> u32 { self.foo }
}
impl FnMut<(u32,)> for Foo {
impl FnOnce<()> for Foo {
type Output = u32;
extern "rust-call" fn call_once(mut self, _: ()) -> u32 { self.call_mut(()) }
}
/////////////////////////////////////////////////////////////////////////
impl FnMut<(u32,)> for Foo {
extern "rust-call" fn call_mut(&mut self, (x,): (u32,)) -> u32 { self.foo + x }
}
impl FnMut<(u32,u32)> for Foo {
impl FnOnce<(u32,)> for Foo {
type Output = u32;
extern "rust-call" fn call_once(mut self, args: (u32,)) -> u32 { self.call_mut(args) }
}
/////////////////////////////////////////////////////////////////////////
impl FnMut<(u32,u32)> for Foo {
extern "rust-call" fn call_mut(&mut self, (x, y): (u32, u32)) -> u32 { self.foo + x + y }
}
impl FnOnce<(u32,u32)> for Foo {
type Output = u32;
extern "rust-call" fn call_once(mut self, args: (u32,u32)) -> u32 { self.call_mut(args) }
}
fn main() {
let mut f = box Foo { foo: 42 } as Box<FnMut() -> u32>;
assert_eq!(f.call_mut(()), 42);

View file

@ -16,9 +16,17 @@
struct Foo;
impl<'a> Fn<(&'a (),)> for Foo {
type Output = ();
extern "rust-call" fn call(&self, (_,): (&(),)) {}
}
impl<'a> FnMut<(&'a (),)> for Foo {
extern "rust-call" fn call_mut(&mut self, (_,): (&(),)) {}
}
impl<'a> FnOnce<(&'a (),)> for Foo {
type Output = ();
extern "rust-call" fn call_once(self, (_,): (&(),)) {}
}
fn main() {}

View file

@ -21,13 +21,20 @@ use std::ops::Add;
struct G<A>(PhantomData<A>);
impl<'a, A: Add<i32, Output=i32>> Fn<(A,)> for G<A> {
type Output = i32;
extern "rust-call" fn call(&self, (arg,): (A,)) -> i32 {
arg.add(1)
}
}
impl<'a, A: Add<i32, Output=i32>> FnMut<(A,)> for G<A> {
extern "rust-call" fn call_mut(&mut self, args: (A,)) -> i32 { self.call(args) }
}
impl<'a, A: Add<i32, Output=i32>> FnOnce<(A,)> for G<A> {
type Output = i32;
extern "rust-call" fn call_once(self, args: (A,)) -> i32 { self.call(args) }
}
fn main() {
// ICE trigger
(G(PhantomData))(1);

View file

@ -20,24 +20,38 @@ struct S1 {
}
impl FnMut<(i32,)> for S1 {
type Output = i32;
extern "rust-call" fn call_mut(&mut self, (z,): (i32,)) -> i32 {
self.x * self.y * z
}
}
impl FnOnce<(i32,)> for S1 {
type Output = i32;
extern "rust-call" fn call_once(mut self, args: (i32,)) -> i32 {
self.call_mut(args)
}
}
struct S2 {
x: i32,
y: i32,
}
impl Fn<(i32,)> for S2 {
type Output = i32;
extern "rust-call" fn call(&self, (z,): (i32,)) -> i32 {
self.x * self.y * z
}
}
impl FnMut<(i32,)> for S2 {
extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> i32 { self.call(args) }
}
impl FnOnce<(i32,)> for S2 {
type Output = i32;
extern "rust-call" fn call_once(self, args: (i32,)) -> i32 { self.call(args) }
}
struct S3 {
x: i32,
y: i32,

View file

@ -20,12 +20,16 @@ struct S {
}
impl FnMut<()> for S {
type Output = i32;
extern "rust-call" fn call_mut(&mut self, (): ()) -> i32 {
self.x * self.y
}
}
impl FnOnce<()> for S {
type Output = i32;
extern "rust-call" fn call_once(mut self, args: ()) -> i32 { self.call_mut(args) }
}
fn main() {
let mut s = S {
x: 3,

View file

@ -20,12 +20,20 @@ use std::ops::{Fn,FnMut,FnOnce};
struct S;
impl Fn<(i32,)> for S {
type Output = i32;
extern "rust-call" fn call(&self, (x,): (i32,)) -> i32 {
x * x
}
}
impl FnMut<(i32,)> for S {
extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> i32 { self.call(args) }
}
impl FnOnce<(i32,)> for S {
type Output = i32;
extern "rust-call" fn call_once(self, args: (i32,)) -> i32 { self.call(args) }
}
fn call_it<F:Fn(i32)->i32>(f: &F, x: i32) -> i32 {
f(x)
}

View file

@ -20,13 +20,17 @@ use std::ops::{FnMut,FnOnce};
struct S;
impl FnMut<(i32,)> for S {
type Output = i32;
extern "rust-call" fn call_mut(&mut self, (x,): (i32,)) -> i32 {
x * x
}
}
impl FnOnce<(i32,)> for S {
type Output = i32;
extern "rust-call" fn call_once(mut self, args: (i32,)) -> i32 { self.call_mut(args) }
}
fn call_it_mut<F:FnMut(i32)->i32>(f: &mut F, x: i32) -> i32 {
f(x)
}

View file

@ -32,13 +32,20 @@ impl<F,A,R> YCombinator<F,A,R> {
}
impl<A,R,F : Fn(&Fn(A) -> R, A) -> R> Fn<(A,)> for YCombinator<F,A,R> {
type Output = R;
extern "rust-call" fn call(&self, (arg,): (A,)) -> R {
(self.func)(self, arg)
}
}
impl<A,R,F : Fn(&Fn(A) -> R, A) -> R> FnMut<(A,)> for YCombinator<F,A,R> {
extern "rust-call" fn call_mut(&mut self, args: (A,)) -> R { self.call(args) }
}
impl<A,R,F : Fn(&Fn(A) -> R, A) -> R> FnOnce<(A,)> for YCombinator<F,A,R> {
type Output = R;
extern "rust-call" fn call_once(self, args: (A,)) -> R { self.call(args) }
}
fn main() {
let factorial = |recur: &Fn(u32) -> u32, arg: u32| -> u32 {
if arg == 0 {1} else {arg * recur(arg-1)}

View file

@ -17,13 +17,17 @@ use std::ops::FnMut;
struct S;
impl FnMut<(i32,)> for S {
type Output = i32;
extern "rust-call" fn call_mut(&mut self, (x,): (i32,)) -> i32 {
x * x
}
}
impl FnOnce<(i32,)> for S {
type Output = i32;
extern "rust-call" fn call_once(mut self, args: (i32,)) -> i32 { self.call_mut(args) }
}
fn call_it<F:FnMut(i32)->i32>(mut f: F, x: i32) -> i32 {
f(x) + 3
}