Skip to content

Commit 32dfaec

Browse files
committed
Compute object lifetime defaults for type-relative assoc ty paths, too
1 parent 75fc4e1 commit 32dfaec

File tree

6 files changed

+147
-153
lines changed

6 files changed

+147
-153
lines changed

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

Lines changed: 138 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
900900
self.tcx.parent(def_id),
901901
&path.segments[..path.segments.len() - 1],
902902
)),
903+
// FIXME(mgca): @fmease thinks we also need to handle AssocConsts here.
903904
_ => None,
904905
};
905906
let object_lifetime_defaults =
@@ -914,17 +915,27 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
914915
}
915916
}
916917
}
917-
hir::QPath::TypeRelative(qself, segment) => {
918-
// Resolving object lifetime defaults for type-relative paths requires full
919-
// type-dependent resolution as performed by HIR ty lowering whose results
920-
// we don't have access to (and we'd results for both FnCtxts and ItemCtxts).
921-
// FIXME: Figure out if there's a feasible way to obtain the map of
922-
// type-dependent definitions.
918+
&hir::QPath::TypeRelative(qself, segment) => {
919+
if let Some(args) = segment.args {
920+
// FIXME(mgca): @fmease thinks we also need to handle AssocConsts here.
921+
let container = self
922+
.limited_resolve_type_relative_path(
923+
ty::AssocTag::Type,
924+
qself,
925+
segment,
926+
true,
927+
)
928+
.map(|(_, assoc_item)| (assoc_item.def_id, std::slice::from_ref(segment)));
929+
self.visit_segment_args(container, args);
930+
}
931+
932+
// For forward compatibility we reject elided object lifetimes in the self type as
933+
// "indeterminate" by passing `None`. `limited_resolve_type_relative_path` is not
934+
// complete compared to HIR ty lowering's `lower_assoc_path_shared`, so we need to
935+
// be conservative. Consider paths like `<dyn Trait>::X` which may resolve in the
936+
// future (under IATs or mGCA (IACs)).
923937
let scope = Scope::ObjectLifetimeDefault { lifetime: None, s: self.scope };
924-
self.with(scope, |this| {
925-
this.visit_ty_unambig(qself);
926-
this.visit_path_segment(segment)
927-
});
938+
self.with(scope, |this| this.visit_ty_unambig(qself));
928939
}
929940
hir::QPath::LangItem(..) => {}
930941
}
@@ -933,7 +944,38 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
933944
fn visit_path(&mut self, path: &hir::Path<'tcx>, hir_id: HirId) {
934945
for (index, segment) in path.segments.iter().enumerate() {
935946
if let Some(args) = segment.args {
936-
self.visit_segment_args(path, index, args);
947+
// Figure out if this is an "eligible generic container" that brings along ambient object
948+
// lifetime defaults for trait object types contained in any of the type arguments passed to
949+
// it (any inner generic containers will of course end up shadowing that the default).
950+
let depth = path.segments.len() - index - 1;
951+
let container = match (path.res, depth) {
952+
(Res::Def(DefKind::AssocTy, def_id), 1) => {
953+
Some((self.tcx.parent(def_id), &path.segments[..=index]))
954+
}
955+
(Res::Def(DefKind::Variant, def_id), 0) => {
956+
Some((self.tcx.parent(def_id), path.segments))
957+
}
958+
// FIXME(trait_alias): Arguably, trait aliases are eligible generic containers.
959+
(
960+
Res::Def(
961+
DefKind::Struct
962+
| DefKind::Union
963+
| DefKind::Enum
964+
| DefKind::TyAlias
965+
| DefKind::Trait
966+
| DefKind::AssocTy,
967+
def_id,
968+
),
969+
0,
970+
) => Some((def_id, path.segments)),
971+
// Note: We don't need to care about definitions kinds that can have generics if they
972+
// can only ever appear in positions where we can perform type inference (i.e., bodies).
973+
// FIXME(mgca): @fmease thinks that under (m)GCA we now also need to care about e.g.,
974+
// type-level Consts (GCI) and AssocConsts (maybe also Fns, AssocFns) here
975+
// since they appear outside of bodies (once the feature is more complete).
976+
_ => None,
977+
};
978+
self.visit_segment_args(container, args);
937979
}
938980
}
939981
if let Res::Def(DefKind::TyParam | DefKind::ConstParam, param_def_id) = path.res {
@@ -1656,8 +1698,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
16561698
#[instrument(level = "debug", skip(self))]
16571699
fn visit_segment_args(
16581700
&mut self,
1659-
path: &hir::Path<'tcx>,
1660-
index: usize,
1701+
container: Option<(DefId, &[hir::PathSegment<'tcx>])>,
16611702
generic_args: &'tcx hir::GenericArgs<'tcx>,
16621703
) {
16631704
if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() {
@@ -1673,40 +1714,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
16731714
}
16741715
}
16751716

1676-
// Figure out if this is an "eligible generic container" that brings along ambient object
1677-
// lifetime defaults for trait object types contained in any of the type arguments passed to
1678-
// it (any inner generic containers will of course end up shadowing that the default).
1679-
let depth = path.segments.len() - index - 1;
1680-
let container = match (path.res, depth) {
1681-
(Res::Def(DefKind::AssocTy, def_id), 1) => {
1682-
Some((self.tcx.parent(def_id), &path.segments[..=index]))
1683-
}
1684-
(Res::Def(DefKind::Variant, def_id), 0) => {
1685-
Some((self.tcx.parent(def_id), path.segments))
1686-
}
1687-
// FIXME(trait_alias): Arguably, trait aliases are eligible generic containers.
1688-
(
1689-
Res::Def(
1690-
DefKind::Struct
1691-
| DefKind::Union
1692-
| DefKind::Enum
1693-
| DefKind::TyAlias
1694-
| DefKind::Trait
1695-
| DefKind::AssocTy,
1696-
def_id,
1697-
),
1698-
0,
1699-
) => Some((def_id, path.segments)),
1700-
// Note: We don't need to care about definitions kinds that can have generics if they
1701-
// can only ever appear in positions where we can perform type inference (i.e., bodies).
1702-
// FIXME(mgca): @fmease thinks that under (m)GCA we now also need to care about e.g.,
1703-
// type-level Consts (GCI) and AssocConsts (maybe also Fns, AssocFns) here
1704-
// since they appear outside of bodies (once the feature is more complete).
1705-
_ => None,
1706-
};
1707-
1708-
debug!(?container);
1709-
17101717
let object_lifetime_defaults = container.map_or_else(Vec::new, |(def_id, segs)| {
17111718
self.compute_ambient_object_lifetime_defaults(def_id, segs)
17121719
});
@@ -2152,72 +2159,15 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
21522159
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
21532160
}) =>
21542161
{
2155-
// First, ignore a qself that isn't a type or `Self` param. Those are the
2156-
// only ones that support `T::Assoc` anyways in HIR lowering.
2157-
let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind else {
2162+
let Some((bound_vars, assoc_item)) = self.limited_resolve_type_relative_path(
2163+
ty::AssocTag::Fn,
2164+
qself,
2165+
item_segment,
2166+
false,
2167+
) else {
21582168
return;
21592169
};
2160-
match path.res {
2161-
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => {
2162-
let mut bounds =
2163-
self.for_each_trait_bound_on_res(path.res).filter_map(|trait_def_id| {
2164-
BoundVarContext::supertrait_hrtb_vars(
2165-
self.tcx,
2166-
trait_def_id,
2167-
item_segment.ident,
2168-
ty::AssocTag::Fn,
2169-
)
2170-
});
2171-
2172-
let Some((bound_vars, assoc_item)) = bounds.next() else {
2173-
// This will error in HIR lowering.
2174-
self.tcx
2175-
.dcx()
2176-
.span_delayed_bug(path.span, "no resolution for RTN path");
2177-
return;
2178-
};
2179-
2180-
// Don't bail if we have identical bounds, which may be collected from
2181-
// something like `T: Bound + Bound`, or via elaborating supertraits.
2182-
for (second_vars, second_assoc_item) in bounds {
2183-
if second_vars != bound_vars || second_assoc_item != assoc_item {
2184-
// This will error in HIR lowering.
2185-
self.tcx.dcx().span_delayed_bug(
2186-
path.span,
2187-
"ambiguous resolution for RTN path",
2188-
);
2189-
return;
2190-
}
2191-
}
2192-
2193-
(bound_vars, assoc_item.def_id, item_segment)
2194-
}
2195-
// If we have a self type alias (in an impl), try to resolve an
2196-
// associated item from one of the supertraits of the impl's trait.
2197-
Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. } => {
2198-
let hir::ItemKind::Impl(hir::Impl { of_trait: Some(trait_ref), .. }) = self
2199-
.tcx
2200-
.hir_node_by_def_id(impl_def_id.expect_local())
2201-
.expect_item()
2202-
.kind
2203-
else {
2204-
return;
2205-
};
2206-
let Some(trait_def_id) = trait_ref.trait_def_id() else {
2207-
return;
2208-
};
2209-
let Some((bound_vars, assoc_item)) = BoundVarContext::supertrait_hrtb_vars(
2210-
self.tcx,
2211-
trait_def_id,
2212-
item_segment.ident,
2213-
ty::AssocTag::Fn,
2214-
) else {
2215-
return;
2216-
};
2217-
(bound_vars, assoc_item.def_id, item_segment)
2218-
}
2219-
_ => return,
2220-
}
2170+
(bound_vars, assoc_item.def_id, item_segment)
22212171
}
22222172

22232173
_ => return,
@@ -2256,6 +2206,83 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
22562206
self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved);
22572207
}
22582208

