Skip to content

Commit 47fe867

Browse files
generate obligations instead of reporting ambiguity
A new mode of type relating is introduced so that obligations are generated instead of outright rejecting projection clauses. This allows project candidates that are sourced from more than one predicates, such as supertrait bounds, provided that they do not conflict each other.
1 parent afa859f commit 47fe867

File tree

4 files changed

+185
-27
lines changed

4 files changed

+185
-27
lines changed

compiler/rustc_infer/src/infer/at.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,34 @@ impl<'a, 'tcx> At<'a, 'tcx> {
238238
}
239239
}
240240

241+
// FIXME(arbitrary_self_types): remove this interface
242+
// when the new solver is stabilised.
243+
/// Almost like `eq_trace` except this type relating procedure will
244+
/// also generate the obligations arising from equating projection
245+
/// candidates.
246+
pub fn eq_with_proj<T>(
247+
self,
248+
define_opaque_types: DefineOpaqueTypes,
249+
expected: T,
250+
actual: T,
251+
) -> InferResult<'tcx, ()>
252+
where
253+
T: ToTrace<'tcx>,
254+
{
255+
assert!(!self.infcx.next_trait_solver);
256+
let trace = ToTrace::to_trace(self.cause, expected, actual);
257+
let mut op = TypeRelating::new(
258+
self.infcx,
259+
trace,
260+
self.param_env,
261+
define_opaque_types,
262+
ty::Invariant,
263+
)
264+
.through_projections(true);
265+
op.relate(expected, actual)?;
266+
Ok(InferOk { value: (), obligations: op.into_obligations() })
267+
}
268+
241269
pub fn relate<T>(
242270
self,
243271
define_opaque_types: DefineOpaqueTypes,
@@ -369,6 +397,18 @@ impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> {
369397
}
370398
}
371399

400+
impl<'tcx> ToTrace<'tcx> for ty::Binder<'tcx, ty::Term<'tcx>> {
401+
fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
402+
TypeTrace {
403+
cause: cause.clone(),
404+
values: ValuePairs::Terms(ExpectedFound {
405+
expected: a.skip_binder(),
406+
found: b.skip_binder(),
407+
}),
408+
}
409+
}
410+
}
411+
372412
impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {
373413
fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
374414
TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }

compiler/rustc_infer/src/infer/relate/type_relating.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ pub(crate) struct TypeRelating<'infcx, 'tcx> {
2222
param_env: ty::ParamEnv<'tcx>,
2323
define_opaque_types: DefineOpaqueTypes,
2424

25+
/// This indicates whether the relation should
26+
/// report obligations arising from equating aliasing terms
27+
/// involving associated types, instead of rejection.
28+
through_projections: bool,
29+
2530
// Mutable fields.
2631
ambient_variance: ty::Variance,
2732
obligations: PredicateObligations<'tcx>,
@@ -67,9 +72,15 @@ impl<'infcx, 'tcx> TypeRelating<'infcx, 'tcx> {
6772
ambient_variance,
6873
obligations: PredicateObligations::new(),
6974
cache: Default::default(),
75+
through_projections: false,
7076
}
7177
}
7278

79+
pub(crate) fn through_projections(mut self, walk_through: bool) -> Self {
80+
self.through_projections = walk_through;
81+
self
82+
}
83+
7384
pub(crate) fn into_obligations(self) -> PredicateObligations<'tcx> {
7485
self.obligations
7586
}
@@ -128,6 +139,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
128139
if self.cache.contains(&(self.ambient_variance, a, b)) {
129140
return Ok(a);
130141
}
142+
let mut relate_result = a;
131143

132144
match (a.kind(), b.kind()) {
133145
(&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
@@ -201,14 +213,42 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
201213
)?);
202214
}
203215

216+
(
217+
ty::Alias(ty::Projection | ty::Opaque, _),
218+
ty::Alias(ty::Projection | ty::Opaque, _),
219+
) => {
220+
super_combine_tys(infcx, self, a, b)?;
221+
}
222+
223+
(&ty::Alias(ty::Projection, ty::AliasTy { def_id, args, .. }), _)
224+
if matches!(self.ambient_variance, ty::Variance::Invariant)
225+
&& self.through_projections =>
226+
{
227+
self.register_predicates([ty::ProjectionPredicate {
228+
projection_term: ty::AliasTerm::new(self.cx(), def_id, args),
229+
term: b.into(),
230+
}]);
231+
relate_result = b;
232+
}
233+
234+
(_, &ty::Alias(ty::Projection, ty::AliasTy { def_id, args, .. }))
235+
if matches!(self.ambient_variance, ty::Variance::Invariant)
236+
&& self.through_projections =>
237+
{
238+
self.register_predicates([ty::ProjectionPredicate {
239+
projection_term: ty::AliasTerm::new(self.cx(), def_id, args),
240+
term: a.into(),
241+
}]);
242+
}
243+
204244
_ => {
205245
super_combine_tys(infcx, self, a, b)?;
206246
}
207247
}
208248

