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