Skip to content

Commit 352dfcb

Browse files
committed
Compute object lifetime defaults for type-relative assoc ty paths, too
1 parent b31ee55 commit 352dfcb

File tree

5 files changed

+146
-152
lines changed

5 files changed

+146
-152
lines changed

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

+138-111
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 definition kinds that may 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 {
@@ -1652,8 +1694,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
16521694
#[instrument(level = "debug", skip(self))]
16531695
fn visit_segment_args(
16541696
&mut self,
1655-
path: &hir::Path<'tcx>,
1656-
index: usize,
1697+
container: Option<(DefId, &[hir::PathSegment<'tcx>])>,
16571698
generic_args: &'tcx hir::GenericArgs<'tcx>,
16581699
) {
16591700
if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() {
@@ -1669,40 +1710,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
16691710
}
16701711
}
16711712

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

22142164
_ => return,
@@ -2247,6 +2197,83 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
22472197
self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved);
22482198
}
22492199

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

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

+1-2
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

-25
This file was deleted.
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

-9
This file was deleted.

0 commit comments

Comments
 (0)