@@ -2,7 +2,9 @@ use super::potentially_plural_count;
22use crate :: errors:: LifetimesOrBoundsMismatchOnTrait ;
33use hir:: def_id:: { DefId , LocalDefId } ;
44use rustc_data_structures:: fx:: { FxHashMap , FxIndexSet } ;
5- use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticId , ErrorGuaranteed } ;
5+ use rustc_errors:: {
6+ pluralize, struct_span_err, Applicability , DiagnosticId , ErrorGuaranteed , MultiSpan ,
7+ } ;
68use rustc_hir as hir;
79use rustc_hir:: def:: { DefKind , Res } ;
810use rustc_hir:: intravisit;
@@ -320,15 +322,6 @@ fn compare_method_predicate_entailment<'tcx>(
320322 ty:: Binder :: dummy ( ty:: PredicateKind :: WellFormed ( unnormalized_impl_fty. into ( ) ) ) ,
321323 ) ) ;
322324 }
323- let emit_implied_wf_lint = || {
324- infcx. tcx . struct_span_lint_hir (
325- rustc_session:: lint:: builtin:: IMPLIED_BOUNDS_ENTAILMENT ,
326- impl_m_hir_id,
327- infcx. tcx . def_span ( impl_m. def_id ) ,
328- "impl method assumes more implied bounds than the corresponding trait method" ,
329- |lint| lint,
330- ) ;
331- } ;
332325
333326 // Check that all obligations are satisfied by the implementation's
334327 // version.
@@ -346,7 +339,7 @@ fn compare_method_predicate_entailment<'tcx>(
346339 )
347340 . map ( |( ) | {
348341 // If the skip-mode was successful, emit a lint.
349- emit_implied_wf_lint ( ) ;
342+ emit_implied_wf_lint ( infcx . tcx , impl_m , impl_m_hir_id , vec ! [ ] ) ;
350343 } ) ;
351344 }
352345 CheckImpliedWfMode :: Skip => {
@@ -382,8 +375,16 @@ fn compare_method_predicate_entailment<'tcx>(
382375 CheckImpliedWfMode :: Skip ,
383376 )
384377 . map ( |( ) | {
378+ let bad_args = extract_bad_args_for_implies_lint (
379+ tcx,
380+ & errors,
381+ ( trait_m, trait_sig) ,
382+ // Unnormalized impl sig corresponds to the HIR types written
383+ ( impl_m, unnormalized_impl_sig) ,
384+ impl_m_hir_id,
385+ ) ;
385386 // If the skip-mode was successful, emit a lint.
386- emit_implied_wf_lint ( ) ;
387+ emit_implied_wf_lint ( tcx , impl_m , impl_m_hir_id , bad_args ) ;
387388 } ) ;
388389 }
389390 CheckImpliedWfMode :: Skip => {
@@ -400,6 +401,141 @@ fn compare_method_predicate_entailment<'tcx>(
400401 Ok ( ( ) )
401402}
402403
404+ fn extract_bad_args_for_implies_lint < ' tcx > (
405+ tcx : TyCtxt < ' tcx > ,
406+ errors : & [ infer:: RegionResolutionError < ' tcx > ] ,
407+ ( trait_m, trait_sig) : ( & ty:: AssocItem , ty:: FnSig < ' tcx > ) ,
408+ ( impl_m, impl_sig) : ( & ty:: AssocItem , ty:: FnSig < ' tcx > ) ,
409+ hir_id : hir:: HirId ,
410+ ) -> Vec < ( Span , Option < String > ) > {
411+ let mut blame_generics = vec ! [ ] ;
412+ for error in errors {
413+ // Look for the subregion origin that contains an input/output type
414+ let origin = match error {
415+ infer:: RegionResolutionError :: ConcreteFailure ( o, ..) => o,
416+ infer:: RegionResolutionError :: GenericBoundFailure ( o, ..) => o,
417+ infer:: RegionResolutionError :: SubSupConflict ( _, _, o, ..) => o,
418+ infer:: RegionResolutionError :: UpperBoundUniverseConflict ( .., o, _) => o,
419+ } ;
420+ // Extract (possible) input/output types from origin
421+ match origin {
422+ infer:: SubregionOrigin :: Subtype ( trace) => {
423+ if let Some ( ( a, b) ) = trace. values . ty ( ) {
424+ blame_generics. extend ( [ a, b] ) ;
425+ }
426+ }
427+ infer:: SubregionOrigin :: RelateParamBound ( _, ty, _) => blame_generics. push ( * ty) ,
428+ infer:: SubregionOrigin :: ReferenceOutlivesReferent ( ty, _) => blame_generics. push ( * ty) ,
429+ _ => { }
430+ }
431+ }
432+
433+ let fn_decl = tcx. hir ( ) . fn_decl_by_hir_id ( hir_id) . unwrap ( ) ;
434+ let opt_ret_ty = match fn_decl. output {
435+ hir:: FnRetTy :: DefaultReturn ( _) => None ,
436+ hir:: FnRetTy :: Return ( ty) => Some ( ty) ,
437+ } ;
438+
439+ // Map late-bound regions from trait to impl, so the names are right.
440+ let mapping = std:: iter:: zip (
441+ tcx. fn_sig ( trait_m. def_id ) . bound_vars ( ) ,
442+ tcx. fn_sig ( impl_m. def_id ) . bound_vars ( ) ,
443+ )
444+ . filter_map ( |( impl_bv, trait_bv) | {
445+ if let ty:: BoundVariableKind :: Region ( impl_bv) = impl_bv
446+ && let ty:: BoundVariableKind :: Region ( trait_bv) = trait_bv
447+ {
448+ Some ( ( impl_bv, trait_bv) )
449+ } else {
450+ None
451+ }
452+ } )
453+ . collect ( ) ;
454+
455+ // For each arg, see if it was in the "blame" of any of the region errors.
456+ // If so, then try to produce a suggestion to replace the argument type with
457+ // one from the trait.
458+ let mut bad_args = vec ! [ ] ;
459+ for ( idx, ( ty, hir_ty) ) in
460+ std:: iter:: zip ( impl_sig. inputs_and_output , fn_decl. inputs . iter ( ) . chain ( opt_ret_ty) )
461+ . enumerate ( )
462+ {
463+ let expected_ty = trait_sig. inputs_and_output [ idx]
464+ . fold_with ( & mut RemapLateBound { tcx, mapping : & mapping } ) ;
465+ if blame_generics. iter ( ) . any ( |blame| ty. contains ( * blame) ) {
466+ let expected_ty_sugg = expected_ty. to_string ( ) ;
467+ bad_args. push ( (
468+ hir_ty. span ,
469+ // Only suggest something if it actually changed.
470+ ( expected_ty_sugg != ty. to_string ( ) ) . then_some ( expected_ty_sugg) ,
471+ ) ) ;
472+ }
473+ }
474+
475+ bad_args
476+ }
477+
478+ struct RemapLateBound < ' a , ' tcx > {
479+ tcx : TyCtxt < ' tcx > ,
480+ mapping : & ' a FxHashMap < ty:: BoundRegionKind , ty:: BoundRegionKind > ,
481+ }
482+
483+ impl < ' tcx > TypeFolder < ' tcx > for RemapLateBound < ' _ , ' tcx > {
484+ fn tcx ( & self ) -> TyCtxt < ' tcx > {
485+ self . tcx
486+ }
487+
488+ fn fold_region ( & mut self , r : ty:: Region < ' tcx > ) -> ty:: Region < ' tcx > {
489+ if let ty:: ReFree ( fr) = * r {
490+ self . tcx . mk_region ( ty:: ReFree ( ty:: FreeRegion {
491+ bound_region : self
492+ . mapping
493+ . get ( & fr. bound_region )
494+ . copied ( )
495+ . unwrap_or ( fr. bound_region ) ,
496+ ..fr
497+ } ) )
498+ } else {
499+ r
500+ }
501+ }
502+ }
503+
504+ fn emit_implied_wf_lint < ' tcx > (
505+ tcx : TyCtxt < ' tcx > ,
506+ impl_m : & ty:: AssocItem ,
507+ hir_id : hir:: HirId ,
508+ bad_args : Vec < ( Span , Option < String > ) > ,
509+ ) {
510+ let span: MultiSpan = if bad_args. is_empty ( ) {
511+ tcx. def_span ( impl_m. def_id ) . into ( )
512+ } else {
513+ bad_args. iter ( ) . map ( |( span, _) | * span) . collect :: < Vec < _ > > ( ) . into ( )
514+ } ;
515+ tcx. struct_span_lint_hir (
516+ rustc_session:: lint:: builtin:: IMPLIED_BOUNDS_ENTAILMENT ,
517+ hir_id,
518+ span,
519+ "impl method assumes more implied bounds than the corresponding trait method" ,
520+ |lint| {
521+ let bad_args: Vec < _ > =
522+ bad_args. into_iter ( ) . filter_map ( |( span, sugg) | Some ( ( span, sugg?) ) ) . collect ( ) ;
523+ if !bad_args. is_empty ( ) {
524+ lint. multipart_suggestion (
525+ format ! (
526+ "replace {} type{} to make the impl signature compatible" ,
527+ pluralize!( "this" , bad_args. len( ) ) ,
528+ pluralize!( bad_args. len( ) )
529+ ) ,
530+ bad_args,
531+ Applicability :: MaybeIncorrect ,
532+ ) ;
533+ }
534+ lint
535+ } ,
536+ ) ;
537+ }
538+
403539#[ derive( Debug , PartialEq , Eq ) ]
404540enum CheckImpliedWfMode {
405541 /// Checks implied well-formedness of the impl method. If it fails, we will
0 commit comments