From 79941973af54db7f7b941582fdc9537b2ee95a00 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Sun, 28 Oct 2018 15:27:29 +0900 Subject: [PATCH 01/12] Make FnBox a subtrait of FnOnce. --- src/liballoc/boxed.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index b2315c6a73907..9166f917293d1 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -735,9 +735,7 @@ impl FusedIterator for Box {} #[rustc_paren_sugar] #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] -pub trait FnBox { - type Output; - +pub trait FnBox: FnOnce { fn call_box(self: Box, args: A) -> Self::Output; } @@ -746,8 +744,6 @@ pub trait FnBox { impl FnBox for F where F: FnOnce { - type Output = F::Output; - fn call_box(self: Box, args: A) -> F::Output { self.call_once(args) } From 059ec76d9b2ba33be5d7b092ffeb401590a5d39d Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Sun, 28 Oct 2018 15:28:15 +0900 Subject: [PATCH 02/12] Add Fn* blanket impls for Box. --- src/liballoc/boxed.rs | 33 +++++++++++++++++++++++++++++++++ src/liballoc/lib.rs | 1 + 2 files changed, 34 insertions(+) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 9166f917293d1..09554a1a34da8 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -694,6 +694,37 @@ impl ExactSizeIterator for Box { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Box {} +#[cfg(not(stage0))] +#[unstable(feature = "boxed_closure_impls", + reason = "Box relies on unsized rvalues and needs to be tested more", + issue = "48055")] +impl + ?Sized> FnOnce for Box { + type Output = >::Output; + + default extern "rust-call" fn call_once(self, args: A) -> Self::Output { + >::call_once(*self, args) + } +} + +#[cfg(not(stage0))] +#[unstable(feature = "boxed_closure_impls", + reason = "Box relies on unsized rvalues and needs to be tested more", + issue = "48055")] +impl + ?Sized> FnMut for Box { + extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output { + >::call_mut(self, args) + } +} + +#[cfg(not(stage0))] +#[unstable(feature = "boxed_closure_impls", + reason = "Box relies on unsized rvalues and needs to be tested more", + issue = "48055")] +impl + ?Sized> Fn for Box { + extern "rust-call" fn call(&self, args: A) -> Self::Output { + >::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 @@ -752,6 +783,7 @@ impl FnBox for F #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl FnOnce for Box + '_> { + #[cfg(stage0)] type Output = R; extern "rust-call" fn call_once(self, args: A) -> R { @@ -762,6 +794,7 @@ impl FnOnce for Box + '_> { #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl FnOnce for Box + Send + '_> { + #[cfg(stage0)] type Output = R; extern "rust-call" fn call_once(self, args: A) -> R { diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 90ff56814fbb1..9064b4ccd6a88 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -107,6 +107,7 @@ #![feature(unboxed_closures)] #![feature(unicode_internals)] #![feature(unsize)] +#![feature(unsized_locals)] #![feature(allocator_internals)] #![feature(on_unimplemented)] #![feature(rustc_const_unstable)] From 480dcb403caa90ecd2cc717ad4801805c010d3f6 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Sun, 28 Oct 2018 15:28:47 +0900 Subject: [PATCH 03/12] Add tests for boxed_closure_impls. --- .../run-pass/unsized-locals/box-fnonce.rs | 10 +++++++ .../run-pass/unsized-locals/fnbox-compat.rs | 12 +++++++++ src/test/ui/unsized-locals/fnbox-compat.rs | 12 +++++++++ .../ui/unsized-locals/fnbox-compat.stderr | 27 +++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 src/test/run-pass/unsized-locals/box-fnonce.rs create mode 100644 src/test/run-pass/unsized-locals/fnbox-compat.rs create mode 100644 src/test/ui/unsized-locals/fnbox-compat.rs create mode 100644 src/test/ui/unsized-locals/fnbox-compat.stderr diff --git a/src/test/run-pass/unsized-locals/box-fnonce.rs b/src/test/run-pass/unsized-locals/box-fnonce.rs new file mode 100644 index 0000000000000..97007a9423917 --- /dev/null +++ b/src/test/run-pass/unsized-locals/box-fnonce.rs @@ -0,0 +1,10 @@ +#![feature(boxed_closure_impls)] + +fn call_it(f: Box T>) -> T { + f() +} + +fn main() { + let s = "hello".to_owned(); + assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); +} diff --git a/src/test/run-pass/unsized-locals/fnbox-compat.rs b/src/test/run-pass/unsized-locals/fnbox-compat.rs new file mode 100644 index 0000000000000..5ec54ada13bb0 --- /dev/null +++ b/src/test/run-pass/unsized-locals/fnbox-compat.rs @@ -0,0 +1,12 @@ +#![feature(fnbox)] + +use std::boxed::FnBox; + +fn call_it(f: Box T>) -> T { + f() +} + +fn main() { + let s = "hello".to_owned(); + assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); +} diff --git a/src/test/ui/unsized-locals/fnbox-compat.rs b/src/test/ui/unsized-locals/fnbox-compat.rs new file mode 100644 index 0000000000000..3cb9ac560a256 --- /dev/null +++ b/src/test/ui/unsized-locals/fnbox-compat.rs @@ -0,0 +1,12 @@ +#![feature(fnbox)] + +use std::boxed::FnBox; + +fn call_it(f: Box T>) -> T { + f(&42) +} + +fn main() { + let s = "hello".to_owned(); + assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); +} diff --git a/src/test/ui/unsized-locals/fnbox-compat.stderr b/src/test/ui/unsized-locals/fnbox-compat.stderr new file mode 100644 index 0000000000000..5172092fb1cd5 --- /dev/null +++ b/src/test/ui/unsized-locals/fnbox-compat.stderr @@ -0,0 +1,27 @@ +error[E0593]: closure is expected to take 1 argument, but it takes 0 arguments + --> $DIR/fnbox-compat.rs:11:34 + | +LL | assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); + | ^^ + | | + | expected closure that takes 1 argument + | takes 0 arguments +help: consider changing the closure to take and ignore the expected argument + | +LL | assert_eq!(&call_it(Box::new(|_| s)) as &str, "hello"); + | ^^^ + +error[E0277]: the size for values of type `dyn for<'r> std::boxed::FnBox<(&'r i32,), Output=_>` cannot be known at compilation time + --> $DIR/fnbox-compat.rs:11:25 + | +LL | assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `dyn for<'r> std::boxed::FnBox<(&'r i32,), Output=_>` + = note: to learn more, visit + = note: required by `>::new` + +error: aborting due to 2 previous errors + +Some errors occurred: E0277, E0593. +For more information about an error, try `rustc --explain E0277`. From 219097ecf6026954db100fb00089a2188915615d Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Sun, 28 Oct 2018 15:36:58 +0900 Subject: [PATCH 04/12] Add unstable-book articles on fnbox and boxed_closure_impls. --- .../library-features/boxed-closure-impls.md | 98 +++++++ .../src/library-features/fnbox.md | 252 ++++++++++++++++++ 2 files changed, 350 insertions(+) create mode 100644 src/doc/unstable-book/src/library-features/boxed-closure-impls.md create mode 100644 src/doc/unstable-book/src/library-features/fnbox.md diff --git a/src/doc/unstable-book/src/library-features/boxed-closure-impls.md b/src/doc/unstable-book/src/library-features/boxed-closure-impls.md new file mode 100644 index 0000000000000..0c738d0f78e03 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/boxed-closure-impls.md @@ -0,0 +1,98 @@ +# `boxed_closure_impls` + +The tracking issue for this feature is [#48055] + +[#48055]: https://github.com/rust-lang/rust/issues/48055 + +------------------------ + +This includes the following blanket impls for closure traits: + +```rust,ignore +impl + ?Sized> FnOnce for Box { + // ... +} +impl + ?Sized> FnMut for Box { + // ... +} +impl + ?Sized> Fn for Box { + // ... +} +``` + +## Usage + +`Box` can be used almost transparently. You can even use `Box` now. + +```rust +#![feature(boxed_closure_impls)] + +fn main() { + let resource = "hello".to_owned(); + // Create a boxed once-callable closure + let f: Box = Box::new(|x| { + let s = resource; + println!("{}", x); + println!("{}", s); + }); + + // Call it + f(); +} +``` + +## The reason for instability + +This is unstable because of the first impl. + +It would have been easy if we're allowed to tighten the bound: + +```rust,ignore +impl + ?Sized> FnOnce for Box { + // ... +} +``` + +However, `Box` drops out of the modified impl. +To rescue this, we had had a temporary solution called [`fnbox`][fnbox]. + +[fnbox]: library-features/fnbox.html + +Unfortunately, due to minor coherence reasons, `fnbox` and +`FnOnce for Box` had not been able to coexist. +We had preferred `fnbox` for the time being. + +Now, as [`unsized_locals`][unsized_locals] is implemented, we can just write the +original impl: + +[unsized_locals]: language-features/unsized-locals.html + +```rust,ignore +impl + ?Sized> FnOnce for Box { + type Output = >::Output; + + extern "rust-call" fn call_once(self, args: A) -> Self::Output { + // *self is an unsized rvalue + >::call_once(*self, args) + } +} +``` + +However, since `unsized_locals` is a very young feature, we're careful about +this `FnOnce` impl now. + +There's another reason for instability: for compatibility with `fnbox`, +we currently allow specialization of the `Box` impl: + +```rust,ignore +impl + ?Sized> FnOnce for Box { + type Output = >::Output; + + // we have "default" here + default extern "rust-call" fn call_once(self, args: A) -> Self::Output { + >::call_once(*self, args) + } +} +``` + +This isn't what we desire in the long term. diff --git a/src/doc/unstable-book/src/library-features/fnbox.md b/src/doc/unstable-book/src/library-features/fnbox.md new file mode 100644 index 0000000000000..3200601e557f6 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/fnbox.md @@ -0,0 +1,252 @@ +# `fnbox` + +The tracking issue for this feature is [#28796] + +[#28796]: https://github.com/rust-lang/rust/issues/28796 + +------------------------ + +As an analogy to `&dyn Fn()` and `&mut dyn FnMut()`, you may have expected +`Box` to work. But it hadn't until the recent improvement! +`FnBox` had been a **temporary** solution for this until we are able to pass +trait objects by value. + +See [`boxed_closure_impls`][boxed_closure_impls] for the newer approach. + +[boxed_closure_impls]: library-features/boxed-closure-impls.html + +## Usage + +If you want to box `FnOnce` closures, you can use `Box` instead of `Box`. + +```rust +#![feature(fnbox)] + +use std::boxed::FnBox; + +fn main() { + let resource = "hello".to_owned(); + // Create a boxed once-callable closure + let f: Box String> = Box::new(|| resource); + + // Call it + let s = f(); + println!("{}", s); +} +``` + +## How `Box` did not work + +**Spoiler**: [`boxed_closure_impls`][boxed_closure_impls] actually implements +`Box`! This didn't work because we lacked features like +[`unsized_locals`][unsized_locals] for a long time. Therefore, this section +just explains historical reasons for `FnBox`. + +[unsized_locals]: language-features/unsized-locals.html + +### First approach: just provide `Box` adapter impl + +The first (and natural) attempt for `Box` would look like: + +```rust,ignore +impl + ?Sized> FnOnce for Box { + type Output = >::Output; + + extern "rust-call" fn call_once(self, args: A) -> Self::Output { + >::call_once(*self, args) + } +} +``` + +However, this doesn't work. We have to relax the `Sized` bound for `F` because +we expect trait objects here, but `*self` must be `Sized` because it is passed +as a function argument. + +### The second attempt: add `FnOnce::call_box` + +One may come up with this workaround: modify `FnOnce`'s definition like this: + +```rust,ignore +pub trait FnOnce { + type Output; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; + // Add this new method + extern "rust-call" fn call_box(self: Box, args: Args) -> Self::Output; +} +``` + +...and then, modify the `impl` like this: + +```rust,ignore +impl + ?Sized> FnOnce for Box { + type Output = >::Output; + + extern "rust-call" fn call_once(self, args: A) -> Self::Output { + // We can use `call_box` here! + >::call_box(self, args) + } + // We'll have to define this in every impl of `FnOnce`. + extern "rust-call" fn call_box(self: Box, args: A) -> Self::Output { + >::call_box(*self, args) + } +} +``` + +What's wrong with this? The problem here is crates: + +- `FnOnce` is in `libcore`, as it shouldn't depend on allocations. +- `Box` is in `liballoc`, as it:s the very allocated pointer. + +It is impossible to add `FnOnce::call_box` because it is reverse-dependency. + +There's another problem: `call_box` can't have defaults. +`default impl` from the specialization RFC may resolve this problem. + +### The third attempt: add `FnBox` that contains `call_box` + +`call_box` can't reside in `FnOnce`, but how about defining a new trait in +`liballoc`? + +`FnBox` is almost a copy of `FnOnce`, but with `call_box`: + +```rust,ignore +pub trait FnBox { + type Output; + + extern "rust-call" fn call_box(self: Box, args: Args) -> Self::Output; +} +``` + +For `Sized` types (from which we coerce into `dyn FnBox`), we define +the blanket impl that proxies calls to `FnOnce`: + +```rust,ignore +impl> FnBox for F { + type Output = >::Output; + + extern "rust-call" fn call_box(self: Box, args: A) -> Self::Output { + // Here we assume `F` to be sized. + >::call_once(*self, args) + } +} +``` + +Now it looks like that we can define `FnOnce` for `Box`. + +```rust,ignore +impl + ?Sized> FnOnce for Box { + type Output = >::Output; + + extern "rust-call" fn call_once(self, args: A) -> Self::Output { + >::call_box(self, args) + } +} +``` + +## Limitations of `FnBox` + +### Interaction with HRTB + +Firstly, the actual implementation is different from the one presented above. +Instead of implementing `FnOnce` for `Box`, `liballoc` only +implements `FnOnce` for `Box`. + +```rust,ignore +impl<'a, A, R> FnOnce for Box + 'a> { + type Output = R; + + extern "rust-call" fn call_once(self, args: A) -> Self::Output { + FnBox::call_box(*self, args) + } +} + +// Sendable variant +impl<'a, A, R> FnOnce for Box + Send + 'a> { + type Output = R; + + extern "rust-call" fn call_once(self, args: A) -> Self::Output { + FnBox::call_box(*self, args) + } +} +``` + +The consequence is that the following example doesn't work: + +```rust,compile_fail +#![feature(fnbox)] + +use std::boxed::FnBox; + +fn main() { + let f: Box = Box::new(|x| println!("{}", x)); + f(42); +} +``` + +Note that `dyn FnBox(&i32)` desugars to +`dyn for<'r> FnBox<(&'r i32,), Output = ()>`. +It isn't covered in `dyn FnBox + 'a` or +`dyn FnBox + Send + 'a` due to HRTB. + +### Interaction with `Fn`/`FnMut` + +It would be natural to have the following impls: + +```rust,ignore +impl + ?Sized> FnMut for Box { + // ... +} +impl + ?Sized> Fn for Box { + // ... +} +``` + +However, we hadn't been able to write these in presense of `FnBox` +(until [`boxed_closure_impls`][boxed_closure_impls] lands). + +To have `FnMut` for `Box`, we should have (at least) this impl: + +```rust,ignore +// Note here we only impose `F: FnMut`. +// If we can write `F: FnOnce` here, that will resolve all problems. +impl + ?Sized> FnOnce for Box { + // ... +} +``` + +Unfortunately, the compiler complains that it **overlaps** with our +`dyn FnBox()` impls. At first glance, the overlap must not happen. +The `A` generic parameter does the trick here: due to coherence rules, +a downstream crate may define the following impl: + +```rust,ignore +struct MyStruct; +impl<'a> FnMut for dyn FnBox + 'a { + // ... +} +``` + +The trait solver doesn't know that `A` is always a tuple type, so this is +still possible. With this in mind, the compiler emits the overlap error. + +## Modification + +For compatibility with [`boxed_closure_impls`][boxed_closure_impls], +we now have a slightly modified version of `FnBox`: + +```rust,ignore +// It's now a subtrait of `FnOnce` +pub trait FnBox: FnOnce { + // now uses FnOnce::Output + // type Output; + + extern "rust-call" fn call_box(self: Box, args: Args) -> Self::Output; +} +``` + +## The future of `fnbox` + +`FnBox` has long been considered a temporary solution for `Box` +problem. Since we have [`boxed_closure_impls`][boxed_closure_impls] now, +it may be deprecated and removed in the future. From e55d82c8a3a05e433897de4a991366bb2963470d Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Sun, 28 Oct 2018 17:53:43 +0900 Subject: [PATCH 05/12] Fix expectations on some ui tests involving FnOnce. --- .../two-phase-nonrecv-autoref.ast.nll.stderr | 32 ++++++----------- .../two-phase-nonrecv-autoref.ast.stderr | 22 ++++++------ .../two-phase-nonrecv-autoref.nll.stderr | 36 ++++++------------- .../ui/borrowck/two-phase-nonrecv-autoref.rs | 16 ++++----- ...owck-call-is-borrow-issue-12224.nll.stderr | 2 +- .../borrowck-call-is-borrow-issue-12224.rs | 2 +- ...borrowck-call-is-borrow-issue-12224.stderr | 4 +-- 7 files changed, 42 insertions(+), 72 deletions(-) diff --git a/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr b/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr index d026f81b7aad6..df74feb4f0564 100644 --- a/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr +++ b/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr @@ -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 i32>(f: Box) { @@ -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`, 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 @@ -28,19 +28,7 @@ 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)); @@ -48,10 +36,10 @@ LL | f(f(10)); | | | 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 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 @@ -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; | --^---- @@ -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]; | --^---- @@ -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`. diff --git a/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.stderr b/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.stderr index 426939f371ca5..d98b272944fdc 100644 --- a/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.stderr +++ b/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.stderr @@ -15,7 +15,7 @@ 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)); @@ -23,7 +23,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`, 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 @@ -34,7 +34,7 @@ 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)); @@ -42,10 +42,10 @@ LL | f(f(10)); | | | 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr b/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr index d026f81b7aad6..223de36f0df31 100644 --- a/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr +++ b/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr @@ -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 i32>(f: Box) { - | - consider adding a `Copy` constraint to this type argument + | - move occurs because `f` has type `std::boxed::Box`, 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 @@ -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 i32>) { + | - move occurs because `f` has type `std::boxed::Box 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 @@ -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; | --^---- @@ -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]; | --^---- @@ -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`. diff --git a/src/test/ui/borrowck/two-phase-nonrecv-autoref.rs b/src/test/ui/borrowck/two-phase-nonrecv-autoref.rs index 1a14cb90f38ef..1005da052970b 100644 --- a/src/test/ui/borrowck/two-phase-nonrecv-autoref.rs +++ b/src/test/ui/borrowck/two-phase-nonrecv-autoref.rs @@ -67,9 +67,9 @@ fn overloaded_call_traits() { } fn twice_ten_so i32>(f: Box) { f(f(10)); - //[nll]~^ ERROR use of moved value: `*f` - //[g2p]~^^ ERROR use of moved value: `*f` - //[ast]~^^^ ERROR use of moved value: `*f` + //[nll]~^ ERROR use of moved value: `f` + //[g2p]~^^ ERROR use of moved value: `f` + //[ast]~^^^ ERROR use of moved value: `f` } fn twice_ten_om(f: &mut FnMut(i32) -> i32) { @@ -83,13 +83,9 @@ fn overloaded_call_traits() { } fn twice_ten_oo(f: Box i32>) { f(f(10)); - //[nll]~^ ERROR cannot move a value of type - //[nll]~^^ ERROR cannot move a value of type - //[nll]~^^^ ERROR use of moved value: `*f` - //[g2p]~^^^^ ERROR cannot move a value of type - //[g2p]~^^^^^ ERROR cannot move a value of type - //[g2p]~^^^^^^ ERROR use of moved value: `*f` - //[ast]~^^^^^^^ ERROR use of moved value: `*f` + //[nll]~^ ERROR use of moved value: `f` + //[g2p]~^^ ERROR use of moved value: `f` + //[ast]~^^^ ERROR use of moved value: `f` } twice_ten_sm(&mut |x| x + 1); diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr index 1a5ab7a7d56a0..4e2ab59f82245 100644 --- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr +++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr @@ -18,7 +18,7 @@ LL | fn test2(f: &F) where F: FnMut() { LL | (*f)(); | ^^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable -error[E0596]: cannot borrow `*f.f` as mutable, as it is behind a `&` reference +error[E0596]: cannot borrow `f.f` as mutable, as it is behind a `&` reference --> $DIR/borrowck-call-is-borrow-issue-12224.rs:34:5 | LL | fn test4(f: &Test) { diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs index cfebd6f700359..f246f1118bf4e 100644 --- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs +++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs @@ -32,7 +32,7 @@ fn test3(f: &mut F) where F: FnMut() { fn test4(f: &Test) { f.f.call_mut(()) - //~^ ERROR: cannot borrow `Box` content `*f.f` of immutable binding as mutable + //~^ ERROR: cannot borrow field `f.f` of immutable binding as mutable } fn test5(f: &mut Test) { diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr index 6c8f477e31062..a61ee8334afff 100644 --- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -19,13 +19,13 @@ LL | fn test2(f: &F) where F: FnMut() { LL | (*f)(); | ^^^^ cannot borrow as mutable -error[E0596]: cannot borrow `Box` content `*f.f` of immutable binding as mutable +error[E0596]: cannot borrow field `f.f` of immutable binding as mutable --> $DIR/borrowck-call-is-borrow-issue-12224.rs:34:5 | LL | fn test4(f: &Test) { | ----- use `&mut Test` here to make mutable LL | f.f.call_mut(()) - | ^^^ cannot borrow as mutable + | ^^^ cannot mutably borrow field of immutable binding error[E0504]: cannot move `f` into closure because it is borrowed --> $DIR/borrowck-call-is-borrow-issue-12224.rs:56:13 From 4dcd6cc208e143309d481f0ecf4051bd9d139a18 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Sun, 3 Feb 2019 22:08:34 +0900 Subject: [PATCH 06/12] Fix failing tests. --- .../library-features/boxed-closure-impls.md | 2 +- src/test/ui/unsized-locals/fnbox-compat.rs | 3 +- .../ui/unsized-locals/fnbox-compat.stderr | 30 +++++-------------- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/doc/unstable-book/src/library-features/boxed-closure-impls.md b/src/doc/unstable-book/src/library-features/boxed-closure-impls.md index 0c738d0f78e03..5ceb54ff3b96e 100644 --- a/src/doc/unstable-book/src/library-features/boxed-closure-impls.md +++ b/src/doc/unstable-book/src/library-features/boxed-closure-impls.md @@ -37,7 +37,7 @@ fn main() { }); // Call it - f(); + f(&42); } ``` diff --git a/src/test/ui/unsized-locals/fnbox-compat.rs b/src/test/ui/unsized-locals/fnbox-compat.rs index 3cb9ac560a256..c2c385e9fea56 100644 --- a/src/test/ui/unsized-locals/fnbox-compat.rs +++ b/src/test/ui/unsized-locals/fnbox-compat.rs @@ -4,9 +4,10 @@ use std::boxed::FnBox; fn call_it(f: Box T>) -> T { f(&42) + //~^ERROR implementation of `std::ops::FnOnce` is not general enough } fn main() { let s = "hello".to_owned(); - assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); + assert_eq!(&call_it(Box::new(|_| s)) as &str, "hello"); } diff --git a/src/test/ui/unsized-locals/fnbox-compat.stderr b/src/test/ui/unsized-locals/fnbox-compat.stderr index 5172092fb1cd5..c37bfaa47f7eb 100644 --- a/src/test/ui/unsized-locals/fnbox-compat.stderr +++ b/src/test/ui/unsized-locals/fnbox-compat.stderr @@ -1,27 +1,11 @@ -error[E0593]: closure is expected to take 1 argument, but it takes 0 arguments - --> $DIR/fnbox-compat.rs:11:34 +error: implementation of `std::ops::FnOnce` is not general enough + --> $DIR/fnbox-compat.rs:6:5 | -LL | assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); - | ^^ - | | - | expected closure that takes 1 argument - | takes 0 arguments -help: consider changing the closure to take and ignore the expected argument +LL | f(&42) + | ^^^^^^ | -LL | assert_eq!(&call_it(Box::new(|_| s)) as &str, "hello"); - | ^^^ + = note: `std::ops::FnOnce<(&'0 i32,)>` would have to be implemented for the type `std::boxed::Box<(dyn for<'r> std::boxed::FnBox<(&'r i32,), Output=T> + 'static)>`, for some specific lifetime `'0` + = note: but `std::ops::FnOnce<(&'1 i32,)>` is actually implemented for the type `std::boxed::Box<(dyn std::boxed::FnBox<(&'1 i32,), Output=T> + '_)>`, for some specific lifetime `'1` -error[E0277]: the size for values of type `dyn for<'r> std::boxed::FnBox<(&'r i32,), Output=_>` cannot be known at compilation time - --> $DIR/fnbox-compat.rs:11:25 - | -LL | assert_eq!(&call_it(Box::new(|| s)) as &str, "hello"); - | ^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `std::marker::Sized` is not implemented for `dyn for<'r> std::boxed::FnBox<(&'r i32,), Output=_>` - = note: to learn more, visit - = note: required by `>::new` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors occurred: E0277, E0593. -For more information about an error, try `rustc --explain E0277`. From a38f29272ef4d04f0cc77e4f8d4fa5fac7ed746d Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Sun, 10 Feb 2019 19:25:56 +0900 Subject: [PATCH 07/12] We already have unsized_locals in stage0. --- src/liballoc/boxed.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 09554a1a34da8..864add1ecfe8e 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -694,7 +694,6 @@ impl ExactSizeIterator for Box { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Box {} -#[cfg(not(stage0))] #[unstable(feature = "boxed_closure_impls", reason = "Box relies on unsized rvalues and needs to be tested more", issue = "48055")] @@ -706,7 +705,6 @@ impl + ?Sized> FnOnce for Box { } } -#[cfg(not(stage0))] #[unstable(feature = "boxed_closure_impls", reason = "Box relies on unsized rvalues and needs to be tested more", issue = "48055")] @@ -716,7 +714,6 @@ impl + ?Sized> FnMut for Box { } } -#[cfg(not(stage0))] #[unstable(feature = "boxed_closure_impls", reason = "Box relies on unsized rvalues and needs to be tested more", issue = "48055")] @@ -783,9 +780,6 @@ impl FnBox for F #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl FnOnce for Box + '_> { - #[cfg(stage0)] - type Output = R; - extern "rust-call" fn call_once(self, args: A) -> R { self.call_box(args) } @@ -794,9 +788,6 @@ impl FnOnce for Box + '_> { #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl FnOnce for Box + Send + '_> { - #[cfg(stage0)] - type Output = R; - extern "rust-call" fn call_once(self, args: A) -> R { self.call_box(args) } From 45c0b28bcb6e383cd9d24d3845ee8accda31c889 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 11 Feb 2019 10:34:24 +0900 Subject: [PATCH 08/12] Remove FnBox specialization of impl FnOnce for Box. --- src/liballoc/boxed.rs | 18 +----------------- src/test/ui/unsized-locals/fnbox-compat.rs | 13 ------------- src/test/ui/unsized-locals/fnbox-compat.stderr | 11 ----------- 3 files changed, 1 insertion(+), 41 deletions(-) delete mode 100644 src/test/ui/unsized-locals/fnbox-compat.rs delete mode 100644 src/test/ui/unsized-locals/fnbox-compat.stderr diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 864add1ecfe8e..d4fe8be36d6f5 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -700,7 +700,7 @@ impl FusedIterator for Box {} impl + ?Sized> FnOnce for Box { type Output = >::Output; - default extern "rust-call" fn call_once(self, args: A) -> Self::Output { + extern "rust-call" fn call_once(self, args: A) -> Self::Output { >::call_once(*self, args) } } @@ -777,22 +777,6 @@ impl FnBox for F } } -#[unstable(feature = "fnbox", - reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] -impl FnOnce for Box + '_> { - 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` becomes usable", issue = "28796")] -impl FnOnce for Box + Send + '_> { - extern "rust-call" fn call_once(self, args: A) -> R { - self.call_box(args) - } -} - #[unstable(feature = "coerce_unsized", issue = "27732")] impl, U: ?Sized> CoerceUnsized> for Box {} diff --git a/src/test/ui/unsized-locals/fnbox-compat.rs b/src/test/ui/unsized-locals/fnbox-compat.rs deleted file mode 100644 index c2c385e9fea56..0000000000000 --- a/src/test/ui/unsized-locals/fnbox-compat.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(fnbox)] - -use std::boxed::FnBox; - -fn call_it(f: Box T>) -> T { - f(&42) - //~^ERROR implementation of `std::ops::FnOnce` is not general enough -} - -fn main() { - let s = "hello".to_owned(); - assert_eq!(&call_it(Box::new(|_| s)) as &str, "hello"); -} diff --git a/src/test/ui/unsized-locals/fnbox-compat.stderr b/src/test/ui/unsized-locals/fnbox-compat.stderr deleted file mode 100644 index c37bfaa47f7eb..0000000000000 --- a/src/test/ui/unsized-locals/fnbox-compat.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: implementation of `std::ops::FnOnce` is not general enough - --> $DIR/fnbox-compat.rs:6:5 - | -LL | f(&42) - | ^^^^^^ - | - = note: `std::ops::FnOnce<(&'0 i32,)>` would have to be implemented for the type `std::boxed::Box<(dyn for<'r> std::boxed::FnBox<(&'r i32,), Output=T> + 'static)>`, for some specific lifetime `'0` - = note: but `std::ops::FnOnce<(&'1 i32,)>` is actually implemented for the type `std::boxed::Box<(dyn std::boxed::FnBox<(&'1 i32,), Output=T> + '_)>`, for some specific lifetime `'1` - -error: aborting due to previous error - From ecc3e89dd072ed20d9aa6d53be0ab1c44d160232 Mon Sep 17 00:00:00 2001 From: Charles Lew Date: Mon, 11 Feb 2019 11:09:26 +0900 Subject: [PATCH 09/12] Stabilize boxed_closure_impls in 1.35.0. --- .../library-features/boxed-closure-impls.md | 98 ------------------- src/liballoc/boxed.rs | 12 +-- .../run-pass/unsized-locals/box-fnonce.rs | 2 - 3 files changed, 3 insertions(+), 109 deletions(-) delete mode 100644 src/doc/unstable-book/src/library-features/boxed-closure-impls.md diff --git a/src/doc/unstable-book/src/library-features/boxed-closure-impls.md b/src/doc/unstable-book/src/library-features/boxed-closure-impls.md deleted file mode 100644 index 5ceb54ff3b96e..0000000000000 --- a/src/doc/unstable-book/src/library-features/boxed-closure-impls.md +++ /dev/null @@ -1,98 +0,0 @@ -# `boxed_closure_impls` - -The tracking issue for this feature is [#48055] - -[#48055]: https://github.com/rust-lang/rust/issues/48055 - ------------------------- - -This includes the following blanket impls for closure traits: - -```rust,ignore -impl + ?Sized> FnOnce for Box { - // ... -} -impl + ?Sized> FnMut for Box { - // ... -} -impl + ?Sized> Fn for Box { - // ... -} -``` - -## Usage - -`Box` can be used almost transparently. You can even use `Box` now. - -```rust -#![feature(boxed_closure_impls)] - -fn main() { - let resource = "hello".to_owned(); - // Create a boxed once-callable closure - let f: Box = Box::new(|x| { - let s = resource; - println!("{}", x); - println!("{}", s); - }); - - // Call it - f(&42); -} -``` - -## The reason for instability - -This is unstable because of the first impl. - -It would have been easy if we're allowed to tighten the bound: - -```rust,ignore -impl + ?Sized> FnOnce for Box { - // ... -} -``` - -However, `Box` drops out of the modified impl. -To rescue this, we had had a temporary solution called [`fnbox`][fnbox]. - -[fnbox]: library-features/fnbox.html - -Unfortunately, due to minor coherence reasons, `fnbox` and -`FnOnce for Box` had not been able to coexist. -We had preferred `fnbox` for the time being. - -Now, as [`unsized_locals`][unsized_locals] is implemented, we can just write the -original impl: - -[unsized_locals]: language-features/unsized-locals.html - -```rust,ignore -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - // *self is an unsized rvalue - >::call_once(*self, args) - } -} -``` - -However, since `unsized_locals` is a very young feature, we're careful about -this `FnOnce` impl now. - -There's another reason for instability: for compatibility with `fnbox`, -we currently allow specialization of the `Box` impl: - -```rust,ignore -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - // we have "default" here - default extern "rust-call" fn call_once(self, args: A) -> Self::Output { - >::call_once(*self, args) - } -} -``` - -This isn't what we desire in the long term. diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index d4fe8be36d6f5..f6dee7c9eefd6 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -694,9 +694,7 @@ impl ExactSizeIterator for Box { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Box {} -#[unstable(feature = "boxed_closure_impls", - reason = "Box relies on unsized rvalues and needs to be tested more", - issue = "48055")] +#[stable(feature = "boxed_closure_impls", since = "1.35.0")] impl + ?Sized> FnOnce for Box { type Output = >::Output; @@ -705,18 +703,14 @@ impl + ?Sized> FnOnce for Box { } } -#[unstable(feature = "boxed_closure_impls", - reason = "Box relies on unsized rvalues and needs to be tested more", - issue = "48055")] +#[stable(feature = "boxed_closure_impls", since = "1.35.0")] impl + ?Sized> FnMut for Box { extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output { >::call_mut(self, args) } } -#[unstable(feature = "boxed_closure_impls", - reason = "Box relies on unsized rvalues and needs to be tested more", - issue = "48055")] +#[stable(feature = "boxed_closure_impls", since = "1.35.0")] impl + ?Sized> Fn for Box { extern "rust-call" fn call(&self, args: A) -> Self::Output { >::call(self, args) diff --git a/src/test/run-pass/unsized-locals/box-fnonce.rs b/src/test/run-pass/unsized-locals/box-fnonce.rs index 97007a9423917..16bdeae4fad41 100644 --- a/src/test/run-pass/unsized-locals/box-fnonce.rs +++ b/src/test/run-pass/unsized-locals/box-fnonce.rs @@ -1,5 +1,3 @@ -#![feature(boxed_closure_impls)] - fn call_it(f: Box T>) -> T { f() } From 440e873a4739e7cb8f6aec0f675ca3796c336e60 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 11 Feb 2019 11:09:41 +0900 Subject: [PATCH 10/12] Simplify fnbox docs. --- .../src/library-features/fnbox.md | 250 ++---------------- 1 file changed, 15 insertions(+), 235 deletions(-) diff --git a/src/doc/unstable-book/src/library-features/fnbox.md b/src/doc/unstable-book/src/library-features/fnbox.md index 3200601e557f6..16ebab3abeb50 100644 --- a/src/doc/unstable-book/src/library-features/fnbox.md +++ b/src/doc/unstable-book/src/library-features/fnbox.md @@ -6,247 +6,27 @@ The tracking issue for this feature is [#28796] ------------------------ -As an analogy to `&dyn Fn()` and `&mut dyn FnMut()`, you may have expected -`Box` to work. But it hadn't until the recent improvement! -`FnBox` had been a **temporary** solution for this until we are able to pass -trait objects by value. - -See [`boxed_closure_impls`][boxed_closure_impls] for the newer approach. - -[boxed_closure_impls]: library-features/boxed-closure-impls.html - -## Usage - -If you want to box `FnOnce` closures, you can use `Box` instead of `Box`. +This had been a temporary alternative to the following impls: ```rust -#![feature(fnbox)] - -use std::boxed::FnBox; - -fn main() { - let resource = "hello".to_owned(); - // Create a boxed once-callable closure - let f: Box String> = Box::new(|| resource); - - // Call it - let s = f(); - println!("{}", s); -} +impl FnOnce for Box where F: FnOnce + ?Sized {} +impl FnMut for Box where F: FnMut + ?Sized {} +impl Fn for Box where F: Fn + ?Sized {} ``` -## How `Box` did not work - -**Spoiler**: [`boxed_closure_impls`][boxed_closure_impls] actually implements -`Box`! This didn't work because we lacked features like -[`unsized_locals`][unsized_locals] for a long time. Therefore, this section -just explains historical reasons for `FnBox`. - -[unsized_locals]: language-features/unsized-locals.html - -### First approach: just provide `Box` adapter impl - -The first (and natural) attempt for `Box` would look like: - -```rust,ignore -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - >::call_once(*self, args) - } -} -``` - -However, this doesn't work. We have to relax the `Sized` bound for `F` because -we expect trait objects here, but `*self` must be `Sized` because it is passed -as a function argument. - -### The second attempt: add `FnOnce::call_box` - -One may come up with this workaround: modify `FnOnce`'s definition like this: - -```rust,ignore -pub trait FnOnce { - type Output; - - extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - // Add this new method - extern "rust-call" fn call_box(self: Box, args: Args) -> Self::Output; -} -``` - -...and then, modify the `impl` like this: - -```rust,ignore -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - // We can use `call_box` here! - >::call_box(self, args) - } - // We'll have to define this in every impl of `FnOnce`. - extern "rust-call" fn call_box(self: Box, args: A) -> Self::Output { - >::call_box(*self, args) - } -} -``` - -What's wrong with this? The problem here is crates: - -- `FnOnce` is in `libcore`, as it shouldn't depend on allocations. -- `Box` is in `liballoc`, as it:s the very allocated pointer. - -It is impossible to add `FnOnce::call_box` because it is reverse-dependency. - -There's another problem: `call_box` can't have defaults. -`default impl` from the specialization RFC may resolve this problem. - -### The third attempt: add `FnBox` that contains `call_box` - -`call_box` can't reside in `FnOnce`, but how about defining a new trait in -`liballoc`? - -`FnBox` is almost a copy of `FnOnce`, but with `call_box`: - -```rust,ignore -pub trait FnBox { - type Output; - - extern "rust-call" fn call_box(self: Box, args: Args) -> Self::Output; -} -``` - -For `Sized` types (from which we coerce into `dyn FnBox`), we define -the blanket impl that proxies calls to `FnOnce`: - -```rust,ignore -impl> FnBox for F { - type Output = >::Output; +The impls are parallel to these (relatively old) impls: - extern "rust-call" fn call_box(self: Box, args: A) -> Self::Output { - // Here we assume `F` to be sized. - >::call_once(*self, args) - } -} -``` - -Now it looks like that we can define `FnOnce` for `Box`. - -```rust,ignore -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - >::call_box(self, args) - } -} -``` - -## Limitations of `FnBox` - -### Interaction with HRTB - -Firstly, the actual implementation is different from the one presented above. -Instead of implementing `FnOnce` for `Box`, `liballoc` only -implements `FnOnce` for `Box`. - -```rust,ignore -impl<'a, A, R> FnOnce for Box + 'a> { - type Output = R; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - FnBox::call_box(*self, args) - } -} - -// Sendable variant -impl<'a, A, R> FnOnce for Box + Send + 'a> { - type Output = R; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - FnBox::call_box(*self, args) - } -} -``` - -The consequence is that the following example doesn't work: - -```rust,compile_fail -#![feature(fnbox)] - -use std::boxed::FnBox; - -fn main() { - let f: Box = Box::new(|x| println!("{}", x)); - f(42); -} -``` - -Note that `dyn FnBox(&i32)` desugars to -`dyn for<'r> FnBox<(&'r i32,), Output = ()>`. -It isn't covered in `dyn FnBox + 'a` or -`dyn FnBox + Send + 'a` due to HRTB. - -### Interaction with `Fn`/`FnMut` - -It would be natural to have the following impls: - -```rust,ignore -impl + ?Sized> FnMut for Box { - // ... -} -impl + ?Sized> Fn for Box { - // ... -} -``` - -However, we hadn't been able to write these in presense of `FnBox` -(until [`boxed_closure_impls`][boxed_closure_impls] lands). - -To have `FnMut` for `Box`, we should have (at least) this impl: - -```rust,ignore -// Note here we only impose `F: FnMut`. -// If we can write `F: FnOnce` here, that will resolve all problems. -impl + ?Sized> FnOnce for Box { - // ... -} -``` - -Unfortunately, the compiler complains that it **overlaps** with our -`dyn FnBox()` impls. At first glance, the overlap must not happen. -The `A` generic parameter does the trick here: due to coherence rules, -a downstream crate may define the following impl: - -```rust,ignore -struct MyStruct; -impl<'a> FnMut for dyn FnBox + 'a { - // ... -} +```rust +impl FnOnce for &mut F where F: FnMut + ?Sized {} +impl FnMut for &mut F where F: FnMut + ?Sized {} +impl Fn for &mut F where F: Fn + ?Sized {} +impl FnOnce for &F where F: Fn + ?Sized {} +impl FnMut for &F where F: Fn + ?Sized {} +impl Fn for &F where F: Fn + ?Sized {} ``` -The trait solver doesn't know that `A` is always a tuple type, so this is -still possible. With this in mind, the compiler emits the overlap error. - -## Modification - -For compatibility with [`boxed_closure_impls`][boxed_closure_impls], -we now have a slightly modified version of `FnBox`: +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` at that time. -```rust,ignore -// It's now a subtrait of `FnOnce` -pub trait FnBox: FnOnce { - // now uses FnOnce::Output - // type Output; - - extern "rust-call" fn call_box(self: Box, args: Args) -> Self::Output; -} -``` - -## The future of `fnbox` +[unsized_locals]: language-features/unsized-locals.html -`FnBox` has long been considered a temporary solution for `Box` -problem. Since we have [`boxed_closure_impls`][boxed_closure_impls] now, -it may be deprecated and removed in the future. +`FnBox()` is an alternative approach to `Box` is delegated to `FnBox::call_box` which doesn't need unsized locals. As we now have `Box` working, the `fnbox` feature is going to be removed. From 7a63c7f01050cd612787372f7a2ca7a8ec2105bc Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 11 Feb 2019 16:32:42 +0900 Subject: [PATCH 11/12] Add ignore to doc code --- src/doc/unstable-book/src/library-features/fnbox.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/unstable-book/src/library-features/fnbox.md b/src/doc/unstable-book/src/library-features/fnbox.md index 16ebab3abeb50..cb3386b715211 100644 --- a/src/doc/unstable-book/src/library-features/fnbox.md +++ b/src/doc/unstable-book/src/library-features/fnbox.md @@ -8,7 +8,7 @@ The tracking issue for this feature is [#28796] This had been a temporary alternative to the following impls: -```rust +```rust,ignore impl FnOnce for Box where F: FnOnce + ?Sized {} impl FnMut for Box where F: FnMut + ?Sized {} impl Fn for Box where F: Fn + ?Sized {} @@ -16,7 +16,7 @@ impl Fn for Box where F: Fn + ?Sized {} The impls are parallel to these (relatively old) impls: -```rust +```rust,ignore impl FnOnce for &mut F where F: FnMut + ?Sized {} impl FnMut for &mut F where F: FnMut + ?Sized {} impl Fn for &mut F where F: Fn + ?Sized {} From 812d89c87d9346ac4b70426b67a8eb989a13c853 Mon Sep 17 00:00:00 2001 From: Charles Lew Date: Fri, 5 Apr 2019 12:06:29 +0800 Subject: [PATCH 12/12] Fix expectations on some ui test in nll compare mode. --- .../ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr b/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr index df74feb4f0564..223de36f0df31 100644 --- a/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr +++ b/src/test/ui/borrowck/two-phase-nonrecv-autoref.ast.nll.stderr @@ -11,13 +11,11 @@ error[E0382]: use of moved value: `f` --> $DIR/two-phase-nonrecv-autoref.rs:69:11 | LL | fn twice_ten_so i32>(f: Box) { - | - consider adding a `Copy` constraint to this type argument + | - move occurs because `f` has type `std::boxed::Box`, 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 `std::boxed::Box`, 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 @@ -31,12 +29,12 @@ LL | f(f(10)); error[E0382]: use of moved value: `f` --> $DIR/two-phase-nonrecv-autoref.rs:85:11 | +LL | fn twice_ten_oo(f: Box i32>) { + | - move occurs because `f` has type `std::boxed::Box 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 `std::boxed::Box 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:125:27