1616//! relate them structurally.
1717
1818use super :: EvalCtxt ;
19+ use rustc_data_structures:: fx:: FxHashSet ;
20+ use rustc_middle:: traits:: query:: NoSolution ;
1921use rustc_middle:: traits:: solve:: { Certainty , Goal , QueryResult } ;
20- use rustc_middle:: ty;
22+ use rustc_middle:: ty:: TypeVisitor ;
23+ use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
24+ use rustc_middle:: ty:: { TypeSuperVisitable , TypeVisitable , TypeVisitableExt } ;
2125
2226impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
2327 #[ instrument( level = "debug" , skip( self ) , ret) ]
@@ -28,6 +32,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
2832 let tcx = self . tcx ( ) ;
2933 let Goal { param_env, predicate : ( lhs, rhs, direction) } = goal;
3034
35+ if self . fast_reject_unnameable_rigid_term ( param_env, lhs, rhs)
36+ && self . fast_reject_unnameable_rigid_term ( param_env, rhs, lhs)
37+ {
38+ return Err ( NoSolution ) ;
39+ }
40+
3141 // Structurally normalize the lhs.
3242 let lhs = if let Some ( alias) = lhs. to_alias_ty ( self . tcx ( ) ) {
3343 let term = self . next_term_infer_of_kind ( lhs) ;
@@ -83,3 +93,106 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
8393 }
8494 }
8595}
96+
97+ enum IgnoreAliases {
98+ Yes ,
99+ No ,
100+ }
101+
102+ impl < ' tcx > EvalCtxt < ' _ , ' 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 fast_reject_unnameable_rigid_term (
108+ & mut self ,
109+ param_env : ty:: ParamEnv < ' tcx > ,
110+ rigid_term : ty:: Term < ' tcx > ,
111+ alias : ty:: Term < ' tcx > ,
112+ ) -> bool {
113+ let tcx = self . tcx ( ) ;
114+ // Check that the rigid term is actually rigid.
115+ if rigid_term. to_alias_ty ( tcx) . is_some ( ) || alias. to_alias_ty ( tcx) . 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