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) ]
@@ -29,6 +32,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
2932 let tcx = self . tcx ( ) ;
3033 let Goal { param_env, predicate : ( lhs, rhs, direction) } = goal;
3134
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+
3241 // Structurally normalize the lhs.
3342 let lhs = if let Some ( alias) = lhs. to_alias_term ( ) {
3443 let term = self . next_term_infer_of_kind ( lhs) ;
@@ -84,3 +93,105 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
8493 }
8594 }
8695}
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