16
16
//! relate them structurally.
17
17
18
18
use super :: EvalCtxt ;
19
+ use rustc_data_structures:: fx:: FxHashSet ;
19
20
use rustc_infer:: infer:: InferCtxt ;
21
+ use rustc_middle:: traits:: query:: NoSolution ;
20
22
use rustc_middle:: traits:: solve:: { Certainty , Goal , QueryResult } ;
21
- use rustc_middle:: ty;
23
+ use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
24
+ use rustc_middle:: ty:: { TypeSuperVisitable , TypeVisitable , TypeVisitableExt , TypeVisitor } ;
22
25
23
26
impl < ' tcx > EvalCtxt < ' _ , InferCtxt < ' tcx > > {
24
27
#[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -29,6 +32,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
29
32
let tcx = self . tcx ( ) ;
30
33
let Goal { param_env, predicate : ( lhs, rhs, direction) } = goal;
31
34
35
+ if self . alias_cannot_name_placeholder_in_rigid ( param_env, lhs, rhs)
36
+ || self . alias_cannot_name_placeholder_in_rigid ( param_env, rhs, lhs)
37
+ {
38
+ return Err ( NoSolution ) ;
39
+ }
40
+
32
41
// Structurally normalize the lhs.
33
42
let lhs = if let Some ( alias) = lhs. to_alias_term ( ) {
34
43
let term = self . next_term_infer_of_kind ( lhs) ;
@@ -84,3 +93,105 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
84
93
}
85
94
}
86
95
}
96
+
97
+ enum IgnoreAliases {
98
+ Yes ,
99
+ No ,
100
+ }
101
+
102
+ impl < ' tcx > EvalCtxt < ' _ , InferCtxt < ' tcx > > {
103
+ /// In case a rigid term refers to a placeholder which is not referenced by the
104
+ /// alias, the alias cannot be normalized to that rigid term unless it contains
105
+ /// either inference variables or these placeholders are referenced in a term
106
+ /// of a `Projection`-clause in the environment.
107
+ fn alias_cannot_name_placeholder_in_rigid (
108
+ & mut self ,
109
+ param_env : ty:: ParamEnv < ' tcx > ,
110
+ rigid_term : ty:: Term < ' tcx > ,
111
+ alias : ty:: Term < ' tcx > ,
112
+ ) -> bool {
113
+ // Check that the rigid term is actually rigid.
114
+ if rigid_term. to_alias_term ( ) . is_some ( ) || alias. to_alias_term ( ) . is_none ( ) {
115
+ return false ;
116
+ }
117
+
118
+ // If the alias has any type or const inference variables,
119
+ // do not try to apply the fast path as these inference variables
120
+ // may resolve to something containing placeholders.
121
+ if alias. has_non_region_infer ( ) {
122
+ return false ;
123
+ }
124
+
125
+ let mut referenced_placeholders =
126
+ self . collect_placeholders_in_term ( rigid_term, IgnoreAliases :: Yes ) ;
127
+ for clause in param_env. caller_bounds ( ) {
128
+ match clause. kind ( ) . skip_binder ( ) {
129
+ ty:: ClauseKind :: Projection ( ty:: ProjectionPredicate { term, .. } ) => {
130
+ if term. has_non_region_infer ( ) {
131
+ return false ;
132
+ }
133
+
134
+ let env_term_placeholders =
135
+ self . collect_placeholders_in_term ( term, IgnoreAliases :: No ) ;
136
+ #[ allow( rustc:: potential_query_instability) ]
137
+ referenced_placeholders. retain ( |p| !env_term_placeholders. contains ( p) ) ;
138
+ }
139
+ ty:: ClauseKind :: Trait ( _)
140
+ | ty:: ClauseKind :: TypeOutlives ( _)
141
+ | ty:: ClauseKind :: RegionOutlives ( _)
142
+ | ty:: ClauseKind :: ConstArgHasType ( ..)
143
+ | ty:: ClauseKind :: WellFormed ( _)
144
+ | ty:: ClauseKind :: ConstEvaluatable ( _) => continue ,
145
+ }
146
+ }
147
+
148
+ if referenced_placeholders. is_empty ( ) {
149
+ return false ;
150
+ }
151
+
152
+ let alias_placeholders = self . collect_placeholders_in_term ( alias, IgnoreAliases :: No ) ;
153
+ // If the rigid term references a placeholder not mentioned by the alias,
154
+ // they can never unify.
155
+ !referenced_placeholders. is_subset ( & alias_placeholders)
156
+ }
157
+
158
+ fn collect_placeholders_in_term (
159
+ & mut self ,
160
+ term : ty:: Term < ' tcx > ,
161
+ ignore_aliases : IgnoreAliases ,
162
+ ) -> FxHashSet < ty:: Term < ' tcx > > {
163
+ // Fast path to avoid walking the term.
164
+ if !term. has_placeholders ( ) {
165
+ return Default :: default ( ) ;
166
+ }
167
+
168
+ struct PlaceholderCollector < ' tcx > {
169
+ ignore_aliases : IgnoreAliases ,
170
+ placeholders : FxHashSet < ty:: Term < ' tcx > > ,
171
+ }
172
+ impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for PlaceholderCollector < ' tcx > {
173
+ type Result = ( ) ;
174
+
175
+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) {
176
+ match t. kind ( ) {
177
+ ty:: Placeholder ( _) => drop ( self . placeholders . insert ( t. into ( ) ) ) ,
178
+ ty:: Alias ( ..) if matches ! ( self . ignore_aliases, IgnoreAliases :: Yes ) => { }
179
+ _ => t. super_visit_with ( self ) ,
180
+ }
181
+ }
182
+
183
+ fn visit_const ( & mut self , ct : ty:: Const < ' tcx > ) {
184
+ match ct. kind ( ) {
185
+ ty:: ConstKind :: Placeholder ( _) => drop ( self . placeholders . insert ( ct. into ( ) ) ) ,
186
+ ty:: ConstKind :: Unevaluated ( _) | ty:: ConstKind :: Expr ( _)
187
+ if matches ! ( self . ignore_aliases, IgnoreAliases :: Yes ) => { }
188
+ _ => ct. super_visit_with ( self ) ,
189
+ }
190
+ }
191
+ }
192
+
193
+ let mut visitor = PlaceholderCollector { ignore_aliases, placeholders : Default :: default ( ) } ;
194
+ term. visit_with ( & mut visitor) ;
195
+ visitor. placeholders
196
+ }
197
+ }
0 commit comments