@@ -902,9 +902,10 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
902
902
tcx. impl_trait_refs . borrow_mut ( ) . insert ( it. id , trait_ref) ;
903
903
}
904
904
905
- enforce_impl_ty_params_are_constrained ( tcx,
906
- generics,
907
- local_def ( it. id ) ) ;
905
+ enforce_impl_params_are_constrained ( tcx,
906
+ generics,
907
+ local_def ( it. id ) ,
908
+ impl_items) ;
908
909
} ,
909
910
ast:: ItemTrait ( _, _, _, ref trait_items) => {
910
911
let trait_def = trait_def_of_item ( ccx, it) ;
@@ -2188,9 +2189,10 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
2188
2189
}
2189
2190
2190
2191
/// Checks that all the type parameters on an impl
2191
- fn enforce_impl_ty_params_are_constrained < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
2192
- ast_generics : & ast:: Generics ,
2193
- impl_def_id : ast:: DefId )
2192
+ fn enforce_impl_params_are_constrained < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
2193
+ ast_generics : & ast:: Generics ,
2194
+ impl_def_id : ast:: DefId ,
2195
+ impl_items : & [ P < ast:: ImplItem > ] )
2194
2196
{
2195
2197
let impl_scheme = ty:: lookup_item_type ( tcx, impl_def_id) ;
2196
2198
let impl_predicates = ty:: lookup_predicates ( tcx, impl_def_id) ;
@@ -2215,10 +2217,66 @@ fn enforce_impl_ty_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
2215
2217
idx : index as u32 ,
2216
2218
name : ty_param. ident . name } ;
2217
2219
if !input_parameters. contains ( & ctp:: Parameter :: Type ( param_ty) ) {
2218
- span_err ! ( tcx. sess, ty_param. span, E0207 ,
2219
- "the type parameter `{}` is not constrained by the \
2220
- impl trait, self type, or predicates",
2221
- param_ty. user_string( tcx) ) ;
2220
+ report_unused_parameter ( tcx, ty_param. span , "type" , & param_ty. user_string ( tcx) ) ;
2221
+ }
2222
+ }
2223
+
2224
+ // Every lifetime used in an associated type must be constrained.
2225
+
2226
+ let lifetimes_in_associated_types: HashSet < _ > =
2227
+ impl_items. iter ( )
2228
+ . filter_map ( |item| match item. node {
2229
+ ast:: TypeImplItem ( ..) => Some ( ty:: node_id_to_type ( tcx, item. id ) ) ,
2230
+ ast:: MethodImplItem ( ..) | ast:: MacImplItem ( ..) => None ,
2231
+ } )
2232
+ . flat_map ( |ty| ctp:: parameters_for_type ( ty) . into_iter ( ) )
2233
+ . filter_map ( |p| match p {
2234
+ ctp:: Parameter :: Type ( _) => None ,
2235
+ ctp:: Parameter :: Region ( r) => Some ( r) ,
2236
+ } )
2237
+ . collect ( ) ;
2238
+
2239
+ for ( index, lifetime_def) in ast_generics. lifetimes . iter ( ) . enumerate ( ) {
2240
+ let region = ty:: EarlyBoundRegion { param_id : lifetime_def. lifetime . id ,
2241
+ space : TypeSpace ,
2242
+ index : index as u32 ,
2243
+ name : lifetime_def. lifetime . name } ;
2244
+ if
2245
+ lifetimes_in_associated_types. contains ( & region) && // (*)
2246
+ !input_parameters. contains ( & ctp:: Parameter :: Region ( region) )
2247
+ {
2248
+ report_unused_parameter ( tcx, lifetime_def. lifetime . span ,
2249
+ "lifetime" , & region. name . user_string ( tcx) ) ;
2222
2250
}
2223
2251
}
2252
+
2253
+ // (*) This is a horrible concession to reality. I think it'd be
2254
+ // better to just ban unconstrianed lifetimes outright, but in
2255
+ // practice people do non-hygenic macros like:
2256
+ //
2257
+ // ```
2258
+ // macro_rules! __impl_slice_eq1 {
2259
+ // ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
2260
+ // impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
2261
+ // ....
2262
+ // }
2263
+ // }
2264
+ // }
2265
+ // ```
2266
+ //
2267
+ // In a concession to backwards compatbility, we continue to
2268
+ // permit those, so long as the lifetimes aren't used in
2269
+ // associated types. I believe this is sound, because lifetimes
2270
+ // used elsewhere are not projected back out.
2271
+ }
2272
+
2273
+ fn report_unused_parameter ( tcx : & ty:: ctxt ,
2274
+ span : Span ,
2275
+ kind : & str ,
2276
+ name : & str )
2277
+ {
2278
+ span_err ! ( tcx. sess, span, E0207 ,
2279
+ "the {} parameter `{}` is not constrained by the \
2280
+ impl trait, self type, or predicates",
2281
+ kind, name) ;
2224
2282
}
0 commit comments