Skip to content

Commit 5b36c18

Browse files
committed
review comments
1 parent 4a75ef9 commit 5b36c18

22 files changed

+206
-218
lines changed

src/librustc/traits/error_reporting/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,8 @@ pub fn suggest_constraining_type_param(
14121412
false
14131413
}
14141414

1415+
/// Collect all the returned expressions within the input expression.
1416+
/// Used to point at the return spans when we want to suggest some change to them.
14151417
struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>);
14161418

14171419
impl<'v> Visitor<'v> for ReturnsVisitor<'v> {

src/librustc/traits/error_reporting/suggestions.rs

Lines changed: 128 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::{
44
};
55

66
use crate::infer::InferCtxt;
7+
use crate::traits::object_safety::object_safety_violations;
78
use crate::ty::TypeckTables;
89
use 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
}

src/librustc/traits/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ pub struct ObligationCause<'tcx> {
155155
pub code: ObligationCauseCode<'tcx>,
156156
}
157157

158-
impl<'tcx> ObligationCause<'tcx> {
159-
pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span {
158+
impl ObligationCause<'_> {
159+
pub fn span(&self, tcx: TyCtxt<'_>) -> Span {
160160
match self.code {
161161
ObligationCauseCode::CompareImplMethodObligation { .. }
162162
| ObligationCauseCode::MainFunctionType
@@ -1172,13 +1172,13 @@ impl<'tcx> ObligationCause<'tcx> {
11721172
}
11731173

11741174
impl<'tcx> ObligationCauseCode<'tcx> {
1175+
// Return the base obligation, ignoring derived obligations.
11751176
pub fn peel_derives(&self) -> &Self {
1176-
match self {
1177-
BuiltinDerivedObligation(cause) | ImplDerivedObligation(cause) => {
1178-
cause.parent_code.peel_derives()
1179-
}
1180-
_ => self,
1177+
let mut base_cause = self;
1178+
while let BuiltinDerivedObligation(cause) | ImplDerivedObligation(cause) = base_cause {
1179+
base_cause = &cause.parent_code;
11811180
}
1181+
base_cause
11821182
}
11831183
}
11841184

src/librustc/ty/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,9 @@ impl<'tcx> ty::TyS<'tcx> {
244244
ty::FnPtr(_) => "fn pointer".into(),
245245
ty::Dynamic(ref inner, ..) => {
246246
if let Some(principal) = inner.principal() {
247-
format!("trait `{}`", tcx.def_path_str(principal.def_id())).into()
247+
format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into()
248248
} else {
249-
"trait".into()
249+
"trait object".into()
250250
}
251251
}
252252
ty::Closure(..) => "closure".into(),

0 commit comments

Comments
 (0)