@@ -4,6 +4,7 @@ use super::{
44} ;
55
66use crate :: infer:: InferCtxt ;
7+ use crate :: traits:: object_safety:: object_safety_violations;
78use crate :: ty:: TypeckTables ;
89use crate :: ty:: { self , AdtKind , DefIdTree , ToPredicate , Ty , TyCtxt , TypeFoldable } ;
910
@@ -543,16 +544,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
543544 }
544545 }
545546
547+ /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if
548+ /// applicable and signal that the error has been expanded appropriately and needs to be
549+ /// emitted.
546550 crate fn suggest_impl_trait (
547551 & self ,
548552 err : & mut DiagnosticBuilder < ' tcx > ,
549553 span : Span ,
550554 obligation : & PredicateObligation < ' tcx > ,
551555 trait_ref : & ty:: Binder < ty:: TraitRef < ' tcx > > ,
552556 ) -> bool {
553- if let ObligationCauseCode :: SizedReturnType = obligation. cause . code . peel_derives ( ) {
554- } else {
555- return false ;
557+ match obligation. cause . code . peel_derives ( ) {
558+ // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`.
559+ ObligationCauseCode :: SizedReturnType => { }
560+ _ => return false ,
556561 }
557562
558563 let hir = self . tcx . hir ( ) ;
@@ -565,12 +570,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
565570 let body = hir. body ( * body_id) ;
566571 let trait_ref = self . resolve_vars_if_possible ( trait_ref) ;
567572 let ty = trait_ref. skip_binder ( ) . self_ty ( ) ;
568- if let ty:: Dynamic ( ..) = ty. kind {
569- } else {
573+ let is_object_safe;
574+ match ty. kind {
575+ ty:: Dynamic ( predicates, _) => {
576+ // The `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
577+ is_object_safe = predicates. principal_def_id ( ) . map_or ( true , |def_id| {
578+ !object_safety_violations ( self . tcx , def_id) . is_empty ( )
579+ } )
580+ }
570581 // We only want to suggest `impl Trait` to `dyn Trait`s.
571582 // For example, `fn foo() -> str` needs to be filtered out.
572- return false ;
583+ _ => return false ,
573584 }
585+
586+ let ret_ty = if let hir:: FunctionRetTy :: Return ( ret_ty) = sig. decl . output {
587+ ret_ty
588+ } else {
589+ return false ;
590+ } ;
591+
574592 // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
575593 // cases like `fn foo() -> (dyn Trait, i32) {}`.
576594 // Recursively look for `TraitObject` types and if there's only one, use that span to
@@ -583,122 +601,120 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
583601
584602 let tables = self . in_progress_tables . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
585603
586- if let hir:: FunctionRetTy :: Return ( ret_ty) = sig. decl . output {
587- let mut all_returns_conform_to_trait = true ;
588- let mut all_returns_have_same_type = true ;
589- let mut last_ty = None ;
590- if let Some ( ty_ret_ty) = tables. node_type_opt ( ret_ty. hir_id ) {
591- let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
592- if let ty:: Dynamic ( predicates, _) = & ty_ret_ty. kind {
593- for predicate in predicates. iter ( ) {
594- for expr in & visitor. 0 {
595- if let Some ( returned_ty) = tables. node_type_opt ( expr. hir_id ) {
596- if let Some ( ty) = last_ty {
597- all_returns_have_same_type &= ty == returned_ty;
598- }
599- last_ty = Some ( returned_ty) ;
600-
601- let param_env = ty:: ParamEnv :: empty ( ) ;
602- let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
603- let obligation =
604- Obligation :: new ( cause. clone ( ) , param_env, pred) ;
605- all_returns_conform_to_trait &=
606- self . predicate_may_hold ( & obligation) ;
607- }
608- }
609- }
610- }
611- } else {
612- // We still want to verify whether all the return types conform to each other.
604+ let mut all_returns_conform_to_trait = true ;
605+ let mut all_returns_have_same_type = true ;
606+ let mut last_ty = None ;
607+ if let Some ( ty_ret_ty) = tables. node_type_opt ( ret_ty. hir_id ) {
608+ let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
609+ let param_env = ty:: ParamEnv :: empty ( ) ;
610+ if let ty:: Dynamic ( predicates, _) = & ty_ret_ty. kind {
613611 for expr in & visitor. 0 {
614612 if let Some ( returned_ty) = tables. node_type_opt ( expr. hir_id ) {
615- if let Some ( ty) = last_ty {
616- all_returns_have_same_type &= ty == returned_ty;
617- }
613+ all_returns_have_same_type &=
614+ Some ( returned_ty) == last_ty || last_ty. is_none ( ) ;
618615 last_ty = Some ( returned_ty) ;
616+ for predicate in predicates. iter ( ) {
617+ let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
618+ let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
619+ all_returns_conform_to_trait &= self . predicate_may_hold ( & obl) ;
620+ }
621+ }
622+ }
623+ }
624+ } else {
625+ // We still want to verify whether all the return types conform to each other.
626+ for expr in & visitor. 0 {
627+ if let Some ( returned_ty) = tables. node_type_opt ( expr. hir_id ) {
628+ if let Some ( ty) = last_ty {
629+ all_returns_have_same_type &= ty == returned_ty;
619630 }
631+ last_ty = Some ( returned_ty) ;
620632 }
621633 }
634+ }
622635
636+ let ( snippet, last_ty) =
623637 if let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true , Some ( last_ty) ) = (
638+ // Verify that we're dealing with a return `dyn Trait`
624639 ret_ty. span . overlaps ( span) ,
625640 & ret_ty. kind ,
626641 self . tcx . sess . source_map ( ) . span_to_snippet ( ret_ty. span ) ,
642+ // If any of the return types does not conform to the trait, then we can't
643+ // suggest `impl Trait` nor trait objects, it is a type mismatch error.
627644 all_returns_conform_to_trait,
628645 last_ty,
629646 ) {
630- err. code = Some ( error_code ! ( E0746 ) ) ;
631- err. set_primary_message (
632- "return type cannot have a bare trait because it must be `Sized`" ,
647+ ( snippet, last_ty)
648+ } else {
649+ return false ;
650+ } ;
651+ err. code ( error_code ! ( E0746 ) ) ;
652+ err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
653+ err. children . clear ( ) ;
654+ let impl_trait_msg = "for information on `impl Trait`, see \
655+ <https://doc.rust-lang.org/book/ch10-02-traits.html\
656+ #returning-types-that-implement-traits>";
657+ let trait_obj_msg = "for information on trait objects, see \
658+ <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
659+ #using-trait-objects-that-allow-for-values-of-different-types>";
660+ let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
661+ let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet[ ..] } ;
662+ if all_returns_have_same_type {
663+ // Suggest `-> impl Trait`.
664+ err. span_suggestion (
665+ ret_ty. span ,
666+ & format ! (
667+ "return `impl {1}` instead, as all return paths are of type `{}`, \
668+ which implements `{1}`",
669+ last_ty, trait_obj,
670+ ) ,
671+ format ! ( "impl {}" , trait_obj) ,
672+ Applicability :: MachineApplicable ,
673+ ) ;
674+ err. note ( impl_trait_msg) ;
675+ } else {
676+ if is_object_safe {
677+ // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
678+ // Get all the return values and collect their span and suggestion.
679+ let mut suggestions = visitor
680+ . 0
681+ . iter ( )
682+ . map ( |expr| {
683+ (
684+ expr. span ,
685+ format ! (
686+ "Box::new({})" ,
687+ self . tcx. sess. source_map( ) . span_to_snippet( expr. span) . unwrap( )
688+ ) ,
689+ )
690+ } )
691+ . collect :: < Vec < _ > > ( ) ;
692+ // Add the suggestion for the return type.
693+ suggestions. push ( (
694+ ret_ty. span ,
695+ format ! ( "Box<{}{}>" , if has_dyn { "" } else { "dyn " } , snippet) ,
696+ ) ) ;
697+ err. multipart_suggestion (
698+ "return a trait object instead" ,
699+ suggestions,
700+ Applicability :: MaybeIncorrect ,
633701 ) ;
634- err. children . clear ( ) ;
635- let impl_trait_msg = "for information on `impl Trait`, see \
636- <https://doc.rust-lang.org/book/ch10-02-traits.html\
637- #returning-types-that-implement-traits>";
638- let trait_obj_msg = "for information on trait objects, see \
639- <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
640- #using-trait-objects-that-allow-for-values-of-different-types>";
641- let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
642- let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet[ ..] } ;
643- if all_returns_have_same_type {
644- err. span_suggestion (
645- ret_ty. span ,
646- & format ! (
647- "you can use the `impl Trait` feature \
648- in the return type because all the return paths are of type \
649- `{}`, which implements `dyn {}`",
650- last_ty, trait_obj,
651- ) ,
652- format ! ( "impl {}" , trait_obj) ,
653- Applicability :: MachineApplicable ,
654- ) ;
655- err. note ( impl_trait_msg) ;
656- } else {
657- let mut suggestions = visitor
658- . 0
659- . iter ( )
660- . map ( |expr| {
661- (
662- expr. span ,
663- format ! (
664- "Box::new({})" ,
665- self . tcx
666- . sess
667- . source_map( )
668- . span_to_snippet( expr. span)
669- . unwrap( )
670- ) ,
671- )
672- } )
673- . collect :: < Vec < _ > > ( ) ;
674- suggestions. push ( (
675- ret_ty. span ,
676- format ! ( "Box<{}{}>" , if has_dyn { "" } else { "dyn " } , snippet) ,
677- ) ) ;
678- err. multipart_suggestion (
679- "if the performance implications are acceptable, you can return a \
680- trait object",
681- suggestions,
682- Applicability :: MaybeIncorrect ,
683- ) ;
684- err. span_help (
685- visitor. 0 . iter ( ) . map ( |expr| expr. span ) . collect :: < Vec < _ > > ( ) ,
686- & format ! (
687- "if all the returned values were of the same type you could use \
688- `impl {}` as the return type",
689- trait_obj,
690- ) ,
691- ) ;
692- err. help (
693- "alternatively, you can always create a new `enum` with a variant \
694- for each returned type",
695- ) ;
696- err. note ( impl_trait_msg) ;
697- err. note ( trait_obj_msg) ;
698- }
699- return true ;
702+ } else {
703+ err. note ( & format ! (
704+ "if trait `{}` was object safe, you could return a trait object" ,
705+ trait_obj,
706+ ) ) ;
700707 }
708+ err. note ( & format ! (
709+ "if all the returned values were of the same type you could use \
710+ `impl {}` as the return type",
711+ trait_obj,
712+ ) ) ;
713+ err. note ( impl_trait_msg) ;
714+ err. note ( trait_obj_msg) ;
715+ err. note ( "you can create a new `enum` with a variant for each returned type" ) ;
701716 }
717+ return true ;
702718 }
703719 false
704720 }
@@ -708,9 +724,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
708724 err : & mut DiagnosticBuilder < ' tcx > ,
709725 obligation : & PredicateObligation < ' tcx > ,
710726 ) {
711- if let ObligationCauseCode :: SizedReturnType = obligation. cause . code . peel_derives ( ) {
712- } else {
713- return ;
727+ match obligation. cause . code . peel_derives ( ) {
728+ ObligationCauseCode :: SizedReturnType => { }
729+ _ => return ,
714730 }
715731
716732 let hir = self . tcx . hir ( ) ;
@@ -726,10 +742,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
726742 let tables = self . in_progress_tables . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
727743 for expr in & visitor. 0 {
728744 if let Some ( returned_ty) = tables. node_type_opt ( expr. hir_id ) {
729- err. span_label (
730- expr. span ,
731- & format ! ( "this returned value is of type `{}`" , returned_ty) ,
732- ) ;
745+ let ty = self . resolve_vars_if_possible ( & returned_ty) ;
746+ err. span_label ( expr. span , & format ! ( "this returned value is of type `{}`" , ty) ) ;
733747 }
734748 }
735749 }
@@ -1685,9 +1699,8 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
16851699 }
16861700
16871701 fn visit_expr ( & mut self , ex : & ' v hir:: Expr < ' v > ) {
1688- match ex. kind {
1689- hir:: ExprKind :: Ret ( Some ( ex) ) => self . 0 . push ( ex) ,
1690- _ => { }
1702+ if let hir:: ExprKind :: Ret ( Some ( ex) ) = ex. kind {
1703+ self . 0 . push ( ex) ;
16911704 }
16921705 hir:: intravisit:: walk_expr ( self , ex) ;
16931706 }
0 commit comments