@@ -432,33 +432,18 @@ pub fn report_dyn_incompatibility<'tcx>(
432432 hir:: Node :: Item ( item) => Some ( item. ident . span ) ,
433433 _ => None ,
434434 } ) ;
435+
435436 let mut err = struct_span_code_err ! (
436437 tcx. dcx( ) ,
437438 span,
438439 E0038 ,
439- "the trait `{}` cannot be made into an object " ,
440+ "the trait `{}` is not dyn compatible " ,
440441 trait_str
441442 ) ;
442- err. span_label ( span, format ! ( "`{trait_str}` cannot be made into an object" ) ) ;
443-
444- if let Some ( hir_id) = hir_id
445- && let hir:: Node :: Ty ( ty) = tcx. hir_node ( hir_id)
446- && let hir:: TyKind :: TraitObject ( [ trait_ref, ..] , ..) = ty. kind
447- {
448- let mut hir_id = hir_id;
449- while let hir:: Node :: Ty ( ty) = tcx. parent_hir_node ( hir_id) {
450- hir_id = ty. hir_id ;
451- }
452- if tcx. parent_hir_node ( hir_id) . fn_sig ( ) . is_some ( ) {
453- // Do not suggest `impl Trait` when dealing with things like super-traits.
454- err. span_suggestion_verbose (
455- ty. span . until ( trait_ref. span ) ,
456- "consider using an opaque type instead" ,
457- "impl " ,
458- Applicability :: MaybeIncorrect ,
459- ) ;
460- }
461- }
443+ err. span_label ( span, format ! ( "`{trait_str}` is not dyn compatible" ) ) ;
444+
445+ attempt_dyn_to_impl_suggestion ( tcx, hir_id, & mut err) ;
446+
462447 let mut reported_violations = FxIndexSet :: default ( ) ;
463448 let mut multi_span = vec ! [ ] ;
464449 let mut messages = vec ! [ ] ;
@@ -473,7 +458,7 @@ pub fn report_dyn_incompatibility<'tcx>(
473458 if reported_violations. insert ( violation. clone ( ) ) {
474459 let spans = violation. spans ( ) ;
475460 let msg = if trait_span. is_none ( ) || spans. is_empty ( ) {
476- format ! ( "the trait cannot be made into an object because {}" , violation. error_msg( ) )
461+ format ! ( "the trait is not dyn compatible because {}" , violation. error_msg( ) )
477462 } else {
478463 format ! ( "...because {}" , violation. error_msg( ) )
479464 } ;
@@ -490,24 +475,20 @@ pub fn report_dyn_incompatibility<'tcx>(
490475 let has_multi_span = !multi_span. is_empty ( ) ;
491476 let mut note_span = MultiSpan :: from_spans ( multi_span. clone ( ) ) ;
492477 if let ( Some ( trait_span) , true ) = ( trait_span, has_multi_span) {
493- note_span. push_span_label ( trait_span, "this trait cannot be made into an object ..." ) ;
478+ note_span. push_span_label ( trait_span, "this trait is not dyn compatible ..." ) ;
494479 }
495480 for ( span, msg) in iter:: zip ( multi_span, messages) {
496481 note_span. push_span_label ( span, msg) ;
497482 }
498483 // FIXME(dyn_compat_renaming): Update the URL.
499484 err. span_note (
500485 note_span,
501- "for a trait to be \" dyn-compatible\" it needs to allow building a vtable to allow the call \
502- to be resolvable dynamically; for more information visit \
503- <https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
486+ "for a trait to be dyn compatible it needs to allow building a vtable\n \
487+ for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
504488 ) ;
505489
506490 // Only provide the help if its a local trait, otherwise it's not actionable.
507491 if trait_span. is_some ( ) {
508- let mut reported_violations: Vec < _ > = reported_violations. into_iter ( ) . collect ( ) ;
509- reported_violations. sort ( ) ;
510-
511492 let mut potential_solutions: Vec < _ > =
512493 reported_violations. into_iter ( ) . map ( |violation| violation. solution ( ) ) . collect ( ) ;
513494 potential_solutions. sort ( ) ;
@@ -518,68 +499,109 @@ pub fn report_dyn_incompatibility<'tcx>(
518499 }
519500 }
520501
502+ attempt_dyn_to_enum_suggestion ( tcx, trait_def_id, & * trait_str, & mut err) ;
503+
504+ err
505+ }
506+
507+ /// Attempt to suggest converting the `dyn Trait` argument to an enumeration
508+ /// over the types that implement `Trait`.
509+ fn attempt_dyn_to_enum_suggestion (
510+ tcx : TyCtxt < ' _ > ,
511+ trait_def_id : DefId ,
512+ trait_str : & str ,
513+ err : & mut Diag < ' _ > ,
514+ ) {
521515 let impls_of = tcx. trait_impls_of ( trait_def_id) ;
522- let impls = if impls_of. blanket_impls ( ) . is_empty ( ) {
523- impls_of
524- . non_blanket_impls ( )
525- . values ( )
526- . flatten ( )
527- . filter ( |def_id| {
528- !matches ! ( tcx. type_of( * def_id) . instantiate_identity( ) . kind( ) , ty:: Dynamic ( ..) )
529- } )
530- . collect :: < Vec < _ > > ( )
531- } else {
532- vec ! [ ]
533- } ;
534- let externally_visible = if !impls. is_empty ( )
535- && let Some ( def_id) = trait_def_id. as_local ( )
516+
517+ if !impls_of. blanket_impls ( ) . is_empty ( ) {
518+ return ;
519+ }
520+
521+ let concrete_impls: Option < Vec < Ty < ' _ > > > = impls_of
522+ . non_blanket_impls ( )
523+ . values ( )
524+ . flatten ( )
525+ . map ( |impl_id| {
526+ let Some ( impl_type) = tcx. type_of ( * impl_id) . no_bound_vars ( ) else { return None } ;
527+ if let ty:: Dynamic ( ..) = impl_type. kind ( ) {
528+ return None ;
529+ }
530+ Some ( impl_type)
531+ } )
532+ . collect ( ) ;
533+ let Some ( concrete_impls) = concrete_impls else { return } ;
534+ if concrete_impls. is_empty ( ) {
535+ return ;
536+ }
537+
538+ const MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM : usize = 9 ;
539+ if concrete_impls. len ( ) > MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM {
540+ return ;
541+ }
542+
543+ let externally_visible = if let Some ( def_id) = trait_def_id. as_local ( ) {
536544 // We may be executing this during typeck, which would result in cycle
537545 // if we used effective_visibilities query, which looks into opaque types
538546 // (and therefore calls typeck).
539- && tcx. resolutions ( ( ) ) . effective_visibilities . is_exported ( def_id)
540- {
541- true
547+ tcx. resolutions ( ( ) ) . effective_visibilities . is_exported ( def_id)
542548 } else {
543549 false
544550 } ;
545- match & impls[ ..] {
546- [ ] => { }
547- _ if impls. len ( ) > 9 => { }
548- [ only] if externally_visible => {
549- err. help ( with_no_trimmed_paths ! ( format!(
550- "only type `{}` is seen to implement the trait in this crate, consider using it \
551- directly instead",
552- tcx. type_of( * only) . instantiate_identity( ) ,
553- ) ) ) ;
554- }
555- [ only] => {
556- err. help ( with_no_trimmed_paths ! ( format!(
557- "only type `{}` implements the trait, consider using it directly instead" ,
558- tcx. type_of( * only) . instantiate_identity( ) ,
559- ) ) ) ;
560- }
561- impls => {
562- let types = impls
563- . iter ( )
564- . map ( |t| {
565- with_no_trimmed_paths ! ( format!( " {}" , tcx. type_of( * t) . instantiate_identity( ) , ) )
566- } )
567- . collect :: < Vec < _ > > ( ) ;
568- err. help ( format ! (
569- "the following types implement the trait, consider defining an enum where each \
570- variant holds one of these types, implementing `{}` for this new enum and using \
571- it instead:\n {}",
572- trait_str,
573- types. join( "\n " ) ,
574- ) ) ;
575- }
551+
552+ if let [ only_impl] = & concrete_impls[ ..] {
553+ let within = if externally_visible { " within this crate" } else { "" } ;
554+ err. help ( with_no_trimmed_paths ! ( format!(
555+ "only type `{only_impl}` implements `{trait_str}`{within}. \
556+ Consider using it directly instead."
557+ ) ) ) ;
558+ } else {
559+ let types = concrete_impls
560+ . iter ( )
561+ . map ( |t| with_no_trimmed_paths ! ( format!( " {}" , t) ) )
562+ . collect :: < Vec < String > > ( )
563+ . join ( "\n " ) ;
564+
565+ err. help ( format ! (
566+ "the following types implement `{trait_str}`:\n \
567+ {types}\n \
568+ consider defining an enum where each variant holds one of these types,\n \
569+ implementing `{trait_str}` for this new enum and using it instead",
570+ ) ) ;
576571 }
572+
577573 if externally_visible {
578574 err. note ( format ! (
579- "`{trait_str}` can be implemented in other crates; if you want to support your users \
575+ "`{trait_str}` may be implemented in other crates; if you want to support your users \
580576 passing their own types here, you can't refer to a specific type",
581577 ) ) ;
582578 }
579+ }
583580
584- err
581+ /// Attempt to suggest that a `dyn Trait` argument or return type be converted
582+ /// to use `impl Trait`.
583+ fn attempt_dyn_to_impl_suggestion ( tcx : TyCtxt < ' _ > , hir_id : Option < hir:: HirId > , err : & mut Diag < ' _ > ) {
584+ let Some ( hir_id) = hir_id else { return } ;
585+ let hir:: Node :: Ty ( ty) = tcx. hir_node ( hir_id) else { return } ;
586+ let hir:: TyKind :: TraitObject ( [ trait_ref, ..] , ..) = ty. kind else { return } ;
587+
588+ // Only suggest converting `dyn` to `impl` if we're in a function signature.
589+ // This ensures that we don't suggest converting e.g.
590+ // `type Alias = Box<dyn DynIncompatibleTrait>;` to
591+ // `type Alias = Box<impl DynIncompatibleTrait>;`
592+ let Some ( ( _id, first_non_type_parent_node) ) =
593+ tcx. hir ( ) . parent_iter ( hir_id) . find ( |( _id, node) | !matches ! ( node, hir:: Node :: Ty ( _) ) )
594+ else {
595+ return ;
596+ } ;
597+ if first_non_type_parent_node. fn_sig ( ) . is_none ( ) {
598+ return ;
599+ }
600+
601+ err. span_suggestion_verbose (
602+ ty. span . until ( trait_ref. span ) ,
603+ "consider using an opaque type instead" ,
604+ "impl " ,
605+ Applicability :: MaybeIncorrect ,
606+ ) ;
585607}
0 commit comments