Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsized rvalues: implement boxed closure impls. #55431

Closed
wants to merge 11 commits into from
32 changes: 32 additions & 0 deletions src/doc/unstable-book/src/library-features/fnbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# `fnbox`

The tracking issue for this feature is [#28796]

[#28796]: https://github.com/rust-lang/rust/issues/28796

------------------------

This had been a temporary alternative to the following impls:

```rust,ignore
impl<A, F> FnOnce for Box<F> where F: FnOnce<A> + ?Sized {}
impl<A, F> FnMut for Box<F> where F: FnMut<A> + ?Sized {}
impl<A, F> Fn for Box<F> where F: Fn<A> + ?Sized {}
```

The impls are parallel to these (relatively old) impls:

```rust,ignore
impl<A, F> FnOnce for &mut F where F: FnMut<A> + ?Sized {}
impl<A, F> FnMut for &mut F where F: FnMut<A> + ?Sized {}
impl<A, F> Fn for &mut F where F: Fn<A> + ?Sized {}
impl<A, F> FnOnce for &F where F: Fn<A> + ?Sized {}
impl<A, F> FnMut for &F where F: Fn<A> + ?Sized {}
impl<A, F> Fn for &F where F: Fn<A> + ?Sized {}
```

Before the introduction of [`unsized_locals`][unsized_locals], we had been unable to provide the former impls. That means, unlike `&dyn Fn()` or `&mut dyn FnMut()` we could not use `Box<dyn FnOnce()>` at that time.

[unsized_locals]: language-features/unsized-locals.html

`FnBox()` is an alternative approach to `Box<dyn FnBox()>` is delegated to `FnBox::call_box` which doesn't need unsized locals. As we now have `Box<dyn FnOnce()>` working, the `fnbox` feature is going to be removed.
48 changes: 23 additions & 25 deletions src/liballoc/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,28 @@ impl<I: ExactSizeIterator + ?Sized> ExactSizeIterator for Box<I> {
#[stable(feature = "fused", since = "1.26.0")]
impl<I: FusedIterator + ?Sized> FusedIterator for Box<I> {}

#[stable(feature = "boxed_closure_impls", since = "1.34.0")]
impl<A, F: FnOnce<A> + ?Sized> FnOnce<A> for Box<F> {
type Output = <F as FnOnce<A>>::Output;

extern "rust-call" fn call_once(self, args: A) -> Self::Output {
<F as FnOnce<A>>::call_once(*self, args)
}
}

#[stable(feature = "boxed_closure_impls", since = "1.34.0")]
impl<A, F: FnMut<A> + ?Sized> FnMut<A> for Box<F> {
extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output {
<F as FnMut<A>>::call_mut(self, args)
}
}

#[stable(feature = "boxed_closure_impls", since = "1.34.0")]
impl<A, F: Fn<A> + ?Sized> Fn<A> for Box<F> {
extern "rust-call" fn call(&self, args: A) -> Self::Output {
<F as Fn<A>>::call(self, args)
}
}

/// `FnBox` is a version of the `FnOnce` intended for use with boxed
/// closure objects. The idea is that where one would normally store a
Expand Down Expand Up @@ -717,9 +739,7 @@ impl<I: FusedIterator + ?Sized> FusedIterator for Box<I> {}
#[rustc_paren_sugar]
#[unstable(feature = "fnbox",
reason = "will be deprecated if and when `Box<FnOnce>` becomes usable", issue = "28796")]
pub trait FnBox<A> {
type Output;

pub trait FnBox<A>: FnOnce<A> {
fn call_box(self: Box<Self>, args: A) -> Self::Output;
}

Expand All @@ -728,33 +748,11 @@ pub trait FnBox<A> {
impl<A, F> FnBox<A> for F
where F: FnOnce<A>
{
type Output = F::Output;

fn call_box(self: Box<F>, args: A) -> F::Output {
self.call_once(args)
}
}

#[unstable(feature = "fnbox",
reason = "will be deprecated if and when `Box<FnOnce>` becomes usable", issue = "28796")]
impl<A, R> FnOnce<A> for Box<dyn FnBox<A, Output = R> + '_> {
type Output = R;

extern "rust-call" fn call_once(self, args: A) -> R {
self.call_box(args)
}
}

