Skip to content

Commit 4043493

Browse files
For a rigid projection, recursively look at the self type's item bounds
1 parent 4a2fe44 commit 4043493

23 files changed

+272
-430
lines changed

compiler/rustc_trait_selection/src/solve/assembly/mod.rs

+88-41
Original file line numberDiff line numberDiff line change
@@ -542,50 +542,97 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
542542
goal: Goal<'tcx, G>,
543543
candidates: &mut Vec<Candidate<'tcx>>,
544544
) {
545-
let alias_ty = match goal.predicate.self_ty().kind() {
546-
ty::Bool
547-
| ty::Char
548-
| ty::Int(_)
549-
| ty::Uint(_)
550-
| ty::Float(_)
551-
| ty::Adt(_, _)
552-
| ty::Foreign(_)
553-
| ty::Str
554-
| ty::Array(_, _)
555-
| ty::Slice(_)
556-
| ty::RawPtr(_)
557-
| ty::Ref(_, _, _)
558-
| ty::FnDef(_, _)
559-
| ty::FnPtr(_)
560-
| ty::Dynamic(..)
561-
| ty::Closure(..)
562-
| ty::CoroutineClosure(..)
563-
| ty::Coroutine(..)
564-
| ty::CoroutineWitness(..)
565-
| ty::Never
566-
| ty::Tuple(_)
567-
| ty::Param(_)
568-
| ty::Placeholder(..)
569-
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
570-
| ty::Alias(ty::Inherent, _)
571-
| ty::Alias(ty::Weak, _)
572-
| ty::Error(_) => return,
573-
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
574-
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
575-
// Excluding IATs and type aliases here as they don't have meaningful item bounds.
576-
ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty,
577-
};
545+
let _ = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
546+
let mut self_ty = goal.predicate.self_ty();
547+
548+
// For some deeply nested `<T>::A::B::C::D` rigid associated type,
549+
// we should explore the item bounds for all levels, since the
550+
// `associated_type_bounds` feature means that a parent associated
551+
// type may carry bounds for a nested associated type.
552+
loop {
553+
let (kind, alias_ty) = match *self_ty.kind() {
554+
ty::Bool
555+
| ty::Char
556+
| ty::Int(_)
557+
| ty::Uint(_)
558+
| ty::Float(_)
559+
| ty::Adt(_, _)
560+
| ty::Foreign(_)
561+
| ty::Str
562+
| ty::Array(_, _)
563+
| ty::Slice(_)
564+
| ty::RawPtr(_)
565+
| ty::Ref(_, _, _)
566+
| ty::FnDef(_, _)
567+
| ty::FnPtr(_)
568+
| ty::Dynamic(..)
569+
| ty::Closure(..)
570+
| ty::CoroutineClosure(..)
571+
| ty::Coroutine(..)
572+
| ty::CoroutineWitness(..)
573+
| ty::Never
574+
| ty::Tuple(_)
575+
| ty::Param(_)
576+
| ty::Placeholder(..)
577+
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
578+
| ty::Error(_) => break,
579+
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
580+
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
581+
582+
// If we hit infer when normalizing the self type of an alias,
583+
// then bail with ambiguity.
584+
ty::Infer(ty::TyVar(_)) => {
585+
if let Ok(result) = ecx
586+
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
587+
{
588+
candidates
589+
.push(Candidate { source: CandidateSource::AliasBound, result });
590+
}
591+
break;
592+
}
578593

579-
for assumption in
580-
self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
581-
{
582-
match G::consider_alias_bound_candidate(self, goal, assumption) {
583-
Ok(result) => {
584-
candidates.push(Candidate { source: CandidateSource::AliasBound, result })
594+
ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
595+
ty::Alias(ty::Inherent | ty::Weak, _) => {
596+
unreachable!("Weak and Inherent aliases should have been normalized away")
597+
}
598+
};
599+
600+
for assumption in
601+
ecx.tcx().item_bounds(alias_ty.def_id).instantiate(ecx.tcx(), alias_ty.args)
602+
{
603+
match G::consider_alias_bound_candidate(ecx, goal, assumption) {
604+
Ok(result) => {
605+
candidates
606+
.push(Candidate { source: CandidateSource::AliasBound, result });
607+
}
608+
Err(NoSolution) => {}
609+
}
610+
}
611+
612+
// If we have a projection, check that its self type is a rigid projection.
613+
// If so, continue searching.
614+
if kind == ty::Projection {
615+
match ecx.try_normalize_ty(goal.param_env, alias_ty.self_ty()) {
616+
Some(next_self_ty) => self_ty = next_self_ty,
617+
None => {
618+
if let Ok(result) = ecx
619+
.evaluate_added_goals_and_make_canonical_response(
620+
Certainty::OVERFLOW,
621+
)
622+
{
623+
candidates.push(Candidate {
624+
source: CandidateSource::AliasBound,
625+
result,
626+
});
627+
}
628+
break;
629+
}
630+
}
631+
} else {
632+
break;
585633
}
586-
Err(NoSolution) => (),
587634
}
588-
}
635+
});
589636
}
590637

