11use std:: assert_matches;
22
33use hir:: Node ;
4- use rustc_data_structures:: fx:: FxIndexSet ;
4+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexSet } ;
55use rustc_hir as hir;
66use rustc_hir:: def:: DefKind ;
77use rustc_hir:: def_id:: { DefId , LocalDefId } ;
88use rustc_hir:: find_attr;
99use rustc_middle:: ty:: {
10- self , GenericPredicates , ImplTraitInTraitData , Ty , TyCtxt , TypeVisitable , TypeVisitor , Upcast ,
10+ self , Binder , Clause , GenericArgKind , GenericArgs , GenericPredicates , ImplTraitInTraitData , Ty ,
11+ TyCtxt , TypeFoldable , TypeSuperFoldable , TypeVisitable , TypeVisitor , Upcast , UpcastFrom ,
1112} ;
1213use rustc_middle:: { bug, span_bug} ;
1314use rustc_span:: { DUMMY_SP , Ident , Span } ;
@@ -30,6 +31,13 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
3031 let mut result = tcx. explicit_predicates_of ( def_id) ;
3132 debug ! ( "predicates_of: explicit_predicates_of({:?}) = {:?}" , def_id, result) ;
3233
34+ if tcx. sess . opts . unstable_opts . strict_projection_item_bounds {
35+ let implied_item_bounds = elaborate_projection_predicates ( tcx, result. predicates . to_vec ( ) ) ;
36+ result. predicates = tcx
37+ . arena
38+ . alloc_from_iter ( result. predicates . into_iter ( ) . copied ( ) . chain ( implied_item_bounds) ) ;
39+ }
40+
3341 let inferred_outlives = tcx. inferred_outlives_of ( def_id) ;
3442 if !inferred_outlives. is_empty ( ) {
3543 debug ! ( "predicates_of: inferred_outlives_of({:?}) = {:?}" , def_id, inferred_outlives, ) ;
@@ -76,6 +84,216 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
7684 debug ! ( "predicates_of({:?}) = {:?}" , def_id, result) ;
7785 result
7886}
87+ // If the term of projection is a generic param, we try to add implied item bounds to predicates.
88+ fn elaborate_projection_predicates < ' tcx > (
89+ tcx : TyCtxt < ' tcx > ,
90+ predicates : Vec < ( Clause < ' tcx > , Span ) > ,
91+ ) -> Vec < ( ty:: Clause < ' tcx > , Span ) > {
92+ // We use a folder rather than instantiation because we need to map `Self::Assoc` to the rhs of
93+ // the projection. Instantiation doesn't do that.
94+ // We need to support higher ranked region and friends in the item bounds.
95+ // However, we can't bind item bounds predicates twice. So we just append the bound vars of each item
96+ // bound to the projection predicate binder and shift bound var indices in this folder.
97+ // See `tests/ui/wf/wf-item-bounds-on-projection-with-binder.rs`.
98+ struct PredicateArgFolder < ' tcx > {
99+ tcx : TyCtxt < ' tcx > ,
100+ ty_mapping : FxHashMap < Ty < ' tcx > , Ty < ' tcx > > ,
101+ region_mapping : FxHashMap < ty:: Region < ' tcx > , ty:: Region < ' tcx > > ,
102+ const_mapping : FxHashMap < ty:: Const < ' tcx > , ty:: Const < ' tcx > > ,
103+ bound_num : usize ,
104+ current_index : ty:: DebruijnIndex ,
105+ }
106+ impl < ' tcx > PredicateArgFolder < ' tcx > {
107+ fn new ( tcx : TyCtxt < ' tcx > , projection : Binder < ' tcx , ty:: ProjectionPredicate < ' tcx > > ) -> Self {
108+ let bound_num = projection. bound_vars ( ) . len ( ) ;
109+ let projection = projection. skip_binder ( ) ;
110+ let mut ty_mapping = FxHashMap :: default ( ) ;
111+ let mut region_mapping = FxHashMap :: default ( ) ;
112+ let mut const_mapping = FxHashMap :: default ( ) ;
113+ let assoc_ty = Ty :: new_alias (
114+ tcx,
115+ ty:: AliasTyKind :: Projection ,
116+ ty:: AliasTy :: new (
117+ tcx,
118+ projection. projection_term . def_id ,
119+ GenericArgs :: identity_for_item ( tcx, projection. projection_term . def_id ) ,
120+ ) ,
121+ ) ;
122+ ty_mapping. insert ( assoc_ty, projection. term . expect_type ( ) ) ;
123+ let target_assoc_args =
124+ GenericArgs :: identity_for_item ( tcx, projection. projection_term . def_id ) ;
125+ for ( target_arg, proj_arg) in
126+ target_assoc_args. into_iter ( ) . zip ( projection. projection_term . args )
127+ {
128+ match ( target_arg. kind ( ) , proj_arg. kind ( ) ) {
129+ ( GenericArgKind :: Lifetime ( r1) , GenericArgKind :: Lifetime ( r2) ) => {
130+ region_mapping. insert ( r1, r2) ;
131+ }
132+ ( GenericArgKind :: Type ( t1) , GenericArgKind :: Type ( t2) ) => {
133+ ty_mapping. insert ( t1, t2) ;
134+ }
135+ ( GenericArgKind :: Const ( c1) , GenericArgKind :: Const ( c2) ) => {
136+ const_mapping. insert ( c1, c2) ;
137+ }
138+ _ => bug ! ( "mismatched generic arg kinds in projection predicate" ) ,
139+ }
140+ }
141+ debug ! (
142+ "elaborate_projection_predicates: ty_mapping = {:?}, region_mapping = {:?}, const_mapping = {:?}" ,
143+ ty_mapping, region_mapping, const_mapping
144+ ) ;
145+ Self {
146+ tcx,
147+ ty_mapping,
148+ region_mapping,
149+ const_mapping,
150+ bound_num,
151+ current_index : ty:: INNERMOST ,
152+ }
153+ }
154+ }
155+ impl < ' tcx > ty:: TypeFolder < TyCtxt < ' tcx > > for PredicateArgFolder < ' tcx > {
156+ fn cx ( & self ) -> TyCtxt < ' tcx > {
157+ self . tcx
158+ }
159+
160+ fn fold_ty ( & mut self , t : Ty < ' tcx > ) -> Ty < ' tcx > {
161+ if let ty:: Bound ( ty:: BoundVarIndexKind :: Bound ( debruijn) , bound_ty) = t. kind ( )
162+ && * debruijn == self . current_index
163+ {
164+ let shifted_bound_var =
165+ ty:: BoundVar :: from_usize ( bound_ty. var . index ( ) + self . bound_num ) ;
166+ return Ty :: new_bound (
167+ self . tcx ,
168+ * debruijn,
169+ ty:: BoundTy { var : shifted_bound_var, kind : bound_ty. kind } ,
170+ ) ;
171+ }
172+ if let Some ( replacement) = self . ty_mapping . get ( & t) {
173+ * replacement
174+ } else {
175+ t. super_fold_with ( self )
176+ }
177+ }
178+
179+ fn fold_region ( & mut self , r : ty:: Region < ' tcx > ) -> ty:: Region < ' tcx > {
180+ if let ty:: RegionKind :: ReBound ( ty:: BoundVarIndexKind :: Bound ( debruijn) , bound_re) =
181+ r. kind ( )
182+ && debruijn == self . current_index
183+ {
184+ let shifted_bound_var =
185+ ty:: BoundVar :: from_usize ( bound_re. var . index ( ) + self . bound_num ) ;
186+ return ty:: Region :: new_bound (
187+ self . tcx ,
188+ debruijn,
189+ ty:: BoundRegion { var : shifted_bound_var, kind : bound_re. kind } ,
190+ ) ;
191+ }
192+ if let Some ( replacement) = self . region_mapping . get ( & r) { * replacement } else { r }
193+ }
194+
195+ fn fold_const ( & mut self , c : ty:: Const < ' tcx > ) -> ty:: Const < ' tcx > {
196+ if let ty:: ConstKind :: Bound ( ty:: BoundVarIndexKind :: Bound ( debruijn) , bound_ct) = c. kind ( )
197+ && debruijn == self . current_index
198+ {
199+ let shifted_bound_var =
200+ ty:: BoundVar :: from_usize ( bound_ct. var . index ( ) + self . bound_num ) ;
201+ return ty:: Const :: new_bound (
202+ self . tcx ,
203+ debruijn,
204+ ty:: BoundConst :: new ( shifted_bound_var) ,
205+ ) ;
206+ }
207+ if let Some ( replacement) = self . const_mapping . get ( & c) { * replacement } else { c }
208+ }
209+
210+ fn fold_binder < T : TypeFoldable < TyCtxt < ' tcx > > > (
211+ & mut self ,
212+ t : Binder < ' tcx , T > ,
213+ ) -> Binder < ' tcx , T > {
214+ self . current_index . shift_in ( 1 ) ;
215+ let folded = t. super_fold_with ( self ) ;
216+ self . current_index . shift_out ( 1 ) ;
217+ folded
218+ }
219+ }
220+
221+ let mut new_preds = Vec :: new ( ) ;
222+ for ( pred, _span) in & predicates {
223+ if let ty:: ClauseKind :: Projection ( proj_pred) = pred. kind ( ) . skip_binder ( )
224+ && let Some ( proj_ty) = proj_pred. term . as_type ( )
225+ {
226+ // We should minimize this to allow the where clause check to be useful.
227+ fn should_add_clause ( t : Ty < ' _ > ) -> bool {
228+ match t. kind ( ) {
229+ ty:: Param ( _) => true ,
230+ ty:: Alias ( ty:: Projection , alias) => alias. args . types ( ) . any ( should_add_clause) ,
231+ _ => false ,
232+ }
233+ }
234+ if !should_add_clause ( proj_ty) {
235+ continue ;
236+ }
237+ debug ! ( "elaborate_projection_predicates: projection predicate = {:?}" , pred) ;
238+ let assoc_bounds = tcx. explicit_item_bounds ( proj_pred. projection_term . def_id ) ;
239+ debug ! ( "elaborate_projection_predicates: original assoc_bounds = {:?}" , assoc_bounds) ;
240+ let mut folder = PredicateArgFolder :: new ( tcx, pred. kind ( ) . rebind ( proj_pred) ) ;
241+ // FIXME: optimize allocation later.
242+ let assoc_bounds: Vec < _ > = assoc_bounds
243+ . skip_binder ( )
244+ . iter ( )
245+ . map ( |( c, span) | {
246+ let concated_bound_vars: Vec < _ > =
247+ pred. kind ( ) . bound_vars ( ) . iter ( ) . chain ( c. kind ( ) . bound_vars ( ) ) . collect ( ) ;
248+ debug ! (
249+ "elaborate_projection_predicates: concated_bound_vars = {:?}" ,
250+ concated_bound_vars
251+ ) ;
252+ (
253+ Binder :: bind_with_vars (
254+ c. kind ( ) . skip_binder ( ) . fold_with ( & mut folder) ,
255+ tcx. mk_bound_variable_kinds ( & concated_bound_vars) ,
256+ ) ,
257+ * span,
258+ )
259+ } )
260+ . filter ( |( c, _) | {
261+ if let ty:: ClauseKind :: Projection ( p) = c. skip_binder ( ) {
262+ if p. projection_term . to_term ( tcx) == p. term {
263+ // No need to add identity projection.
264+ // They cause cycles later.
265+ return false ;
266+ }
267+ // We shouldn't add opposite projection of existing ones.
268+ // They will be normalized to identity projection by each other.
269+ // We also filter out projections which have the same lhs with existing
270+ // projections.
271+ // They cause ambiguity in normalization.
272+ if predicates. iter ( ) . any ( |( existing_c, _) | {
273+ if let ty:: ClauseKind :: Projection ( existing_p) =
274+ existing_c. kind ( ) . skip_binder ( )
275+ {
276+ return p. projection_term == existing_p. projection_term
277+ || ( existing_p. projection_term . to_term ( tcx) == p. term
278+ && existing_p. term == p. projection_term . to_term ( tcx) ) ;
279+ }
280+ false
281+ } ) {
282+ return false ;
283+ }
284+ }
285+ true
286+ } )
287+ . collect ( ) ;
288+ debug ! ( "elaborate_projection_predicates: replaced assoc_bounds = {:?}" , assoc_bounds) ;
289+ let assoc_bounds: Vec < _ > =
290+ assoc_bounds. into_iter ( ) . map ( |( c, s) | ( Clause :: upcast_from ( c, tcx) , s) ) . collect ( ) ;
291+ debug ! ( "elaborate_projection_predicates: upcasted assoc_bounds = {:?}" , assoc_bounds) ;
292+ new_preds. extend ( assoc_bounds) ;
293+ }
294+ }
295+ new_preds
296+ }
79297
80298/// Returns a list of user-specified type predicates for the definition with ID `def_id`.
81299/// N.B., this does not include any implied/inferred constraints.
0 commit comments