209249
assert!(self.cache.insert((self.ambient_variance, a, b)));
210250

211-
Ok(a)
251+
Ok(relate_result)
212252
}
213253

214254
#[instrument(skip(self), level = "trace")]

compiler/rustc_trait_selection/src/traits/project.rs

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Code for projecting associated types out of trait references.
22
3+
use std::iter::Extend;
34
use std::ops::ControlFlow;
45

56
use rustc_data_structures::sso::SsoHashSet;
@@ -65,6 +66,7 @@ enum ProjectionCandidate<'tcx> {
6566
Select(Selection<'tcx>),
6667
}
6768

69+
#[derive(Debug)]
6870
enum ProjectionCandidateSet<'tcx> {
6971
None,
7072
Single(ProjectionCandidate<'tcx>),
@@ -648,15 +650,26 @@ fn project<'cx, 'tcx>(
648650
}
649651

650652
let mut candidates = ProjectionCandidateSet::None;
653+
let mut derived_obligations = PredicateObligations::default();
651654

652655
// Make sure that the following procedures are kept in order. ParamEnv
653656
// needs to be first because it has highest priority, and Select checks
654657
// the return value of push_candidate which assumes it's ran at last.
655-
assemble_candidates_from_param_env(selcx, obligation, &mut candidates);
658+
assemble_candidates_from_param_env(
659+
selcx,
660+
obligation,
661+
&mut candidates,
662+
&mut derived_obligations,
663+
);
656664

657665
assemble_candidates_from_trait_def(selcx, obligation, &mut candidates);
658666

659-
assemble_candidates_from_object_ty(selcx, obligation, &mut candidates);
667+
assemble_candidates_from_object_ty(
668+
selcx,
669+
obligation,
670+
&mut candidates,
671+
&mut derived_obligations,
672+
);
660673

661674
if let ProjectionCandidateSet::Single(ProjectionCandidate::Object(_)) = candidates {
662675
// Avoid normalization cycle from selection (see
@@ -669,7 +682,13 @@ fn project<'cx, 'tcx>(
669682

670683
match candidates {
671684
ProjectionCandidateSet::Single(candidate) => {
672-
confirm_candidate(selcx, obligation, candidate)
685+
confirm_candidate(selcx, obligation, candidate).map(move |proj| {
686+
if let Projected::Progress(progress) = proj {
687+
Projected::Progress(progress.with_addl_obligations(derived_obligations))
688+
} else {
689+
proj
690+
}
691+
})
673692
}
674693
ProjectionCandidateSet::None => {
675694
let tcx = selcx.tcx();
@@ -691,14 +710,15 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>(
691710
selcx: &mut SelectionContext<'cx, 'tcx>,
692711
obligation: &ProjectionTermObligation<'tcx>,
693712
candidate_set: &mut ProjectionCandidateSet<'tcx>,
713+
derived_obligations: &mut impl Extend<PredicateObligation<'tcx>>,
694714
) {
695715
assemble_candidates_from_predicates(
696716
selcx,
697717
obligation,
698718
candidate_set,
699719
ProjectionCandidate::ParamEnv,
700720
obligation.param_env.caller_bounds().iter(),
701-
false,
721+
derived_obligations,
702722
);
703723
}
704724

@@ -712,6 +732,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>(
712732
/// ```
713733
///
714734
/// Here, for example, we could conclude that the result is `i32`.
735+
#[instrument(level = "debug", skip(selcx))]
715736
fn assemble_candidates_from_trait_def<'cx, 'tcx>(
716737
selcx: &mut SelectionContext<'cx, 'tcx>,
717738
obligation: &ProjectionTermObligation<'tcx>,
@@ -774,6 +795,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
774795
selcx: &mut SelectionContext<'cx, 'tcx>,
775796
obligation: &ProjectionTermObligation<'tcx>,
776797
candidate_set: &mut ProjectionCandidateSet<'tcx>,
798+
derived_obligations: &mut impl Extend<PredicateObligation<'tcx>>,
777799
) {
778800
debug!("assemble_candidates_from_object_ty(..)");
779801

@@ -806,21 +828,18 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
806828
candidate_set,
807829
ProjectionCandidate::Object,
808830
env_predicates,
809-
false,
831+
derived_obligations,
810832
);
811833
}
812834

813-
#[instrument(
814-
level = "debug",
815-
skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates)
816-
)]
835+
#[instrument(level = "debug", skip(selcx, env_predicates, derived_obligations))]
817836
fn assemble_candidates_from_predicates<'cx, 'tcx>(
818837
selcx: &mut SelectionContext<'cx, 'tcx>,
819838
obligation: &ProjectionTermObligation<'tcx>,
820839
candidate_set: &mut ProjectionCandidateSet<'tcx>,
821840
ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'tcx>,
822841
env_predicates: impl Iterator<Item = ty::Clause<'tcx>>,
823-
potentially_unnormalized_candidates: bool,
842+
derived_obligations: &mut impl Extend<PredicateObligation<'tcx>>,
824843
) {
825844
let infcx = selcx.infcx;
826845
let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
@@ -838,28 +857,39 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
838857
continue;
839858
}
840859

841-
let is_match = infcx.probe(|_| {
842-
selcx.match_projection_projections(
843-
obligation,
844-
data,
845-
potentially_unnormalized_candidates,
846-
)
847-
});
860+
let is_match =
861+
infcx.probe(|_| selcx.match_projection_projections(obligation, data, false));
848862

849863
match is_match {
850864
ProjectionMatchesProjection::Yes => {
851-
candidate_set.push_candidate(ctor(data));
852-
853-
if potentially_unnormalized_candidates
854-
&& !obligation.predicate.has_non_region_infer()
865+
debug!(?data, "push");
866+
if let ProjectionCandidateSet::Single(
867+
ProjectionCandidate::ParamEnv(proj)
868+
| ProjectionCandidate::Object(proj)
869+
| ProjectionCandidate::TraitDef(proj),
870+
) = candidate_set
855871
{
856-
// HACK: Pick the first trait def candidate for a fully
857-
// inferred predicate. This is to allow duplicates that
858-
// differ only in normalization.
859-
return;
872+
match infcx.commit_if_ok(|_| {
873+
infcx.at(&obligation.cause, obligation.param_env).eq_with_proj(
874+
DefineOpaqueTypes::No,
875+
data.term(),
876+
proj.term(),
877+
)
878+
}) {
879+
Ok(InferOk { value: (), obligations }) => {
880+
derived_obligations.extend(obligations);
881+
}
882+
Err(e) => {
883+
debug!(?e, "refuse to unify candidates");
884+
candidate_set.push_candidate(ctor(data));
885+
}
886+
}
887+
} else {
888+
candidate_set.push_candidate(ctor(data));
860889
}
861890
}
862891
ProjectionMatchesProjection::Ambiguous => {
892+
debug!("mark ambiguous");
863893
candidate_set.mark_ambiguous();
864894
}
865895
ProjectionMatchesProjection::No => {}
@@ -868,7 +898,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
868898
}
869899
}
870900

871-
#[instrument(level = "debug", skip(selcx, obligation, candidate_set))]
901+
#[instrument(level = "debug", skip(selcx))]
872902
fn assemble_candidates_from_impls<'cx, 'tcx>(
873903
selcx: &mut SelectionContext<'cx, 'tcx>,
874904
obligation: &ProjectionTermObligation<'tcx>,
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//@ revisions: traditional next_solver
2+
//@ [next_solver] compile-flags: -Znext-solver
3+
//@ check-pass
4+
5+
use std::marker::PhantomData;
6+
7+
pub trait Receiver {
8+
type Target: ?Sized;
9+
}
10+
11+
pub trait Deref: Receiver<Target = <Self as Deref>::Target> {
12+
type Target: ?Sized;
13+
fn deref(&self) -> &<Self as Deref>::Target;
14+
}
15+
16+
impl<T: Deref> Receiver for T {
17+
type Target = <T as Deref>::Target;
18+
}
19+
20+
// ===
21+
pub struct Type<Id, T>(PhantomData<(Id, T)>);
22+
pub struct AliasRef<Id, T: TypePtr<Id = Id>>(PhantomData<(Id, T)>);
23+
24+
pub trait TypePtr: Deref<Target = Type<<Self as TypePtr>::Id, Self>> + Sized {
25+
// ^ the impl head here provides the first candidate
26+
// <T as Deref>::Target := Type<<T as TypePtr>::Id>
27+
type Id;
28+
}
29+
30+
pub struct Alias<Id, T>(PhantomData<(Id, T)>);
31+
32+
impl<Id, T> Deref for Alias<Id, T>
33+
where
34+
T: TypePtr<Id = Id> + Deref<Target = Type<Id, T>>,
35+
// ^ the impl head here provides the second candidate
36+
// <T as Deref>::Target := Type<Id, T>
37+
// and additionally a normalisation is mandatory due to
38+
// the following supertrait relation trait
39+
// Deref: Receiver<Target = <Self as Deref>::Target>
40+
{
41+
type Target = AliasRef<Id, T>;
42+
43+
fn deref(&self) -> &<Self as Deref>::Target {
44+
todo!()
45+
}
46+
}
47+
48+
fn main() {}

0 commit comments

Comments
 (0)