Skip to content

Commit 18a08f1

Browse files
Robustify and genericize RTN resolution in RBV
1 parent 7db7489 commit 18a08f1

File tree

3 files changed

+119
-32
lines changed

3 files changed

+119
-32
lines changed

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

+86-32
Original file line numberDiff line numberDiff line change
@@ -2058,45 +2058,30 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
20582058
};
20592059
match path.res {
20602060
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => {
2061-
// Get the generics of this type's hir owner. This is *different*
2062-
// from the generics of the parameter's definition, since we want
2063-
// to be able to resolve an RTN path on a nested body (e.g. method
2064-
// inside an impl) using the where clauses on the method.
2065-
// FIXME(return_type_notation): Think of some better way of doing this.
2066-
let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics()
2067-
else {
2068-
return;
2069-
};
2070-
2071-
// Look for the first bound that contains an associated type that
2072-
// matches the segment that we're looking for. We ignore any subsequent
2073-
// bounds since we'll be emitting a hard error in HIR lowering, so this
2074-
// is purely speculative.
2075-
let one_bound = generics.predicates.iter().find_map(|predicate| {
2076-
let hir::WherePredicate::BoundPredicate(predicate) = predicate else {
2077-
return None;
2078-
};
2079-
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
2080-
predicate.bounded_ty.kind
2081-
else {
2082-
return None;
2083-
};
2084-
if bounded_path.res != path.res {
2085-
return None;
2086-
}
2087-
predicate.bounds.iter().find_map(|bound| {
2088-
let hir::GenericBound::Trait(trait_) = bound else {
2089-
return None;
2090-
};
2061+
let mut bounds =
2062+
self.for_each_in_scope_predicate(path.res).filter_map(|trait_| {
20912063
BoundVarContext::supertrait_hrtb_vars(
20922064
self.tcx,
20932065
trait_.trait_ref.trait_def_id()?,
20942066
item_segment.ident,
20952067
ty::AssocKind::Fn,
20962068
)
2097-
})
2098-
});
2069+
});
2070+
2071+
let one_bound = bounds.next();
2072+
let second_bound = bounds.next();
2073+
2074+
if second_bound.is_some() {
2075+
self.tcx
2076+
.dcx()
2077+
.span_delayed_bug(path.span, "ambiguous resolution for RTN path");
2078+
return;
2079+
}
2080+
20992081
let Some((bound_vars, assoc_item)) = one_bound else {
2082+
self.tcx
2083+
.dcx()
2084+
.span_delayed_bug(path.span, "no resolution for RTN path");
21002085
return;
21012086
};
21022087
(bound_vars, assoc_item.def_id, item_segment)
@@ -2164,6 +2149,75 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
21642149
existing_bound_vars.extend(bound_vars);
21652150
self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved);
21662151
}
2152+
2153+
/// Walk the generics of the item for a trait-ref whose self type
2154+
/// corresponds to the expected res.
2155+
fn for_each_in_scope_predicate(
2156+
&self,
2157+
expected_res: Res,
2158+
) -> impl Iterator<Item = &'tcx hir::PolyTraitRef<'tcx>> + use<'tcx, '_> {
2159+
std::iter::from_coroutine(
2160+
#[coroutine]
2161+
move || {
2162+
let mut next_scope = Some(self.scope);
2163+
while let Some(current_scope) = next_scope {
2164+
next_scope = None;
2165+
let hir_id = match *current_scope {
2166+
Scope::Binder { s, hir_id, .. } => {
2167+
next_scope = Some(s);
2168+
hir_id
2169+
}
2170+
Scope::Body { s, .. }
2171+
| Scope::ObjectLifetimeDefault { s, .. }
2172+
| Scope::Supertrait { s, .. }
2173+
| Scope::TraitRefBoundary { s }
2174+
| Scope::LateBoundary { s, .. } => {
2175+
next_scope = Some(s);
2176+
continue;
2177+
}
2178+
Scope::Root { opt_parent_item } => {
2179+
if let Some(parent_id) = opt_parent_item {
2180+
self.tcx.local_def_id_to_hir_id(parent_id)
2181+
} else {
2182+
continue;
2183+
}
2184+
}
2185+
};
2186+
let node = self.tcx.hir_node(hir_id);
2187+
if let Some(generics) = node.generics() {
2188+
for pred in generics.predicates {
2189+
let hir::WherePredicate::BoundPredicate(pred) = pred else {
2190+
continue;
2191+
};
2192+
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
2193+
pred.bounded_ty.kind
2194+
else {
2195+
continue;
2196+
};
2197+
// Match the expected res.
2198+
if bounded_path.res != expected_res {
2199+
continue;
2200+
}
2201+
yield pred.bounds;
2202+
}
2203+
}
2204+
// Also consider supertraits for `Self` res...
2205+
if let Res::SelfTyParam { trait_: _ } = expected_res
2206+
&& let hir::Node::Item(item) = node
2207+
&& let hir::ItemKind::Trait(_, _, _, supertraits, _) = item.kind
2208+
{
2209+
yield supertraits;
2210+
}
2211+
}
2212+
},
2213+
)
2214+
.flatten()
2215+
.filter_map(|pred| match pred {
2216+
hir::GenericBound::Trait(poly_trait_ref) => Some(poly_trait_ref),
2217+
hir::GenericBound::Outlives(_) | hir::GenericBound::Use(_, _) => None,
2218+
})
2219+
.fuse()
2220+
}
21672221
}
21682222

21692223
/// Detects late-bound lifetimes and inserts them into

compiler/rustc_hir_analysis/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ This API is completely unstable and subject to change.
6262
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
6363
#![doc(rust_logo)]
6464
#![feature(assert_matches)]
65+
#![feature(coroutines)]
6566
#![feature(if_let_guard)]
67+
#![feature(iter_from_coroutine)]
6668
#![feature(iter_intersperse)]
6769
#![feature(let_chains)]
6870
#![feature(never_type)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ check-pass
2+
3+
#![feature(return_type_notation)]
4+
5+
trait Trait {
6+
fn method(&self) -> impl Sized;
7+
}
8+
9+
impl Trait for () {
10+
fn method(&self) -> impl Sized {}
11+
}
12+
13+
struct Struct<T>(T);
14+
15+
// This test used to fail a debug assertion since we weren't resolving the item
16+
// for `T::method(..)` correctly, leading to two bound vars being given the
17+
// index 0. The solution is to look at both generics of `test` and its parent impl.
18+
19+
impl<T> Struct<T>
20+
where
21+
T: Trait,
22+
{
23+
fn test()
24+
where
25+
T::method(..): Send
26+
{}
27+
}
28+
29+
fn main() {
30+
Struct::<()>::test();
31+
}

0 commit comments

Comments
 (0)