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