2209+
/// In a limited fashion, try to resolve the given type-relative path of the given kind.
2210+
fn limited_resolve_type_relative_path(
2211+
&self,
2212+
tag: ty::AssocTag,
2213+
qself: &'tcx hir::Ty<'tcx>,
2214+
segment: &'tcx hir::PathSegment<'tcx>,
2215+
speculative: bool,
2216+
) -> Option<(Vec<ty::BoundVariableKind>, &'tcx ty::AssocItem)> {
2217+
// This mimics HIR ty lowering's `lower_assoc_path_shared`.
2218+
// FIXME: Duplicating efforts is not robust or sustainable/maintainable.
2219+
// Ideally, we'd simply obtain the resulting type-dependent defs from
2220+
// HIR ty lowering (not only in FnCtxts but also in ItemCtxts!).
2221+
2222+
// First, ignore a qself that isn't a type or `Self` param. Those are the only ones
2223+
// that support `T::Assoc` anyways in HIR ty lowering at the time of writing.
2224+
let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind else {
2225+
return None;
2226+
};
2227+
2228+
match path.res {
2229+
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => {
2230+
let mut bounds =
2231+
self.for_each_trait_bound_on_res(path.res).filter_map(|trait_def_id| {
2232+
BoundVarContext::supertrait_hrtb_vars(
2233+
self.tcx,
2234+
trait_def_id,
2235+
segment.ident,
2236+
tag,
2237+
)
2238+
});
2239+
2240+
let Some((bound_vars, assoc_item)) = bounds.next() else {
2241+
if !speculative {
2242+
// This will error in HIR ty lowering.
2243+
self.tcx
2244+
.dcx()
2245+
.span_delayed_bug(path.span, "no resolution for type-relative path");
2246+
}
2247+
return None;
2248+
};
2249+
2250+
// Don't bail if we have identical bounds, which may be collected from
2251+
// something like `T: Bound + Bound`, or via elaborating supertraits.
2252+
for (second_vars, second_assoc_item) in bounds {
2253+
if second_vars != bound_vars || second_assoc_item != assoc_item {
2254+
if !speculative {
2255+
// This will error in HIR ty lowering.
2256+
self.tcx.dcx().span_delayed_bug(
2257+
path.span,
2258+
"ambiguous resolution for type-relative path",
2259+
);
2260+
}
2261+
return None;
2262+
}
2263+
}
2264+
2265+
Some((bound_vars, assoc_item))
2266+
}
2267+
// If we have a self type alias (in an impl), try to resolve an
2268+
// associated item from one of the supertraits of the impl's trait.
2269+
Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. } => {
2270+
let hir::ItemKind::Impl(hir::Impl { of_trait: Some(trait_ref), .. }) =
2271+
self.tcx.hir_node_by_def_id(impl_def_id.expect_local()).expect_item().kind
2272+
else {
2273+
return None;
2274+
};
2275+
BoundVarContext::supertrait_hrtb_vars(
2276+
self.tcx,
2277+
trait_ref.trait_def_id()?,
2278+
segment.ident,
2279+
tag,
2280+
)
2281+
}
2282+
_ => None,
2283+
}
2284+
}
2285+
22592286
/// Walk the generics of the item for a trait bound whose self type
22602287
/// corresponds to the expected res, and return the trait def id.
22612288
fn for_each_trait_bound_on_res(&self, expected_res: Res) -> impl Iterator<Item = DefId> {

tests/ui/deriving/issue-89188-gat-hrtb.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// FIXME(fmease): I've regressed this one since we now reject TypeRelative paths as too complex.
2-
//@ known-bug: unknown
1+
//@ check-pass
32

43
trait CallWithShim: Sized {
54
type Shim<'s>

tests/ui/deriving/issue-89188-gat-hrtb.stderr

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

tests/ui/object-lifetime/object-lifetime-default-gat-resolved.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Properly deduce the object lifetime default in generic associated type *paths*.
1+
// Properly deduce the object lifetime default in resolved generic associated type *paths*.
22
// issue: <https://github.com/rust-lang/rust/issues/115379>
33
//@ check-pass
44

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
// Properly deduce the object lifetime default in type-relative generic associated type *paths*.
2+
// issue: <https://github.com/rust-lang/rust/issues/115379>
3+
//@ check-pass
4+
15
trait Outer { type Ty<'a, T: 'a + ?Sized>; }
26
trait Inner {}
37

4-
// FIXME: Ideally, we would deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`
5-
// but for that we'd need to somehow obtain the resolution of the type-relative path `T::Ty`
6-
// from HIR ty lowering (it resolves to `<T as Outer>::Ty`).
7-
fn f<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {}
8-
//~^ ERROR lifetime bound for this object type cannot be deduced from context
8+
fn f<'r, T: Outer>(x: T::Ty<'r, dyn Inner + 'r>) { g::<T>(x) }
9+
// Deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`
10+
fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {}
911

1012
fn main() {}

tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.stderr

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

0 commit comments

Comments
 (0)