-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
special-case #[derive(Copy, Clone)] with a shallow clone #31414
Conversation
r? @brson (rust_highfive has picked a reviewer for you, use r? to override) |
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"))() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax::attr::contains_name(item.attrs, "derive_Copy")
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(). |
☔ The latest upstream changes (presumably #31400) made this pull request unmergeable. Please resolve the merge conflicts. |
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. |
Rebased. |
This PR LGTM, but I’d like an opinion from somebody more familiar with the code as well. |
|
||
#[derive(Copy, Clone)] | ||
struct B<T> { a: T } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you put this in its own rmake test case instead of piggy-backing on this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll split the tests and revert my changes to the Makefile for this one.
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. |
Travis failed because I didn't run the full test suite locally. Will fix later tonight. |
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 |
As a bonus, this extends the reach of |
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. |
@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 :) |
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 |
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 |
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. |
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. |
That does still leave the behavior change that |
@sfackler I'm trying this. I still think it's insignificant since the existence of |
@durka Good question, it would likely make winapi worse, not better. Maybe replace |
@Manishearth sorry about my slowness. I still need to fix the spans so I
|
Nah, it's okay, we can wait a few days 😄 I wasn't sure what the status of this PR was. |
@eddyb I don't use |
@retep998 but you could in a parallel universe where #[derive(Clone)]
|
@durka But I can't due some |
How lovely, winapi needs the bugs that this PR takes care to not expose. 😄 Can't please them all |
The stage1-rmake test passes now. And the stage2-rpass-full one. And I guess travis will try them all. |
Breaking change removed. Performance gains retained. |
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of no-pretty-expanded
I think you can just remove these directives altogether
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah yeah it's fine to just remove everywhere, right now that phase of testing is opt-in
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah it's fine to just remove these comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Thanks again for the PR @durka! Everything looks good to me, wanna squash and I'll r+? |
Squashed and wrote a more extensive commit message. On Tue, Apr 26, 2016 at 1:06 PM, Alex Crichton notifications@github.com
|
Changes #[derive(Copy, Clone)] to use a faster impl of Clone when both derives are present, and there are no generics in the type. The faster impl is simply returning *self (which works because the type is also Copy). See the comments in libsyntax_ext/deriving/clone.rs for more details. There are a few types which are Copy but not Clone, in violation of the definition of Copy. These include large arrays and tuples. The very existence of these types is arguably a bug, but in order for this optimization not to change the applicability of #[derive(Copy, Clone)], the faster Clone impl also injects calls to a new function, core::clone::assert_receiver_is_clone, to verify that all members are actually Clone. This is not a breaking change, because pursuant to RFC 1521, any type that implements Copy should not do any observable work in its Clone impl.
special-case #[derive(Copy, Clone)] with a shallow clone 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.
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.