Skip to content

Commit fa83c10

Browse files
committed
implement compute_alias_eq_goal
1 parent 1f89e2a commit fa83c10

File tree

3 files changed

+163
-14
lines changed

3 files changed

+163
-14
lines changed

compiler/rustc_infer/src/infer/projection.rs

+23-11
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,28 @@ impl<'tcx> InferCtxt<'tcx> {
2121
recursion_depth: usize,
2222
obligations: &mut Vec<PredicateObligation<'tcx>>,
2323
) -> Ty<'tcx> {
24-
let def_id = projection_ty.def_id;
25-
let ty_var = self.next_ty_var(TypeVariableOrigin {
26-
kind: TypeVariableOriginKind::NormalizeProjectionType,
27-
span: self.tcx.def_span(def_id),
28-
});
29-
let projection =
30-
ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, term: ty_var.into() });
31-
let obligation =
32-
Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
33-
obligations.push(obligation);
34-
ty_var
24+
if self.tcx.trait_solver_next() {
25+
// FIXME(-Ztrait-solver=next): Instead of branching here,
26+
// completely change the normalization routine with the new solver.
27+
//
28+
// The new solver correctly handles projection equality so this hack
29+
// is not necessary. if re-enabled it should emit `PredicateKind::AliasEq`
30+
// not `PredicateKind::Clause(Clause::Projection(..))` as in the new solver
31+
// `Projection` is used as `normalizes-to` which will fail for `<T as Trait>::Assoc eq ?0`.
32+
return projection_ty.to_ty(self.tcx);
33+
} else {
34+
let def_id = projection_ty.def_id;
35+
let ty_var = self.next_ty_var(TypeVariableOrigin {
36+
kind: TypeVariableOriginKind::NormalizeProjectionType,
37+
span: self.tcx.def_span(def_id),
38+
});
39+
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
40+
ty::ProjectionPredicate { projection_ty, term: ty_var.into() },
41+
)));
42+
let obligation =
43+
Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
44+
obligations.push(obligation);
45+
ty_var
46+
}
3547
}
3648
}

compiler/rustc_middle/src/ty/mod.rs

+27
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,33 @@ impl<'tcx> Term<'tcx> {
970970
TermKind::Const(c) => c.into(),
971971
}
972972
}
973+
974+
/// This function returns `None` for `AliasKind::Opaque`.
975+
///
976+
/// FIXME: rename `AliasTy` to `AliasTerm` and make sure we correctly
977+
/// deal with constants.
978+
pub fn to_alias_term_no_opaque(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> {
979+
match self.unpack() {
980+
TermKind::Ty(ty) => match ty.kind() {
981+
ty::Alias(kind, alias_ty) => match kind {
982+
AliasKind::Projection => Some(*alias_ty),
983+
AliasKind::Opaque => None,
984+
},
985+
_ => None,
986+
},
987+
TermKind::Const(ct) => match ct.kind() {
988+
ConstKind::Unevaluated(uv) => Some(tcx.mk_alias_ty(uv.def.did, uv.substs)),
989+
_ => None,
990+
},
991+
}
992+
}
993+
994+
pub fn is_infer(&self) -> bool {
995+
match self.unpack() {
996+
TermKind::Ty(ty) => ty.is_ty_or_numeric_infer(),
997+
TermKind::Const(ct) => ct.is_ct_infer(),
998+
}
999+
}
9731000
}
9741001

9751002
const TAG_MASK: usize = 0b11;

compiler/rustc_trait_selection/src/solve/mod.rs

+113-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ mod trait_goals;
4242

4343
pub use fulfill::FulfillmentCtxt;
4444

45+
use self::infcx_ext::InferCtxtExt;
46+
4547
/// A goal is a statement, i.e. `predicate`, we want to prove
4648
/// given some assumptions, i.e. `param_env`.
4749
///
@@ -81,6 +83,21 @@ pub struct Response<'tcx> {
8183
pub certainty: Certainty,
8284
}
8385

