Skip to content

Commit 0b1392f

Browse files
For a rigid projection, recursively look at the self type's item bounds
1 parent bf3c6c5 commit 0b1392f

23 files changed

+268
-429
lines changed

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

+83-40
Original file line numberDiff line numberDiff line change
@@ -521,49 +521,92 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
521521
goal: Goal<'tcx, G>,
522522
candidates: &mut Vec<Candidate<'tcx>>,
523523
) {
524-
let alias_ty = match goal.predicate.self_ty().kind() {
525-
ty::Bool
526-
| ty::Char
527-
| ty::Int(_)
528-
| ty::Uint(_)
529-
| ty::Float(_)
530-
| ty::Adt(_, _)
531-
| ty::Foreign(_)
532-
| ty::Str
533-
| ty::Array(_, _)
534-
| ty::Slice(_)
535-
| ty::RawPtr(_)
536-
| ty::Ref(_, _, _)
537-
| ty::FnDef(_, _)
538-
| ty::FnPtr(_)
539-
| ty::Dynamic(..)
540-
| ty::Closure(..)
541-
| ty::Coroutine(..)
542-
| ty::CoroutineWitness(..)
543-
| ty::Never
544-
| ty::Tuple(_)
545-
| ty::Param(_)
546-
| ty::Placeholder(..)
547-
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
548-
| ty::Alias(ty::Inherent, _)
549-
| ty::Alias(ty::Weak, _)
550-
| ty::Error(_) => return,
551-
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
552-
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
553-
// Excluding IATs and type aliases here as they don't have meaningful item bounds.
554-
ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty,
555-
};
524+
let _ = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
525+
let mut self_ty = goal.predicate.self_ty();
526+
527+
// For some deeply nested `<T>::A::B::C::D` rigid associated type,
528+
// we should explore the item bounds for all levels, since the
529+
// `associated_type_bounds` feature means that a parent associated
530+
// type may carry bounds for a nested associated type.
531+
loop {
532+
let (kind, alias_ty) = match *self_ty.kind() {
533+
ty::Bool
534+
| ty::Char
535+
| ty::Int(_)
536+
| ty::Uint(_)
537+
| ty::Float(_)
538+
| ty::Adt(_, _)
539+
| ty::Foreign(_)
540+
| ty::Str
541+
| ty::Array(_, _)
542+
| ty::Slice(_)
543+
| ty::RawPtr(_)
544+
| ty::Ref(_, _, _)
545+
| ty::FnDef(_, _)
546+
| ty::FnPtr(_)
547+
| ty::Dynamic(..)
548+
| ty::Closure(..)
549+
| ty::Coroutine(..)
550+
| ty::CoroutineWitness(..)
551+
| ty::Never
552+
| ty::Tuple(_)
553+
| ty::Param(_)
554+
| ty::Placeholder(..)
555+
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
556+
| ty::Alias(ty::Inherent, _)
557+
| ty::Alias(ty::Weak, _)
558+
| ty::Error(_) => break,
559+
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
560+
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
561+
562+
// If we hit infer when normalizing the self type of an alias,
563+
// then bail with ambiguity.
564+
ty::Infer(ty::TyVar(_)) => {
565+
if let Ok(result) = ecx
566+
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
567+
{
568+
candidates
569+
.push(Candidate { source: CandidateSource::AliasBound, result });
570+
}
571+
break;
572+
}
556573

557-
for assumption in
558-
self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
559-
{
560-
match G::consider_alias_bound_candidate(self, goal, assumption) {
561-
Ok(result) => {
562-
candidates.push(Candidate { source: CandidateSource::AliasBound, result })
574+
// Excluding IATs and type aliases here as they don't have meaningful item bounds.
575+
ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
576+
};
577+
578+
for assumption in
579+
ecx.tcx().item_bounds(alias_ty.def_id).instantiate(ecx.tcx(), alias_ty.args)
580+
{
581+
match G::consider_alias_bound_candidate(ecx, goal, assumption) {
582+
Ok(result) => {
583+
candidates
584+
.push(Candidate { source: CandidateSource::AliasBound, result });
585+
}
586+
Err(NoSolution) => {}
587+
}
588+
}
589+
590+
// If we have a projection, check that its self type is a rigid projection.
591+
// If so, continue searching.
592+
if kind == ty::Projection {
593+
let Some(next_self_ty) =
594+
ecx.try_normalize_ty(goal.param_env, alias_ty.self_ty())
595+
else {
596+
if let Ok(result) = ecx
597+
.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
598+
{
599+
candidates
600+
.push(Candidate { source: CandidateSource::AliasBound, result });
601+
}
602+
break;
603+
};
604+
self_ty = next_self_ty;
605+
} else {
606+
break;
563607
}
564-
Err(NoSolution) => (),
565608
}
566-
}
609+
});
567610
}
568611

569612
/// 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+
// borrowck :(
1651+
ambiguous = true;
1652+
ControlFlow::Continue(())
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

+50-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;
@@ -153,11 +156,53 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
153156
_ => return,
154157
}
155158

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

160-
candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx)));
197+
ControlFlow::Continue(())
198+
},
199+
|| {
200+
// On ambiguity.
201+
candidates.ambiguous = true;
202+
ControlFlow::Continue(())
203+
},
204+
);
205+
});
161206
}
162207

163208
/// 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
@@ -153,20 +153,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
153153
self.infcx.instantiate_binder_with_placeholders(trait_predicate).trait_ref;
154154
let placeholder_self_ty = placeholder_trait_predicate.self_ty();
155155
let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate);
156-
let (def_id, args) = match *placeholder_self_ty.kind() {
157-
// Excluding IATs and type aliases here as they don't have meaningful item bounds.
158-
ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
159-
(def_id, args)
160-
}
161-
_ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty),
162-
};
163156

164-
let candidate_predicate =
165-
tcx.item_bounds(def_id).map_bound(|i| i[idx]).instantiate(tcx, args);
157+
let candidate_predicate = self
158+
.for_each_item_bound(
159+
placeholder_self_ty,
160+
|_, clause, clause_idx| {
161+
if clause_idx == idx {
162+
ControlFlow::Break(clause)
163+
} else {
164+
ControlFlow::Continue(())
165+
}
166+
},
167+
|| unreachable!(),
168+
)
169+
.break_value()
170+
.expect("expected to index into clause that exists");
166171
let candidate = candidate_predicate
167172
.as_trait_clause()
168173
.expect("projection candidate is not a trait predicate")
169174
.map_bound(|t| t.trait_ref);
175+
170176
let mut obligations = Vec::new();
171177
let candidate = normalize_with_depth_to(
172178
self,
@@ -185,8 +191,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
185191
.map_err(|_| Unimplemented)
186192
})?);
187193

188-
if let ty::Alias(ty::Projection, ..) = placeholder_self_ty.kind() {
189-
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
194+
// FIXME(compiler-errors): I don't think this is needed.
195+
if let ty::Alias(ty::Projection, alias_ty) = placeholder_self_ty.kind() {
196+
let predicates = tcx.predicates_of(alias_ty.def_id).instantiate_own(tcx, alias_ty.args);
190197
for (predicate, _) in predicates {
191198
let normalized = normalize_with_depth_to(
192199
self,

0 commit comments

Comments
 (0)