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 upCopy clone semantics #1521
Conversation
This comment has been minimized.
This comment has been minimized.
durka
commented on text/0000-copy-clone-semantics.md in d69cd0a
Mar 1, 2016
|
Specialization would allow [...] in the case of |
This comment has been minimized.
This comment has been minimized.
durka
commented on text/0000-copy-clone-semantics.md in d69cd0a
Mar 1, 2016
|
Shouldn't we also have a note like "Manual implementors of Clone must be careful to uphold this."? |
This comment has been minimized.
This comment has been minimized.
Aatch
commented on text/0000-copy-clone-semantics.md in d69cd0a
Mar 1, 2016
|
I think this section should probably also include a "Explicitly define the behaviour/semantics in this case" aspect. Rather than just "add X to the documentation". The documentation reflects the specification, but it probably shouldn't be the specification. |
This comment has been minimized.
This comment has been minimized.
|
Note that these comments were originally line comments, but that fact seems to have been lost by github. |
This comment has been minimized.
This comment has been minimized.
briansmith
commented
Mar 1, 2016
|
I originally thought this was a good idea, and even suggested something very much like this on the internals mailing list, but I've changed my mind. Every type that implements |
This comment has been minimized.
This comment has been minimized.
|
@briansmith The issue isn't derived The optimizer is also not turned on in debug mode. Allowing us to turn Edit: not invalid like undefined behavior or anything, just to be clear. |
This comment has been minimized.
This comment has been minimized.
|
Note, we've already made breaking changes based on this assumption. The change in |
This comment has been minimized.
This comment has been minimized.
briansmith
commented
Mar 1, 2016
IMO, it is OK to say that if you want
Yes, but IMO that was also a short-term mistake. Once the optimizer is doing the right thing, that shortcut won't be necessary and we can return to the simpler semantics. In particular, the optimizer can already notice that a loop is equivalent to memcpy. It just needs to get smarter to recognize non-loop equivalents, including recursive invocations of cone/copy, are equivalent to memcpy are equivalent to memcpy too. Consider |
This comment has been minimized.
This comment has been minimized.
|
@briansmith This isn't an optimization. This is specification. It's saying that the standard library (and anyone else) may rely on the fact that a properly implemented |
This comment has been minimized.
This comment has been minimized.
briansmith
commented
Mar 1, 2016
|
Yes, but I'm saying that if the optimizer is working well, then you don't need to specify anything special here. It would be useful to see an example where this rule is needed that can't be done without the rule by simply improving the optimizer. |
This comment has been minimized.
This comment has been minimized.
|
The RFC should specify what the consequences are for types that violate this rule. In particular this must not result in memory unsafety. |
This comment has been minimized.
This comment has been minimized.
|
@briansmith The issue isn't where the optimizer is working. It's where it isn't. Optimization is not always on, like in debug mode. Also, building a language for a sufficiently smart compiler doesn't seem like the correct way of doing things. Often we will have to specifically specialize. |
This comment has been minimized.
This comment has been minimized.
|
@Amanieu yes, that is the intention, I will write it down. |
This comment has been minimized.
This comment has been minimized.
briansmith
commented
Mar 1, 2016
|
Why not specify that even in debug mode, the copy coalescing optimizations are done? Then there is no semantic change to the language needed and debug build results are faster. |
This comment has been minimized.
This comment has been minimized.
|
@briansmith The semantics would still change, and then you would have different semantics between debug and release. |
This comment has been minimized.
This comment has been minimized.
briansmith
commented
Mar 1, 2016
|
To be clear, there's nothing that would change semantically in my definition. The only thing that would change is that debug builds would be specified to have an optimization pass enabled, for at least |
This comment has been minimized.
This comment has been minimized.
|
@briansmith Ah. You can't really do that. The necessary optimizations to realize that:
is equivalent to
is... most of them. |
petrochenkov
reviewed
Mar 1, 2016
| # Unresolved questions | ||
| [unresolved]: #unresolved-questions | ||
|
|
||
| What the exact wording should be. |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Mar 1, 2016
Contributor
Alternative wording: "When Copy type has user-defined implementation of Clone it's unspecified whether clone or memcpy is called when cloning a value of this type." or (shamelessly stolen from somewhere) "<...if Copy...> An implementation is allowed to omit the call to clone even if is has side effects. Such elision of calls to clone is called clone elision."
This comment has been minimized.
This comment has been minimized.
ubsan
Mar 1, 2016
Author
Contributor
@petrochenkov it's not that it's unspecified whether clone or ptr::read is called when you actually call .clone(). It's just that libraries (including the standard library) are able to assume that Clone::clone == ptr::read for T: Copy; and, if that requirement is not met, libraries are allowed to exhibit unexpected (but defined) behavior. (Just like if an Ord type doesn't actually have an absolute ordering, or if an Eq type doesn't actually have transitive equality).
This comment has been minimized.
This comment has been minimized.
petrochenkov
Mar 1, 2016
Contributor
it's not that it's unspecified whether clone or ptr::read is called when you actually call .clone()
Why not? If we go this route anyway, let's give the compiler/libraries all the freedom.
I'd even write "user-defined implementations of Clone for Copy types are ignored", but I'm not sure about generics:
fn f<T: Clone>(arg: T) -> T { arg.clone() } // It's not known if `T` is `Copy` or not and I'm not sure it's convenient to turn `clone`s into `memcpy`s during monomorphisation.
This comment has been minimized.
This comment has been minimized.
petrochenkov
Mar 1, 2016
Contributor
It's just that libraries (including the standard library) are able to assume that Clone::clone == ptr::read for T: Copy
derive is not a library, so the language already can omit clones as well.
This comment has been minimized.
This comment has been minimized.
petrochenkov
Mar 1, 2016
Contributor
Also, "clone is always ignored" is better than "clone is sometimes ignored" from pedagogical point of view.
This comment has been minimized.
This comment has been minimized.
ubsan
Mar 1, 2016
Author
Contributor
hmm...
At first I thought this was a bad idea, but then I started thinking. Not "always ignored", but if your clone has different semantics from a ptr::read then it should be implementation defined whether it is called.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
ubsan
Mar 2, 2016
Author
Contributor
@arielb1 Yeah, I guess. I do think it would be better if it's just kept to libraries and language constructs like #[derive]
This comment has been minimized.
This comment has been minimized.
|
Is there any reason to write a manual |
This comment has been minimized.
This comment has been minimized.
comex
commented
Mar 1, 2016
|
@glaebhoerl Unfortunately, derived impls on generic structs sometimes have the wrong trait bounds, so you have to implement them manually. |
This comment has been minimized.
This comment has been minimized.
|
@comex: isn't that "just a bug"? |
This comment has been minimized.
This comment has been minimized.
|
Also, it's possible to implement manually, so it's possible to implement manually wrong. This RFC isn't about all those people who |
This comment has been minimized.
This comment has been minimized.
|
@ubsan I manually implement |
This comment has been minimized.
This comment has been minimized.
|
Some people do have to implement manually, like @retep998. |
This comment has been minimized.
This comment has been minimized.
|
I am not sure how much teeth this RFC has. We already have both |
This comment has been minimized.
This comment has been minimized.
|
@arielb1 Yes, because it's specified that |
aturon
self-assigned this
Mar 2, 2016
bluss
reviewed
Mar 3, 2016
| and may not always be optimized (although it often will be). Specialization | ||
| would allow us to simply `memcpy` the values from the old `Vec` to the new | ||
| `Vec` in the case of `T: Copy`. However, if we don't specify this, we will not | ||
| be able to, and we will be stuck looping over every value. |
This comment has been minimized.
This comment has been minimized.
bluss
Mar 3, 2016
We will be stuck relying on the optimizer to figure it out. Which it can do and insert memcpy, typically for Copy values of 8 bytes or smaller per element, it looks like.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
bluss
commented
Mar 3, 2016
|
What the memcpy equivalent exactly is, is not specified. A derived I think I agree with the intention of this RFC build I would like it to simply say two things:
|
This comment has been minimized.
This comment has been minimized.
|
@bluss |
This comment has been minimized.
This comment has been minimized.
|
@ubsan My point was not that there should be As in, if |
This comment has been minimized.
This comment has been minimized.
|
I see this as pretty similar to the situation with |
This comment has been minimized.
This comment has been minimized.
|
|
alexcrichton
added
the
final-comment-period
label
Apr 15, 2016
This comment has been minimized.
This comment has been minimized.
e-oz
commented
Apr 17, 2016
|
Hate breaking changes. Have you checked how many cargo crates will be affected? If yes - please share the numbers. From discussion it looks like breaking change just to make debug mode faster, but I think it can't be true. |
This comment has been minimized.
This comment has been minimized.
|
@e-oz This won't cause anything to stop compiling. This merely makes an assumption that the standard library and compiler can optimize based on. If you can find any crates in the wild that would be affected negatively by this change, please do bring them up as there isn't really any way to find out in an automated fashion. |
This comment has been minimized.
This comment has been minimized.
|
I think it is very unlikely that such a change will break anything at all. |
This comment has been minimized.
This comment has been minimized.
|
(I also don't consider this a change, merely formally stating an existing assumption.) Pre-emptive update: I don't really want to get into dickering over what is or is not a breaking change. What I meant here was that there is surely existing code that would exhibit bugs if clone and copy are not aligned (think generics), and that we've used the assumption that copy and clone align in their semantics elsewhere, but failed to document that assumption. |
This comment has been minimized.
This comment has been minimized.
e-oz
commented
Apr 17, 2016
|
Committed code contains words "this is a breaking change", that's why I asked. Sorry for being suspicious:) |
This comment has been minimized.
This comment has been minimized.
|
I'm in favor of documenting these expectations for the semantics of |
This comment has been minimized.
This comment has been minimized.
|
Huzzah! The @rust-lang/lang team has decided to accept this RFC. However, since the libs team also claims jurisdiction, I'll let them merge it. :) |
durka
referenced this pull request
Apr 25, 2016
Merged
special-case #[derive(Copy, Clone)] with a shallow clone #31414
alexcrichton
referenced this pull request
May 4, 2016
Closed
Document that T: Copy + Clone should have "obvious" Clone impl #33416
This comment has been minimized.
This comment has been minimized.
|
The libs team discussed this RFC at triage today and the decision was to merge. It was brought up that the user-facing documentation probably wants to indicate Thanks again for the RFC @ubsan! Tracking issue: rust-lang/rust#33416 |
alexcrichton
merged commit b9b7854
into
rust-lang:master
May 4, 2016
retep998
referenced this pull request
May 9, 2016
Closed
Implement Clone for all Copy types "by magic" #33507
eefriedman
referenced this pull request
May 9, 2016
Closed
Performance issue in `write_all` (`Vec::extend_from_slice`) #33518
Stebalien
referenced this pull request
Sep 27, 2016
Closed
Specialize methods on iter::Cloned<I> where I::Item: Copy. #36791
This comment has been minimized.
This comment has been minimized.
bluss
commented
Jan 5, 2017
|
Vec::clone specialization for T: Copy is realized since rust-lang/rust/pull/38182 (Through |
ubsan commentedMar 1, 2016
Specifying that
<T as Clone>::clone(&t)whereT: Copyshould be equivalent toptr::read(&t).