591638
/// Check that we are allowed to use an alias bound originating from the self

compiler/rustc_trait_selection/src/traits/project.rs

+38-23
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
4040
use rustc_span::symbol::sym;
4141

4242
use std::collections::BTreeMap;
43+
use std::ops::ControlFlow;
4344

4445
pub use rustc_middle::traits::Reveal;
4546

@@ -1615,32 +1616,46 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
16151616
candidate_set: &mut ProjectionCandidateSet<'tcx>,
16161617
) {
16171618
debug!("assemble_candidates_from_trait_def(..)");
1619+
let mut ambiguous = false;
1620+
selcx.for_each_item_bound(
1621+
obligation.predicate.self_ty(),
1622+
|selcx, clause, _| {
1623+
let Some(clause) = clause.as_projection_clause() else {
1624+
return ControlFlow::Continue(());
1625+
};
16181626

1619-
let tcx = selcx.tcx();
1620-
// Check whether the self-type is itself a projection.
1621-
// If so, extract what we know from the trait and try to come up with a good answer.
1622-
let bounds = match *obligation.predicate.self_ty().kind() {
1623-
// Excluding IATs and type aliases here as they don't have meaningful item bounds.
1624-
ty::Alias(ty::Projection | ty::Opaque, ref data) => {
1625-
tcx.item_bounds(data.def_id).instantiate(tcx, data.args)
1626-
}
1627-
ty::Infer(ty::TyVar(_)) => {
1628-
// If the self-type is an inference variable, then it MAY wind up
1629-
// being a projected type, so induce an ambiguity.
1630-
candidate_set.mark_ambiguous();
1631-
return;
1632-
}
1633-
_ => return,
1634-
};
1627+
let is_match =
1628+
selcx.infcx.probe(|_| selcx.match_projection_projections(obligation, clause, true));
16351629

1636-
assemble_candidates_from_predicates(
1637-
selcx,
1638-
obligation,
1639-
candidate_set,
1640-
ProjectionCandidate::TraitDef,
1641-
bounds.iter(),
1642-
true,
1630+
match is_match {
1631+
ProjectionMatchesProjection::Yes => {
1632+
candidate_set.push_candidate(ProjectionCandidate::TraitDef(clause));
1633+
1634+
if !obligation.predicate.has_non_region_infer() {
1635+
// HACK: Pick the first trait def candidate for a fully
1636+
// inferred predicate. This is to allow duplicates that
1637+
// differ only in normalization.
1638+
return ControlFlow::Break(());
1639+
}
1640+
}
1641+
ProjectionMatchesProjection::Ambiguous => {
1642+
candidate_set.mark_ambiguous();
1643+
}
1644+
ProjectionMatchesProjection::No => {}
1645+
}
1646+
1647+
ControlFlow::Continue(())
1648+
},
1649+
|| {
1650+
// `ProjectionCandidateSet` is borrowed in the above closure,
1651+
// so just mark ambiguous outside of the closure.
1652+
ambiguous = true;
1653+
},
16431654
);
1655+
1656+
if ambiguous {
1657+
candidate_set.mark_ambiguous();
1658+
}
16441659
}
16451660

16461661
/// In the case of a trait object like

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+49-5
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66
//!
77
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
88
9+
use std::ops::ControlFlow;
10+
911
use hir::def_id::DefId;
1012
use hir::LangItem;
13+
use rustc_data_structures::fx::FxHashSet;
1114
use rustc_hir as hir;
1215
use rustc_infer::traits::ObligationCause;
1316
use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
1417
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
15-
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
18+
use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt};
1619

