HRTB on subtrait unsoundly provides HTRB on supertrait with weaker implied bounds · Issue #84591 · rust-lang/rust (original) (raw)

I’m giving an exploitation below at the end of this description. This is my interpretation of where exactly the unsoundness lies:

If I have a trait hierarchy

trait Subtrait<'a, 'b, R>: Supertrait<'a, 'b> {} trait Supertrait<'a, 'b> {}

then a higher ranked trait bound (HRTB) like this

for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>

does/should only apply to such lifetimes 'a, 'b that fulfill the outlives-relation 'a: 'b.
('a: 'b is needed for &'b &'a () to be a valid type.)

However, the bound

for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>

appears to imply the bound

for<'a, 'b> Supertrait<'a, 'b>,

and in this one, the lifetimes 'a and 'b are universally quantified without any implicit outlives-relation.

This implication is demonstrated below:

fn need_hrtb_subtrait() where S: for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>, { need_hrtb_supertrait::() // <- this call works }

fn need_hrtb_supertrait() where S: for<'a, 'b> Supertrait<'a, 'b>, { }

(playground)

One could of course debate whether for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> should perhaps in-fact include a for<'a, 'b> Supertrait<'a, 'b> bound, but that seems kind-of weird IMO, and also the following code demonstrates that you only need Supertrait<'a, 'b> with 'a: 'b for a fully generic Subtrait<'a, 'b, &'b &'a ()> implementation:

struct MyStruct; impl<'a: 'b, 'b> Supertrait<'a, 'b> for MyStruct {} impl<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {}

fn main() { need_hrtb_subtrait::(); }

(playground)


Finally, here’s how to turn this into actual UB:

trait Subtrait<'a, 'b, R>: Supertrait<'a, 'b> {} trait Supertrait<'a, 'b> { fn convert<T: ?Sized>(x: &'a T) -> &'b T; }

fn need_hrtb_subtrait<'a_, 'b_, S, T: ?Sized>(x: &'a_ T) -> &'b_ T where S: for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>, { need_hrtb_supertrait::<S, T>(x) // <- this call works }

fn need_hrtb_supertrait<'a_, 'b_, S, T: ?Sized>(x: &'a_ T) -> &'b_ T where S: for<'a, 'b> Supertrait<'a, 'b>, { S::convert(x) }

struct MyStruct; impl<'a: 'b, 'b> Supertrait<'a, 'b> for MyStruct { fn convert<T: ?Sized>(x: &'a T) -> &'b T { x } } impl<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {}

fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { need_hrtb_subtrait::<MyStruct, T>(x) }

fn main() { let d; { let x = "Hello World".to_string(); d = extend_lifetime(&x); } println!("{}", d); }

(playground)

This demonstration compiles since Rust 1.7.

@rustbot modify labels: T-compiler, A-traits, A-lifetimes, A-typesystem
and someone please add “I-unsound 💥”.