Skip to content

Commit 0f8c502

Browse files
committed
Propagate the object lifetime default to the self ty of resolved projections
1 parent 30ad2c1 commit 0f8c502

File tree

6 files changed

+190
-167
lines changed

6 files changed

+190
-167
lines changed

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

Lines changed: 176 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -887,12 +887,28 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
887887
fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: HirId, _: Span) {
888888
match qpath {
889889
hir::QPath::Resolved(maybe_qself, path) => {
890+
// Visit the path before the self type since computing the ambient object lifetime default
891+
// for the latter requires all lifetime arguments of the trait ref to be already resolved.
892+
self.visit_path(path, id);
890893
if let Some(qself) = maybe_qself {
891-
// FIXME: Actually determine the ambient object lifetime defaults for the self ty!
892-
let scope = Scope::ObjectLifetimeDefault { lifetime: None, s: self.scope };
893-
self.with(scope, |this| this.visit_ty_unambig(qself));
894+
let container = match path.res {
895+
Res::Def(DefKind::AssocTy, def_id) => Some((
896+
self.tcx.parent(def_id),
897+
&path.segments[..path.segments.len() - 1],
898+
)),
899+
_ => None,
900+
};
901+
let object_lifetime_defaults =
902+
container.map_or(Vec::new(), |(def_id, segs)| {
903+
self.compute_ambient_object_lifetime_defaults(def_id, segs)
904+
});
905+
if let Some(&lt) = object_lifetime_defaults.get(0) {
906+
let scope = Scope::ObjectLifetimeDefault { lifetime: lt, s: self.scope };
907+
self.with(scope, |this| this.visit_ty_unambig(qself));
908+
} else {
909+
self.visit_ty_unambig(qself);
910+
}
894911
}
895-
self.visit_path(path, id);
896912
}
897913
hir::QPath::TypeRelative(qself, segment) => {
898914
// Resolving object lifetime defaults for type-relative paths requires full
@@ -1056,52 +1072,57 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
10561072
}
10571073

10581074
fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectLifetimeDefault {
1059-
debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam);
1060-
let hir::Node::GenericParam(param) = tcx.hir_node_by_def_id(param_def_id) else {
1061-
bug!("expected GenericParam for object_lifetime_default");
1062-
};
1063-
match param.source {
1064-
hir::GenericParamSource::Generics => {
1065-
let parent_def_id = tcx.local_parent(param_def_id);
1066-
let generics = tcx.hir_get_generics(parent_def_id).unwrap();
1067-
1068-
// Scan the bounds and where-clauses on parameters to extract bounds
1069-
// of the form `T:'a` so as to determine the `ObjectLifetimeDefault`
1070-
// for each type parameter.
1071-
match param.kind {
1075+
// Scan the bounds and where-clauses on parameters to extract bounds of the form `T: 'a`
1076+
// so as to determine the `ObjectLifetimeDefault` for each type parameter.
1077+
1078+
let Ok((generics, bounds)) = (match tcx.hir_node_by_def_id(param_def_id) {
1079+
hir::Node::GenericParam(param) => match param.source {
1080+
hir::GenericParamSource::Generics => match param.kind {
10721081
GenericParamKind::Type { .. } => {
1073-
let mut set = Set1::Empty;
1074-
1075-
// Look for `type: ...` where clauses.
1076-
for bound in generics.bounds_for_param(param_def_id) {
1077-
// Ignore `for<'a> type: ...` as they can change what
1078-
// lifetimes mean (although we could "just" handle it).
1079-
if !bound.bound_generic_params.is_empty() {
1080-
continue;
1081-
}
1082+
Ok((tcx.hir_get_generics(tcx.local_parent(param_def_id)).unwrap(), &[][..]))
1083+
}
1084+
_ => Err(()),
1085+
},
1086+
hir::GenericParamSource::Binder => return ObjectLifetimeDefault::Empty,
1087+
},
1088+
// For `Self` type parameters
1089+
hir::Node::Item(&hir::Item {
1090+
kind: hir::ItemKind::Trait(_, _, _, _, generics, bounds, _),
1091+
..
1092+
}) => Ok((generics, bounds)),
1093+
_ => Err(()),
1094+
}) else {
1095+
bug!("`object_lifetime_default` must only be called on type parameters")
1096+
};
10821097

1083-
for bound in bound.bounds {
1084-
if let hir::GenericBound::Outlives(lifetime) = bound {
1085-
set.insert(lifetime.kind);
1086-
}
1087-
}
1088-
}
1098+
let mut set = Set1::Empty;
10891099

1090-
match set {
1091-
Set1::Empty => ObjectLifetimeDefault::Empty,
1092-
Set1::One(hir::LifetimeKind::Static) => ObjectLifetimeDefault::Static,
1093-
Set1::One(hir::LifetimeKind::Param(param_def_id)) => {
1094-
ObjectLifetimeDefault::Param(param_def_id.to_def_id())
1095-
}
1096-
_ => ObjectLifetimeDefault::Ambiguous,
1097-
}
1098-
}
1099-
_ => {
1100-
bug!("object_lifetime_default must only be called on a type parameter")
1101-
}
1100+
let mut add_outlives_bounds = |bounds: &[hir::GenericBound<'_>]| {
1101+
for bound in bounds {
1102+
if let hir::GenericBound::Outlives(lifetime) = bound {
1103+
set.insert(lifetime.kind);
11021104
}
11031105
}
1104-
hir::GenericParamSource::Binder => ObjectLifetimeDefault::Empty,
1106+
};
1107+
1108+
add_outlives_bounds(bounds);
1109+
1110+
// Look for `Type: ...` where clauses.
1111+
for bound in generics.bounds_for_param(param_def_id) {
1112+
// Ignore `for<'a> Type: ...` as they can change what
1113+
// lifetimes mean (although we could "just" handle it).
1114+
if bound.bound_generic_params.is_empty() {
1115+
add_outlives_bounds(&bound.bounds);
1116+
}
1117+
}
1118+
1119+
match set {
1120+
Set1::Empty => ObjectLifetimeDefault::Empty,
1121+
Set1::One(hir::LifetimeKind::Static) => ObjectLifetimeDefault::Static,
1122+
Set1::One(hir::LifetimeKind::Param(param_def_id)) => {
1123+
ObjectLifetimeDefault::Param(param_def_id.to_def_id())
1124+
}
1125+
_ => ObjectLifetimeDefault::Ambiguous,
11051126
}
11061127
}
11071128

@@ -1690,106 +1711,16 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
16901711

16911712
debug!(?container);
16921713

1693-
// Compute a vector of ambient object lifetime defaults, one for each type parameter,
1694-
// per the rules initially given in RFCs 599 and 1156. Example:
1695-
//
1696-
// ```rust
1697-
// struct Foo<'a, T: 'a + ?Sized, U: ?Sized>(&'a T, &'a U);
1698-
// ```
1699-
//
1700-
// If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default
1701-
// `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound)
1702-
// and `dyn Baz` to `dyn Baz + 'static` (because there is no
1703-
// such bound).
1704-
//
1705-
// Therefore, we would compute a vector like `['x, 'static]`.
1706-
// Note that the vector only includes type parameters.
1707-
let object_lifetime_defaults = container.map_or_else(Vec::new, |(def_id, segments)| {
1708-
let in_body = {
1709-
let mut scope = self.scope;
1710-
loop {
1711-
match *scope {
1712-
Scope::Root { .. } => break false,
1713-
1714-
Scope::Body { .. } => break true,
1715-
1716-
Scope::Binder { s, .. }
1717-
| Scope::ObjectLifetimeDefault { s, .. }
1718-
| Scope::Opaque { s, .. }
1719-
| Scope::Supertrait { s, .. }
1720-
| Scope::TraitRefBoundary { s, .. }
1721-
| Scope::LateBoundary { s, .. } => {
1722-
scope = s;
1723-
}
1724-
}
1725-
}
1726-
};
1727-
1728-
let rbv = &self.rbv;
1729-
let generics = self.tcx.generics_of(def_id);
1730-
1731-
let set_to_region = |set: ObjectLifetimeDefault| match set {
1732-
ObjectLifetimeDefault::Empty => {
1733-
if in_body {
1734-
None
1735-
} else {
1736-
Some(ResolvedArg::StaticLifetime)
1737-
}
1738-
}
1739-
ObjectLifetimeDefault::Static => Some(ResolvedArg::StaticLifetime),
1740-
ObjectLifetimeDefault::Param(param_def_id) => {
1741-
fn param_to_depth_and_index(
1742-
generics: &ty::Generics,
1743-
tcx: TyCtxt<'_>,
1744-
def_id: DefId,
1745-
) -> (usize, usize) {
1746-
if let Some(&index) = generics.param_def_id_to_index.get(&def_id) {
1747-
let has_self = generics.parent.is_none() && generics.has_self;
1748-
(0, index as usize - generics.parent_count - has_self as usize)
1749-
} else if let Some(parent) = generics.parent {
1750-
let parent = tcx.generics_of(parent);
1751-
let (depth, index) = param_to_depth_and_index(parent, tcx, def_id);
1752-
(depth + 1, index)
1753-
} else {
1754-
unreachable!()
1755-
}
1756-
}
1757-
1758-
let (depth, index) = param_to_depth_and_index(generics, self.tcx, param_def_id);
1759-
segments[segments.len() - depth - 1]
1760-
.args
1761-
.and_then(|args| args.args.get(index))
1762-
.and_then(|arg| match arg {
1763-
GenericArg::Lifetime(lt) => rbv.defs.get(&lt.hir_id.local_id).copied(),
1764-
_ => None,
1765-
})
1766-
}
1767-
ObjectLifetimeDefault::Ambiguous => None,
1768-
};
1769-
generics
1770-
.own_params
1771-
.iter()
1772-
.filter_map(|param| {
1773-
match self.tcx.def_kind(param.def_id) {
1774-
// Generic consts don't impose any constraints.
1775-
//
1776-
// We still store a dummy value here to allow generic parameters
1777-
// in an arbitrary order.
1778-
DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty),
1779-
DefKind::TyParam => Some(self.tcx.object_lifetime_default(param.def_id)),
1780-
// We may also get a `Trait` or `TraitAlias` because of how generics `Self` parameter
1781-
// works. Ignore it because it can't have a meaningful lifetime default.
1782-
DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None,
1783-
dk => bug!("unexpected def_kind {:?}", dk),
1784-
}
1785-
})
1786-
.map(set_to_region)
1787-
.collect()
1714+
let object_lifetime_defaults = container.map_or_else(Vec::new, |(def_id, segs)| {
1715+
self.compute_ambient_object_lifetime_defaults(def_id, segs)
17881716
});
17891717

17901718
debug!(?object_lifetime_defaults);
17911719

1792-
let mut i = 0;
1720+
let has_self = container
1721+
.map(|(def_id, _)| self.tcx.generics_of(def_id))
1722+
.is_some_and(|generics| generics.parent.is_none() && generics.has_self);
1723+
let mut i = has_self as usize;
17931724
for arg in generic_args.args {
17941725
match arg {
17951726
// We've already visited all lifetime arguments at the start.
@@ -1921,6 +1852,110 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
19211852
}
19221853
}
19231854

1855+
/// Compute a vector of ambient object lifetime defaults, one for each type parameter,
1856+
/// per the rules initially given in RFCs 599 and 1156.
1857+
///
1858+
/// # Example
1859+
///
1860+
/// ```ignore (illustrative)
1861+
/// struct Foo<'a, T: 'a + ?Sized, U: ?Sized>(&'a T, &'a U);
1862+
/// ```
1863+
///
1864+
/// If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default
1865+
/// `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound)
1866+
/// and `dyn Baz` to `dyn Baz + 'static` (because there is no
1867+
/// such bound).
1868+
///
1869+
/// Therefore, we would compute a vector like `['x, 'static]`.
1870+
/// Note that the vector only includes type parameters.
1871+
fn compute_ambient_object_lifetime_defaults(
1872+
&self,
1873+
def_id: DefId,
1874+
segments: &[hir::PathSegment<'_>],
1875+
) -> Vec<Option<ResolvedArg>> {
1876+
let in_body = {
1877+
let mut scope = self.scope;
1878+
loop {
1879+
match *scope {
1880+
Scope::Root { .. } => break false,
1881+
1882+
Scope::Body { .. } => break true,
1883+
1884+
Scope::Binder { s, .. }
1885+
| Scope::ObjectLifetimeDefault { s, .. }
1886+
| Scope::Opaque { s, .. }
1887+
| Scope::Supertrait { s, .. }
1888+
| Scope::TraitRefBoundary { s, .. }
1889+
| Scope::LateBoundary { s, .. } => {
1890+
scope = s;
1891+
}
1892+
}
1893+
}
1894+
};
1895+
1896+
let generics = self.tcx.generics_of(def_id);
1897+
1898+
let set_to_region = |set: ObjectLifetimeDefault| match set {
1899+
ObjectLifetimeDefault::Empty => {
1900+
if in_body {
1901+
None
1902+
} else {
1903+
Some(ResolvedArg::StaticLifetime)
1904+
}
1905+
}
1906+
ObjectLifetimeDefault::Static => Some(ResolvedArg::StaticLifetime),
1907+
ObjectLifetimeDefault::Param(param_def_id) => {
1908+
fn param_to_depth_and_index(
1909+
generics: &ty::Generics,
1910+
tcx: TyCtxt<'_>,
1911+
def_id: DefId,
1912+
) -> (usize, usize) {
1913+
if let Some(&index) = generics.param_def_id_to_index.get(&def_id) {
1914+
let has_self = generics.parent.is_none() && generics.has_self;
1915+
(0, index as usize - generics.parent_count - has_self as usize)
1916+
} else if let Some(parent) = generics.parent {
1917+
let parent = tcx.generics_of(parent);
1918+
let (depth, index) = param_to_depth_and_index(parent, tcx, def_id);
1919+
(depth + 1, index)
1920+
} else {
1921+
unreachable!()
1922+
}
1923+
}
1924+
1925+
let (depth, index) = param_to_depth_and_index(generics, self.tcx, param_def_id);
1926+
segments[segments.len() - depth - 1]
1927+
.args
1928+
.and_then(|args| args.args.get(index))
1929+
.and_then(|arg| match arg {
1930+
GenericArg::Lifetime(lt) => self.rbv.defs.get(&lt.hir_id.local_id).copied(),
1931+
_ => None,
1932+
})
1933+
}
1934+
ObjectLifetimeDefault::Ambiguous => None,
1935+
};
1936+
generics
1937+
.own_params
1938+
.iter()
1939+
.filter_map(|param| {
1940+
match self.tcx.def_kind(param.def_id) {
1941+
// Generic consts don't impose any constraints.
1942+
//
1943+
// We still store a dummy value here to allow generic parameters
1944+
// in an arbitrary order.
1945+
DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty),
1946+
// `Self` type params share the `DefId` of the corresp. trait.
1947+
DefKind::TyParam | DefKind::Trait => {
1948+
Some(self.tcx.object_lifetime_default(param.def_id))
1949+
}
1950+
// `Self` type params of trait aliases may show up here, ignore them.
1951+
DefKind::LifetimeParam | DefKind::TraitAlias => None,
1952+
dk => bug!("unexpected def_kind {:?}", dk),
1953+
}
1954+
})
1955+
.map(set_to_region)
1956+
.collect()
1957+
}
1958+
19241959
/// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the
19251960
/// associated type name and starting trait.
19261961
/// For example, imagine we have

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
15341534
if let Some(name) = tcx.intrinsic(def_id) {
15351535
record!(self.tables.intrinsic[def_id] <- name);
15361536
}
1537-
if let DefKind::TyParam = def_kind {
1537+
if let DefKind::TyParam | DefKind::Trait = def_kind {
15381538
let default = self.tcx.object_lifetime_default(def_id);
15391539
record!(self.tables.object_lifetime_default[def_id] <- default);
15401540
}

tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
// FIXME: Explainer.
2-
//@ known-bug: unknown
1+
// Check that we correctly deduce object lifetime defaults inside self types of qualified paths.
2+
//@ check-pass
33

44
trait Outer { type Ty; }
55
trait Inner {}
66

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

9-
// FIXME: Deduce `dyn Inner + 'static` from absence of any bounds on self ty param of trait `Outer`.
9+
// We deduce `dyn Inner + 'static` from absence of any bounds on self ty param of trait `Outer`.
10+
//
11+
// Prior to PR rust-lang/rust#129543, assoc tys weren't considered *eligible generic containers* and
12+
// thus we'd use the *ambient object lifetime default* induced by the reference type ctor `&`,
13+
// namely `'r`. Now however, the assoc ty shadows/overwrites that ambient default to `'static`.
1014
fn f<'r>(x: &'r <dyn Inner as Outer>::Ty) { g(x) }
1115
fn g<'r>(x: &'r <dyn Inner + 'static as Outer>::Ty) {}
1216

tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)