86+
trait CanonicalResponseExt {
87+
fn has_no_inference_or_external_constraints(&self) -> bool;
88+
}
89+
90+
impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
91+
fn has_no_inference_or_external_constraints(&self) -> bool {
92+
// so that we get a compile error when regions are supported
93+
// so this code can be checked for being correct
94+
let _: () = self.value.external_constraints.regions;
95+
96+
self.value.var_values.is_identity()
97+
&& self.value.external_constraints.opaque_types.is_empty()
98+
}
99+
}
100+
84101
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
85102
pub enum Certainty {
86103
Yes,
@@ -302,9 +319,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
302319
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
303320
bug!("TypeWellFormedFromEnv is only used for Chalk")
304321
}
305-
ty::PredicateKind::AliasEq(..) => {
306-
// FIXME(deferred_projection_equality)
307-
todo!()
322+
ty::PredicateKind::AliasEq(lhs, rhs) => {
323+
self.compute_alias_eq_goal(Goal { param_env, predicate: (lhs, rhs) })
308324
}
309325
}
310326
} else {
@@ -402,6 +418,63 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
402418
None => self.make_canonical_response(Certainty::AMBIGUOUS),
403419
}
404420
}
421+
422+
#[instrument(level = "debug", skip(self), ret)]
423+
fn compute_alias_eq_goal(
424+
&mut self,
425+
goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>)>,
426+
) -> QueryResult<'tcx> {
427+
let tcx = self.tcx();
428+
429+
let evaluate_normalizes_to = |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other| {
430+
debug!("evaluate_normalizes_to(alias={:?}, other={:?})", alias, other);
431+
let r = ecx.infcx.probe(|_| {
432+
let (_, certainty) = ecx.evaluate_goal(goal.with(
433+
tcx,
434+
ty::Binder::dummy(ty::ProjectionPredicate {
435+
projection_ty: alias,
436+
term: other,
437+
}),
438+
))?;
439+
ecx.make_canonical_response(certainty)
440+
});
441+
debug!("evaluate_normalizes_to(..) -> {:?}", r);
442+
r
443+
};
444+
445+
if goal.predicate.0.is_infer() || goal.predicate.1.is_infer() {
446+
bug!(
447+
"`AliasEq` goal with an infer var on lhs or rhs which should have been instantiated"
448+
);
449+
}
450+
451+
match (
452+
goal.predicate.0.to_alias_term_no_opaque(tcx),
453+
goal.predicate.1.to_alias_term_no_opaque(tcx),
454+
) {
455+
(None, None) => bug!("`AliasEq` goal without an alias on either lhs or rhs"),
456+
(Some(alias), None) => evaluate_normalizes_to(self, alias, goal.predicate.1),
457+
(None, Some(alias)) => evaluate_normalizes_to(self, alias, goal.predicate.0),
458+
(Some(alias_lhs), Some(alias_rhs)) => {
459+
debug!("compute_alias_eq_goal: both sides are aliases");
460+
461+
let mut candidates = Vec::with_capacity(3);
462+
463+
// Evaluate all 3 potential candidates for the alias' being equal
464+
candidates.push(evaluate_normalizes_to(self, alias_lhs, goal.predicate.1));
465+
candidates.push(evaluate_normalizes_to(self, alias_rhs, goal.predicate.0));
466+
candidates.push(self.infcx.probe(|_| {
467+
debug!("compute_alias_eq_goal: alias defids are equal, equating substs");
468+
let nested_goals = self.infcx.eq(goal.param_env, alias_lhs, alias_rhs)?;
469+
self.evaluate_all_and_make_canonical_response(nested_goals)
470+
}));
471+
472+
debug!(?candidates);
473+
474+
self.try_merge_responses(candidates.into_iter())
475+
}
476+
}
477+
}
405478
}
406479

407480
impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -453,6 +526,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
453526
) -> QueryResult<'tcx> {
454527
self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty))
455528
}
529+
530+
fn try_merge_responses(
531+
&mut self,
532+
responses: impl Iterator<Item = QueryResult<'tcx>>,
533+
) -> QueryResult<'tcx> {
534+
let candidates = responses.into_iter().flatten().collect::<Box<[_]>>();
535+
536+
if candidates.is_empty() {
537+
return Err(NoSolution);
538+
}
539+
540+
// FIXME(-Ztreat-solver=next): We should instead try to find a `Certainty::Yes` response with
541+
// a subset of the constraints that all the other responses have.
542+
let one = candidates[0];
543+
if candidates[1..].iter().all(|resp| resp == &one) {
544+
return Ok(one);
545+
}
546+
547+
if let Some(response) = candidates.iter().find(|response| {
548+
response.value.certainty == Certainty::Yes
549+
&& response.has_no_inference_or_external_constraints()
550+
}) {
551+
return Ok(response.clone());
552+
}
553+
554+
let certainty = candidates.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
555+
certainty.unify_and(response.value.certainty)
556+
});
557+
// FIXME(-Ztrait-solver=next): We should take the intersection of the constraints on all the
558+
// responses and use that for the constraints of this ambiguous response.
559+
let response = self.make_canonical_response(certainty);
560+
if let Ok(response) = &response {
561+
assert!(response.has_no_inference_or_external_constraints());
562+
}
563+
564+
response
565+
}
456566
}
457567

458568
#[instrument(level = "debug", skip(infcx), ret)]

0 commit comments

Comments
 (0)