Skip to content

Commit

Permalink
Rollup merge of rust-lang#132047 - compiler-errors:rbv-rtn-cleanup, r…
Browse files Browse the repository at this point in the history
…=pnkfelix

Robustify and genericize return-type-notation resolution in `resolve_bound_vars`

rust-lang#129629 implemented return-type-notation (RTN) in its path form, like `where T::method(..): Bound`. As part of lowering, we must record the late-bound vars for the where clause introduced by the method (namely, its early- and late-bound lifetime arguments, since `where T::method(..)` turns into a higher-ranked where clause over all of the lifetimes according to [RFC 3654](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds)).

However, this logic was only looking at the where clauses of the parent item that the `T::method(..)` bound was written on, and not any parent items. This PR generalizes that logic to look at the parent item (i.e. the outer impl or trait) instead and fixes a (debug only) assertion as an effect.

This logic is also more general and likely easier to adapt to more interesting (though likely very far off) cases like non-lifetime binder `for<T: Trait> T::method(..): Send` bounds.

Tracking:

- rust-lang#109417
  • Loading branch information
GuillaumeGomez authored Nov 7, 2024
2 parents 2a1792b + 0ed6c46 commit 81cc4e4
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 32 deletions.
118 changes: 86 additions & 32 deletions compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2051,45 +2051,30 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
};
match path.res {
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => {
// Get the generics of this type's hir owner. This is *different*
// from the generics of the parameter's definition, since we want
// to be able to resolve an RTN path on a nested body (e.g. method
// inside an impl) using the where clauses on the method.
// FIXME(return_type_notation): Think of some better way of doing this.
let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics()
else {
return;
};

// Look for the first bound that contains an associated type that
// matches the segment that we're looking for. We ignore any subsequent
// bounds since we'll be emitting a hard error in HIR lowering, so this
// is purely speculative.
let one_bound = generics.predicates.iter().find_map(|predicate| {
let hir::WherePredicate::BoundPredicate(predicate) = predicate else {
return None;
};
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
predicate.bounded_ty.kind
else {
return None;
};
if bounded_path.res != path.res {
return None;
}
predicate.bounds.iter().find_map(|bound| {
let hir::GenericBound::Trait(trait_) = bound else {
return None;
};
let mut bounds =
self.for_each_in_scope_predicate(path.res).filter_map(|trait_| {
BoundVarContext::supertrait_hrtb_vars(
self.tcx,
trait_.trait_ref.trait_def_id()?,
item_segment.ident,
ty::AssocKind::Fn,
)
})
});
});

let one_bound = bounds.next();
let second_bound = bounds.next();

if second_bound.is_some() {
self.tcx
.dcx()
.span_delayed_bug(path.span, "ambiguous resolution for RTN path");
return;
}

let Some((bound_vars, assoc_item)) = one_bound else {
self.tcx
.dcx()
.span_delayed_bug(path.span, "no resolution for RTN path");
return;
};
(bound_vars, assoc_item.def_id, item_segment)
Expand Down Expand Up @@ -2157,6 +2142,75 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
existing_bound_vars.extend(bound_vars);
self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved);
}

/// Walk the generics of the item for a trait-ref whose self type
/// corresponds to the expected res.
fn for_each_in_scope_predicate(
&self,
expected_res: Res,
) -> impl Iterator<Item = &'tcx hir::PolyTraitRef<'tcx>> + use<'tcx, '_> {
std::iter::from_coroutine(
#[coroutine]
move || {
let mut next_scope = Some(self.scope);
while let Some(current_scope) = next_scope {
next_scope = None;
let hir_id = match *current_scope {
Scope::Binder { s, hir_id, .. } => {
next_scope = Some(s);
hir_id
}
Scope::Body { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s }
| Scope::LateBoundary { s, .. } => {
next_scope = Some(s);
continue;
}
Scope::Root { opt_parent_item } => {
if let Some(parent_id) = opt_parent_item {
self.tcx.local_def_id_to_hir_id(parent_id)
} else {
continue;
}
}
};
let node = self.tcx.hir_node(hir_id);
if let Some(generics) = node.generics() {
for pred in generics.predicates {
let hir::WherePredicate::BoundPredicate(pred) = pred else {
continue;
};
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
pred.bounded_ty.kind
else {
continue;
};
// Match the expected res.
if bounded_path.res != expected_res {
continue;
}
yield pred.bounds;
}
}
// Also consider supertraits for `Self` res...
if let Res::SelfTyParam { trait_: _ } = expected_res
&& let hir::Node::Item(item) = node
&& let hir::ItemKind::Trait(_, _, _, supertraits, _) = item.kind
{
yield supertraits;
}
}
},
)
.flatten()
.filter_map(|pred| match pred {
hir::GenericBound::Trait(poly_trait_ref) => Some(poly_trait_ref),
hir::GenericBound::Outlives(_) | hir::GenericBound::Use(_, _) => None,
})
.fuse()
}
}

/// Detects late-bound lifetimes and inserts them into
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(coroutines)]
#![feature(if_let_guard)]
#![feature(iter_from_coroutine)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(never_type)]
Expand Down
31 changes: 31 additions & 0 deletions tests/ui/associated-type-bounds/all-generics-lookup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//@ check-pass

#![feature(return_type_notation)]

trait Trait {
fn method(&self) -> impl Sized;
}

impl Trait for () {
fn method(&self) -> impl Sized {}
}

struct Struct<T>(T);

// This test used to fail a debug assertion since we weren't resolving the item
// for `T::method(..)` correctly, leading to two bound vars being given the
// index 0. The solution is to look at both generics of `test` and its parent impl.

impl<T> Struct<T>
where
T: Trait,
{
fn test()
where
T::method(..): Send
{}
}

fn main() {
Struct::<()>::test();
}

0 comments on commit 81cc4e4

Please sign in to comment.