22//! Doing this via a separate goal is called "deferred alias relation" and part
33//! of our more general approach to "lazy normalization".
44//!
5- //! This is done by first normalizing both sides of the goal, ending up in
6- //! either a concrete type, rigid alias, or an infer variable.
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.
77//! These are related further according to the rules below:
88//!
99//! (1.) If we end up with two rigid aliases, then we relate them structurally.
1414//!
1515//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
1616//! relate them structurally.
17- //!
18- //! Subtle: when relating an opaque to another type, we emit a
19- //! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque.
20- //! This nested goal starts out as ambiguous and does not actually define the opaque.
21- //! However, if `?fresh_var` ends up geteting equated to another type, we retry the
22- //! `NormalizesTo` goal, at which point the opaque is actually defined.
2317
2418use super :: EvalCtxt ;
25- use rustc_infer:: traits:: query:: NoSolution ;
2619use rustc_middle:: traits:: solve:: { Certainty , Goal , QueryResult } ;
27- use rustc_middle:: ty:: { self , Ty } ;
20+ use rustc_middle:: ty;
2821
2922impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
3023 #[ instrument( level = "debug" , skip( self ) , ret) ]
@@ -35,134 +28,58 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
3528 let tcx = self . tcx ( ) ;
3629 let Goal { param_env, predicate : ( lhs, rhs, direction) } = goal;
3730
38- let Some ( lhs) = self . try_normalize_term ( param_env, lhs) ? else {
39- return self
40- . evaluate_added_goals_and_make_canonical_response ( Certainty :: overflow ( true ) ) ;
31+ // Structurally normalize the lhs.
32+ let lhs = if let Some ( alias) = lhs. to_alias_ty ( self . tcx ( ) ) {
33+ let term = self . next_term_infer_of_kind ( lhs) ;
34+ self . add_normalizes_to_goal ( goal. with ( tcx, ty:: NormalizesTo { alias, term } ) ) ;
35+ term
36+ } else {
37+ lhs
4138 } ;
4239
43- let Some ( rhs) = self . try_normalize_term ( param_env, rhs) ? else {
44- return self
45- . evaluate_added_goals_and_make_canonical_response ( Certainty :: overflow ( true ) ) ;
40+ // Structurally normalize the rhs.
41+ let rhs = if let Some ( alias) = rhs. to_alias_ty ( self . tcx ( ) ) {
42+ let term = self . next_term_infer_of_kind ( rhs) ;
43+ self . add_normalizes_to_goal ( goal. with ( tcx, ty:: NormalizesTo { alias, term } ) ) ;
44+ term
45+ } else {
46+ rhs
4647 } ;
4748
49+ // Apply the constraints.
50+ self . try_evaluate_added_goals ( ) ?;
51+ let lhs = self . resolve_vars_if_possible ( lhs) ;
52+ let rhs = self . resolve_vars_if_possible ( rhs) ;
53+ debug ! ( ?lhs, ?rhs) ;
54+
4855 let variance = match direction {
4956 ty:: AliasRelationDirection :: Equate => ty:: Variance :: Invariant ,
5057 ty:: AliasRelationDirection :: Subtype => ty:: Variance :: Covariant ,
5158 } ;
52-
5359 match ( lhs. to_alias_ty ( tcx) , rhs. to_alias_ty ( tcx) ) {
5460 ( None , None ) => {
5561 self . relate ( param_env, lhs, variance, rhs) ?;
5662 self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
5763 }
5864
5965 ( Some ( alias) , None ) => {
60- self . relate_rigid_alias_non_alias ( param_env, alias, variance, rhs)
66+ self . relate_rigid_alias_non_alias ( param_env, alias, variance, rhs) ?;
67+ self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
68+ }
69+ ( None , Some ( alias) ) => {
70+ self . relate_rigid_alias_non_alias (
71+ param_env,
72+ alias,
73+ variance. xform ( ty:: Variance :: Contravariant ) ,
74+ lhs,
75+ ) ?;
76+ self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
6177 }
62- ( None , Some ( alias) ) => self . relate_rigid_alias_non_alias (
63- param_env,
64- alias,
65- variance. xform ( ty:: Variance :: Contravariant ) ,
66- lhs,
67- ) ,
6878
6979 ( Some ( alias_lhs) , Some ( alias_rhs) ) => {
7080 self . relate ( param_env, alias_lhs, variance, alias_rhs) ?;
7181 self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
7282 }
7383 }
7484 }
75-
76- /// Relate a rigid alias with another type. This is the same as
77- /// an ordinary relate except that we treat the outer most alias
78- /// constructor as rigid.
79- #[ instrument( level = "debug" , skip( self , param_env) , ret) ]
80- fn relate_rigid_alias_non_alias (
81- & mut self ,
82- param_env : ty:: ParamEnv < ' tcx > ,
83- alias : ty:: AliasTy < ' tcx > ,
84- variance : ty:: Variance ,
85- term : ty:: Term < ' tcx > ,
86- ) -> QueryResult < ' tcx > {
87- // NOTE: this check is purely an optimization, the structural eq would
88- // always fail if the term is not an inference variable.
89- if term. is_infer ( ) {
90- let tcx = self . tcx ( ) ;
91- // We need to relate `alias` to `term` treating only the outermost
92- // constructor as rigid, relating any contained generic arguments as
93- // normal. We do this by first structurally equating the `term`
94- // with the alias constructor instantiated with unconstrained infer vars,
95- // and then relate this with the whole `alias`.
96- //
97- // Alternatively we could modify `Equate` for this case by adding another
98- // variant to `StructurallyRelateAliases`.
99- let identity_args = self . fresh_args_for_item ( alias. def_id ) ;
100- let rigid_ctor = ty:: AliasTy :: new ( tcx, alias. def_id , identity_args) ;
101- self . eq_structurally_relating_aliases ( param_env, term, rigid_ctor. to_ty ( tcx) . into ( ) ) ?;
102- self . eq ( param_env, alias, rigid_ctor) ?;
103- self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
104- } else {
105- Err ( NoSolution )
106- }
107- }
108-
109- // FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var.
110- /// Normalize the `term` to equate it later.
111- #[ instrument( level = "debug" , skip( self , param_env) , ret) ]
112- fn try_normalize_term (
113- & mut self ,
114- param_env : ty:: ParamEnv < ' tcx > ,
115- term : ty:: Term < ' tcx > ,
116- ) -> Result < Option < ty:: Term < ' tcx > > , NoSolution > {
117- match term. unpack ( ) {
118- ty:: TermKind :: Ty ( ty) => {
119- Ok ( self . try_normalize_ty_recur ( param_env, 0 , ty) . map ( Into :: into) )
120- }
121- ty:: TermKind :: Const ( _) => {
122- if let Some ( alias) = term. to_alias_ty ( self . tcx ( ) ) {
123- let term = self . next_term_infer_of_kind ( term) ;
124- self . add_normalizes_to_goal ( Goal :: new (
125- self . tcx ( ) ,
126- param_env,
127- ty:: NormalizesTo { alias, term } ,
128- ) ) ;
129- self . try_evaluate_added_goals ( ) ?;
130- Ok ( Some ( self . resolve_vars_if_possible ( term) ) )
131- } else {
132- Ok ( Some ( term) )
133- }
134- }
135- }
136- }
137-
138- #[ instrument( level = "debug" , skip( self , param_env) , ret) ]
139- fn try_normalize_ty_recur (
140- & mut self ,
141- param_env : ty:: ParamEnv < ' tcx > ,
142- depth : usize ,
143- ty : Ty < ' tcx > ,
144- ) -> Option < Ty < ' tcx > > {
145- if !self . tcx ( ) . recursion_limit ( ) . value_within_limit ( depth) {
146- return None ;
147- }
148-
149- let ty:: Alias ( _, alias) = * ty. kind ( ) else {
150- return Some ( ty) ;
151- } ;
152-
153- match self . commit_if_ok ( |this| {
154- let tcx = this. tcx ( ) ;
155- let normalized_ty = this. next_ty_infer ( ) ;
156- this. add_normalizes_to_goal ( Goal :: new (
157- tcx,
158- param_env,
159- ty:: NormalizesTo { alias, term : normalized_ty. into ( ) } ,
160- ) ) ;
161- this. try_evaluate_added_goals ( ) ?;
162- Ok ( this. resolve_vars_if_possible ( normalized_ty) )
163- } ) {
164- Ok ( ty) => self . try_normalize_ty_recur ( param_env, depth + 1 , ty) ,
165- Err ( NoSolution ) => Some ( ty) ,
166- }
167- }
16885}
0 commit comments