rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.98k stars 12.68k forks source link

late-bound lifetimes on type-alias-impl-trait can be unsound #97104

Closed aliemjay closed 2 years ago

aliemjay commented 2 years ago
#![feature(type_alias_impl_trait)]

mod lifetime_params {
    type Ty<'a> = impl Sized + 'a;
    fn define(s: &str) -> Ty<'_> { s }

    type BadFnSig = fn(Ty<'_>) -> &str;
    type BadTraitRef = dyn Fn(Ty<'_>) -> &str;
}

mod type_params {
    type Ty<T> = impl Sized;
    fn define<T>(s: T) -> Ty<T> { s }

    type BadFnSig = fn(Ty<&str>) -> &str;
    type BadTraitRef = dyn Fn(Ty<&str>) -> &str;
}

Playground.

Prior to #96903, these types were rejected with this error:

error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types
 --> /tmp/test9.rs:7:35
  |
7 |     type BadFnSig = fn(Ty<'_>) -> &str;
  |                                   ^^^^
  |
  = note: lifetimes appearing in an associated type are not considered constrained

Currently, they're accepted.

Based on the assumption that generic parameters on opaque type are not constrained (see Niko's comment https://github.com/rust-lang/rust/pull/95474#issuecomment-1122953370), I believe the previous behavior is correct.

CC: @oli-obk

@rustbot label F-type_alias_impl_trait A-impl-trait T-compiler A-lifetimes

aliemjay commented 2 years ago

Out of curiosity, here's an exploit derived from #84366.

It type-checks but fails at monomorphization.

playground

#![feature(type_alias_impl_trait)]

trait Static: 'static {}
impl Static for () {}

type Gal<T> = impl Static;
fn _defining<T>() -> Gal<T> {}

trait Callable<Arg> { type Output; }

/// We can infer `<C as Callable<Arg>>::Output: 'static`,
/// because we know `C: 'static` and `Arg: 'static`,
fn box_str<C, Arg>(s: C::Output) -> Box<dyn AsRef<str> + 'static>
where
    Arg: Static,
    C: ?Sized + Callable<Arg> + 'static,
    C::Output: AsRef<str>,
{
    Box::new(s)
}

fn extend_lifetime(s: &str) -> Box<dyn AsRef<str> + 'static> {
    type MalformedTy = dyn for<'a> Callable<Gal<&'a ()>, Output = &'a str>;
    box_str::<MalformedTy, _>(s)
}

fn main() {
    //let extended = extend_lifetime(&String::from("hello"));
    //println!("{}", extended.as_ref().as_ref());
}
aliemjay commented 2 years ago

The above exploit is check-pass and should be easier to do with #95474, where indirection through Static would not be necessary, so

@rustbot label I-unsound requires-nightly T-types

oli-obk commented 2 years ago

Prior to #96903, these types were rejected with this error:

if the same code were written without lifetime params and used &'a () arguments instead, we'd see the same problem before #96903, right?

aliemjay commented 2 years ago

if the same code were written without lifetime params and used &'a () arguments instead, we'd see the same problem before #96903, right?

Sorry, I've clearly missed the notification :sweat_smile:. I think you already know the answer given that you've just pushed a fix :).