Skip to content

Properly deduce object lifetime defaults in projections & trait refs #129543

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
552 changes: 334 additions & 218 deletions compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1476,7 +1476,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
if let Some(name) = tcx.intrinsic(def_id) {
record!(self.tables.intrinsic[def_id] <- name);
}
if let DefKind::TyParam = def_kind {
if let DefKind::TyParam | DefKind::Trait = def_kind {
let default = self.tcx.object_lifetime_default(def_id);
record!(self.tables.object_lifetime_default[def_id] <- default);
}
Expand Down
26 changes: 12 additions & 14 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,20 +710,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
/// Debugging aid for `object_lifetime_default` query.
fn check_object_lifetime_default(&self, hir_id: HirId) {
let tcx = self.tcx;
if let Some(owner_id) = hir_id.as_owner()
&& let Some(generics) = tcx.hir_get_generics(owner_id.def_id)
{
for p in generics.params {
let hir::GenericParamKind::Type { .. } = p.kind else { continue };
let default = tcx.object_lifetime_default(p.def_id);
let repr = match default {
ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
ObjectLifetimeDefault::Static => "'static".to_owned(),
ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
};
tcx.dcx().emit_err(errors::ObjectLifetimeErr { span: p.span, repr });
}
let Some(owner_id) = hir_id.as_owner() else { return };
for param in &tcx.generics_of(owner_id.def_id).own_params {
let ty::GenericParamDefKind::Type { .. } = param.kind else { continue };
let default = tcx.object_lifetime_default(param.def_id);
let repr = match default {
ObjectLifetimeDefault::Empty => "Empty".to_owned(),
ObjectLifetimeDefault::Static => "'static".to_owned(),
ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
};
tcx.dcx()
.emit_err(errors::ObjectLifetimeErr { span: tcx.def_span(param.def_id), repr });
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,9 @@ impl<'tcx> ContainerTy<'_, 'tcx> {
match self {
Self::Ref(region) => ObjectLifetimeDefault::Arg(region),
Self::Regular { ty: container, args, arg: index } => {
// FIXME(fmease): rustc now also computes ambient object lifetime defaults for
// `AssocTy`s. Re-elide these, too!

let (DefKind::Struct
| DefKind::Union
| DefKind::Enum
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/did_you_mean/bad-assoc-ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type G = dyn 'static + (Send)::AssocTy;
// This is actually a legal path with fn-like generic arguments in the middle!
// Recovery should not apply in this context.
type H = Fn(u8) -> (u8)::Output;
//~^ ERROR ambiguous associated type
//~^ ERROR the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
//~| WARN trait objects without an explicit `dyn` are deprecated
//~| WARN this is accepted in the current edition

Expand Down
15 changes: 3 additions & 12 deletions tests/ui/did_you_mean/bad-assoc-ty.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -193,20 +193,11 @@ help: if this is a dyn-compatible trait, use `dyn`
LL | type H = <dyn Fn(u8) -> (u8)>::Output;
| ++++ +

error[E0223]: ambiguous associated type
error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
--> $DIR/bad-assoc-ty.rs:33:10
|
LL | type H = Fn(u8) -> (u8)::Output;
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: use fully-qualified syntax
|
LL - type H = Fn(u8) -> (u8)::Output;
LL + type H = <(dyn Fn(u8) -> u8 + 'static) as BitOr>::Output;
|
LL - type H = Fn(u8) -> (u8)::Output;
LL + type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output;
|
| ^^^^^^^^^^^^^^

error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:39:19
Expand Down Expand Up @@ -354,5 +345,5 @@ LL + trait P<F, T> where F: Fn() -> T {

error: aborting due to 29 previous errors; 1 warning emitted

Some errors have detailed explanations: E0121, E0223, E0740.
Some errors have detailed explanations: E0121, E0223, E0228, E0740.
For more information about an error, try `rustc --explain E0121`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Check that we correctly deduce object lifetime defaults inside self types of qualified paths.
//@ check-pass

trait Outer { type Ty; }
trait Inner {}

impl<'a> Outer for dyn Inner + 'a { type Ty = &'a (); }

// We deduce `dyn Inner + 'static` from absence of any bounds on self ty param of trait `Outer`.
fn f<'r>(x: &'r <dyn Inner as Outer>::Ty) { g(x) }
fn g<'r>(x: &'r <dyn Inner + 'static as Outer>::Ty) {}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Check that we correctly deduce object lifetime defaults inside self types of qualified paths.
//@ check-pass
//@ revisions: bound clause

#[cfg(bound)] trait Outer<'a>: 'a { type Ty; }
#[cfg(clause)] trait Outer<'a> where Self: 'a { type Ty; }
trait Inner {}

impl<'a> Outer<'a> for dyn Inner + 'a { type Ty = &'a (); }

fn f<'r>(x: <dyn Inner + 'r as Outer<'r>>::Ty) { g(x) }
// We deduce `dyn Inner + 'r` from bound `'a` on self ty param of trait `Outer`.
fn g<'r>(x: <dyn Inner as Outer<'r>>::Ty) {}

fn main() {}
33 changes: 33 additions & 0 deletions tests/ui/object-lifetime/object-lifetime-default-gat-resolved.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Check that we correctly deduce object lifetime defaults inside resolved GAT *paths*.
// issue: <https://github.com/rust-lang/rust/issues/115379>
//@ check-pass

mod own { // the object lifetime default comes from the own generics
trait Outer {
type Ty<'a, T: ?Sized + 'a>;
}
impl Outer for () {
type Ty<'a, T: ?Sized + 'a> = &'a T;
}
trait Inner {}

fn f<'r>(x: <() as Outer>::Ty<'r, dyn Inner + 'r>) { g(x) }
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`.
fn g<'r>(_: <() as Outer>::Ty<'r, dyn Inner>) {}
}

mod parent { // the object lifetime default comes from the parent generics
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add a test with both self and parent lifetimes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean one where the assoc ty is type AssocTy<'own, T: ?Sized + 'own + 'parent>; or type AssocTy<'own, T: ?Sized + 'own, U: ?Sized + 'parent>; or something else entirely?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had in mind just type AssocTy<'own, T: ?Sized + 'own> (basically identical to the first example, but showing that the extra lifetime doesn't throw it off), and ideally some sort of failure tests of cases that are using the wrong lifetime.

trait Outer<'a> {
type Ty<T: ?Sized + 'a>;
}
impl<'a> Outer<'a> for () {
type Ty<T: ?Sized + 'a> = &'a T;
}
trait Inner {}

fn f<'r>(x: <() as Outer<'r>>::Ty<dyn Inner + 'r>) { g(x) }
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`.
fn g<'r>(_: <() as Outer<'r>>::Ty<dyn Inner>) {}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Properly deduce the object lifetime default in type-relative generic associated type *paths*.
// issue: <https://github.com/rust-lang/rust/issues/115379>
//@ check-pass

trait Outer { type Ty<'a, T: 'a + ?Sized>; }
trait Inner {}

fn f<'r, T: Outer>(x: T::Ty<'r, dyn Inner + 'r>) { g::<T>(x) }
// Deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`
fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {}

fn main() {}
26 changes: 26 additions & 0 deletions tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Check that we correctly deduce object lifetime defaults inside *trait refs*!
// For the longest time these examples used to get rejected as "inderminate" due to an off-by-one.
//@ check-pass

trait Inner {}
trait Outer<'a, T: 'a + ?Sized> { type Project where Self: Sized; }

fn bound0<'r, T>() where T: Outer<'r, dyn Inner + 'r> { bound1::<'r, T>() }
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`.
fn bound1<'r, T>() where T: Outer<'r, dyn Inner> {}

fn dyn0<'r>(x: Box<dyn Outer<'r, dyn Inner + 'r>>) { dyn1(x) }
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`.
fn dyn1<'r>(_: Box<dyn Outer<'r, dyn Inner>>) {}

fn impl0<'r>(x: impl Outer<'r, dyn Inner + 'r>) { impl1(x) }
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`.
fn impl1<'r>(_: impl Outer<'r, dyn Inner>) {} // deduce `dyn Inner + 'r`

fn proj<'r>(x: <() as Outer<'r, dyn Inner + 'r>>::Project) { proj1(x) }
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`.
fn proj1<'r>(_: <() as Outer<'r, dyn Inner>>::Project) {}

impl<'a, T: 'a + ?Sized> Outer<'a, T> for () { type Project = &'a T; }

fn main() {}
12 changes: 10 additions & 2 deletions tests/ui/object-lifetime/object-lifetime-default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

#[rustc_object_lifetime_default]
struct A<
T, //~ ERROR BaseDefault
T, //~ ERROR Empty
>(T);

#[rustc_object_lifetime_default]
struct B<
'a,
T, //~ ERROR BaseDefault
T, //~ ERROR Empty
>(&'a (), T);

#[rustc_object_lifetime_default]
Expand Down Expand Up @@ -47,4 +47,12 @@ struct G<
U: 'a + 'b, //~ ERROR Ambiguous
>(&'a T, &'b U);

// Check that we also dump the default for the implicit `Self` type param of traits.
#[rustc_object_lifetime_default]
trait H< //~ ERROR 'a
'a,
'b,
T: 'b, //~ ERROR 'b
>: 'a {}

fn main() {}
22 changes: 19 additions & 3 deletions tests/ui/object-lifetime/object-lifetime-default.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error: BaseDefault
error: Empty
--> $DIR/object-lifetime-default.rs:5:5
|
LL | T,
| ^

error: BaseDefault
error: Empty
--> $DIR/object-lifetime-default.rs:11:5
|
LL | T,
Expand Down Expand Up @@ -52,5 +52,21 @@ error: Ambiguous
LL | U: 'a + 'b,
| ^

error: aborting due to 9 previous errors
error: 'a
--> $DIR/object-lifetime-default.rs:52:1
|
LL | / trait H<
LL | | 'a,
LL | | 'b,
LL | | T: 'b,
LL | | >: 'a {}
| |_____^

error: 'b
--> $DIR/object-lifetime-default.rs:55:5
|
LL | T: 'b,
| ^

error: aborting due to 11 previous errors

Loading