@@ -4,6 +4,7 @@ use super::{
4
4
} ;
5
5
6
6
use crate :: infer:: InferCtxt ;
7
+ use crate :: traits:: object_safety:: object_safety_violations;
7
8
use crate :: ty:: TypeckTables ;
8
9
use crate :: ty:: { self , AdtKind , DefIdTree , ToPredicate , Ty , TyCtxt , TypeFoldable } ;
9
10
@@ -543,16 +544,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
543
544
}
544
545
}
545
546
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.
546
550
crate fn suggest_impl_trait (
547
551
& self ,
548
552
err : & mut DiagnosticBuilder < ' tcx > ,
549
553
span : Span ,
550
554
obligation : & PredicateObligation < ' tcx > ,
551
555
trait_ref : & ty:: Binder < ty:: TraitRef < ' tcx > > ,
552
556
) -> 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 ,
556
561
}
557
562
558
563
let hir = self . tcx . hir ( ) ;
@@ -565,12 +570,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
565
570
let body = hir. body ( * body_id) ;
566
571
let trait_ref = self . resolve_vars_if_possible ( trait_ref) ;
567
572
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
+ }
570
581
// We only want to suggest `impl Trait` to `dyn Trait`s.
571
582
// For example, `fn foo() -> str` needs to be filtered out.
572
- return false ;
583
+ _ => return false ,
573
584
}
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
+
574
592
// Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
575
593
// cases like `fn foo() -> (dyn Trait, i32) {}`.
576
594
// 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> {
583
601
584
602
let tables = self . in_progress_tables . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
585
603
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 {
613
611
for expr in & visitor. 0 {
614
612
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 ( ) ;
618
615
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;
619
630
}
631
+ last_ty = Some ( returned_ty) ;
620
632
}
621
633
}
634
+ }
622
635
636
+ let ( snippet, last_ty) =
623
637
if let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true , Some ( last_ty) ) = (
638
+ // Verify that we're dealing with a return `dyn Trait`
624
639
ret_ty. span . overlaps ( span) ,
625
640
& ret_ty. kind ,
626
641
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.
627
644
all_returns_conform_to_trait,
628
645
last_ty,
629
646
) {
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 ,
633
701
) ;
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
+ ) ) ;
700
707
}
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" ) ;
701
716
}
717
+ return true ;
702
718
}
703
719
false
704
720
}
@@ -708,9 +724,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
708
724
err : & mut DiagnosticBuilder < ' tcx > ,
709
725
obligation : & PredicateObligation < ' tcx > ,
710
726
) {
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 ,
714
730
}
715
731
716
732
let hir = self . tcx . hir ( ) ;
@@ -726,10 +742,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
726
742
let tables = self . in_progress_tables . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
727
743
for expr in & visitor. 0 {
728
744
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) ) ;
733
747
}
734
748
}
735
749
}
@@ -1685,9 +1699,8 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
1685
1699
}
1686
1700
1687
1701
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) ;
1691
1704
}
1692
1705
hir:: intravisit:: walk_expr ( self , ex) ;
1693
1706
}
0 commit comments