rust-lang / rust

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

Lifetime bound in type alias is checked #100270

Open conradludgate opened 1 year ago

conradludgate commented 1 year ago

I tried this code:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c358b7f20cd1b96bf3919a4339288971

#![allow(dead_code)]

pub type Borrow<'a, T: 'a> = &'a T;

pub trait Foo {
    fn foo(&self);
}

impl<T: AsRef<str> + ?Sized> Foo for T {
    fn foo(&self) {}
}

pub struct Bar<'a> {
    foo: Borrow<'a, dyn Foo>,
}

fn bar(name: &str) {
    let _ = Bar { foo: &name };
}

I expected to see this happen:

error[E0759]: `name` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
  --> src/lib.rs:18:24
   |
17 | fn bar(name: &str) {
   |              ---- this data with an anonymous lifetime `'_`...
18 |     let _ = Bar { foo: &name };
   |                        ^^^^^ ...is used and required to live as long as `'static` here

For more information about this error, try `rustc --explain E0759`.

Borrow<'a, dyn Foo> should default to dyn Foo + 'static and the T: 'a should be ignored.

Instead, this happened:

warning: bounds on generic parameters are not enforced in type aliases
 --> src/lib.rs:3:24
  |
3 | pub type Borrow<'a, T: 'a> = &'a T;
  |                        ^^
  |
  = note: `#[warn(type_alias_bounds)]` on by default
help: the bound will not be checked when the type alias is used, and should be removed
  |
3 - pub type Borrow<'a, T: 'a> = &'a T;
3 + pub type Borrow<'a, T> = &'a T;
  | 

If you follow the guidance from the warning, it no longer compiles due to dyn Foo being static-ish, as mentioned

danielhenrymantilla commented 1 year ago

Reduced:

  pub fn check<'a>(r: &'a &'a ()) {
-     pub type Borrow<'a, T : 'a> = &'a T;
+     pub type Borrow<'a, T> = &'a T;
      type DynSend<'a> = Borrow<'a, dyn Send>;
      let _: DynSend<'a> = r;
  }

removes the lint but leads to a compile failure.

QuineDot commented 1 year ago

I did some more exploration here (and playground). More generally, using an alias will apply the dyn lifetime defaults (RFC 0599, RFC 1156) based on the alias bounds (or lack of bounds), be they stronger or looser than the aliased type.

So they definitely have semantic impact. I don't think they're checked (enforced) per se, though.

pub type Lt<'a, 'b: 'a> = (&'a (), &'b ());

// Compiles
fn check<'a, 'b>(r: &'a &'b ()) {
    let _: Lt<'a, 'b> = (*r, *r);
    let _: Lt<'b, 'a> = (*r, *r);
}
fmease commented 4 months ago

@QuineDot is correct, the outlives-bounds T: 'a on the (eager) type alias Borrow influences the default object lifetime of dyn Foo.

I don't think they're checked (enforced) per se, though.

Bounds on lazy type aliases (feature lazy_type_alias) on the other hand are enforced and they are checked for well-formedness. Lazy type aliases will become the default in Rust 202X (the plan is Rust 2024 but the implementation stills needs to be completed by me). CC #112792. I don't know how to proceed with this issue? Should I close it or keep it open?

QuineDot commented 2 weeks ago

If nothing else this should be documented somewhere. Based on some light testing, it seems lazy_type_alias preserves this behavior, correct? That also seems reasonable to me.