1//! Implements the `AliasRelate` goal, which is used when unifying aliases.
2//! Doing this via a separate goal is called "deferred alias relation" and part
3//! of our more general approach to "lazy normalization".
4//!
5//! This is done by first structurally normalizing both sides of the goal, ending
6//! up in either a concrete type, rigid alias, or an infer variable.
7//! These are related further according to the rules below:
8//!
9//! (1.) If we end up with two rigid aliases, then we relate them structurally.
10//!
11//! (2.) If we end up with an infer var and a rigid alias, then we instantiate
12//! the infer var with the constructor of the alias and then recursively relate
13//! the terms.
14//!
15//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
16//! relate them structurally.
1718use rustc_type_ir::inherent::*;
19use rustc_type_ir::{selfas ty, Interner};
20use tracing::{instrument, trace};
2122use crate::delegate::SolverDelegate;
23use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
2425impl<D, I> EvalCtxt<'_, D>
26where
27D: SolverDelegate<Interner = I>,
28 I: Interner,
29{
30#[instrument(level = "trace", skip(self), ret)]
31pub(super) fn compute_alias_relate_goal(
32&mut self,
33 goal: Goal<I, (I::Term, I::Term, ty::AliasRelationDirection)>,
34 ) -> QueryResult<I> {
35let cx = self.cx();
36let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
3738// Check that the alias-relate goal is reasonable. Writeback for
39 // `coroutine_stalled_predicates` can replace alias terms with
40 // `{type error}` if the alias still contains infer vars, so we also
41 // accept alias-relate goals where one of the terms is an error.
42debug_assert!(
43 lhs.to_alias_term().is_some()
44 || rhs.to_alias_term().is_some()
45 || lhs.is_error()
46 || rhs.is_error()
47 );
4849// Structurally normalize the lhs.
50let lhs = if let Some(alias) = lhs.to_alias_term() {
51let term = self.next_term_infer_of_kind(lhs);
52self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term }));
53 term
54 } else {
55 lhs
56 };
5758// Structurally normalize the rhs.
59let rhs = if let Some(alias) = rhs.to_alias_term() {
60let term = self.next_term_infer_of_kind(rhs);
61self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term }));
62 term
63 } else {
64 rhs
65 };
6667// Add a `make_canonical_response` probe step so that we treat this as
68 // a candidate, even if `try_evaluate_added_goals` bails due to an error.
69 // It's `Certainty::AMBIGUOUS` because this candidate is not "finished",
70 // since equating the normalized terms will lead to additional constraints.
71self.inspect.make_canonical_response(Certainty::AMBIGUOUS);
7273// Apply the constraints.
74self.try_evaluate_added_goals()?;
75let lhs = self.resolve_vars_if_possible(lhs);
76let rhs = self.resolve_vars_if_possible(rhs);
77trace!(?lhs, ?rhs);
7879let variance = match direction {
80 ty::AliasRelationDirection::Equate => ty::Invariant,
81 ty::AliasRelationDirection::Subtype => ty::Covariant,
82 };
83match (lhs.to_alias_term(), rhs.to_alias_term()) {
84 (None, None) => {
85self.relate(param_env, lhs, variance, rhs)?;
86self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
87 }
8889 (Some(alias), None) => {
90self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)?;
91self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
92 }
93 (None, Some(alias)) => {
94self.relate_rigid_alias_non_alias(
95 param_env,
96 alias,
97 variance.xform(ty::Contravariant),
98 lhs,
99 )?;
100self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
101 }
102103 (Some(alias_lhs), Some(alias_rhs)) => {
104self.relate(param_env, alias_lhs, variance, alias_rhs)?;
105self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
106 }
107 }
108 }
109}