1616//! relate them structurally.
1717
1818use super :: EvalCtxt ;
19+ use rustc_data_structures:: fx:: FxHashSet ;
1920use rustc_infer:: infer:: InferCtxt ;
21+ use rustc_middle:: traits:: query:: NoSolution ;
2022use 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 } ;
2225
2326impl < ' tcx > EvalCtxt < ' _ , InferCtxt < ' tcx > > {
2427 #[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -30,6 +33,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
3033 let Goal { param_env, predicate : ( lhs, rhs, direction) } = goal;
3134 debug_assert ! ( lhs. to_alias_term( ) . is_some( ) || rhs. to_alias_term( ) . is_some( ) ) ;
3235
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+
3342 // Structurally normalize the lhs.
3443 let lhs = if let Some ( alias) = lhs. to_alias_term ( ) {
3544 let term = self . next_term_infer_of_kind ( lhs) ;
@@ -85,3 +94,105 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
8594 }
8695 }
8796}
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