#[unstable(feature = "fnbox",
reason = "will be deprecated if and when `Box<FnOnce>` becomes usable", issue = "28796")]
impl<A, R> FnOnce<A> for Box<dyn FnBox<A, Output = R> + Send + '_> {
type Output = R;

extern "rust-call" fn call_once(self, args: A) -> R {
self.call_box(args)
}
}

#[unstable(feature = "coerce_unsized", issue = "27732")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}

Expand Down
1 change: 1 addition & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
#![feature(unboxed_closures)]
#![feature(unicode_internals)]
#![feature(unsize)]
#![feature(unsized_locals)]
#![feature(allocator_internals)]
#![feature(on_unimplemented)]
#![feature(rustc_const_unstable)]
Expand Down
8 changes: 8 additions & 0 deletions src/test/run-pass/unsized-locals/box-fnonce.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn call_it<T>(f: Box<dyn FnOnce() -> T>) -> T {
f()
}

fn main() {
let s = "hello".to_owned();
assert_eq!(&call_it(Box::new(|| s)) as &str, "hello");
}
12 changes: 12 additions & 0 deletions src/test/run-pass/unsized-locals/fnbox-compat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(fnbox)]

use std::boxed::FnBox;

fn call_it<T>(f: Box<dyn FnBox() -> T>) -> T {
f()
}

fn main() {
let s = "hello".to_owned();
assert_eq!(&call_it(Box::new(|| s)) as &str, "hello");
}
32 changes: 10 additions & 22 deletions src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | f(f(10));
| first mutable borrow occurs here
| first borrow later used by call

