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 upLifetime bounds in Copy impls are ignored #29149
Comments
arielb1
added
A-traits
A-amusing
labels
Oct 18, 2015
nikomatsakis
added
I-nominated
T-lang
T-compiler
labels
Oct 21, 2015
This comment has been minimized.
This comment has been minimized.
|
Nominating. |
nikomatsakis
removed
the
T-lang
label
Oct 22, 2015
nikomatsakis
self-assigned this
Oct 22, 2015
This comment has been minimized.
This comment has been minimized.
|
triage: P-high |
rust-highfive
added
P-high
and removed
I-nominated
labels
Oct 22, 2015
nikomatsakis
added
the
T-lang
label
Nov 5, 2015
This comment has been minimized.
This comment has been minimized.
|
Correct behavior here is not entirely obvious, given that we erase lifetimes at trans. Possibly we should be treating this affine, or possibly the impl should be an error? Have to mull this over. |
nikomatsakis
added
the
I-nominated
label
Nov 5, 2015
This comment has been minimized.
This comment has been minimized.
|
Nominating for discussion at the lang meeting. |
nikomatsakis
removed
the
I-nominated
label
Nov 5, 2015
This comment has been minimized.
This comment has been minimized.
|
So my feeling is that the |
This comment has been minimized.
This comment has been minimized.
(I assume this means e.g. that |
This comment has been minimized.
This comment has been minimized.
|
So now that I've thought about this I agree with @pnkfelix. We should use the dropck code to enforce this constraint. |
This comment has been minimized.
This comment has been minimized.
|
There is a catch -- we can't use the precise dropck check. We don't want all instances of a type to be impl<T:Copy> Copy for Option<T> { }Probably we want a "lifetime dispatch" predicate exactly like the one that @aturon was experimenting with for specialization -- basically we want to know the same property here. That is, we want to know that the Copy-ness is not decided by the lifetimes, because we view those as outputs of trait selection, not inputs. |
This comment has been minimized.
This comment has been minimized.
|
And, in fact, the problem runs deeper -- just as with specialization, the All this makes me wonder: maybe we are taking the wrong approach. Maybe we should run borrowck and find all values that are used more than once and then add a requirement then that they be Not sure just what would be affected. I know that MIR construction uses "is this Copy?" to avoid creating needless drops, but that's really just a heuristic. I'd have to go look where-else this comes up. |
This comment has been minimized.
This comment has been minimized.
|
This can get messier with associated types, as they can introduce lifetime bounds on their own: trait Foo { type Out; }
#[derive(Clone)]
struct S<T: Foo>(T::Out);
impl<T: Foo> Copy for S where T::Out: Copy {}
impl<'a> Foo for (&'a u32, &'a u32) {
type Out = u32;
} |
This comment has been minimized.
This comment has been minimized.
But then the new lifetime bounds can affect (lifetime) inference. |
This comment has been minimized.
This comment has been minimized.
|
@arielb1 Yes, that's true. The other place that I've found where a complication arises is in closure inference, which uses copy-vs-move to decide how to handle upvars in some cases. |
This comment has been minimized.
This comment has been minimized.
|
OK, so, I see two viable candidates as of now:
The first seems obviously superior in that it doesn't require explaining to people why a move occurred when they expected a copy. |
This comment has been minimized.
This comment has been minimized.
|
So I did some investigation here and I believe I have a simple proposal that would solve this problem. We can restrict |
This comment has been minimized.
This comment has been minimized.
|
Will associated types not be a problem? |
This comment has been minimized.
This comment has been minimized.
Maybe, but I don't quite see how yet. Can you give an example of where you think a problem would arise? Earlier, you wrote this, but I don't quite understand what you were getting at: trait Foo { type Out; }
#[derive(Clone)]
struct S<T: Foo>(T::Out);
impl<T: Foo> Copy for S where T::Out: Copy {}
impl<'a> Foo for (&'a u32, &'a u32) {
type Out = u32;
}In particular here, it doesn't seem like there are any lifetime bounds that are not already guaranteed by WF, right? |
This comment has been minimized.
This comment has been minimized.
|
Maybe they are not actually an issue, because |
This comment has been minimized.
This comment has been minimized.
|
Is anyone interested in spearheading this issue? I'm assigned, but I'm pretty overloaded. An RFC would probably be nice. I think we could just write one, but it'd be even better if we could implement the proposed solution so we can measure the impact with a crater run. I prefer this fix. Logical candidates to do this work: |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis How does that compare to 170b88d? |
This comment has been minimized.
This comment has been minimized.
|
Hey @nikomatsakis thanks for the heads up, yeah if you want I'd be happy to tackle this one :) |
This comment has been minimized.
This comment has been minimized.
|
That's basically it, but instead of |
This comment has been minimized.
This comment has been minimized.
|
If we can ever get rid of MC/EUV, then maybe |
This comment has been minimized.
This comment has been minimized.
|
@eddyb I think we will wind up needing something like MC/EUV, because it's needed for upvar inference, but I suspect we can redesign it to be radically better and simpler. |
This comment has been minimized.
This comment has been minimized.
|
cc @spastorino -- all the pieces are in place to fix this now, I think. We should use this example: #![feature(nll)]
#[derive(Clone)] struct Foo<'a>(&'a u32);
impl Copy for Foo<'static> {}
fn main() {
let s = 2;
let a = Foo(&s);
drop(a);
drop(a);
}we basically want to change this existing code: rust/src/librustc_mir/borrow_check/nll/type_check/mod.rs Lines 376 to 384 in 5c41fce so that it invokes rust/src/librustc_mir/borrow_check/nll/type_check/mod.rs Lines 1486 to 1493 in 5c41fce In other words, rather than testing that |
nikomatsakis
added
E-mentor
and removed
E-hard
labels
Jan 27, 2018
spastorino
self-assigned this
Jan 27, 2018
nikomatsakis
referenced this issue
Jan 30, 2018
Open
tracking issue for bugs fixed by the MIR borrow checker or NLL #47366
spastorino
added a commit
to spastorino/rust
that referenced
this issue
Jan 30, 2018
spastorino
referenced this issue
Jan 30, 2018
Merged
Do not ignore lifetime bounds in Copy impls #47877
spastorino
added a commit
to spastorino/rust
that referenced
this issue
Jan 30, 2018
spastorino
added a commit
to spastorino/rust
that referenced
this issue
Jan 30, 2018
This comment has been minimized.
This comment has been minimized.
|
Fixed (with NLL) in #47877 |
arielb1 commentedOct 18, 2015
•
edited by nikomatsakis
UPDATE: Mentoring instructions below.
Affected Versions
rustc 1.3.0 - 1.5.0
STR
Expected Result
Foo<'a>is notCopy, so this should cause a borrowck error.Actual Result
The code compiles :-).
cc @nikomatsakis