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 upLint for undesirable, implicit copies #45683
Comments
nikomatsakis
added
E-needs-mentor
T-lang
WG-compiler-middle
labels
Nov 1, 2017
aturon
referenced this issue
Nov 1, 2017
Open
Tracking issue for experiments around coercions, generics, and Copy type ergonomics #44619
nikomatsakis
referenced this issue
Nov 1, 2017
Closed
What should we do about `#[derive(...)]` on `#[repr(packed)]` structs? #39696
This comment has been minimized.
This comment has been minimized.
|
I think the lint would work roughly like this. We define a set of types that should not be implicitly copied. This would include any type whose size is >N bytes (where N is some arbitrary number, perhaps tunable via an attribute on the crate), but also any struct that is tagged with We would then comb code for implicit copies. This is (somewhat) easy to define at the MIR level: any use of a user-defined variable (not a temporary) that is of a type implementing let y = x;
foo(x);
let y = [x; 22]; // at least I think this would count =)One interesting case that would, by this definition, be linted against is this:
This one is interesting because, unlike the others, there is no way to readily silence the lint, except by adding an It is also interesting that the use cases here perhaps breakdown into two categories:
In fact, I would argue that the latter is the majority case, so maybe we just want to separate this into two lints, or else say that structs with the let z = {
let x0 = x.clone();
move || use(x0) // this is a last-use of `x0`, hence no warning
};
use(x); // uses original `x` |
This comment has been minimized.
This comment has been minimized.
|
I would prefer it if this was more specific, e.g. |
This comment has been minimized.
This comment has been minimized.
|
You don't want to warn on any use - that would warn on moves too. You want to warn on uses where the variable is live after the use. |
This comment has been minimized.
This comment has been minimized.
|
@arielb1 indeed. |
This comment has been minimized.
This comment has been minimized.
|
@eddyb do you have a specific example of something you would want to warn, and something you would not want? |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis Copying a |
nikomatsakis
referenced this issue
Jan 10, 2018
Closed
Add missing Copy/Clone impls for Iterators #47304
This comment has been minimized.
This comment has been minimized.
|
@eddyb it seems like we could generalize then to a lint that works like this:
Maybe a better name than |
This comment has been minimized.
This comment has been minimized.
|
Anyway, regardless of the nitty gritty details, let me leave a few pointers here for how we might implement this. @cramertj is hopefully gonna take a look. First off, let's look a bit at MIR. MIR definition is here.. A crucial concept I referenced above is a Lines 1231 to 1243 in 4e3901d The arguments to MIR statements are always operands, which explicitly identify a copy: Lines 1399 to 1403 in 4e3901d I think right now we will produce a One way to do that is with the MIR visitor, which lets you quickly walk the IR. In particular we'd probably want to override the rust/src/librustc/mir/visit.rs Lines 142 to 146 in 4e3901d There is code for computing liveness -- basically, whether a variable may be used again. That code lives in rust/src/librustc_mir/util/liveness.rs Lines 117 to 120 in 4e3901d but that only gives you values at basic block boundaries. You will want rust/src/librustc_mir/util/liveness.rs Lines 161 to 167 in 4e3901d So I guess that roughly the idae would be to identify copies where the value's type is marked to be linted. We would then compute the liveness at the point of copy and see if the Some annoying challenges:
Still, this is a lint after all, it doesn't have to be totally precise. I think that for the first version of the code I would only worry about local variables and ignore more complex |
cramertj
self-assigned this
Jan 23, 2018
jkordish
added
A-lint
C-future-compatibility
labels
Jan 25, 2018
This comment has been minimized.
This comment has been minimized.
nikomatsakis
assigned
qmx
and unassigned
cramertj
Feb 16, 2018
This comment has been minimized.
This comment has been minimized.
|
@qmx I started https://github.com/cramertj/rust/tree/copy-lint if you want to take a look. Not sure how useful it is-- I only got the skeleton. |
cramertj
referenced this issue
Feb 22, 2018
Closed
Tracking issue for RFC 2132: copy_closures/clone_closures #44490
This comment has been minimized.
This comment has been minimized.
|
Same for EDIT: As @rkruppe pointed out, "thread" doesn't make sense here. That doesn't change the problem though, see below. |
This comment has been minimized.
This comment has been minimized.
|
RefCell is not Sync so how could you have a |
This comment has been minimized.
This comment has been minimized.
|
This can all happen in one thread. |
This comment has been minimized.
This comment has been minimized.
let r = RefCell::new(42);
let r1 = &r;
let r2 = &r;
let _mutref = r1.borrow_mut();
let illegal_copy = *r2; // copying while there is an outstanding mutable reference |
This comment has been minimized.
This comment has been minimized.
|
Oh yeah you're right. The mention of another thread really mislead me there. |
This comment has been minimized.
This comment has been minimized.
|
Sorry for that, not sure what I thought. "Another function" is really what I meant (though it can be the same, as in my counterexample). |
This comment has been minimized.
This comment has been minimized.
|
@RalfJung in retrospect, the RefCell problem is obvious -- basically, of course copying is bad, for the same reason that |
This comment has been minimized.
This comment has been minimized.
|
Just found out about rust-lang/rfcs#2407 - that would be a nice suggestion for the linter in the future, wouldn't it? |
nikomatsakis commentedNov 1, 2017
As part of #44619, one topic that keeps coming up is that we have to find some way to mitigate the risk of large, implicit copies. Indeed, this risk exists today even without any changes to the language:
In addition to performance hazards, implicit copies can have surprising semantics. For example, there are several iterator types that would implement
Copy, but we were afraid that people would be surprised. Another, clearer example is a type likeCell<i32>, which could certainly be copy, but for this interaction:For a time before 1.0, we briefly considered introducing a new
Podtrait that acted likeCopy(memcpy is safe) but without the implicit copy semantics. At the time, @eddyb argued persuasively against this, basically saying (and rightly so) that this is more of a linting concern than anything else -- the implicit copies in the example above, after all, don't lead to any sort of unsoundness, they just may not be the semantics you expected.Since then, a number of use cases have arisen where having some kind of warning against implicit, unexpected copies would be useful:
CopyCell,RefCell, and other types with interior mutability implementingCopy#[derive(PartiallEq)]and friends with packed structs&TtoT(i.e., it'd be nice to be able to dofoo(x)wherefoo: fn(u32)andx: &u32)I will writeup a more specific proposal in the thread below.