1720
use crate::traits;
1821
use crate::traits::query::evaluate_obligation::InferCtxtExt;
@@ -158,11 +161,52 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
158161
_ => return,
159162
}
160163

161-
let result = self
162-
.infcx
163-
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
164+
self.infcx.probe(|_| {
165+
let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
166+
let placeholder_trait_predicate =
167+
self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate);
168+
debug!(?placeholder_trait_predicate);
169+
170+
// The bounds returned by `item_bounds` may contain duplicates after
171+
// normalization, so try to deduplicate when possible to avoid
172+
// unnecessary ambiguity.
173+
let mut distinct_normalized_bounds = FxHashSet::default();
174+
self.for_each_item_bound::<!>(
175+
placeholder_trait_predicate.self_ty(),
176+
|selcx, bound, idx| {
177+
let Some(bound) = bound.as_trait_clause() else {
178+
return ControlFlow::Continue(());
179+
};
180+
if bound.polarity() != placeholder_trait_predicate.polarity {
181+
return ControlFlow::Continue(());
182+
}
183+
184+
selcx.infcx.probe(|_| {
185+
match selcx.match_normalize_trait_ref(
186+
obligation,
187+
bound.to_poly_trait_ref(),
188+
placeholder_trait_predicate.trait_ref,
189+
) {
190+
Ok(None) => {
191+
candidates.vec.push(ProjectionCandidate(idx));
192+
}
193+
Ok(Some(normalized_trait))
194+
if distinct_normalized_bounds.insert(normalized_trait) =>
195+
{
196+
candidates.vec.push(ProjectionCandidate(idx));
197+
}
198+
_ => {}
199+
}
200+
});
164201

165-
candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx)));
202+
ControlFlow::Continue(())
203+
},
204+
|| {
205+
// On ambiguity.
206+
candidates.ambiguous = true;
207+
},
208+
);
209+
});
166210
}
167211

168212
/// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+18-11
Original file line numberDiff line numberDiff line change
@@ -162,20 +162,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
162162
self.infcx.instantiate_binder_with_placeholders(trait_predicate).trait_ref;
163163
let placeholder_self_ty = placeholder_trait_predicate.self_ty();
164164
let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate);
165-
let (def_id, args) = match *placeholder_self_ty.kind() {
166-
// Excluding IATs and type aliases here as they don't have meaningful item bounds.
167-
ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
168-
(def_id, args)
169-
}
170-
_ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty),
171-
};
172165

173-
let candidate_predicate =
174-
tcx.item_bounds(def_id).map_bound(|i| i[idx]).instantiate(tcx, args);
166+
let candidate_predicate = self
167+
.for_each_item_bound(
168+
placeholder_self_ty,
169+
|_, clause, clause_idx| {
170+
if clause_idx == idx {
171+
ControlFlow::Break(clause)
172+
} else {
173+
ControlFlow::Continue(())
174+
}
175+
},
176+
|| unreachable!(),
177+
)
178+
.break_value()
179+
.expect("expected to index into clause that exists");
175180
let candidate = candidate_predicate
176181
.as_trait_clause()
177182
.expect("projection candidate is not a trait predicate")
178183
.map_bound(|t| t.trait_ref);
184+
179185
let mut obligations = Vec::new();
180186
let candidate = normalize_with_depth_to(
181187
self,
@@ -194,8 +200,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
194200
.map_err(|_| Unimplemented)
195201
})?);
196202

197-
if let ty::Alias(ty::Projection, ..) = placeholder_self_ty.kind() {
198-
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
203+
// FIXME(compiler-errors): I don't think this is needed.
204+
if let ty::Alias(ty::Projection, alias_ty) = placeholder_self_ty.kind() {
205+
let predicates = tcx.predicates_of(alias_ty.def_id).instantiate_own(tcx, alias_ty.args);
199206
for (predicate, _) in predicates {
200207
let normalized = normalize_with_depth_to(
201208
self,

0 commit comments

Comments
 (0)