Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upspecial-case #[derive(Copy, Clone)] with a shallow clone #31414
Conversation
rust-highfive
assigned
brson
Feb 4, 2016
durka
referenced this pull request
Feb 4, 2016
Closed
#[derive(Clone)] should rely on Copy when possible #31085
This comment has been minimized.
This comment has been minimized.
|
r? @brson (rust_highfive has picked a reviewer for you, use r? to override) |
durka
force-pushed the
durka:clone-copy
branch
from
69cb84b
to
0aaacb3
Feb 4, 2016
durka
changed the title
special-case #[derive(Copy, Clone)] with a shallow copy
special-case #[derive(Copy, Clone)] with a shallow clone
Feb 4, 2016
nagisa
reviewed
Feb 4, 2016
| ast::ItemStruct(_, ast::Generics { ref ty_params, .. }) | ||
| | ast::ItemEnum(_, ast::Generics { ref ty_params, .. }) | ||
| if ty_params.is_empty() | ||
| && (|| item.attrs.iter().any(|a| a.name() == "derive_Copy"))() => { |
This comment has been minimized.
This comment has been minimized.
nagisa
reviewed
Feb 4, 2016
| } | ||
| } | ||
|
|
||
| _ => cx.span_bug(span, "#[derive(Clone)] on trait item or impl item") |
This comment has been minimized.
This comment has been minimized.
nagisa
Feb 4, 2016
Contributor
So this branch is not trigerred for non-trait/impl items? Consider: functions, type aliases, mod items, etc?
This comment has been minimized.
This comment has been minimized.
durka
Feb 4, 2016
Author
Contributor
AFAIK those should never reach this path because they error out here: https://github.com/rust-lang/rust/blob/master/src/libsyntax_ext/deriving/mod.rs#L122
This comment has been minimized.
This comment has been minimized.
durka
Feb 4, 2016
Author
Contributor
Oh, I misinterpreted what you meant. There are only three choices for Annotatable: http://manishearth.github.io/rust-internals-docs/syntax/ext/base/enum.Annotatable.html
This comment has been minimized.
This comment has been minimized.
durka
Feb 4, 2016
Author
Contributor
I'm wrong again because it could be a different kind of Item. So it should just ICE with "... on non-struct/enum item"
This comment has been minimized.
This comment has been minimized.
nagisa
Feb 4, 2016
Contributor
Right, I’m surprised this branch is necessary, when any sort of item “files” just fine behind the Item variant.
This comment has been minimized.
This comment has been minimized.
durka
Feb 4, 2016
Author
Contributor
No, I'm going back to my second comment. There's a nested match. This unreachable catchall arm is in the outer one, which differentiates between Item vs TraitItem/ImplItem. The inner one does the check to see which kind of Clone we can derive.
This comment has been minimized.
This comment has been minimized.
nagisa
Feb 4, 2016
Contributor
I'm wrong again because it could be a different kind of Item. So it should just ICE with "... on non-struct/enum item"
I think you could just have something like
match *item {
Annotatable::Item(ast::Item { node: ast::ItemStruct(...) }) |
Annotatable::Item(ast::Item { node: ast::ItemEnum(...) })
if /* the other conditions */ => {
// this is copy branch
}
_ => /* the clone branch */
}
This comment has been minimized.
This comment has been minimized.
durka
Feb 5, 2016
Author
Contributor
It doesn't work because it's enum Annotatable { Item(P<Item>), ... } and you can't match through a P. At least, I can't. I tried, with #![feature(box_patterns)] but the ptr field is private so I can't get to the inner Box.
This comment has been minimized.
This comment has been minimized.
|
According to #31085 (comment) this kind of improvement can improve compile times (that's a case with a lot of structs). According to me @ #31085 (comment) using the Copy-based clone can improve codegen for loops calling .clone(). |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
It occurs to me that the shallow Clone can also be done in this situation: #[derive(Copy, Clone)]
struct S<T: Copy>(T);i.e. when there are generic parameters but they are all already constrained to be Copy. Is it worth it to add this check as well? Edit: seems like it would be somewhat unworkably unhygienic. We'd have to check for the literal name "Copy", since all this happens before resolve. |
durka
force-pushed the
durka:clone-copy
branch
from
0aaacb3
to
fe59b7c
Feb 5, 2016
This comment has been minimized.
This comment has been minimized.
|
Rebased. |
This comment has been minimized.
This comment has been minimized.
|
This PR LGTM, but I’d like an opinion from somebody more familiar with the code as well. |
brson
reviewed
Feb 5, 2016
|
|
||
| #[derive(Copy, Clone)] | ||
| struct B<T> { a: T } | ||
|
|
This comment has been minimized.
This comment has been minimized.
brson
Feb 5, 2016
Contributor
Can you put this in its own rmake test case instead of piggy-backing on this one?
This comment has been minimized.
This comment has been minimized.
durka
Feb 5, 2016
Author
Contributor
I'll split the tests and revert my changes to the Makefile for this one.
brson
added
the
relnotes
label
Feb 5, 2016
This comment has been minimized.
This comment has been minimized.
|
Do we have any concrete numbers that this gives to either compile time or runtime? @retep998 said he worked around this on winapi because of bad compile times. Perhaps we can change winapi to use deriving again and see if this improves perf there. |
This comment has been minimized.
This comment has been minimized.
|
Travis failed because I didn't run the full test suite locally. Will fix later tonight. |
durka
force-pushed the
durka:clone-copy
branch
from
fe59b7c
to
298ddac
Feb 6, 2016
This comment has been minimized.
This comment has been minimized.
|
I split the tests and tried to fix the fallout in the rpass-full tests. I'm getting linker errors locally which I think will be solved by |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
As a bonus, this extends the reach of |
This comment has been minimized.
This comment has been minimized.
|
Compile time win for structs and enums might be the best thing with this. A bit unnerving that it enables deriving Clone on more things, though. That makes it hard to go back on this optimization. |
This comment has been minimized.
This comment has been minimized.
|
@bluss I don't see why we'd want to go back on it. Plus, more motivation to fix the issue where types can be magically Copy but not Clone :) |
This comment has been minimized.
This comment has been minimized.
|
Wow, great numbers. I'm also uneasy about the semantic change this brings, making derive Copy/Clone work on types containing those that aren't clone. It exposes the optimization - semantically we may actually want derive Clone to be defined as if it was implemented the naive way. I assume this doesn't magically make just |
This comment has been minimized.
This comment has been minimized.
Correct. The derives just do their little syntactical shufflings and leave it to typeck to enforce the constraints later.
I agree, but it's only observable if you have a type that's |
This comment has been minimized.
This comment has been minimized.
|
We could revert the semantic change by generating code like this: impl Clone for Foo {
fn clone(&self) -> Foo {
if false { /* naive implementation */ }
else { *self }
}
}but we'd of course lose the compile-time wins. |
This comment has been minimized.
This comment has been minimized.
|
I think we optimize Can't we also do something like struct Foo {
bar: Bar,
}
// generated
impl Clone for Foo {
fn clone(&self) -> Foo {
fn is_clone<C: Clone>() {}
is_clone::<Bar>();
*self
}
}which should avoid the compile time sadness. |
This comment has been minimized.
This comment has been minimized.
|
That does still leave the behavior change that |
This comment has been minimized.
This comment has been minimized.
|
@sfackler I'm trying this. I still think it's insignificant since the existence of |
This comment has been minimized.
This comment has been minimized.
|
@durka Good question, it would likely make winapi worse, not better. Maybe replace |
This comment has been minimized.
This comment has been minimized.
|
@Manishearth sorry about my slowness. I still need to fix the spans so I
|
This comment has been minimized.
This comment has been minimized.
|
Nah, it's okay, we can wait a few days I wasn't sure what the status of this PR was. |
This comment has been minimized.
This comment has been minimized.
|
@eddyb I don't use |
This comment has been minimized.
This comment has been minimized.
|
@retep998 but you could in a parallel universe where #[derive(Clone)]
|
This comment has been minimized.
This comment has been minimized.
|
@durka But I can't due some |
This comment has been minimized.
This comment has been minimized.
|
How lovely, winapi needs the bugs that this PR takes care to not expose. |
durka
force-pushed the
durka:clone-copy
branch
from
6712c02
to
5d800cb
Apr 24, 2016
This comment has been minimized.
This comment has been minimized.
|
The stage1-rmake test passes now. And the stage2-rpass-full one. And I guess travis will try them all. |
This comment has been minimized.
This comment has been minimized.
durka
force-pushed the
durka:clone-copy
branch
from
e54419a
to
33c1c86
Apr 25, 2016
This comment has been minimized.
This comment has been minimized.
|
Breaking change removed. Performance gains retained. |
durka
force-pushed the
durka:clone-copy
branch
from
33c1c86
to
64d22d4
Apr 25, 2016
alexcrichton
reviewed
Apr 25, 2016
| @@ -8,7 +8,7 @@ | |||
| // option. This file may not be copied, modified, or distributed | |||
| // except according to those terms. | |||
|
|
|||
| // pretty-expanded FIXME #23616 | |||
| // no-pretty-expanded FIXME #23616 assert_receiver_is_clone | |||
This comment has been minimized.
This comment has been minimized.
alexcrichton
Apr 25, 2016
Member
Instead of no-pretty-expanded I think you can just remove these directives altogether
This comment has been minimized.
This comment has been minimized.
durka
Apr 25, 2016
•
Author
Contributor
Oh is it opt-in? Ok. I thought it was opt-out. I also figured if I left the mention of the method name here, then someone in the future stabilizing or removing this hack might grep for it and realize they could re-enable the test. But it's fine with me to remove it.
This comment has been minimized.
This comment has been minimized.
alexcrichton
Apr 25, 2016
Member
Nah yeah it's fine to just remove everywhere, right now that phase of testing is opt-in
This comment has been minimized.
This comment has been minimized.
durka
Apr 25, 2016
Author
Contributor
Fair enough. Does that mean when I write a new run-pass test I should try
to add "// pretty-expanded"? Do we have a policy on that?
On Mon, Apr 25, 2016 at 2:24 PM, Alex Crichton notifications@github.com
wrote:
In src/test/run-pass/issue-3121.rs
#31414 (comment):@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.-// pretty-expanded FIXME #23616
+// no-pretty-expanded FIXME #23616 assert_receiver_is_cloneNah yeah it's fine to just remove everywhere, right now that phase of
testing is opt-in—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rust/pull/31414/files/64d22d453d5b71b33b149dac3c28b2d626446d9f#r60962453
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
alexcrichton
reviewed
Apr 25, 2016
| fn clone(&self) -> A { | ||
| match *self { | ||
| A { a: ref __self_0_0 } => { | ||
| ::clone::assert_receiver_is_clone(&(*__self_0_0)); |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Apr 25, 2016
Member
I think we have src/test/pretty for this, right? Always just good to avoid run-make wherever possible :)
This comment has been minimized.
This comment has been minimized.
durka
Apr 25, 2016
Author
Contributor
Hmm what does src/test/pretty actually do? This test asserts that the pretty expansion is exactly as intended but src/test/pretty doesn't seem to have expected output.
This comment has been minimized.
This comment has been minimized.
eddyb
Apr 25, 2016
Member
There's one .pp file in there which is the expected output for one test. If nobody has removed it yet for being painful to update, that is.
This comment has been minimized.
This comment has been minimized.
durka
Apr 25, 2016
Author
Contributor
Oh, so there is. I can migrate this test to src/test/pretty then. But
there are more run-make tests of the same class (pretty-expanded,
pretty-expanded-hygiene). I guess they can be migrated in a separate PR.
On Mon, Apr 25, 2016 at 2:04 PM, Eduard-Mihai Burtescu <
notifications@github.com> wrote:
In src/test/run-make/expand-derive/input_pp.rs
#31414 (comment):
+// minimal junk
+#![feature(no_core)]
+#![no_core]
+
+struct A {
- a: i32,
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::clone::Clone for A {- #[inline]
- fn clone(&self) -> A {
match *self {A { a: ref __self_0_0 } => {::clone::assert_receiver_is_clone(&(*__self_0_0));There's one .pp file in there which is the expected output for one test.
If nobody has removed it yet for being painful to update, that is.—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rust/pull/31414/files/64d22d453d5b71b33b149dac3c28b2d626446d9f#r60958975
This comment has been minimized.
This comment has been minimized.
alexcrichton
Apr 25, 2016
Member
Yeah I'd also be fine just leaving this test out, it'd end up being pretty brittle :(
This comment has been minimized.
This comment has been minimized.
durka
Apr 25, 2016
Author
Contributor
I'm trying to think of a way to test the shallowness of a Clone impl without pp-exactness.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
durka
Apr 25, 2016
Author
Contributor
In other words this is a behavior change, but it is now OKed by accepted rust-lang/rfcs#1521.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
durka
Apr 26, 2016
Author
Contributor
OK, I deleted the pretty-printing test and added the Innocent(Liar) test.
durka
force-pushed the
durka:clone-copy
branch
from
b47ffb4
to
1e82084
Apr 26, 2016
This comment has been minimized.
This comment has been minimized.
|
Thanks again for the PR @durka! Everything looks good to me, wanna squash and I'll r+? |
durka
force-pushed the
durka:clone-copy
branch
from
c8ae7ee
to
34b7575
Apr 26, 2016
This comment has been minimized.
This comment has been minimized.
|
Squashed and wrote a more extensive commit message. On Tue, Apr 26, 2016 at 1:06 PM, Alex Crichton notifications@github.com
|
durka commentedFeb 4, 2016
•
edited
If a type is Copy then its Clone implementation can be a no-op. Currently
#[derive(Clone)]generates a deep clone anyway. This can lead to lots of code bloat.This PR detects the case where Copy and Clone are both being derived (the general case of "is this type Copy" can't be determined by a syntax extension) and generates the shallow Clone impl. Right now this can only be done if there are no type parameters (see #31085 (comment)), but this restriction can be removed after specialization.
Fixes #31085.