Description
I tried this code:
#[derive(Clone)]
struct Foo<'lt>(&'lt ());
impl Copy for Foo<'static> {}
#[derive(Clone)]
struct Bar<'lt>(Foo<'lt>);
impl<'any> Copy for Bar<'any> {}
fn check(b: Bar<'_>) -> (Bar<'_>, Bar<'_>) {
(b, b)
}
- (Note about
#[derive(Clone)]
: while this simple snippet features aClone
impl for all lifetimes, it is possible to restrict the impl ofClone for Foo
to'static
as well, and yet offer aBar<'any>
which isClone
able (thanks to it beingCopy
able). See below for a more detailed example about that).
I expected to see this happen:
-
Either the
impl Copy for Bar<'_> {}
should have failed, or, at the very least, the borrow checker ought to have complained about the actual copy ofb
(from looking at the old related issue, a "late" / on-use borrow-checker issue was deemed good enough since such a check does prevent unsoundness).Alas, we can see here that that check can be dodged by using a wrapper type.
Instead, this happened:
- the code compiled fine.
Meta
stable, beta and nightly are affected by this, and I suspect all versions of Rust do.
Impact
While contrived, this ought to be a I-unsound
issue, since a library could feature a lifetime-generic type Foo<'lt>
with an exploited safety invariant of Foo<'not_static>
not being cloneable. Demo
- That being said, I'd qualify it as low priority, since the example is indeed very contrived.
Bonus / tangential issue 🎁
A type which is only Copy
when 'static
becomes, in practice / in non-generic context, unmoveable when non-'static
:
#[derive(Clone)]
struct Foo<'lt>(&'lt ());
impl Copy for Foo<'static> {}
fn check(f: Foo<'_>) {
assert!(Some(f).is_some()); // Error, lifetime `'static` required
}
-
Indeed, the
move_or_copy
heuristic decides to go for copy since the type is indeedcopy_modulo_regions
, but then the borrow checker (correctly) forbids theCopy
operation since the lifetime isn't (can't be proven to be)'static
Metadata
Metadata
Assignees
Labels
Type
Projects
Status