error[E0382]: use of moved value: `*f`
error[E0382]: use of moved value: `f`
--> $DIR/two-phase-nonrecv-autoref.rs:69:11
|
LL | fn twice_ten_so<F: FnOnce(i32) -> i32>(f: Box<F>) {
Expand All @@ -17,7 +17,7 @@ LL | f(f(10));
| |
| value moved here
|
= note: move occurs because `*f` has type `F`, which does not implement the `Copy` trait
= note: move occurs because `f` has type `std::boxed::Box<F>`, which does not implement the `Copy` trait

error[E0499]: cannot borrow `*f` as mutable more than once at a time
--> $DIR/two-phase-nonrecv-autoref.rs:76:11
Expand All @@ -28,30 +28,18 @@ LL | f(f(10));
| first mutable borrow occurs here
| first borrow later used by call

error[E0161]: cannot move a value of type dyn std::ops::FnOnce(i32) -> i32: the size of dyn std::ops::FnOnce(i32) -> i32 cannot be statically determined
--> $DIR/two-phase-nonrecv-autoref.rs:85:9
|
LL | f(f(10));
| ^

error[E0161]: cannot move a value of type dyn std::ops::FnOnce(i32) -> i32: the size of dyn std::ops::FnOnce(i32) -> i32 cannot be statically determined
--> $DIR/two-phase-nonrecv-autoref.rs:85:11
|
LL | f(f(10));
| ^

error[E0382]: use of moved value: `*f`
error[E0382]: use of moved value: `f`
--> $DIR/two-phase-nonrecv-autoref.rs:85:11
|
LL | f(f(10));
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `*f` has type `dyn std::ops::FnOnce(i32) -> i32`, which does not implement the `Copy` trait
= note: move occurs because `f` has type `std::boxed::Box<dyn std::ops::FnOnce(i32) -> i32>`, which does not implement the `Copy` trait

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:129:27
--> $DIR/two-phase-nonrecv-autoref.rs:125:27
|
LL | double_access(&mut a, &a);
| ------------- ------ ^^ immutable borrow occurs here
Expand All @@ -60,7 +48,7 @@ LL | double_access(&mut a, &a);
| mutable borrow later used by call

error[E0502]: cannot borrow `i` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:157:7
--> $DIR/two-phase-nonrecv-autoref.rs:153:7
|
LL | i[i[3]] = 4;
| --^----
Expand All @@ -70,7 +58,7 @@ LL | i[i[3]] = 4;
| mutable borrow later used here

error[E0502]: cannot borrow `i` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:163:7
--> $DIR/two-phase-nonrecv-autoref.rs:159:7
|
LL | i[i[3]] = i[4];
| --^----
Expand All @@ -79,7 +67,7 @@ LL | i[i[3]] = i[4];
| mutable borrow occurs here
| mutable borrow later used here

error: aborting due to 9 previous errors
error: aborting due to 7 previous errors

Some errors occurred: E0161, E0382, E0499, E0502.
For more information about an error, try `rustc --explain E0161`.
Some errors occurred: E0382, E0499, E0502.
For more information about an error, try `rustc --explain E0382`.
22 changes: 11 additions & 11 deletions src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ LL | f(f(10));
| | second mutable borrow occurs here
| first mutable borrow occurs here

error[E0382]: use of moved value: `*f`
error[E0382]: use of moved value: `f`
--> $DIR/two-phase-nonrecv-autoref.rs:69:11
|
LL | f(f(10));
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `*f` has type `F`, which does not implement the `Copy` trait
= note: move occurs because `f` has type `std::boxed::Box<F>`, which does not implement the `Copy` trait

error[E0499]: cannot borrow `*f` as mutable more than once at a time
--> $DIR/two-phase-nonrecv-autoref.rs:76:11
Expand All @@ -34,18 +34,18 @@ LL | f(f(10));
| | second mutable borrow occurs here
| first mutable borrow occurs here

error[E0382]: use of moved value: `*f`
error[E0382]: use of moved value: `f`
--> $DIR/two-phase-nonrecv-autoref.rs:85:11
|
LL | f(f(10));
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `*f` has type `(dyn std::ops::FnOnce(i32) -> i32 + 'static)`, which does not implement the `Copy` trait
= note: move occurs because `f` has type `std::boxed::Box<(dyn std::ops::FnOnce(i32) -> i32 + 'static)>`, which does not implement the `Copy` trait

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:129:28
--> $DIR/two-phase-nonrecv-autoref.rs:125:28
|
LL | double_access(&mut a, &a);
| - ^- mutable borrow ends here
Expand All @@ -54,7 +54,7 @@ LL | double_access(&mut a, &a);
| mutable borrow occurs here

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:135:9
--> $DIR/two-phase-nonrecv-autoref.rs:131:9
|
LL | a.m(a.i(10));
| - ^ - mutable borrow ends here
Expand All @@ -63,7 +63,7 @@ LL | a.m(a.i(10));
| mutable borrow occurs here

error[E0502]: cannot borrow `i` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:157:7
--> $DIR/two-phase-nonrecv-autoref.rs:153:7
|
LL | i[i[3]] = 4;
| - ^ - mutable borrow ends here
Expand All @@ -72,7 +72,7 @@ LL | i[i[3]] = 4;
| mutable borrow occurs here

error[E0502]: cannot borrow `i` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:163:7
--> $DIR/two-phase-nonrecv-autoref.rs:159:7
|
LL | i[i[3]] = i[4];
| - ^ - mutable borrow ends here
Expand All @@ -81,7 +81,7 @@ LL | i[i[3]] = i[4];
| mutable borrow occurs here

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:172:12
--> $DIR/two-phase-nonrecv-autoref.rs:168:12
|
LL | v.push(v.len());
| - ^ - mutable borrow ends here
Expand All @@ -90,7 +90,7 @@ LL | v.push(v.len());
| mutable borrow occurs here

error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:183:9
--> $DIR/two-phase-nonrecv-autoref.rs:179:9
|
LL | s.m(s.i(10));
| - ^ - mutable borrow ends here
Expand All @@ -99,7 +99,7 @@ LL | s.m(s.i(10));
| mutable borrow occurs here

error[E0502]: cannot borrow `t` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:188:9
--> $DIR/two-phase-nonrecv-autoref.rs:184:9
|
LL | t.m(t.i(10));
| - ^ - mutable borrow ends here
Expand Down
36 changes: 11 additions & 25 deletions src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@ LL | f(f(10));
| first mutable borrow occurs here
| first borrow later used by call

error[E0382]: use of moved value: `*f`
error[E0382]: use of moved value: `f`
--> $DIR/two-phase-nonrecv-autoref.rs:69:11
|
LL | fn twice_ten_so<F: FnOnce(i32) -> i32>(f: Box<F>) {
| - consider adding a `Copy` constraint to this type argument
| - move occurs because `f` has type `std::boxed::Box<F>`, which does not implement the `Copy` trait
LL | f(f(10));
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `*f` has type `F`, which does not implement the `Copy` trait

error[E0499]: cannot borrow `*f` as mutable more than once at a time
--> $DIR/two-phase-nonrecv-autoref.rs:76:11
Expand All @@ -28,30 +26,18 @@ LL | f(f(10));
| first mutable borrow occurs here
| first borrow later used by call

error[E0161]: cannot move a value of type dyn std::ops::FnOnce(i32) -> i32: the size of dyn std::ops::FnOnce(i32) -> i32 cannot be statically determined
--> $DIR/two-phase-nonrecv-autoref.rs:85:9
|
LL | f(f(10));
| ^

error[E0161]: cannot move a value of type dyn std::ops::FnOnce(i32) -> i32: the size of dyn std::ops::FnOnce(i32) -> i32 cannot be statically determined
--> $DIR/two-phase-nonrecv-autoref.rs:85:11
|
LL | f(f(10));
| ^

error[E0382]: use of moved value: `*f`
error[E0382]: use of moved value: `f`
--> $DIR/two-phase-nonrecv-autoref.rs:85:11
|
LL | fn twice_ten_oo(f: Box<FnOnce(i32) -> i32>) {
| - move occurs because `f` has type `std::boxed::Box<dyn std::ops::FnOnce(i32) -> i32>`, which does not implement the `Copy` trait
LL | f(f(10));
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `*f` has type `dyn std::ops::FnOnce(i32) -> i32`, which does not implement the `Copy` trait

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:129:27
--> $DIR/two-phase-nonrecv-autoref.rs:125:27
|
LL | double_access(&mut a, &a);
| ------------- ------ ^^ immutable borrow occurs here
Expand All @@ -60,7 +46,7 @@ LL | double_access(&mut a, &a);
| mutable borrow later used by call

error[E0502]: cannot borrow `i` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:157:7
--> $DIR/two-phase-nonrecv-autoref.rs:153:7
|
LL | i[i[3]] = 4;
| --^----
Expand All @@ -70,7 +56,7 @@ LL | i[i[3]] = 4;
| mutable borrow later used here

error[E0502]: cannot borrow `i` as immutable because it is also borrowed as mutable
--> $DIR/two-phase-nonrecv-autoref.rs:163:7
--> $DIR/two-phase-nonrecv-autoref.rs:159:7
|
LL | i[i[3]] = i[4];
| --^----
Expand All @@ -79,7 +65,7 @@ LL | i[i[3]] = i[4];
| mutable borrow occurs here
| mutable borrow later used here

error: aborting due to 9 previous errors
error: aborting due to 7 previous errors

Some errors occurred: E0161, E0382, E0499, E0502.
For more information about an error, try `rustc --explain E0161`.
Some errors occurred: E0382, E0499, E0502.
For more information about an error, try `rustc --explain E0382`.
Loading