Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Robustify and genericize return-type-notation resolution in resolve_bound_vars #132047

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1891,45 +1891,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 @@ -2001,6 +1986,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();
}
Loading