2
2
//! Doing this via a separate goal is called "deferred alias relation" and part
3
3
//! of our more general approach to "lazy normalization".
4
4
//!
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.
7
7
//! These are related further according to the rules below:
8
8
//!
9
9
//! (1.) If we end up with two rigid aliases, then we relate them structurally.
14
14
//!
15
15
//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
16
16
//! 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.
23
17
24
18
use super :: EvalCtxt ;
25
- use rustc_infer:: traits:: query:: NoSolution ;
26
19
use rustc_middle:: traits:: solve:: { Certainty , Goal , QueryResult } ;
27
- use rustc_middle:: ty:: { self , Ty } ;
20
+ use rustc_middle:: ty;
28
21
29
22
impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
30
23
#[ instrument( level = "debug" , skip( self ) , ret) ]
@@ -35,134 +28,58 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
35
28
let tcx = self . tcx ( ) ;
36
29
let Goal { param_env, predicate : ( lhs, rhs, direction) } = goal;
37
30
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
41
38
} ;
42
39
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
46
47
} ;
47
48
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
+
48
55
let variance = match direction {
49
56
ty:: AliasRelationDirection :: Equate => ty:: Variance :: Invariant ,
50
57
ty:: AliasRelationDirection :: Subtype => ty:: Variance :: Covariant ,
51
58
} ;
52
-
53
59
match ( lhs. to_alias_ty ( tcx) , rhs. to_alias_ty ( tcx) ) {
54
60
( None , None ) => {
55
61
self . relate ( param_env, lhs, variance, rhs) ?;
56
62
self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
57
63
}
58
64
59
65
( 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 )
61
77
}
62
- ( None , Some ( alias) ) => self . relate_rigid_alias_non_alias (
63
- param_env,
64
- alias,
65
- variance. xform ( ty:: Variance :: Contravariant ) ,
66
- lhs,
67
- ) ,
68
78
69
79
( Some ( alias_lhs) , Some ( alias_rhs) ) => {
70
80
self . relate ( param_env, alias_lhs, variance, alias_rhs) ?;
71
81
self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
72
82
}
73
83
}
74
84
}
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
- }
168
85
}
0 commit comments