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