Skip to content

A Lifetime-generic Copy impl can allow fields which are only Copy when 'static #88901

Closed
@danielhenrymantilla

Description

@danielhenrymantilla

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 a Clone impl for all lifetimes, it is possible to restrict the impl of Clone for Foo to 'static as well, and yet offer a Bar<'any> which is Cloneable (thanks to it being Copyable). 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 of b (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
}
  • Playground

  • Indeed, the move_or_copy heuristic decides to go for copy since the type is indeed copy_modulo_regions, but then the borrow checker (correctly) forbids the Copy operation since the lifetime isn't (can't be proven to be) 'static

Metadata

Metadata

Labels

A-lifetimesArea: Lifetimes / regionsA-trait-systemArea: Trait systemC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityT-langRelevant to the language teamT-typesRelevant to the types team, which will review and decide on the PR/issue.

Type

No type

Projects

Status

Completed

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions