From 6fd564112f1ec00f6f8a56e8a3577dd255639131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jan 2020 13:13:12 -0800 Subject: [PATCH 01/15] Specific error for unsized `dyn Trait` return type Suggest `impl Trait` when possible, and `Box` otherwise. --- src/librustc/traits/error_reporting.rs | 201 +++++++++++++++++- src/librustc/traits/mod.rs | 11 + src/librustc_error_codes/error_codes.rs | 1 + src/test/ui/error-codes/E0746.rs | 17 ++ src/test/ui/error-codes/E0746.stderr | 62 ++++++ .../dyn-trait-return-should-be-impl-trait.rs | 36 ++++ ...n-trait-return-should-be-impl-trait.stderr | 186 ++++++++++++++++ 7 files changed, 512 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/error-codes/E0746.rs create mode 100644 src/test/ui/error-codes/E0746.stderr create mode 100644 src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs create mode 100644 src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 7f151af7abe50..8f771658e4098 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength use super::{ ConstEvalFailure, EvaluationResult, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, ObjectSafetyViolation, Obligation, ObligationCause, @@ -22,9 +23,12 @@ use crate::ty::TypeckTables; use crate::ty::{self, AdtKind, DefIdTree, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style}; +use rustc_errors::{ + error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style, +}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::intravisit::Visitor; use rustc_hir::Node; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; @@ -758,7 +762,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { )), Some( "the question mark operation (`?`) implicitly performs a \ - conversion on the error value using the `From` trait" + conversion on the error value using the `From` trait" .to_owned(), ), ) @@ -835,6 +839,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.suggest_remove_reference(&obligation, &mut err, &trait_ref); self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); self.note_version_mismatch(&mut err, &trait_ref); + if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { + err.emit(); + return; + } // Try to report a help message if !trait_ref.has_infer_types() @@ -1696,6 +1704,195 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn suggest_impl_trait( + &self, + err: &mut DiagnosticBuilder<'tcx>, + span: Span, + obligation: &PredicateObligation<'tcx>, + trait_ref: &ty::Binder>, + ) -> bool { + if let ObligationCauseCode::SizedReturnType = obligation.cause.code.peel_derives() { + } else { + return false; + } + + let hir = self.tcx.hir(); + let parent_node = hir.get_parent_node(obligation.cause.body_id); + let node = hir.find(parent_node); + if let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(sig, _, body_id), .. + })) = node + { + let body = hir.body(*body_id); + let trait_ref = self.resolve_vars_if_possible(trait_ref); + let ty = trait_ref.skip_binder().self_ty(); + if let ty::Dynamic(..) = ty.kind { + } else { + // We only want to suggest `impl Trait` to `dyn Trait`s. + // For example, `fn foo() -> str` needs to be filtered out. + return false; + } + // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for + // cases like `fn foo() -> (dyn Trait, i32) {}`. + // Recursively look for `TraitObject` types and if there's only one, use that span to + // suggest `impl Trait`. + + struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>); + + impl<'v> Visitor<'v> for ReturnsVisitor<'v> { + type Map = rustc::hir::map::Map<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<'_, Self::Map> { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + match ex.kind { + hir::ExprKind::Ret(Some(ex)) => self.0.push(ex), + _ => {} + } + hir::intravisit::walk_expr(self, ex); + } + + fn visit_body(&mut self, body: &'v hir::Body<'v>) { + if body.generator_kind().is_none() { + if let hir::ExprKind::Block(block, None) = body.value.kind { + if let Some(expr) = block.expr { + self.0.push(expr); + } + } + } + hir::intravisit::walk_body(self, body); + } + } + + // Visit to make sure there's a single `return` type to suggest `impl Trait`, + // otherwise suggest using `Box` or an enum. + let mut visitor = ReturnsVisitor(vec![]); + visitor.visit_body(&body); + + let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); + + if let hir::FunctionRetTy::Return(ret_ty) = sig.decl.output { + let mut all_returns_conform_to_trait = true; + let mut all_returns_have_same_type = true; + let mut last_ty = None; + if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { + let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); + if let ty::Dynamic(predicates, _) = &ty_ret_ty.kind { + for predicate in predicates.iter() { + for expr in &visitor.0 { + if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { + if let Some(ty) = last_ty { + all_returns_have_same_type &= ty == returned_ty; + } + last_ty = Some(returned_ty); + + let param_env = ty::ParamEnv::empty(); + let pred = predicate.with_self_ty(self.tcx, returned_ty); + let obligation = + Obligation::new(cause.clone(), param_env, pred); + all_returns_conform_to_trait &= + self.predicate_may_hold(&obligation); + } + } + } + } + } else { + // We still want to verify whether all the return types conform to each other. + for expr in &visitor.0 { + if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { + if let Some(ty) = last_ty { + all_returns_have_same_type &= ty == returned_ty; + } + last_ty = Some(returned_ty); + } + } + } + + if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = ( + ret_ty.span.overlaps(span), + &ret_ty.kind, + self.tcx.sess.source_map().span_to_snippet(ret_ty.span), + all_returns_conform_to_trait, + last_ty, + ) { + err.code = Some(error_code!(E0746)); + err.set_primary_message( + "return type cannot have a bare trait because it must be `Sized`", + ); + err.children.clear(); + let impl_trait_msg = "for information on `impl Trait`, see \ + "; + let trait_obj_msg = "for information on trait objects, see \ + "; + let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); + let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] }; + if all_returns_have_same_type { + err.span_suggestion( + ret_ty.span, + &format!( + "you can use the `impl Trait` feature \ + in the return type because all the return paths are of type \ + `{}`, which implements `dyn {}`", + last_ty, trait_obj, + ), + format!("impl {}", trait_obj), + Applicability::MaybeIncorrect, + ); + err.note(impl_trait_msg); + } else { + let mut suggestions = visitor + .0 + .iter() + .map(|expr| { + ( + expr.span, + format!( + "Box::new({})", + self.tcx + .sess + .source_map() + .span_to_snippet(expr.span) + .unwrap() + ), + ) + }) + .collect::>(); + suggestions.push(( + ret_ty.span, + format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet), + )); + err.multipart_suggestion( + "if the performance implications are acceptable, you can return a \ + trait object", + suggestions, + Applicability::MaybeIncorrect, + ); + err.span_help( + visitor.0.iter().map(|expr| expr.span).collect::>(), + &format!( + "if all the returned values were of the same type you could use \ + `impl {}` as the return type", + trait_obj, + ), + ); + err.help( + "alternatively, you can always create a new `enum` with a variant \ + for each returned type", + ); + err.note(impl_trait_msg); + err.note(trait_obj_msg); + } + return true; + } + } + } + false + } + /// Given some node representing a fn-like thing in the HIR map, /// returns a span and `ArgKind` information that describes the /// arguments it expects. This can be supplied to diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 31de5409fc8be..f68711c06205b 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -1171,6 +1171,17 @@ impl<'tcx> ObligationCause<'tcx> { } } +impl<'tcx> ObligationCauseCode<'tcx> { + pub fn peel_derives(&self) -> &Self { + match self { + BuiltinDerivedObligation(cause) | ImplDerivedObligation(cause) => { + cause.parent_code.peel_derives() + } + _ => self, + } + } +} + impl<'tcx, N> Vtable<'tcx, N> { pub fn nested_obligations(self) -> Vec { match self { diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index 272147e28a419..c17cb7dd9f161 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -608,4 +608,5 @@ E0745: include_str!("./error_codes/E0745.md"), E0726, // non-explicit (not `'_`) elided lifetime in unsupported position E0727, // `async` generators are not yet supported E0739, // invalid track_caller application/syntax + E0746, // `dyn Trait` return type } diff --git a/src/test/ui/error-codes/E0746.rs b/src/test/ui/error-codes/E0746.rs new file mode 100644 index 0000000000000..ad257b01e1b40 --- /dev/null +++ b/src/test/ui/error-codes/E0746.rs @@ -0,0 +1,17 @@ +struct Struct; +trait Trait {} +impl Trait for Struct {} +impl Trait for u32 {} + +fn foo() -> dyn Trait { Struct } +//~^ ERROR E0746 +//~| ERROR E0308 + +fn bar() -> dyn Trait { //~ ERROR E0746 + if true { + return 0; //~ ERROR E0308 + } + 42 //~ ERROR E0308 +} + +fn main() {} diff --git a/src/test/ui/error-codes/E0746.stderr b/src/test/ui/error-codes/E0746.stderr new file mode 100644 index 0000000000000..baafcd27c29d4 --- /dev/null +++ b/src/test/ui/error-codes/E0746.stderr @@ -0,0 +1,62 @@ +error[E0308]: mismatched types + --> $DIR/E0746.rs:6:25 + | +LL | fn foo() -> dyn Trait { Struct } + | --------- ^^^^^^ expected trait `Trait`, found struct `Struct` + | | + | expected `(dyn Trait + 'static)` because of return type + | + = note: expected trait object `(dyn Trait + 'static)` + found struct `Struct` + +error[E0746]: return type cannot have a bare trait because it must be `Sized` + --> $DIR/E0746.rs:6:13 + | +LL | fn foo() -> dyn Trait { Struct } + | ^^^^^^^^^ doesn't have a size known at compile-time + | + = note: for information on `impl Trait`, see +help: you can use the `impl Trait` feature in the return type because all the return paths are of type `Struct`, which implements `dyn Trait` + | +LL | fn foo() -> impl Trait { Struct } + | ^^^^^^^^^^ + +error[E0746]: return type cannot have a bare trait because it must be `Sized` + --> $DIR/E0746.rs:10:13 + | +LL | fn bar() -> dyn Trait { + | ^^^^^^^^^ doesn't have a size known at compile-time + | + = note: for information on `impl Trait`, see +help: you can use the `impl Trait` feature in the return type because all the return paths are of type `{integer}`, which implements `dyn Trait` + | +LL | fn bar() -> impl Trait { + | ^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/E0746.rs:12:16 + | +LL | fn bar() -> dyn Trait { + | --------- expected `(dyn Trait + 'static)` because of return type +LL | if true { +LL | return 0; + | ^ expected trait `Trait`, found integer + | + = note: expected trait object `(dyn Trait + 'static)` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/E0746.rs:14:5 + | +LL | fn bar() -> dyn Trait { + | --------- expected `(dyn Trait + 'static)` because of return type +... +LL | 42 + | ^^ expected trait `Trait`, found integer + | + = note: expected trait object `(dyn Trait + 'static)` + found type `{integer}` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs new file mode 100644 index 0000000000000..80168ca825774 --- /dev/null +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs @@ -0,0 +1,36 @@ +#![allow(bare_trait_objects)] +struct Struct; +trait Trait {} +impl Trait for Struct {} +impl Trait for u32 {} + +fn fuz() -> (usize, Trait) { (42, Struct) } +//~^ ERROR E0277 +//~| ERROR E0308 +fn bar() -> (usize, dyn Trait) { (42, Struct) } +//~^ ERROR E0277 +//~| ERROR E0308 +fn bap() -> Trait { Struct } +//~^ ERROR E0746 +//~| ERROR E0308 +fn ban() -> dyn Trait { Struct } +//~^ ERROR E0746 +//~| ERROR E0308 +fn bak() -> dyn Trait { unimplemented!() } //~ ERROR E0277 +// Suggest using `Box` +fn bal() -> dyn Trait { //~ ERROR E0746 + if true { + return Struct; //~ ERROR E0308 + } + 42 //~ ERROR E0308 +} + +// Suggest using `impl Trait` +fn bat() -> dyn Trait { //~ ERROR E0746 + if true { + return 0; //~ ERROR E0308 + } + 42 //~ ERROR E0308 +} + +fn main() {} diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr new file mode 100644 index 0000000000000..ce4c141a0af9d --- /dev/null +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -0,0 +1,186 @@ +error[E0308]: mismatched types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:7:35 + | +LL | fn fuz() -> (usize, Trait) { (42, Struct) } + | ^^^^^^ expected trait `Trait`, found struct `Struct` + | + = note: expected trait object `(dyn Trait + 'static)` + found struct `Struct` + +error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:7:13 + | +LL | fn fuz() -> (usize, Trait) { (42, Struct) } + | ^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `(usize, (dyn Trait + 'static))`, the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` + = note: to learn more, visit + = note: required because it appears within the type `(usize, (dyn Trait + 'static))` + = note: the return type of a function must have a statically known size + +error[E0308]: mismatched types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:10:39 + | +LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } + | ^^^^^^ expected trait `Trait`, found struct `Struct` + | + = note: expected trait object `(dyn Trait + 'static)` + found struct `Struct` + +error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:10:13 + | +LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } + | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `(usize, (dyn Trait + 'static))`, the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` + = note: to learn more, visit + = note: required because it appears within the type `(usize, (dyn Trait + 'static))` + = note: the return type of a function must have a statically known size + +error[E0308]: mismatched types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:13:21 + | +LL | fn bap() -> Trait { Struct } + | ----- ^^^^^^ expected trait `Trait`, found struct `Struct` + | | + | expected `(dyn Trait + 'static)` because of return type + | + = note: expected trait object `(dyn Trait + 'static)` + found struct `Struct` + +error[E0746]: return type cannot have a bare trait because it must be `Sized` + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:13:13 + | +LL | fn bap() -> Trait { Struct } + | ^^^^^ doesn't have a size known at compile-time + | + = note: for information on `impl Trait`, see +help: you can use the `impl Trait` feature in the return type because all the return paths are of type `Struct`, which implements `dyn Trait` + | +LL | fn bap() -> impl Trait { Struct } + | ^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:16:25 + | +LL | fn ban() -> dyn Trait { Struct } + | --------- ^^^^^^ expected trait `Trait`, found struct `Struct` + | | + | expected `(dyn Trait + 'static)` because of return type + | + = note: expected trait object `(dyn Trait + 'static)` + found struct `Struct` + +error[E0746]: return type cannot have a bare trait because it must be `Sized` + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:16:13 + | +LL | fn ban() -> dyn Trait { Struct } + | ^^^^^^^^^ doesn't have a size known at compile-time + | + = note: for information on `impl Trait`, see +help: you can use the `impl Trait` feature in the return type because all the return paths are of type `Struct`, which implements `dyn Trait` + | +LL | fn ban() -> impl Trait { Struct } + | ^^^^^^^^^^ + +error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:19:13 + | +LL | fn bak() -> dyn Trait { unimplemented!() } + | ^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` + = note: to learn more, visit + = note: the return type of a function must have a statically known size + +error[E0308]: mismatched types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:23:16 + | +LL | fn bal() -> dyn Trait { + | --------- expected `(dyn Trait + 'static)` because of return type +LL | if true { +LL | return Struct; + | ^^^^^^ expected trait `Trait`, found struct `Struct` + | + = note: expected trait object `(dyn Trait + 'static)` + found struct `Struct` + +error[E0746]: return type cannot have a bare trait because it must be `Sized` + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:21:13 + | +LL | fn bal() -> dyn Trait { + | ^^^^^^^^^ doesn't have a size known at compile-time + | +help: if all the returned values were of the same type you could use `impl Trait` as the return type + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:25:5 + | +LL | return Struct; + | ^^^^^^ +LL | } +LL | 42 + | ^^ + = help: alternatively, you can always create a new `enum` with a variant for each returned type + = note: for information on `impl Trait`, see + = note: for information on trait objects, see +help: if the performance implications are acceptable, you can return a trait object + | +LL | fn bal() -> Box { +LL | if true { +LL | return Box::new(Struct); +LL | } +LL | Box::new(42) + | + +error[E0308]: mismatched types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:25:5 + | +LL | fn bal() -> dyn Trait { + | --------- expected `(dyn Trait + 'static)` because of return type +... +LL | 42 + | ^^ expected trait `Trait`, found integer + | + = note: expected trait object `(dyn Trait + 'static)` + found type `{integer}` + +error[E0746]: return type cannot have a bare trait because it must be `Sized` + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:29:13 + | +LL | fn bat() -> dyn Trait { + | ^^^^^^^^^ doesn't have a size known at compile-time + | + = note: for information on `impl Trait`, see +help: you can use the `impl Trait` feature in the return type because all the return paths are of type `{integer}`, which implements `dyn Trait` + | +LL | fn bat() -> impl Trait { + | ^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:31:16 + | +LL | fn bat() -> dyn Trait { + | --------- expected `(dyn Trait + 'static)` because of return type +LL | if true { +LL | return 0; + | ^ expected trait `Trait`, found integer + | + = note: expected trait object `(dyn Trait + 'static)` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:33:5 + | +LL | fn bat() -> dyn Trait { + | --------- expected `(dyn Trait + 'static)` because of return type +... +LL | 42 + | ^^ expected trait `Trait`, found integer + | + = note: expected trait object `(dyn Trait + 'static)` + found type `{integer}` + +error: aborting due to 15 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. From 75eabb17aec390fd91b5bf11539012e4fc7307b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jan 2020 13:14:11 -0800 Subject: [PATCH 02/15] Account for diverging types in return `impl Trait` --- src/librustc_typeck/check/coercion.rs | 28 +++++++++++++++++ src/test/ui/impl-trait/equality.stderr | 5 ++++ ...type-err-cause-on-impl-trait-return.stderr | 30 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 1afb703ca1506..698fdfa3897b1 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1348,6 +1348,34 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { "...is found to be `{}` here", fcx.resolve_vars_with_obligations(expected), )); + err.note( + "`impl Trait` as a return type requires that all the returned values must have \ + the same type", + ); + let snippet = fcx + .tcx + .sess + .source_map() + .span_to_snippet(return_sp) + .unwrap_or_else(|_| "dyn Trait".to_string()); + let mut snippet_iter = snippet.split_whitespace(); + let has_impl = snippet_iter.next().map_or(false, |s| s == "impl"); + if has_impl { + err.help(&format!( + "you can instead return a trait object using `Box`", + &snippet[5..] + )); + } + let impl_trait_msg = "for information on `impl Trait`, see \ + "; + let trait_obj_msg = "for information on trait objects, see \ + "; + err.note(impl_trait_msg); + if has_impl { + err.note(trait_obj_msg); + } } err } diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index e53524e58d663..215b6d52918ab 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -9,6 +9,11 @@ LL | return 1_i32; LL | } LL | 0_u32 | ^^^^^ expected `i32`, found `u32` + | + = note: `impl Trait` as a return type requires that all the returned values must have the same type + = help: you can instead return a trait object using `Box` + = note: for information on `impl Trait`, see + = note: for information on trait objects, see error[E0277]: cannot add `impl Foo` to `u32` --> $DIR/equality.rs:24:11 diff --git a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr index 27b86007451d8..9db5250e4d876 100644 --- a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr @@ -9,6 +9,11 @@ LL | return 0i32; LL | } LL | 1u32 | ^^^^ expected `i32`, found `u32` + | + = note: `impl Trait` as a return type requires that all the returned values must have the same type + = help: you can instead return a trait object using `Box` + = note: for information on `impl Trait`, see + = note: for information on trait objects, see error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:13:16 @@ -21,6 +26,11 @@ LL | return 0i32; LL | } else { LL | return 1u32; | ^^^^ expected `i32`, found `u32` + | + = note: `impl Trait` as a return type requires that all the returned values must have the same type + = help: you can instead return a trait object using `Box` + = note: for information on `impl Trait`, see + = note: for information on trait objects, see error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:22:9 @@ -33,6 +43,11 @@ LL | return 0i32; LL | } else { LL | 1u32 | ^^^^ expected `i32`, found `u32` + | + = note: `impl Trait` as a return type requires that all the returned values must have the same type + = help: you can instead return a trait object using `Box` + = note: for information on `impl Trait`, see + = note: for information on trait objects, see error[E0308]: `if` and `else` have incompatible types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:31:9 @@ -57,6 +72,11 @@ LL | 0 => return 0i32, | ---- ...is found to be `i32` here LL | _ => 1u32, | ^^^^ expected `i32`, found `u32` + | + = note: `impl Trait` as a return type requires that all the returned values must have the same type + = help: you can instead return a trait object using `Box` + = note: for information on `impl Trait`, see + = note: for information on trait objects, see error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:45:5 @@ -71,6 +91,11 @@ LL | | 1 => 1u32, LL | | _ => 2u32, LL | | } | |_____^ expected `i32`, found `u32` + | + = note: `impl Trait` as a return type requires that all the returned values must have the same type + = help: you can instead return a trait object using `Box` + = note: for information on `impl Trait`, see + = note: for information on trait objects, see error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:59:13 @@ -83,6 +108,11 @@ LL | return 0i32; ... LL | 1u32 | ^^^^ expected `i32`, found `u32` + | + = note: `impl Trait` as a return type requires that all the returned values must have the same type + = help: you can instead return a trait object using `Box` + = note: for information on `impl Trait`, see + = note: for information on trait objects, see error: aborting due to 7 previous errors From ea7e885204e1ed6b18406e84708abef748925ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jan 2020 16:12:44 -0800 Subject: [PATCH 03/15] Elide E0308 errors in favor of E0746 When a type error involves a `dyn Trait` as the return type, do not emit the type error, as the "return type is not `Sized`" error will provide enough information to the user. --- src/librustc_typeck/check/coercion.rs | 19 ++++- src/librustc_typeck/check/mod.rs | 12 +++ src/test/ui/error-codes/E0746.rs | 5 +- src/test/ui/error-codes/E0746.stderr | 40 +-------- .../dyn-trait-return-should-be-impl-trait.rs | 10 +-- ...n-trait-return-should-be-impl-trait.stderr | 82 ++----------------- 6 files changed, 44 insertions(+), 124 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 698fdfa3897b1..77f16fb79141d 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1222,6 +1222,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { }; let mut err; + let mut unsized_return = false; match cause.code { ObligationCauseCode::ReturnNoExpression => { err = struct_span_err!( @@ -1243,6 +1244,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { parent_id, expression.map(|expr| (expr, blk_id)), ); + if !fcx.tcx.features().unsized_locals { + unsized_return = fcx.is_unsized_return(blk_id); + } } ObligationCauseCode::ReturnValue(id) => { err = self.report_return_mismatched_types( @@ -1254,6 +1258,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { id, None, ); + if !fcx.tcx.features().unsized_locals { + let id = fcx.tcx.hir().get_parent_node(id); + unsized_return = fcx.is_unsized_return(id); + } } _ => { err = fcx.report_mismatched_types(cause, expected, found, coercion_error); @@ -1282,7 +1290,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { .filter(|e| fcx.is_assign_to_bool(e, self.expected_ty())) .is_some(); - err.emit_unless(assign_to_bool); + if unsized_return { + fcx.tcx.sess.delay_span_bug( + cause.span, + &format!( + "elided E0308 in favor of more detailed E0277 or E0746: {:?}", + cause.code + ), + ); + } + err.emit_unless(assign_to_bool || unsized_return); self.final_ty = Some(fcx.tcx.types.err); } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index baf9ae1ac2911..8f531ea6199e1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4964,6 +4964,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn is_unsized_return(&self, blk_id: hir::HirId) -> bool { + if let Some((fn_decl, _)) = self.get_fn_decl(blk_id) { + if let hir::FunctionRetTy::Return(ty) = fn_decl.output { + let ty = AstConv::ast_ty_to_ty(self, ty); + if let ty::Dynamic(..) = ty.kind { + return true; + } + } + } + false + } + /// A possible error is to forget to add a return type that is needed: /// /// ``` diff --git a/src/test/ui/error-codes/E0746.rs b/src/test/ui/error-codes/E0746.rs index ad257b01e1b40..c9ab455a4c5b4 100644 --- a/src/test/ui/error-codes/E0746.rs +++ b/src/test/ui/error-codes/E0746.rs @@ -5,13 +5,12 @@ impl Trait for u32 {} fn foo() -> dyn Trait { Struct } //~^ ERROR E0746 -//~| ERROR E0308 fn bar() -> dyn Trait { //~ ERROR E0746 if true { - return 0; //~ ERROR E0308 + return 0; } - 42 //~ ERROR E0308 + 42 } fn main() {} diff --git a/src/test/ui/error-codes/E0746.stderr b/src/test/ui/error-codes/E0746.stderr index baafcd27c29d4..44bd0d7ed7d37 100644 --- a/src/test/ui/error-codes/E0746.stderr +++ b/src/test/ui/error-codes/E0746.stderr @@ -1,14 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/E0746.rs:6:25 - | -LL | fn foo() -> dyn Trait { Struct } - | --------- ^^^^^^ expected trait `Trait`, found struct `Struct` - | | - | expected `(dyn Trait + 'static)` because of return type - | - = note: expected trait object `(dyn Trait + 'static)` - found struct `Struct` - error[E0746]: return type cannot have a bare trait because it must be `Sized` --> $DIR/E0746.rs:6:13 | @@ -22,7 +11,7 @@ LL | fn foo() -> impl Trait { Struct } | ^^^^^^^^^^ error[E0746]: return type cannot have a bare trait because it must be `Sized` - --> $DIR/E0746.rs:10:13 + --> $DIR/E0746.rs:9:13 | LL | fn bar() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time @@ -33,30 +22,5 @@ help: you can use the `impl Trait` feature in the return type because all the re LL | fn bar() -> impl Trait { | ^^^^^^^^^^ -error[E0308]: mismatched types - --> $DIR/E0746.rs:12:16 - | -LL | fn bar() -> dyn Trait { - | --------- expected `(dyn Trait + 'static)` because of return type -LL | if true { -LL | return 0; - | ^ expected trait `Trait`, found integer - | - = note: expected trait object `(dyn Trait + 'static)` - found type `{integer}` - -error[E0308]: mismatched types - --> $DIR/E0746.rs:14:5 - | -LL | fn bar() -> dyn Trait { - | --------- expected `(dyn Trait + 'static)` because of return type -... -LL | 42 - | ^^ expected trait `Trait`, found integer - | - = note: expected trait object `(dyn Trait + 'static)` - found type `{integer}` - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs index 80168ca825774..b70a51dc82511 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs @@ -12,25 +12,23 @@ fn bar() -> (usize, dyn Trait) { (42, Struct) } //~| ERROR E0308 fn bap() -> Trait { Struct } //~^ ERROR E0746 -//~| ERROR E0308 fn ban() -> dyn Trait { Struct } //~^ ERROR E0746 -//~| ERROR E0308 fn bak() -> dyn Trait { unimplemented!() } //~ ERROR E0277 // Suggest using `Box` fn bal() -> dyn Trait { //~ ERROR E0746 if true { - return Struct; //~ ERROR E0308 + return Struct; } - 42 //~ ERROR E0308 + 42 } // Suggest using `impl Trait` fn bat() -> dyn Trait { //~ ERROR E0746 if true { - return 0; //~ ERROR E0308 + return 0; } - 42 //~ ERROR E0308 + 42 } fn main() {} diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index ce4c141a0af9d..a09ce2bb29863 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -38,17 +38,6 @@ LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } = note: required because it appears within the type `(usize, (dyn Trait + 'static))` = note: the return type of a function must have a statically known size -error[E0308]: mismatched types - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:13:21 - | -LL | fn bap() -> Trait { Struct } - | ----- ^^^^^^ expected trait `Trait`, found struct `Struct` - | | - | expected `(dyn Trait + 'static)` because of return type - | - = note: expected trait object `(dyn Trait + 'static)` - found struct `Struct` - error[E0746]: return type cannot have a bare trait because it must be `Sized` --> $DIR/dyn-trait-return-should-be-impl-trait.rs:13:13 | @@ -61,19 +50,8 @@ help: you can use the `impl Trait` feature in the return type because all the re LL | fn bap() -> impl Trait { Struct } | ^^^^^^^^^^ -error[E0308]: mismatched types - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:16:25 - | -LL | fn ban() -> dyn Trait { Struct } - | --------- ^^^^^^ expected trait `Trait`, found struct `Struct` - | | - | expected `(dyn Trait + 'static)` because of return type - | - = note: expected trait object `(dyn Trait + 'static)` - found struct `Struct` - error[E0746]: return type cannot have a bare trait because it must be `Sized` - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:16:13 + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:15:13 | LL | fn ban() -> dyn Trait { Struct } | ^^^^^^^^^ doesn't have a size known at compile-time @@ -85,7 +63,7 @@ LL | fn ban() -> impl Trait { Struct } | ^^^^^^^^^^ error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:19:13 + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:17:13 | LL | fn bak() -> dyn Trait { unimplemented!() } | ^^^^^^^^^ doesn't have a size known at compile-time @@ -94,26 +72,14 @@ LL | fn bak() -> dyn Trait { unimplemented!() } = note: to learn more, visit = note: the return type of a function must have a statically known size -error[E0308]: mismatched types - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:23:16 - | -LL | fn bal() -> dyn Trait { - | --------- expected `(dyn Trait + 'static)` because of return type -LL | if true { -LL | return Struct; - | ^^^^^^ expected trait `Trait`, found struct `Struct` - | - = note: expected trait object `(dyn Trait + 'static)` - found struct `Struct` - error[E0746]: return type cannot have a bare trait because it must be `Sized` - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:21:13 + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:19:13 | LL | fn bal() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | help: if all the returned values were of the same type you could use `impl Trait` as the return type - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:25:5 + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:23:5 | LL | return Struct; | ^^^^^^ @@ -132,20 +98,8 @@ LL | } LL | Box::new(42) | -error[E0308]: mismatched types - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:25:5 - | -LL | fn bal() -> dyn Trait { - | --------- expected `(dyn Trait + 'static)` because of return type -... -LL | 42 - | ^^ expected trait `Trait`, found integer - | - = note: expected trait object `(dyn Trait + 'static)` - found type `{integer}` - error[E0746]: return type cannot have a bare trait because it must be `Sized` - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:29:13 + --> $DIR/dyn-trait-return-should-be-impl-trait.rs:27:13 | LL | fn bat() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time @@ -156,31 +110,7 @@ help: you can use the `impl Trait` feature in the return type because all the re LL | fn bat() -> impl Trait { | ^^^^^^^^^^ -error[E0308]: mismatched types - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:31:16 - | -LL | fn bat() -> dyn Trait { - | --------- expected `(dyn Trait + 'static)` because of return type -LL | if true { -LL | return 0; - | ^ expected trait `Trait`, found integer - | - = note: expected trait object `(dyn Trait + 'static)` - found type `{integer}` - -error[E0308]: mismatched types - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:33:5 - | -LL | fn bat() -> dyn Trait { - | --------- expected `(dyn Trait + 'static)` because of return type -... -LL | 42 - | ^^ expected trait `Trait`, found integer - | - = note: expected trait object `(dyn Trait + 'static)` - found type `{integer}` - -error: aborting due to 15 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. From b4bbe784a9dc1f97f07e2543c6f726cb1eb4cc86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jan 2020 16:13:51 -0800 Subject: [PATCH 04/15] Make `impl Trait` suggestion in E0746 `MachineApplicable` --- src/librustc/traits/error_reporting.rs | 2 +- src/test/ui/error-codes/E0746.fixed | 18 ++++++++++++++++++ src/test/ui/error-codes/E0746.rs | 2 ++ src/test/ui/error-codes/E0746.stderr | 4 ++-- 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/error-codes/E0746.fixed diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 8f771658e4098..8d0923f571851 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1840,7 +1840,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { last_ty, trait_obj, ), format!("impl {}", trait_obj), - Applicability::MaybeIncorrect, + Applicability::MachineApplicable, ); err.note(impl_trait_msg); } else { diff --git a/src/test/ui/error-codes/E0746.fixed b/src/test/ui/error-codes/E0746.fixed new file mode 100644 index 0000000000000..ca8319aa020dc --- /dev/null +++ b/src/test/ui/error-codes/E0746.fixed @@ -0,0 +1,18 @@ +// run-rustfix +#![allow(dead_code)] +struct Struct; +trait Trait {} +impl Trait for Struct {} +impl Trait for u32 {} + +fn foo() -> impl Trait { Struct } +//~^ ERROR E0746 + +fn bar() -> impl Trait { //~ ERROR E0746 + if true { + return 0; + } + 42 +} + +fn main() {} diff --git a/src/test/ui/error-codes/E0746.rs b/src/test/ui/error-codes/E0746.rs index c9ab455a4c5b4..bf5ba8fff562a 100644 --- a/src/test/ui/error-codes/E0746.rs +++ b/src/test/ui/error-codes/E0746.rs @@ -1,3 +1,5 @@ +// run-rustfix +#![allow(dead_code)] struct Struct; trait Trait {} impl Trait for Struct {} diff --git a/src/test/ui/error-codes/E0746.stderr b/src/test/ui/error-codes/E0746.stderr index 44bd0d7ed7d37..0cffd108226d8 100644 --- a/src/test/ui/error-codes/E0746.stderr +++ b/src/test/ui/error-codes/E0746.stderr @@ -1,5 +1,5 @@ error[E0746]: return type cannot have a bare trait because it must be `Sized` - --> $DIR/E0746.rs:6:13 + --> $DIR/E0746.rs:8:13 | LL | fn foo() -> dyn Trait { Struct } | ^^^^^^^^^ doesn't have a size known at compile-time @@ -11,7 +11,7 @@ LL | fn foo() -> impl Trait { Struct } | ^^^^^^^^^^ error[E0746]: return type cannot have a bare trait because it must be `Sized` - --> $DIR/E0746.rs:9:13 + --> $DIR/E0746.rs:11:13 | LL | fn bar() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time From e1dd8a909531cd66080ca89227fb8828a01d7e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jan 2020 17:19:52 -0800 Subject: [PATCH 05/15] When trait bounds are missing for return values, point at them --- src/librustc/traits/error_reporting.rs | 122 +++++++++++------- .../issue-64130-4-async-move.stderr | 13 +- .../alloc-traits-no-impls-length-33.stderr | 15 +++ .../into-iter-no-impls-length-33.stderr | 21 +++ .../impl_bounds.stderr | 2 +- ...n-trait-return-should-be-impl-trait.stderr | 8 +- src/test/ui/issues/issue-58344.stderr | 6 + src/test/ui/issues/issue-5883.stderr | 3 + .../lifetime-elision-return-type-trait.stderr | 3 + .../feature-gate-never_type_fallback.stderr | 4 + .../generic_underconstrained2.stderr | 4 + 11 files changed, 147 insertions(+), 54 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 8d0923f571851..d5af1a9a42dd7 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1130,6 +1130,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; self.note_obligation_cause(&mut err, obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); err.emit(); } @@ -1737,35 +1738,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Recursively look for `TraitObject` types and if there's only one, use that span to // suggest `impl Trait`. - struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>); - - impl<'v> Visitor<'v> for ReturnsVisitor<'v> { - type Map = rustc::hir::map::Map<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<'_, Self::Map> { - hir::intravisit::NestedVisitorMap::None - } - - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - match ex.kind { - hir::ExprKind::Ret(Some(ex)) => self.0.push(ex), - _ => {} - } - hir::intravisit::walk_expr(self, ex); - } - - fn visit_body(&mut self, body: &'v hir::Body<'v>) { - if body.generator_kind().is_none() { - if let hir::ExprKind::Block(block, None) = body.value.kind { - if let Some(expr) = block.expr { - self.0.push(expr); - } - } - } - hir::intravisit::walk_body(self, body); - } - } - // Visit to make sure there's a single `return` type to suggest `impl Trait`, // otherwise suggest using `Box` or an enum. let mut visitor = ReturnsVisitor(vec![]); @@ -1893,6 +1865,38 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { false } + fn point_at_returns_when_relevant( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) { + if let ObligationCauseCode::SizedReturnType = obligation.cause.code.peel_derives() { + } else { + return; + } + + let hir = self.tcx.hir(); + let parent_node = hir.get_parent_node(obligation.cause.body_id); + let node = hir.find(parent_node); + if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) = + node + { + let body = hir.body(*body_id); + // Point at all the `return`s in the function as they have failed trait bounds. + let mut visitor = ReturnsVisitor(vec![]); + visitor.visit_body(&body); + let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); + for expr in &visitor.0 { + if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { + err.span_label( + expr.span, + &format!("this returned value is of type `{}`", returned_ty), + ); + } + } + } + } + /// Given some node representing a fn-like thing in the HIR map, /// returns a span and `ArgKind` information that describes the /// arguments it expects. This can be supplied to @@ -2911,19 +2915,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expressions) => { err.note( - "the `Copy` trait is required because the \ - repeated element will be copied", + "the `Copy` trait is required because the repeated element will be copied", ); if suggest_const_in_array_repeat_expressions { err.note( "this array initializer can be evaluated at compile-time, for more \ - information, see issue \ - https://github.com/rust-lang/rust/issues/49147", + information, see issue \ + https://github.com/rust-lang/rust/issues/49147", ); if tcx.sess.opts.unstable_features.is_nightly_build() { err.help( "add `#![feature(const_in_array_repeat_expressions)]` to the \ - crate attributes to enable", + crate attributes to enable", ); } } @@ -2941,16 +2944,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } ObligationCauseCode::SizedReturnType => { - err.note( - "the return type of a function must have a \ - statically known size", - ); + err.note("the return type of a function must have a statically known size"); } ObligationCauseCode::SizedYieldType => { - err.note( - "the yield type of a generator must have a \ - statically known size", - ); + err.note("the yield type of a generator must have a statically known size"); } ObligationCauseCode::AssignmentLhsSized => { err.note("the left-hand-side of an assignment must have a statically known size"); @@ -2966,12 +2963,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if last { err.note( "the last field of a packed struct may only have a \ - dynamically sized type if it does not need drop to be run", + dynamically sized type if it does not need drop to be run", ); } else { err.note( - "only the last field of a struct may have a dynamically \ - sized type", + "only the last field of a struct may have a dynamically sized type", ); } } @@ -3025,13 +3021,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ObligationCauseCode::CompareImplMethodObligation { .. } => { err.note(&format!( "the requirement `{}` appears on the impl method \ - but not on the corresponding trait method", + but not on the corresponding trait method", predicate )); } ObligationCauseCode::CompareImplTypeObligation { .. } => { err.note(&format!( - "the requirement `{}` appears on the associated impl type\ + "the requirement `{}` appears on the associated impl type \ but not on the corresponding associated trait type", predicate )); @@ -3043,8 +3039,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.help("see issue #48214"); if tcx.sess.opts.unstable_features.is_nightly_build() { err.help( - "add `#![feature(trivial_bounds)]` to the \ - crate attributes to enable", + "add `#![feature(trivial_bounds)]` to the crate attributes to enable", ); } } @@ -3186,3 +3181,32 @@ pub fn suggest_constraining_type_param( } false } + +struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>); + +impl<'v> Visitor<'v> for ReturnsVisitor<'v> { + type Map = rustc::hir::map::Map<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<'_, Self::Map> { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + match ex.kind { + hir::ExprKind::Ret(Some(ex)) => self.0.push(ex), + _ => {} + } + hir::intravisit::walk_expr(self, ex); + } + + fn visit_body(&mut self, body: &'v hir::Body<'v>) { + if body.generator_kind().is_none() { + if let hir::ExprKind::Block(block, None) = body.value.kind { + if let Some(expr) = block.expr { + self.0.push(expr); + } + } + } + hir::intravisit::walk_body(self, body); + } +} diff --git a/src/test/ui/async-await/issue-64130-4-async-move.stderr b/src/test/ui/async-await/issue-64130-4-async-move.stderr index 77d0885c38d58..f59dbc2638400 100644 --- a/src/test/ui/async-await/issue-64130-4-async-move.stderr +++ b/src/test/ui/async-await/issue-64130-4-async-move.stderr @@ -1,8 +1,17 @@ error: future cannot be sent between threads safely --> $DIR/issue-64130-4-async-move.rs:15:17 | -LL | pub fn foo() -> impl Future + Send { - | ^^^^^^^^^^^^^^^^^^ future returned by `foo` is not `Send` +LL | pub fn foo() -> impl Future + Send { + | ^^^^^^^^^^^^^^^^^^ future returned by `foo` is not `Send` +... +LL | / async move { +LL | | match client.status() { +LL | | 200 => { +LL | | let _x = get().await; +... | +LL | | } +LL | | } + | |_____- this returned value is of type `impl std::future::Future` | = help: the trait `std::marker::Sync` is not implemented for `(dyn std::any::Any + std::marker::Send + 'static)` note: future is not `Send` as this value is used across an await diff --git a/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.stderr index 5c37468130c64..6e5afcdb8bb68 100644 --- a/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.stderr +++ b/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.stderr @@ -3,6 +3,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_vec_partial_eq_array() -> impl PartialEq<[B; 33]> | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` +... +LL | Vec::::new() + | --------------- this returned value is of type `std::vec::Vec` | = note: required because of the requirements on the impl of `std::cmp::PartialEq<[B; 33]>` for `std::vec::Vec` = note: the return type of a function must have a statically known size @@ -12,6 +15,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_vec_partial_eq_ref_array<'a, A, B>() -> impl PartialEq<&'a [B; 33]> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` +... +LL | Vec::::new() + | --------------- this returned value is of type `std::vec::Vec` | = note: required because of the requirements on the impl of `std::cmp::PartialEq<&'a [B; 33]>` for `std::vec::Vec` = note: the return type of a function must have a statically known size @@ -21,6 +27,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_vecdeque_partial_eq_array() -> impl PartialEq<[B; 33]> | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` +... +LL | VecDeque::::new() + | -------------------- this returned value is of type `std::collections::VecDeque` | = note: required because of the requirements on the impl of `std::cmp::PartialEq<[B; 33]>` for `std::collections::VecDeque` = note: the return type of a function must have a statically known size @@ -30,6 +39,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_vecdeque_partial_eq_ref_array<'a, A, B>() -> impl PartialEq<&'a [B; 33]> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` +... +LL | VecDeque::::new() + | -------------------- this returned value is of type `std::collections::VecDeque` | = note: required because of the requirements on the impl of `std::cmp::PartialEq<&'a [B; 33]>` for `std::collections::VecDeque` = note: the return type of a function must have a statically known size @@ -39,6 +51,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_vecdeque_partial_eq_ref_mut_array<'a, A, B>() -> impl PartialEq<&'a mut [B; 33]> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` +... +LL | VecDeque::::new() + | -------------------- this returned value is of type `std::collections::VecDeque` | = note: required because of the requirements on the impl of `std::cmp::PartialEq<&'a mut [B; 33]>` for `std::collections::VecDeque` = note: the return type of a function must have a statically known size diff --git a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr index bfdff8e3bbe61..e615e10bd5f5f 100644 --- a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr +++ b/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr @@ -11,6 +11,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_iterator() -> impl Iterator { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` +LL | +LL | IntoIter::new([0i32; 33]) + | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` | = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -28,6 +31,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_double_ended_iterator() -> impl DoubleEndedIterator { | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` +LL | +LL | IntoIter::new([0i32; 33]) + | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` | = note: required because of the requirements on the impl of `std::iter::DoubleEndedIterator` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -45,6 +51,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_exact_size_iterator() -> impl ExactSizeIterator { | ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` +LL | +LL | IntoIter::new([0i32; 33]) + | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` | = note: required because of the requirements on the impl of `std::iter::ExactSizeIterator` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -62,6 +71,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_fused_iterator() -> impl FusedIterator { | ^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` +LL | +LL | IntoIter::new([0i32; 33]) + | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` | = note: required because of the requirements on the impl of `std::iter::FusedIterator` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -79,6 +91,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_trusted_len() -> impl TrustedLen { | ^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` +LL | +LL | IntoIter::new([0i32; 33]) + | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` | = note: required because of the requirements on the impl of `std::iter::TrustedLen` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -96,6 +111,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_clone() -> impl Clone { | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` +LL | +LL | IntoIter::new([0i32; 33]) + | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` | = note: required because of the requirements on the impl of `std::clone::Clone` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -113,6 +131,9 @@ error[E0277]: arrays only have std trait implementations for lengths 0..=32 | LL | pub fn no_debug() -> impl Debug { | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` +LL | +LL | IntoIter::new([0i32; 33]) + | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` | = note: required because of the requirements on the impl of `std::fmt::Debug` for `std::array::IntoIter` = note: the return type of a function must have a statically known size diff --git a/src/test/ui/generic-associated-types/impl_bounds.stderr b/src/test/ui/generic-associated-types/impl_bounds.stderr index 017990076931b..ca2350ff7577f 100644 --- a/src/test/ui/generic-associated-types/impl_bounds.stderr +++ b/src/test/ui/generic-associated-types/impl_bounds.stderr @@ -38,7 +38,7 @@ LL | type C where Self: Copy = String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | = note: required because of the requirements on the impl of `std::marker::Copy` for `Fooy` - = note: the requirement `Fooy: std::marker::Copy` appears on the associated impl typebut not on the corresponding associated trait type + = note: the requirement `Fooy: std::marker::Copy` appears on the associated impl type but not on the corresponding associated trait type error: aborting due to 3 previous errors diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index a09ce2bb29863..d51cd6aeae6ec 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -11,7 +11,9 @@ error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be know --> $DIR/dyn-trait-return-should-be-impl-trait.rs:7:13 | LL | fn fuz() -> (usize, Trait) { (42, Struct) } - | ^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^^^ ------------ this returned value is of type `(usize, (dyn Trait + 'static))` + | | + | doesn't have a size known at compile-time | = help: within `(usize, (dyn Trait + 'static))`, the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` = note: to learn more, visit @@ -31,7 +33,9 @@ error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be know --> $DIR/dyn-trait-return-should-be-impl-trait.rs:10:13 | LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } - | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^^^^^^^ ------------ this returned value is of type `(usize, (dyn Trait + 'static))` + | | + | doesn't have a size known at compile-time | = help: within `(usize, (dyn Trait + 'static))`, the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` = note: to learn more, visit diff --git a/src/test/ui/issues/issue-58344.stderr b/src/test/ui/issues/issue-58344.stderr index 427d03b679d5f..9b07dbd7ab69c 100644 --- a/src/test/ui/issues/issue-58344.stderr +++ b/src/test/ui/issues/issue-58344.stderr @@ -3,6 +3,9 @@ error[E0277]: the trait bound `impl Trait<::Output>: Trait | LL | ) -> Either>::Output>, impl Trait<>::Output>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Trait<::Output>` +... +LL | add_generic(value, 1u32) + | ------------------------ this returned value is of type `Either>::Output>, impl Trait<<_ as std::ops::Add<_>>::Output>>` | = note: the return type of a function must have a statically known size @@ -11,6 +14,9 @@ error[E0277]: the trait bound `impl Trait<::Output>: Trait | LL | ) -> Either>::Output>, impl Trait<>::Output>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Trait<::Output>` +... +LL | add_generic(value, 1u32) + | ------------------------ this returned value is of type `Either>::Output>, impl Trait<<_ as std::ops::Add<_>>::Output>>` | = note: the return type of a function must have a statically known size diff --git a/src/test/ui/issues/issue-5883.stderr b/src/test/ui/issues/issue-5883.stderr index c2de1d095505a..d886ecc11d17b 100644 --- a/src/test/ui/issues/issue-5883.stderr +++ b/src/test/ui/issues/issue-5883.stderr @@ -14,6 +14,9 @@ error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at | LL | -> Struct { | ^^^^^^ doesn't have a size known at compile-time +LL | +LL | Struct { r: r } + | --------------- this returned value is of type `Struct` | = help: within `Struct`, the trait `std::marker::Sized` is not implemented for `(dyn A + 'static)` = note: to learn more, visit diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr index 228582d0001da..49fa11c35aef8 100644 --- a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr +++ b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr @@ -3,6 +3,9 @@ error[E0277]: the trait bound `std::result::Result<(), _>: Future` is not satisf | LL | fn foo() -> impl Future> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Future` is not implemented for `std::result::Result<(), _>` +LL | +LL | Ok(()) + | ------ this returned value is of type `std::result::Result<_, _>` | = note: the return type of a function must have a statically known size diff --git a/src/test/ui/never_type/feature-gate-never_type_fallback.stderr b/src/test/ui/never_type/feature-gate-never_type_fallback.stderr index 837e90d6ceb9b..88bfed2b54742 100644 --- a/src/test/ui/never_type/feature-gate-never_type_fallback.stderr +++ b/src/test/ui/never_type/feature-gate-never_type_fallback.stderr @@ -3,8 +3,12 @@ error[E0277]: the trait bound `(): T` is not satisfied | LL | fn should_ret_unit() -> impl T { | ^^^^^^ the trait `T` is not implemented for `()` +LL | +LL | panic!() + | -------- this returned value is of type `_` | = note: the return type of a function must have a statically known size + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr index d4c7c7c74529e..9e8414f9c15fe 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -18,6 +18,8 @@ LL | type Underconstrained = impl 'static; ... LL | fn underconstrained(_: U) -> Underconstrained { | - help: consider restricting this bound: `U: std::fmt::Debug` +LL | 5u32 + | ---- this returned value is of type `u32` | = help: the trait `std::fmt::Debug` is not implemented for `U` = note: the return type of a function must have a statically known size @@ -30,6 +32,8 @@ LL | type Underconstrained2 = impl 'static; ... LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { | - help: consider restricting this bound: `V: std::fmt::Debug` +LL | 5u32 + | ---- this returned value is of type `u32` | = help: the trait `std::fmt::Debug` is not implemented for `V` = note: the return type of a function must have a statically known size From 93293c56e85a2afdde8664bc08303d5f5853ba29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jan 2020 17:20:28 -0800 Subject: [PATCH 06/15] fmt --- src/librustc/traits/error_reporting.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index d5af1a9a42dd7..77a73aba45484 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -3038,9 +3038,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); if tcx.sess.opts.unstable_features.is_nightly_build() { - err.help( - "add `#![feature(trivial_bounds)]` to the crate attributes to enable", - ); + err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable"); } } ObligationCauseCode::AssocTypeBound(ref data) => { From 4c13d2555c4535516eb00dd0221c9d158b91e31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jan 2020 17:21:31 -0800 Subject: [PATCH 07/15] Add E0746 explanation to the index --- src/librustc_error_codes/error_codes.rs | 2 +- src/librustc_error_codes/error_codes/E0746.md | 137 ++++++++++++++++++ src/test/ui/error-codes/E0746.stderr | 1 + ...n-trait-return-should-be-impl-trait.stderr | 2 +- 4 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 src/librustc_error_codes/error_codes/E0746.md diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index c17cb7dd9f161..180ccb15977dd 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -414,6 +414,7 @@ E0742: include_str!("./error_codes/E0742.md"), E0743: include_str!("./error_codes/E0743.md"), E0744: include_str!("./error_codes/E0744.md"), E0745: include_str!("./error_codes/E0745.md"), +E0746: include_str!("./error_codes/E0746.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -608,5 +609,4 @@ E0745: include_str!("./error_codes/E0745.md"), E0726, // non-explicit (not `'_`) elided lifetime in unsupported position E0727, // `async` generators are not yet supported E0739, // invalid track_caller application/syntax - E0746, // `dyn Trait` return type } diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md new file mode 100644 index 0000000000000..538c9d720d71b --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0746.md @@ -0,0 +1,137 @@ +Return types cannot be `dyn Trait`s as they must be `Sized`. + +Erroneous code example: + +```compile_fail,E0746 +trait T { + fn bar(&self); +} +struct S(usize); +impl T for S { + fn bar(&self) {} +} + +// Having the trait `T` as return type is invalid because bare traits do not +have a statically known size: +fn foo() -> dyn T { + S(42) +} +``` + +To avoid the error there are a couple of options. + +If there is a single type involved, you can use [`impl Trait`]: + +``` +# trait T { +# fn bar(&self); +# } +# struct S(usize); +# impl T for S { +# fn bar(&self) {} +# } +// The compiler will select `S(usize)` as the materialized return type of this +// function, but callers will only be able to access associated items from `T`. +fn foo() -> impl T { + S(42) +} +``` + +If there are multiple types involved, the only way you care to interact with +them is through the trait's interface and having to rely on dynamic dispatch is +acceptable, then you can use [trait objects] with `Box`, or other container +types like `Rc` or `Arc`: + +``` +# trait T { +# fn bar(&self); +# } +# struct S(usize); +# impl T for S { +# fn bar(&self) {} +# } +struct O(&'static str); +impl T for O { + fn bar(&self) {} +} + +// This now returns a "trait object" and callers are only be able to access +// associated items from `T`. +fn foo(x: bool) -> Box { + if x { + Box::new(S(42)) + } else { + Box::new(O("val")) + } +} +``` + +Finally, if you wish to still be able to access the original type, you can +create a new `enum` with a variant for each type: + +``` +# trait T { +# fn bar(&self); +# } +# struct S(usize); +# impl T for S { +# fn bar(&self) {} +# } +# struct O(&'static str); +# impl T for O { +# fn bar(&self) {} +# } +enum E { + S(S), + O(O), +} + +// The caller can access the original types directly, but it needs to match on +// the returned `enum E`. +fn foo(x: bool) -> E { + if x { + E::S(S(42)) + } else { + E::O(O("val")) + } +} +``` + +You can even implement the `trait` on the returned `enum` so the callers +*don't* have to match on the returned value to invoke the associated items: + +``` +# trait T { +# fn bar(&self); +# } +# struct S(usize); +# impl T for S { +# fn bar(&self) {} +# } +# struct O(&'static str); +# impl T for O { +# fn bar(&self) {} +# } +# enum E { +# S(S), +# O(O), +# } +impl T for E { + fn bar(&self) { + match self { + E::S(s) => s.bar(), + E::O(o) => o.bar(), + } + } +} +``` + +If you decide to use trait objects, be aware that these rely on +[dynamic dispatch], which has performance implications, as the compiler needs +to emit code that will figure out which method to call *at runtime* instead of +during compilation. Using trait objects we are trading flexibility for +performance. + +[`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits +[trait objects]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types +[dynamic dispatch]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch diff --git a/src/test/ui/error-codes/E0746.stderr b/src/test/ui/error-codes/E0746.stderr index 0cffd108226d8..1c88ce64749ae 100644 --- a/src/test/ui/error-codes/E0746.stderr +++ b/src/test/ui/error-codes/E0746.stderr @@ -24,3 +24,4 @@ LL | fn bar() -> impl Trait { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0746`. diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index d51cd6aeae6ec..ff7438e9affc1 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -116,5 +116,5 @@ LL | fn bat() -> impl Trait { error: aborting due to 9 previous errors -Some errors have detailed explanations: E0277, E0308. +Some errors have detailed explanations: E0277, E0308, E0746. For more information about an error, try `rustc --explain E0277`. From 00c82726122d550c586e11bf85501a93ce0fbf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jan 2020 18:27:42 -0800 Subject: [PATCH 08/15] Split `librustc/traits/error_reporting.rs` --- src/librustc/traits/error_reporting/mod.rs | 1442 +++++++++++++++ .../error_reporting/on_unimplemented.rs | 199 +++ .../suggestions.rs} | 1557 +---------------- 3 files changed, 1667 insertions(+), 1531 deletions(-) create mode 100644 src/librustc/traits/error_reporting/mod.rs create mode 100644 src/librustc/traits/error_reporting/on_unimplemented.rs rename src/librustc/traits/{error_reporting.rs => error_reporting/suggestions.rs} (52%) diff --git a/src/librustc/traits/error_reporting/mod.rs b/src/librustc/traits/error_reporting/mod.rs new file mode 100644 index 0000000000000..f8329124851b0 --- /dev/null +++ b/src/librustc/traits/error_reporting/mod.rs @@ -0,0 +1,1442 @@ +pub mod on_unimplemented; +pub mod suggestions; + +use super::{ + ConstEvalFailure, EvaluationResult, FulfillmentError, FulfillmentErrorCode, + MismatchedProjectionTypes, ObjectSafetyViolation, Obligation, ObligationCause, + ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, + OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionContext, SelectionError, + TraitNotObjectSafe, +}; + +use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::{self, InferCtxt}; +use crate::mir::interpret::ErrorHandled; +use crate::session::DiagnosticMessageId; +use crate::traits::object_safety_violations; +use crate::ty::error::ExpectedFound; +use crate::ty::fast_reject; +use crate::ty::fold::TypeFolder; +use crate::ty::SubtypePredicate; +use crate::ty::{self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable}; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::intravisit::Visitor; +use rustc_span::source_map::SourceMap; +use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use std::fmt; +use syntax::ast; + +use rustc_error_codes::*; + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + pub fn report_fulfillment_errors( + &self, + errors: &[FulfillmentError<'tcx>], + body_id: Option, + fallback_has_occurred: bool, + ) { + #[derive(Debug)] + struct ErrorDescriptor<'tcx> { + predicate: ty::Predicate<'tcx>, + index: Option, // None if this is an old error + } + + let mut error_map: FxHashMap<_, Vec<_>> = self + .reported_trait_errors + .borrow() + .iter() + .map(|(&span, predicates)| { + ( + span, + predicates + .iter() + .map(|predicate| ErrorDescriptor { + predicate: predicate.clone(), + index: None, + }) + .collect(), + ) + }) + .collect(); + + for (index, error) in errors.iter().enumerate() { + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + + error_map.entry(span).or_default().push(ErrorDescriptor { + predicate: error.obligation.predicate.clone(), + index: Some(index), + }); + + self.reported_trait_errors + .borrow_mut() + .entry(span) + .or_default() + .push(error.obligation.predicate.clone()); + } + + // We do this in 2 passes because we want to display errors in order, though + // maybe it *is* better to sort errors by span or something. + let mut is_suppressed = vec![false; errors.len()]; + for (_, error_set) in error_map.iter() { + // We want to suppress "duplicate" errors with the same span. + for error in error_set { + if let Some(index) = error.index { + // Suppress errors that are either: + // 1) strictly implied by another error. + // 2) implied by an error with a smaller index. + for error2 in error_set { + if error2.index.map_or(false, |index2| is_suppressed[index2]) { + // Avoid errors being suppressed by already-suppressed + // errors, to prevent all errors from being suppressed + // at once. + continue; + } + + if self.error_implies(&error2.predicate, &error.predicate) + && !(error2.index >= error.index + && self.error_implies(&error.predicate, &error2.predicate)) + { + info!("skipping {:?} (implied by {:?})", error, error2); + is_suppressed[index] = true; + break; + } + } + } + } + } + + for (error, suppressed) in errors.iter().zip(is_suppressed) { + if !suppressed { + self.report_fulfillment_error(error, body_id, fallback_has_occurred); + } + } + } + + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: &ty::Predicate<'tcx>, error: &ty::Predicate<'tcx>) -> bool { + if cond == error { + return true; + } + + let (cond, error) = match (cond, error) { + (&ty::Predicate::Trait(..), &ty::Predicate::Trait(ref error)) => (cond, error), + _ => { + // FIXME: make this work in other cases too. + return false; + } + }; + + for implication in super::elaborate_predicates(self.tcx, vec![cond.clone()]) { + if let ty::Predicate::Trait(implication) = implication { + let error = error.to_poly_trait_ref(); + let implication = implication.to_poly_trait_ref(); + // FIXME: I'm just not taking associated types at all here. + // Eventually I'll need to implement param-env-aware + // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. + let param_env = ty::ParamEnv::empty(); + if self.can_sub(param_env, error, implication).is_ok() { + debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); + return true; + } + } + } + + false + } + + fn report_fulfillment_error( + &self, + error: &FulfillmentError<'tcx>, + body_id: Option, + fallback_has_occurred: bool, + ) { + debug!("report_fulfillment_error({:?})", error); + match error.code { + FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { + self.report_selection_error( + &error.obligation, + selection_error, + fallback_has_occurred, + error.points_at_arg_span, + ); + } + FulfillmentErrorCode::CodeProjectionError(ref e) => { + self.report_projection_error(&error.obligation, e); + } + FulfillmentErrorCode::CodeAmbiguity => { + self.maybe_report_ambiguity(&error.obligation, body_id); + } + FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { + self.report_mismatched_types( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + err.clone(), + ) + .emit(); + } + } + } + + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ) { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + + if predicate.references_error() { + return; + } + + self.probe(|_| { + let err_buf; + let mut err = &error.err; + let mut values = None; + + // try to find the mismatched types to report the error with. + // + // this can fail if the problem was higher-ranked, in which + // cause I have no idea for a good error message. + if let ty::Predicate::Projection(ref data) = predicate { + let mut selcx = SelectionContext::new(self); + let (data, _) = self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + infer::LateBoundRegionConversionTime::HigherRankedType, + data, + ); + let mut obligations = vec![]; + let normalized_ty = super::normalize_projection_type( + &mut selcx, + obligation.param_env, + data.projection_ty, + obligation.cause.clone(), + 0, + &mut obligations, + ); + + debug!( + "report_projection_error obligation.cause={:?} obligation.param_env={:?}", + obligation.cause, obligation.param_env + ); + + debug!( + "report_projection_error normalized_ty={:?} data.ty={:?}", + normalized_ty, data.ty + ); + + let is_normalized_ty_expected = match &obligation.cause.code { + ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ObjectCastObligation(_) => false, + _ => true, + }; + + if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( + is_normalized_ty_expected, + normalized_ty, + data.ty, + ) { + values = Some(infer::ValuePairs::Types(ExpectedFound::new( + is_normalized_ty_expected, + normalized_ty, + data.ty, + ))); + + err_buf = error; + err = &err_buf; + } + } + + let msg = format!("type mismatch resolving `{}`", predicate); + let error_id = (DiagnosticMessageId::ErrorId(271), Some(obligation.cause.span), msg); + let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + let mut diag = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0271, + "type mismatch resolving `{}`", + predicate + ); + self.note_type_err(&mut diag, &obligation.cause, None, values, err); + self.note_obligation_cause(&mut diag, obligation); + diag.emit(); + } + }); + } + + fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + /// returns the fuzzy category of a given type, or None + /// if the type can be equated to any type. + fn type_category(t: Ty<'_>) -> Option { + match t.kind { + ty::Bool => Some(0), + ty::Char => Some(1), + ty::Str => Some(2), + ty::Int(..) | ty::Uint(..) | ty::Infer(ty::IntVar(..)) => Some(3), + ty::Float(..) | ty::Infer(ty::FloatVar(..)) => Some(4), + ty::Ref(..) | ty::RawPtr(..) => Some(5), + ty::Array(..) | ty::Slice(..) => Some(6), + ty::FnDef(..) | ty::FnPtr(..) => Some(7), + ty::Dynamic(..) => Some(8), + ty::Closure(..) => Some(9), + ty::Tuple(..) => Some(10), + ty::Projection(..) => Some(11), + ty::Param(..) => Some(12), + ty::Opaque(..) => Some(13), + ty::Never => Some(14), + ty::Adt(adt, ..) => match adt.adt_kind() { + AdtKind::Struct => Some(15), + AdtKind::Union => Some(16), + AdtKind::Enum => Some(17), + }, + ty::Generator(..) => Some(18), + ty::Foreign(..) => Some(19), + ty::GeneratorWitness(..) => Some(20), + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => None, + ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), + } + } + + match (type_category(a), type_category(b)) { + (Some(cat_a), Some(cat_b)) => match (&a.kind, &b.kind) { + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, + _ => cat_a == cat_b, + }, + // infer and error can be equated to all types + _ => true, + } + } + + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { + self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| match gen_kind { + hir::GeneratorKind::Gen => "a generator", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", + }) + } + + fn find_similar_impl_candidates( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Vec> { + let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true); + let all_impls = self.tcx.all_impls(trait_ref.def_id()); + + match simp { + Some(simp) => all_impls + .iter() + .filter_map(|&def_id| { + let imp = self.tcx.impl_trait_ref(def_id).unwrap(); + let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true); + if let Some(imp_simp) = imp_simp { + if simp != imp_simp { + return None; + } + } + + Some(imp) + }) + .collect(), + None => { + all_impls.iter().map(|&def_id| self.tcx.impl_trait_ref(def_id).unwrap()).collect() + } + } + } + + fn report_similar_impl_candidates( + &self, + impl_candidates: Vec>, + err: &mut DiagnosticBuilder<'_>, + ) { + if impl_candidates.is_empty() { + return; + } + + let len = impl_candidates.len(); + let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 }; + + let normalize = |candidate| { + self.tcx.infer_ctxt().enter(|ref infcx| { + let normalized = infcx + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .normalize(candidate) + .ok(); + match normalized { + Some(normalized) => format!("\n {:?}", normalized.value), + None => format!("\n {:?}", candidate), + } + }) + }; + + // Sort impl candidates so that ordering is consistent for UI tests. + let mut normalized_impl_candidates = + impl_candidates.iter().map(normalize).collect::>(); + + // Sort before taking the `..end` range, + // because the ordering of `impl_candidates` may not be deterministic: + // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 + normalized_impl_candidates.sort(); + + err.help(&format!( + "the following implementations were found:{}{}", + normalized_impl_candidates[..end].join(""), + if len > 5 { format!("\nand {} others", len - 4) } else { String::new() } + )); + } + + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + pub fn report_overflow_error( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: fmt::Display + TypeFoldable<'tcx>, + { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + let mut err = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0275, + "overflow evaluating the requirement `{}`", + predicate + ); + + if suggest_increasing_limit { + self.suggest_new_overflow_limit(&mut err); + } + + self.note_obligation_cause_code( + &mut err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![], + ); + + err.emit(); + self.tcx.sess.abort_if_errors(); + bug!(); + } + + /// Reports that a cycle was detected which led to overflow and halts + /// compilation. This is equivalent to `report_overflow_error` except + /// that we can give a more helpful error message (and, in particular, + /// we do not suggest increasing the overflow limit, which is not + /// going to help). + pub fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + let cycle = self.resolve_vars_if_possible(&cycle.to_owned()); + assert!(cycle.len() > 0); + + debug!("report_overflow_error_cycle: cycle={:?}", cycle); + + self.report_overflow_error(&cycle[0], false); + } + + pub fn report_extra_impl_obligation( + &self, + error_span: Span, + item_name: ast::Name, + _impl_item_def_id: DefId, + trait_item_def_id: DefId, + requirement: &dyn fmt::Display, + ) -> DiagnosticBuilder<'tcx> { + let msg = "impl has stricter requirements than trait"; + let sp = self.tcx.sess.source_map().def_span(error_span); + + let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg); + + if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) { + let span = self.tcx.sess.source_map().def_span(trait_item_span); + err.span_label(span, format!("definition of `{}` from trait", item_name)); + } + + err.span_label(sp, format!("impl has extra requirement {}", requirement)); + + err + } + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(String, Option)> { + match code { + &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + match self.get_parent_trait_ref(&data.parent_code) { + Some(t) => Some(t), + None => { + let ty = parent_trait_ref.skip_binder().self_ty(); + let span = + TyCategory::from_ty(ty).map(|(_, def_id)| self.tcx.def_span(def_id)); + Some((ty.to_string(), span)) + } + } + } + _ => None, + } + } + + pub fn report_selection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + fallback_has_occurred: bool, + points_at_arg: bool, + ) { + let tcx = self.tcx; + let span = obligation.cause.span; + + let mut err = match *error { + SelectionError::Unimplemented => { + if let ObligationCauseCode::CompareImplMethodObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } + | ObligationCauseCode::CompareImplTypeObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } = obligation.cause.code + { + self.report_extra_impl_obligation( + span, + item_name, + impl_item_def_id, + trait_item_def_id, + &format!("`{}`", obligation.predicate), + ) + .emit(); + return; + } + match obligation.predicate { + ty::Predicate::Trait(ref trait_predicate) => { + let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + + if self.tcx.sess.has_errors() && trait_predicate.references_error() { + return; + } + let trait_ref = trait_predicate.to_poly_trait_ref(); + let (post_message, pre_message, type_def) = self + .get_parent_trait_ref(&obligation.cause.code) + .map(|(t, s)| { + ( + format!(" in `{}`", t), + format!("within `{}`, ", t), + s.map(|s| (format!("within this `{}`", t), s)), + ) + }) + .unwrap_or_default(); + + let OnUnimplementedNote { message, label, note, enclosing_scope } = + self.on_unimplemented_note(trait_ref, obligation); + let have_alt_message = message.is_some() || label.is_some(); + let is_try = self + .tcx + .sess + .source_map() + .span_to_snippet(span) + .map(|s| &s == "?") + .unwrap_or(false); + let is_from = format!("{}", trait_ref.print_only_trait_path()) + .starts_with("std::convert::From<"); + let (message, note) = if is_try && is_from { + ( + Some(format!( + "`?` couldn't convert the error to `{}`", + trait_ref.self_ty(), + )), + Some( + "the question mark operation (`?`) implicitly performs a \ + conversion on the error value using the `From` trait" + .to_owned(), + ), + ) + } else { + (message, note) + }; + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0277, + "{}", + message.unwrap_or_else(|| format!( + "the trait bound `{}` is not satisfied{}", + trait_ref.to_predicate(), + post_message, + )) + ); + + let explanation = + if obligation.cause.code == ObligationCauseCode::MainFunctionType { + "consider using `()`, or a `Result`".to_owned() + } else { + format!( + "{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_ref.print_only_trait_path(), + trait_ref.self_ty(), + ) + }; + + if self.suggest_add_reference_to_arg( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + have_alt_message, + ) { + self.note_obligation_cause(&mut err, obligation); + err.emit(); + return; + } + if let Some(ref s) = label { + // If it has a custom `#[rustc_on_unimplemented]` + // error message, let's display it as the label! + err.span_label(span, s.as_str()); + err.help(&explanation); + } else { + err.span_label(span, explanation); + } + if let Some((msg, span)) = type_def { + err.span_label(span, &msg); + } + if let Some(ref s) = note { + // If it has a custom `#[rustc_on_unimplemented]` note, let's display it + err.note(s.as_str()); + } + if let Some(ref s) = enclosing_scope { + let enclosing_scope_span = tcx.def_span( + tcx.hir() + .opt_local_def_id(obligation.cause.body_id) + .unwrap_or_else(|| { + tcx.hir().body_owner_def_id(hir::BodyId { + hir_id: obligation.cause.body_id, + }) + }), + ); + + err.span_label(enclosing_scope_span, s.as_str()); + } + + self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err); + self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); + self.suggest_remove_reference(&obligation, &mut err, &trait_ref); + self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); + self.note_version_mismatch(&mut err, &trait_ref); + if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { + err.emit(); + return; + } + + // Try to report a help message + if !trait_ref.has_infer_types() + && self.predicate_can_apply(obligation.param_env, trait_ref) + { + // If a where-clause may be useful, remind the + // user that they can add it. + // + // don't display an on-unimplemented note, as + // these notes will often be of the form + // "the type `T` can't be frobnicated" + // which is somewhat confusing. + self.suggest_restricting_param_bound( + &mut err, + &trait_ref, + obligation.cause.body_id, + ); + } else { + if !have_alt_message { + // Can't show anything else useful, try to find similar impls. + let impl_candidates = self.find_similar_impl_candidates(trait_ref); + self.report_similar_impl_candidates(impl_candidates, &mut err); + } + self.suggest_change_mut( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + ); + } + + // If this error is due to `!: Trait` not implemented but `(): Trait` is + // implemented, and fallback has occurred, then it could be due to a + // variable that used to fallback to `()` now falling back to `!`. Issue a + // note informing about the change in behaviour. + if trait_predicate.skip_binder().self_ty().is_never() + && fallback_has_occurred + { + let predicate = trait_predicate.map_bound(|mut trait_pred| { + trait_pred.trait_ref.substs = self.tcx.mk_substs_trait( + self.tcx.mk_unit(), + &trait_pred.trait_ref.substs[1..], + ); + trait_pred + }); + let unit_obligation = Obligation { + predicate: ty::Predicate::Trait(predicate), + ..obligation.clone() + }; + if self.predicate_may_hold(&unit_obligation) { + err.note( + "the trait is implemented for `()`. \ + Possibly this error has been caused by changes to \ + Rust's type-inference algorithm \ + (see: https://github.com/rust-lang/rust/issues/48950 \ + for more info). Consider whether you meant to use the \ + type `()` here instead.", + ); + } + } + + err + } + + ty::Predicate::Subtype(ref predicate) => { + // Errors for Subtype predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) + } + + ty::Predicate::RegionOutlives(ref predicate) => { + let predicate = self.resolve_vars_if_possible(predicate); + let err = self + .region_outlives_predicate(&obligation.cause, &predicate) + .err() + .unwrap(); + struct_span_err!( + self.tcx.sess, + span, + E0279, + "the requirement `{}` is not satisfied (`{}`)", + predicate, + err, + ) + } + + ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + struct_span_err!( + self.tcx.sess, + span, + E0280, + "the requirement `{}` is not satisfied", + predicate + ) + } + + ty::Predicate::ObjectSafe(trait_def_id) => { + let violations = object_safety_violations(self.tcx, trait_def_id); + report_object_safety_error(self.tcx, span, trait_def_id, violations) + } + + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + let found_kind = self.closure_kind(closure_def_id, closure_substs).unwrap(); + let closure_span = self + .tcx + .sess + .source_map() + .def_span(self.tcx.hir().span_if_local(closure_def_id).unwrap()); + let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id).unwrap(); + let mut err = struct_span_err!( + self.tcx.sess, + closure_span, + E0525, + "expected a closure that implements the `{}` trait, \ + but this closure only implements `{}`", + kind, + found_kind + ); + + err.span_label( + closure_span, + format!("this closure implements `{}`, not `{}`", found_kind, kind), + ); + err.span_label( + obligation.cause.span, + format!("the requirement to implement `{}` derives from here", kind), + ); + + // Additional context information explaining why the closure only implements + // a particular trait. + if let Some(tables) = self.in_progress_tables { + let tables = tables.borrow(); + match (found_kind, tables.closure_kind_origins().get(hir_id)) { + (ty::ClosureKind::FnOnce, Some((span, name))) => { + err.span_label( + *span, + format!( + "closure is `FnOnce` because it moves the \ + variable `{}` out of its environment", + name + ), + ); + } + (ty::ClosureKind::FnMut, Some((span, name))) => { + err.span_label( + *span, + format!( + "closure is `FnMut` because it mutates the \ + variable `{}` here", + name + ), + ); + } + _ => {} + } + } + + err.emit(); + return; + } + + ty::Predicate::WellFormed(ty) => { + if !self.tcx.sess.opts.debugging_opts.chalk { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); + } else { + // FIXME: we'll need a better message which takes into account + // which bounds actually failed to hold. + self.tcx.sess.struct_span_err( + span, + &format!("the type `{}` is not well-formed (chalk)", ty), + ) + } + } + + ty::Predicate::ConstEvaluatable(..) => { + // Errors for `ConstEvaluatable` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-evaluatable requirement gave wrong error: `{:?}`", + obligation + ) + } + } + } + + OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { + let found_trait_ref = self.resolve_vars_if_possible(&*found_trait_ref); + let expected_trait_ref = self.resolve_vars_if_possible(&*expected_trait_ref); + + if expected_trait_ref.self_ty().references_error() { + return; + } + + let found_trait_ty = found_trait_ref.self_ty(); + + let found_did = match found_trait_ty.kind { + ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), + ty::Adt(def, _) => Some(def.did), + _ => None, + }; + + let found_span = found_did + .and_then(|did| self.tcx.hir().span_if_local(did)) + .map(|sp| self.tcx.sess.source_map().def_span(sp)); // the sp could be an fn def + + if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { + // We check closures twice, with obligations flowing in different directions, + // but we want to complain about them only once. + return; + } + + self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); + + let found = match found_trait_ref.skip_binder().substs.type_at(1).kind { + ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], + _ => vec![ArgKind::empty()], + }; + + let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); + let expected = match expected_ty.kind { + ty::Tuple(ref tys) => tys + .iter() + .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span))) + .collect(), + _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], + }; + + if found.len() == expected.len() { + self.report_closure_arg_mismatch( + span, + found_span, + found_trait_ref, + expected_trait_ref, + ) + } else { + let (closure_span, found) = found_did + .and_then(|did| self.tcx.hir().get_if_local(did)) + .map(|node| { + let (found_span, found) = self.get_fn_like_arguments(node); + (Some(found_span), found) + }) + .unwrap_or((found_span, found)); + + self.report_arg_count_mismatch( + span, + closure_span, + expected, + found, + found_trait_ty.is_closure(), + ) + } + } + + TraitNotObjectSafe(did) => { + let violations = object_safety_violations(self.tcx, did); + report_object_safety_error(self.tcx, span, did, violations) + } + + // already reported in the query + ConstEvalFailure(err) => { + if let ErrorHandled::TooGeneric = err { + // Silence this error, as it can be produced during intermediate steps + // when a constant is not yet able to be evaluated (but will be later). + return; + } + self.tcx.sess.delay_span_bug( + span, + &format!("constant in type had an ignored error: {:?}", err), + ); + return; + } + + Overflow => { + bug!("overflow should be handled before the `report_selection_error` path"); + } + }; + + self.note_obligation_cause(&mut err, obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + + err.emit(); + } + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) { + let get_trait_impl = |trait_def_id| { + let mut trait_impl = None; + self.tcx.for_each_relevant_impl(trait_def_id, trait_ref.self_ty(), |impl_def_id| { + if trait_impl.is_none() { + trait_impl = Some(impl_def_id); + } + }); + trait_impl + }; + let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); + let all_traits = self.tcx.all_traits(LOCAL_CRATE); + let traits_with_same_path: std::collections::BTreeSet<_> = all_traits + .iter() + .filter(|trait_def_id| **trait_def_id != trait_ref.def_id()) + .filter(|trait_def_id| self.tcx.def_path_str(**trait_def_id) == required_trait_path) + .collect(); + for trait_with_same_path in traits_with_same_path { + if let Some(impl_def_id) = get_trait_impl(*trait_with_same_path) { + let impl_span = self.tcx.def_span(impl_def_id); + err.span_help(impl_span, "trait impl with same name found"); + let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); + let crate_msg = format!( + "perhaps two different versions of crate `{}` are being used?", + trait_crate + ); + err.note(&crate_msg); + } + } + } + + fn mk_obligation_for_def_id( + &self, + def_id: DefId, + output_ty: Ty<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> PredicateObligation<'tcx> { + let new_trait_ref = + ty::TraitRef { def_id, substs: self.tcx.mk_substs_trait(output_ty, &[]) }; + Obligation::new(cause, param_env, new_trait_ref.to_predicate()) + } +} + +pub fn recursive_type_with_infinite_size_error( + tcx: TyCtxt<'tcx>, + type_def_id: DefId, +) -> DiagnosticBuilder<'tcx> { + assert!(type_def_id.is_local()); + let span = tcx.hir().span_if_local(type_def_id).unwrap(); + let span = tcx.sess.source_map().def_span(span); + let mut err = struct_span_err!( + tcx.sess, + span, + E0072, + "recursive type `{}` has infinite size", + tcx.def_path_str(type_def_id) + ); + err.span_label(span, "recursive type has infinite size"); + err.help(&format!( + "insert indirection (e.g., a `Box`, `Rc`, or `&`) \ + at some point to make `{}` representable", + tcx.def_path_str(type_def_id) + )); + err +} + +pub fn report_object_safety_error( + tcx: TyCtxt<'tcx>, + span: Span, + trait_def_id: DefId, + violations: Vec, +) -> DiagnosticBuilder<'tcx> { + let trait_str = tcx.def_path_str(trait_def_id); + let span = tcx.sess.source_map().def_span(span); + let mut err = struct_span_err!( + tcx.sess, + span, + E0038, + "the trait `{}` cannot be made into an object", + trait_str + ); + err.span_label(span, format!("the trait `{}` cannot be made into an object", trait_str)); + + let mut reported_violations = FxHashSet::default(); + for violation in violations { + if reported_violations.insert(violation.clone()) { + match violation.span() { + Some(span) => err.span_label(span, violation.error_msg()), + None => err.note(&violation.error_msg()), + }; + } + } + + if tcx.sess.trait_methods_not_found.borrow().contains(&span) { + // Avoid emitting error caused by non-existing method (#58734) + err.cancel(); + } + + err +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + fn maybe_report_ambiguity( + &self, + obligation: &PredicateObligation<'tcx>, + body_id: Option, + ) { + // Unable to successfully determine, probably means + // insufficient type information, but could mean + // ambiguous impls. The latter *ought* to be a + // coherence violation, so we don't report it here. + + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + let span = obligation.cause.span; + + debug!( + "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})", + predicate, obligation, body_id, obligation.cause.code, + ); + + // Ambiguity errors are often caused as fallout from earlier + // errors. So just ignore them if this infcx is tainted. + if self.is_tainted_by_errors() { + return; + } + + let mut err = match predicate { + ty::Predicate::Trait(ref data) => { + let trait_ref = data.to_poly_trait_ref(); + let self_ty = trait_ref.self_ty(); + debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref); + + if predicate.references_error() { + return; + } + // Typically, this ambiguity should only happen if + // there are unresolved type inference variables + // (otherwise it would suggest a coherence + // failure). But given #21974 that is not necessarily + // the case -- we can have multiple where clauses that + // are only distinguished by a region, which results + // in an ambiguity even when all types are fully + // known, since we don't dispatch based on region + // relationships. + + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. We used to check for `self.tcx.sess.has_errors()` to + // avoid inundating the user with unnecessary errors, but we now + // check upstream for type errors and dont add the obligations to + // begin with in those cases. + if self + .tcx + .lang_items() + .sized_trait() + .map_or(false, |sized_id| sized_id == trait_ref.def_id()) + { + self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0282).emit(); + return; + } + let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0283); + err.note(&format!("cannot resolve `{}`", predicate)); + if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); + } else if let ( + Ok(ref snippet), + ObligationCauseCode::BindingObligation(ref def_id, _), + ) = + (self.tcx.sess.source_map().span_to_snippet(span), &obligation.cause.code) + { + let generics = self.tcx.generics_of(*def_id); + if !generics.params.is_empty() && !snippet.ends_with('>') { + // FIXME: To avoid spurious suggestions in functions where type arguments + // where already supplied, we check the snippet to make sure it doesn't + // end with a turbofish. Ideally we would have access to a `PathSegment` + // instead. Otherwise we would produce the following output: + // + // error[E0283]: type annotations needed + // --> $DIR/issue-54954.rs:3:24 + // | + // LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); + // | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + // | | + // | cannot infer type + // | help: consider specifying the type argument + // | in the function call: + // | `Tt::const_val::<[i8; 123]>::` + // ... + // LL | const fn const_val() -> usize { + // | --------- - required by this bound in `Tt::const_val` + // | + // = note: cannot resolve `_: Tt` + + err.span_suggestion( + span, + &format!( + "consider specifying the type argument{} in the function call", + if generics.params.len() > 1 { "s" } else { "" }, + ), + format!( + "{}::<{}>", + snippet, + generics + .params + .iter() + .map(|p| p.name.to_string()) + .collect::>() + .join(", ") + ), + Applicability::HasPlaceholders, + ); + } + } + err + } + + ty::Predicate::WellFormed(ty) => { + // Same hacky approach as above to avoid deluging user + // with error messages. + if ty.references_error() || self.tcx.sess.has_errors() { + return; + } + self.need_type_info_err(body_id, span, ty, ErrorCode::E0282) + } + + ty::Predicate::Subtype(ref data) => { + if data.references_error() || self.tcx.sess.has_errors() { + // no need to overload user in such cases + return; + } + let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder(); + // both must be type variables, or the other would've been instantiated + assert!(a.is_ty_var() && b.is_ty_var()); + self.need_type_info_err(body_id, span, a, ErrorCode::E0282) + } + ty::Predicate::Projection(ref data) => { + let trait_ref = data.to_poly_trait_ref(self.tcx); + let self_ty = trait_ref.self_ty(); + if predicate.references_error() { + return; + } + let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0284); + err.note(&format!("cannot resolve `{}`", predicate)); + err + } + + _ => { + if self.tcx.sess.has_errors() { + return; + } + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot resolve `{}`", + predicate, + ); + err.span_label(span, &format!("cannot resolve `{}`", predicate)); + err + } + }; + self.note_obligation_cause(&mut err, obligation); + err.emit(); + } + + /// Returns `true` if the trait predicate may apply for *some* assignment + /// to the type parameters. + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitRef<'tcx>, + ) -> bool { + struct ParamToVarFolder<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + var_map: FxHashMap, Ty<'tcx>>, + } + + impl<'a, 'tcx> TypeFolder<'tcx> for ParamToVarFolder<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Param(ty::ParamTy { name, .. }) = ty.kind { + let infcx = self.infcx; + self.var_map.entry(ty).or_insert_with(|| { + infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeParameterDefinition(name, None), + span: DUMMY_SP, + }) + }) + } else { + ty.super_fold_with(self) + } + } + } + + self.probe(|_| { + let mut selcx = SelectionContext::new(self); + + let cleaned_pred = + pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); + + let cleaned_pred = super::project::normalize( + &mut selcx, + param_env, + ObligationCause::dummy(), + &cleaned_pred, + ) + .value; + + let obligation = + Obligation::new(ObligationCause::dummy(), param_env, cleaned_pred.to_predicate()); + + self.predicate_may_hold(&obligation) + }) + } + + fn note_obligation_cause( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ) { + // First, attempt to add note to this error with an async-await-specific + // message, and fall back to regular note otherwise. + if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { + self.note_obligation_cause_code( + err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![], + ); + } + } + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<&ty::TyS<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool { + if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + + if obligated_types.iter().any(|ot| ot == &parent_trait_ref.skip_binder().self_ty()) { + return true; + } + } + false + } +} + +/// Summarizes information +#[derive(Clone)] +pub enum ArgKind { + /// An argument of non-tuple type. Parameters are (name, ty) + Arg(String, String), + + /// An argument of tuple type. For a "found" argument, the span is + /// the locationo in the source of the pattern. For a "expected" + /// argument, it will be None. The vector is a list of (name, ty) + /// strings for the components of the tuple. + Tuple(Option, Vec<(String, String)>), +} + +impl ArgKind { + fn empty() -> ArgKind { + ArgKind::Arg("_".to_owned(), "_".to_owned()) + } + + /// Creates an `ArgKind` from the expected type of an + /// argument. It has no name (`_`) and an optional source span. + pub fn from_expected_ty(t: Ty<'_>, span: Option) -> ArgKind { + match t.kind { + ty::Tuple(ref tys) => ArgKind::Tuple( + span, + tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::>(), + ), + _ => ArgKind::Arg("_".to_owned(), t.to_string()), + } + } +} + +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_param( + generics: &hir::Generics<'_>, + err: &mut DiagnosticBuilder<'_>, + param_name: &str, + constraint: &str, + source_map: &SourceMap, + span: Span, +) -> bool { + let restrict_msg = "consider further restricting this bound"; + if let Some(param) = + generics.params.iter().filter(|p| p.name.ident().as_str() == param_name).next() + { + if param_name.starts_with("impl ") { + // `impl Trait` in argument: + // `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}` + err.span_suggestion( + param.span, + restrict_msg, + // `impl CurrentTrait + MissingTrait` + format!("{} + {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } else if generics.where_clause.predicates.is_empty() && param.bounds.is_empty() { + // If there are no bounds whatsoever, suggest adding a constraint + // to the type parameter: + // `fn foo(t: T) {}` → `fn foo(t: T) {}` + err.span_suggestion( + param.span, + "consider restricting this bound", + format!("{}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } else if !generics.where_clause.predicates.is_empty() { + // There is a `where` clause, so suggest expanding it: + // `fn foo(t: T) where T: Debug {}` → + // `fn foo(t: T) where T: Debug, T: Trait {}` + err.span_suggestion( + generics.where_clause.span().unwrap().shrink_to_hi(), + &format!("consider further restricting type parameter `{}`", param_name), + format!(", {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } else { + // If there is no `where` clause lean towards constraining to the + // type parameter: + // `fn foo(t: T, x: X) {}` → `fn foo(t: T) {}` + // `fn foo(t: T) {}` → `fn foo(t: T) {}` + let sp = param.span.with_hi(span.hi()); + let span = source_map.span_through_char(sp, ':'); + if sp != param.span && sp != span { + // Only suggest if we have high certainty that the span + // covers the colon in `foo`. + err.span_suggestion( + span, + restrict_msg, + format!("{}: {} + ", param_name, constraint), + Applicability::MachineApplicable, + ); + } else { + err.span_label( + param.span, + &format!("consider adding a `where {}: {}` bound", param_name, constraint), + ); + } + } + return true; + } + false +} + +struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>); + +impl<'v> Visitor<'v> for ReturnsVisitor<'v> { + type Map = rustc::hir::map::Map<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<'_, Self::Map> { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + match ex.kind { + hir::ExprKind::Ret(Some(ex)) => self.0.push(ex), + _ => {} + } + hir::intravisit::walk_expr(self, ex); + } + + fn visit_body(&mut self, body: &'v hir::Body<'v>) { + if body.generator_kind().is_none() { + if let hir::ExprKind::Block(block, None) = body.value.kind { + if let Some(expr) = block.expr { + self.0.push(expr); + } + } + } + hir::intravisit::walk_body(self, body); + } +} diff --git a/src/librustc/traits/error_reporting/on_unimplemented.rs b/src/librustc/traits/error_reporting/on_unimplemented.rs new file mode 100644 index 0000000000000..9f3fc91548b21 --- /dev/null +++ b/src/librustc/traits/error_reporting/on_unimplemented.rs @@ -0,0 +1,199 @@ +use super::{ + ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, +}; +use crate::infer::InferCtxt; +use crate::ty::subst::Subst; +use crate::ty::{self, GenericParamDefKind}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::sym; + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + fn impl_similar_to( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> Option { + let tcx = self.tcx; + let param_env = obligation.param_env; + let trait_ref = tcx.erase_late_bound_regions(&trait_ref); + let trait_self_ty = trait_ref.self_ty(); + + let mut self_match_impls = vec![]; + let mut fuzzy_match_impls = vec![]; + + self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { + let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); + let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); + + let impl_self_ty = impl_trait_ref.self_ty(); + + if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { + self_match_impls.push(def_id); + + if trait_ref + .substs + .types() + .skip(1) + .zip(impl_trait_ref.substs.types().skip(1)) + .all(|(u, v)| self.fuzzy_match_tys(u, v)) + { + fuzzy_match_impls.push(def_id); + } + } + }); + + let impl_def_id = if self_match_impls.len() == 1 { + self_match_impls[0] + } else if fuzzy_match_impls.len() == 1 { + fuzzy_match_impls[0] + } else { + return None; + }; + + tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) + } + + /// Used to set on_unimplemented's `ItemContext` + /// to be the enclosing (async) block/function/closure + fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { + let hir = &self.tcx.hir(); + let node = hir.find(hir_id)?; + if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) = &node { + self.describe_generator(*body_id).or_else(|| { + Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { + "an async function" + } else { + "a function" + }) + }) + } else if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), + .. + }) = &node + { + self.describe_generator(*body_id).or_else(|| { + Some(if gen_movability.is_some() { "an async closure" } else { "a closure" }) + }) + } else if let hir::Node::Expr(hir::Expr { .. }) = &node { + let parent_hid = hir.get_parent_node(hir_id); + if parent_hid != hir_id { + return self.describe_enclosure(parent_hid); + } else { + None + } + } else { + None + } + } + + crate fn on_unimplemented_note( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> OnUnimplementedNote { + let def_id = + self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); + let trait_ref = *trait_ref.skip_binder(); + + let mut flags = vec![]; + flags.push(( + sym::item_context, + self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), + )); + + match obligation.cause.code { + ObligationCauseCode::BuiltinDerivedObligation(..) + | ObligationCauseCode::ImplDerivedObligation(..) => {} + _ => { + // this is a "direct", user-specified, rather than derived, + // obligation. + flags.push((sym::direct, None)); + } + } + + if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code { + // FIXME: maybe also have some way of handling methods + // from other traits? That would require name resolution, + // which we might want to be some sort of hygienic. + // + // Currently I'm leaving it for what I need for `try`. + if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { + let method = self.tcx.item_name(item); + flags.push((sym::from_method, None)); + flags.push((sym::from_method, Some(method.to_string()))); + } + } + if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) { + flags.push((sym::parent_trait, Some(t))); + } + + if let Some(k) = obligation.cause.span.desugaring_kind() { + flags.push((sym::from_desugaring, None)); + flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); + } + let generics = self.tcx.generics_of(def_id); + let self_ty = trait_ref.self_ty(); + // This is also included through the generics list as `Self`, + // but the parser won't allow you to use it + flags.push((sym::_Self, Some(self_ty.to_string()))); + if let Some(def) = self_ty.ty_adt_def() { + // We also want to be able to select self's original + // signature with no type arguments resolved + flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); + } + + for param in generics.params.iter() { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => continue, + }; + let name = param.name; + flags.push((name, Some(value))); + } + + if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { + flags.push((sym::crate_local, None)); + } + + // Allow targeting all integers using `{integral}`, even if the exact type was resolved + if self_ty.is_integral() { + flags.push((sym::_Self, Some("{integral}".to_owned()))); + } + + if let ty::Array(aty, len) = self_ty.kind { + flags.push((sym::_Self, Some("[]".to_owned()))); + flags.push((sym::_Self, Some(format!("[{}]", aty)))); + if let Some(def) = aty.ty_adt_def() { + // We also want to be able to select the array's type's original + // signature with no type arguments resolved + flags.push(( + sym::_Self, + Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), + )); + let tcx = self.tcx; + if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) { + flags.push(( + sym::_Self, + Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), + )); + } else { + flags.push(( + sym::_Self, + Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), + )); + } + } + } + + if let Ok(Some(command)) = + OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) + { + command.evaluate(self.tcx, trait_ref, &flags[..]) + } else { + OnUnimplementedNote::default() + } + } +} diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting/suggestions.rs similarity index 52% rename from src/librustc/traits/error_reporting.rs rename to src/librustc/traits/error_reporting/suggestions.rs index 77a73aba45484..389edfa071794 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting/suggestions.rs @@ -1,1178 +1,28 @@ -// ignore-tidy-filelength use super::{ - ConstEvalFailure, EvaluationResult, FulfillmentError, FulfillmentErrorCode, - MismatchedProjectionTypes, ObjectSafetyViolation, Obligation, ObligationCause, - ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, - OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionContext, SelectionError, - TraitNotObjectSafe, + ArgKind, EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, + PredicateObligation, }; -use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{self, InferCtxt}; -use crate::mir::interpret::ErrorHandled; -use crate::session::DiagnosticMessageId; -use crate::traits::object_safety_violations; -use crate::ty::error::ExpectedFound; -use crate::ty::fast_reject; -use crate::ty::fold::TypeFolder; -use crate::ty::subst::Subst; -use crate::ty::GenericParamDefKind; -use crate::ty::SubtypePredicate; +use crate::infer::InferCtxt; use crate::ty::TypeckTables; -use crate::ty::{self, AdtKind, DefIdTree, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style, }; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; -use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP}; +use rustc_span::{MultiSpan, Span, DUMMY_SP}; use std::fmt; -use syntax::ast; use rustc_error_codes::*; impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - pub fn report_fulfillment_errors( - &self, - errors: &[FulfillmentError<'tcx>], - body_id: Option, - fallback_has_occurred: bool, - ) { - #[derive(Debug)] - struct ErrorDescriptor<'tcx> { - predicate: ty::Predicate<'tcx>, - index: Option, // None if this is an old error - } - - let mut error_map: FxHashMap<_, Vec<_>> = self - .reported_trait_errors - .borrow() - .iter() - .map(|(&span, predicates)| { - ( - span, - predicates - .iter() - .map(|predicate| ErrorDescriptor { - predicate: predicate.clone(), - index: None, - }) - .collect(), - ) - }) - .collect(); - - for (index, error) in errors.iter().enumerate() { - // We want to ignore desugarings here: spans are equivalent even - // if one is the result of a desugaring and the other is not. - let mut span = error.obligation.cause.span; - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(_) = expn_data.kind { - span = expn_data.call_site; - } - - error_map.entry(span).or_default().push(ErrorDescriptor { - predicate: error.obligation.predicate.clone(), - index: Some(index), - }); - - self.reported_trait_errors - .borrow_mut() - .entry(span) - .or_default() - .push(error.obligation.predicate.clone()); - } - - // We do this in 2 passes because we want to display errors in order, though - // maybe it *is* better to sort errors by span or something. - let mut is_suppressed = vec![false; errors.len()]; - for (_, error_set) in error_map.iter() { - // We want to suppress "duplicate" errors with the same span. - for error in error_set { - if let Some(index) = error.index { - // Suppress errors that are either: - // 1) strictly implied by another error. - // 2) implied by an error with a smaller index. - for error2 in error_set { - if error2.index.map_or(false, |index2| is_suppressed[index2]) { - // Avoid errors being suppressed by already-suppressed - // errors, to prevent all errors from being suppressed - // at once. - continue; - } - - if self.error_implies(&error2.predicate, &error.predicate) - && !(error2.index >= error.index - && self.error_implies(&error.predicate, &error2.predicate)) - { - info!("skipping {:?} (implied by {:?})", error, error2); - is_suppressed[index] = true; - break; - } - } - } - } - } - - for (error, suppressed) in errors.iter().zip(is_suppressed) { - if !suppressed { - self.report_fulfillment_error(error, body_id, fallback_has_occurred); - } - } - } - - // returns if `cond` not occurring implies that `error` does not occur - i.e., that - // `error` occurring implies that `cond` occurs. - fn error_implies(&self, cond: &ty::Predicate<'tcx>, error: &ty::Predicate<'tcx>) -> bool { - if cond == error { - return true; - } - - let (cond, error) = match (cond, error) { - (&ty::Predicate::Trait(..), &ty::Predicate::Trait(ref error)) => (cond, error), - _ => { - // FIXME: make this work in other cases too. - return false; - } - }; - - for implication in super::elaborate_predicates(self.tcx, vec![cond.clone()]) { - if let ty::Predicate::Trait(implication) = implication { - let error = error.to_poly_trait_ref(); - let implication = implication.to_poly_trait_ref(); - // FIXME: I'm just not taking associated types at all here. - // Eventually I'll need to implement param-env-aware - // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. - let param_env = ty::ParamEnv::empty(); - if self.can_sub(param_env, error, implication).is_ok() { - debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); - return true; - } - } - } - - false - } - - fn report_fulfillment_error( - &self, - error: &FulfillmentError<'tcx>, - body_id: Option, - fallback_has_occurred: bool, - ) { - debug!("report_fulfillment_error({:?})", error); - match error.code { - FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { - self.report_selection_error( - &error.obligation, - selection_error, - fallback_has_occurred, - error.points_at_arg_span, - ); - } - FulfillmentErrorCode::CodeProjectionError(ref e) => { - self.report_projection_error(&error.obligation, e); - } - FulfillmentErrorCode::CodeAmbiguity => { - self.maybe_report_ambiguity(&error.obligation, body_id); - } - FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { - self.report_mismatched_types( - &error.obligation.cause, - expected_found.expected, - expected_found.found, - err.clone(), - ) - .emit(); - } - } - } - - fn report_projection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &MismatchedProjectionTypes<'tcx>, - ) { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - - if predicate.references_error() { - return; - } - - self.probe(|_| { - let err_buf; - let mut err = &error.err; - let mut values = None; - - // try to find the mismatched types to report the error with. - // - // this can fail if the problem was higher-ranked, in which - // cause I have no idea for a good error message. - if let ty::Predicate::Projection(ref data) = predicate { - let mut selcx = SelectionContext::new(self); - let (data, _) = self.replace_bound_vars_with_fresh_vars( - obligation.cause.span, - infer::LateBoundRegionConversionTime::HigherRankedType, - data, - ); - let mut obligations = vec![]; - let normalized_ty = super::normalize_projection_type( - &mut selcx, - obligation.param_env, - data.projection_ty, - obligation.cause.clone(), - 0, - &mut obligations, - ); - - debug!( - "report_projection_error obligation.cause={:?} obligation.param_env={:?}", - obligation.cause, obligation.param_env - ); - - debug!( - "report_projection_error normalized_ty={:?} data.ty={:?}", - normalized_ty, data.ty - ); - - let is_normalized_ty_expected = match &obligation.cause.code { - ObligationCauseCode::ItemObligation(_) - | ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ObjectCastObligation(_) => false, - _ => true, - }; - - if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( - is_normalized_ty_expected, - normalized_ty, - data.ty, - ) { - values = Some(infer::ValuePairs::Types(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, - data.ty, - ))); - - err_buf = error; - err = &err_buf; - } - } - - let msg = format!("type mismatch resolving `{}`", predicate); - let error_id = (DiagnosticMessageId::ErrorId(271), Some(obligation.cause.span), msg); - let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); - if fresh { - let mut diag = struct_span_err!( - self.tcx.sess, - obligation.cause.span, - E0271, - "type mismatch resolving `{}`", - predicate - ); - self.note_type_err(&mut diag, &obligation.cause, None, values, err); - self.note_obligation_cause(&mut diag, obligation); - diag.emit(); - } - }); - } - - fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - /// returns the fuzzy category of a given type, or None - /// if the type can be equated to any type. - fn type_category(t: Ty<'_>) -> Option { - match t.kind { - ty::Bool => Some(0), - ty::Char => Some(1), - ty::Str => Some(2), - ty::Int(..) | ty::Uint(..) | ty::Infer(ty::IntVar(..)) => Some(3), - ty::Float(..) | ty::Infer(ty::FloatVar(..)) => Some(4), - ty::Ref(..) | ty::RawPtr(..) => Some(5), - ty::Array(..) | ty::Slice(..) => Some(6), - ty::FnDef(..) | ty::FnPtr(..) => Some(7), - ty::Dynamic(..) => Some(8), - ty::Closure(..) => Some(9), - ty::Tuple(..) => Some(10), - ty::Projection(..) => Some(11), - ty::Param(..) => Some(12), - ty::Opaque(..) => Some(13), - ty::Never => Some(14), - ty::Adt(adt, ..) => match adt.adt_kind() { - AdtKind::Struct => Some(15), - AdtKind::Union => Some(16), - AdtKind::Enum => Some(17), - }, - ty::Generator(..) => Some(18), - ty::Foreign(..) => Some(19), - ty::GeneratorWitness(..) => Some(20), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => None, - ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - } - } - - match (type_category(a), type_category(b)) { - (Some(cat_a), Some(cat_b)) => match (&a.kind, &b.kind) { - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, - _ => cat_a == cat_b, - }, - // infer and error can be equated to all types - _ => true, - } - } - - fn impl_similar_to( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> Option { - let tcx = self.tcx; - let param_env = obligation.param_env; - let trait_ref = tcx.erase_late_bound_regions(&trait_ref); - let trait_self_ty = trait_ref.self_ty(); - - let mut self_match_impls = vec![]; - let mut fuzzy_match_impls = vec![]; - - self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { - let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); - let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); - - let impl_self_ty = impl_trait_ref.self_ty(); - - if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { - self_match_impls.push(def_id); - - if trait_ref - .substs - .types() - .skip(1) - .zip(impl_trait_ref.substs.types().skip(1)) - .all(|(u, v)| self.fuzzy_match_tys(u, v)) - { - fuzzy_match_impls.push(def_id); - } - } - }); - - let impl_def_id = if self_match_impls.len() == 1 { - self_match_impls[0] - } else if fuzzy_match_impls.len() == 1 { - fuzzy_match_impls[0] - } else { - return None; - }; - - tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) - } - - fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { - self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| match gen_kind { - hir::GeneratorKind::Gen => "a generator", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", - }) - } - - /// Used to set on_unimplemented's `ItemContext` - /// to be the enclosing (async) block/function/closure - fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { - let hir = &self.tcx.hir(); - let node = hir.find(hir_id)?; - if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) = &node { - self.describe_generator(*body_id).or_else(|| { - Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { - "an async function" - } else { - "a function" - }) - }) - } else if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), - .. - }) = &node - { - self.describe_generator(*body_id).or_else(|| { - Some(if gen_movability.is_some() { "an async closure" } else { "a closure" }) - }) - } else if let hir::Node::Expr(hir::Expr { .. }) = &node { - let parent_hid = hir.get_parent_node(hir_id); - if parent_hid != hir_id { - return self.describe_enclosure(parent_hid); - } else { - None - } - } else { - None - } - } - - fn on_unimplemented_note( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> OnUnimplementedNote { - let def_id = - self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); - let trait_ref = *trait_ref.skip_binder(); - - let mut flags = vec![]; - flags.push(( - sym::item_context, - self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), - )); - - match obligation.cause.code { - ObligationCauseCode::BuiltinDerivedObligation(..) - | ObligationCauseCode::ImplDerivedObligation(..) => {} - _ => { - // this is a "direct", user-specified, rather than derived, - // obligation. - flags.push((sym::direct, None)); - } - } - - if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code { - // FIXME: maybe also have some way of handling methods - // from other traits? That would require name resolution, - // which we might want to be some sort of hygienic. - // - // Currently I'm leaving it for what I need for `try`. - if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { - let method = self.tcx.item_name(item); - flags.push((sym::from_method, None)); - flags.push((sym::from_method, Some(method.to_string()))); - } - } - if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) { - flags.push((sym::parent_trait, Some(t))); - } - - if let Some(k) = obligation.cause.span.desugaring_kind() { - flags.push((sym::from_desugaring, None)); - flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); - } - let generics = self.tcx.generics_of(def_id); - let self_ty = trait_ref.self_ty(); - // This is also included through the generics list as `Self`, - // but the parser won't allow you to use it - flags.push((sym::_Self, Some(self_ty.to_string()))); - if let Some(def) = self_ty.ty_adt_def() { - // We also want to be able to select self's original - // signature with no type arguments resolved - flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); - } - - for param in generics.params.iter() { - let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize].to_string() - } - GenericParamDefKind::Lifetime => continue, - }; - let name = param.name; - flags.push((name, Some(value))); - } - - if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { - flags.push((sym::crate_local, None)); - } - - // Allow targeting all integers using `{integral}`, even if the exact type was resolved - if self_ty.is_integral() { - flags.push((sym::_Self, Some("{integral}".to_owned()))); - } - - if let ty::Array(aty, len) = self_ty.kind { - flags.push((sym::_Self, Some("[]".to_owned()))); - flags.push((sym::_Self, Some(format!("[{}]", aty)))); - if let Some(def) = aty.ty_adt_def() { - // We also want to be able to select the array's type's original - // signature with no type arguments resolved - flags.push(( - sym::_Self, - Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), - )); - let tcx = self.tcx; - if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) { - flags.push(( - sym::_Self, - Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), - )); - } else { - flags.push(( - sym::_Self, - Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), - )); - } - } - } - - if let Ok(Some(command)) = - OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) - { - command.evaluate(self.tcx, trait_ref, &flags[..]) - } else { - OnUnimplementedNote::default() - } - } - - fn find_similar_impl_candidates( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Vec> { - let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true); - let all_impls = self.tcx.all_impls(trait_ref.def_id()); - - match simp { - Some(simp) => all_impls - .iter() - .filter_map(|&def_id| { - let imp = self.tcx.impl_trait_ref(def_id).unwrap(); - let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true); - if let Some(imp_simp) = imp_simp { - if simp != imp_simp { - return None; - } - } - - Some(imp) - }) - .collect(), - None => { - all_impls.iter().map(|&def_id| self.tcx.impl_trait_ref(def_id).unwrap()).collect() - } - } - } - - fn report_similar_impl_candidates( - &self, - impl_candidates: Vec>, - err: &mut DiagnosticBuilder<'_>, - ) { - if impl_candidates.is_empty() { - return; - } - - let len = impl_candidates.len(); - let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 }; - - let normalize = |candidate| { - self.tcx.infer_ctxt().enter(|ref infcx| { - let normalized = infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .normalize(candidate) - .ok(); - match normalized { - Some(normalized) => format!("\n {:?}", normalized.value), - None => format!("\n {:?}", candidate), - } - }) - }; - - // Sort impl candidates so that ordering is consistent for UI tests. - let mut normalized_impl_candidates = - impl_candidates.iter().map(normalize).collect::>(); - - // Sort before taking the `..end` range, - // because the ordering of `impl_candidates` may not be deterministic: - // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 - normalized_impl_candidates.sort(); - - err.help(&format!( - "the following implementations were found:{}{}", - normalized_impl_candidates[..end].join(""), - if len > 5 { format!("\nand {} others", len - 4) } else { String::new() } - )); - } - - /// Reports that an overflow has occurred and halts compilation. We - /// halt compilation unconditionally because it is important that - /// overflows never be masked -- they basically represent computations - /// whose result could not be truly determined and thus we can't say - /// if the program type checks or not -- and they are unusual - /// occurrences in any case. - pub fn report_overflow_error( - &self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool, - ) -> ! - where - T: fmt::Display + TypeFoldable<'tcx>, - { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - let mut err = struct_span_err!( - self.tcx.sess, - obligation.cause.span, - E0275, - "overflow evaluating the requirement `{}`", - predicate - ); - - if suggest_increasing_limit { - self.suggest_new_overflow_limit(&mut err); - } - - self.note_obligation_cause_code( - &mut err, - &obligation.predicate, - &obligation.cause.code, - &mut vec![], - ); - - err.emit(); - self.tcx.sess.abort_if_errors(); - bug!(); - } - - /// Reports that a cycle was detected which led to overflow and halts - /// compilation. This is equivalent to `report_overflow_error` except - /// that we can give a more helpful error message (and, in particular, - /// we do not suggest increasing the overflow limit, which is not - /// going to help). - pub fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { - let cycle = self.resolve_vars_if_possible(&cycle.to_owned()); - assert!(cycle.len() > 0); - - debug!("report_overflow_error_cycle: cycle={:?}", cycle); - - self.report_overflow_error(&cycle[0], false); - } - - pub fn report_extra_impl_obligation( - &self, - error_span: Span, - item_name: ast::Name, - _impl_item_def_id: DefId, - trait_item_def_id: DefId, - requirement: &dyn fmt::Display, - ) -> DiagnosticBuilder<'tcx> { - let msg = "impl has stricter requirements than trait"; - let sp = self.tcx.sess.source_map().def_span(error_span); - - let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg); - - if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) { - let span = self.tcx.sess.source_map().def_span(trait_item_span); - err.span_label(span, format!("definition of `{}` from trait", item_name)); - } - - err.span_label(sp, format!("impl has extra requirement {}", requirement)); - - err - } - - /// Gets the parent trait chain start - fn get_parent_trait_ref( - &self, - code: &ObligationCauseCode<'tcx>, - ) -> Option<(String, Option)> { - match code { - &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { - let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); - match self.get_parent_trait_ref(&data.parent_code) { - Some(t) => Some(t), - None => { - let ty = parent_trait_ref.skip_binder().self_ty(); - let span = - TyCategory::from_ty(ty).map(|(_, def_id)| self.tcx.def_span(def_id)); - Some((ty.to_string(), span)) - } - } - } - _ => None, - } - } - - pub fn report_selection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &SelectionError<'tcx>, - fallback_has_occurred: bool, - points_at_arg: bool, - ) { - let tcx = self.tcx; - let span = obligation.cause.span; - - let mut err = match *error { - SelectionError::Unimplemented => { - if let ObligationCauseCode::CompareImplMethodObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } - | ObligationCauseCode::CompareImplTypeObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } = obligation.cause.code - { - self.report_extra_impl_obligation( - span, - item_name, - impl_item_def_id, - trait_item_def_id, - &format!("`{}`", obligation.predicate), - ) - .emit(); - return; - } - match obligation.predicate { - ty::Predicate::Trait(ref trait_predicate) => { - let trait_predicate = self.resolve_vars_if_possible(trait_predicate); - - if self.tcx.sess.has_errors() && trait_predicate.references_error() { - return; - } - let trait_ref = trait_predicate.to_poly_trait_ref(); - let (post_message, pre_message, type_def) = self - .get_parent_trait_ref(&obligation.cause.code) - .map(|(t, s)| { - ( - format!(" in `{}`", t), - format!("within `{}`, ", t), - s.map(|s| (format!("within this `{}`", t), s)), - ) - }) - .unwrap_or_default(); - - let OnUnimplementedNote { message, label, note, enclosing_scope } = - self.on_unimplemented_note(trait_ref, obligation); - let have_alt_message = message.is_some() || label.is_some(); - let is_try = self - .tcx - .sess - .source_map() - .span_to_snippet(span) - .map(|s| &s == "?") - .unwrap_or(false); - let is_from = format!("{}", trait_ref.print_only_trait_path()) - .starts_with("std::convert::From<"); - let (message, note) = if is_try && is_from { - ( - Some(format!( - "`?` couldn't convert the error to `{}`", - trait_ref.self_ty(), - )), - Some( - "the question mark operation (`?`) implicitly performs a \ - conversion on the error value using the `From` trait" - .to_owned(), - ), - ) - } else { - (message, note) - }; - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0277, - "{}", - message.unwrap_or_else(|| format!( - "the trait bound `{}` is not satisfied{}", - trait_ref.to_predicate(), - post_message, - )) - ); - - let explanation = - if obligation.cause.code == ObligationCauseCode::MainFunctionType { - "consider using `()`, or a `Result`".to_owned() - } else { - format!( - "{}the trait `{}` is not implemented for `{}`", - pre_message, - trait_ref.print_only_trait_path(), - trait_ref.self_ty(), - ) - }; - - if self.suggest_add_reference_to_arg( - &obligation, - &mut err, - &trait_ref, - points_at_arg, - have_alt_message, - ) { - self.note_obligation_cause(&mut err, obligation); - err.emit(); - return; - } - if let Some(ref s) = label { - // If it has a custom `#[rustc_on_unimplemented]` - // error message, let's display it as the label! - err.span_label(span, s.as_str()); - err.help(&explanation); - } else { - err.span_label(span, explanation); - } - if let Some((msg, span)) = type_def { - err.span_label(span, &msg); - } - if let Some(ref s) = note { - // If it has a custom `#[rustc_on_unimplemented]` note, let's display it - err.note(s.as_str()); - } - if let Some(ref s) = enclosing_scope { - let enclosing_scope_span = tcx.def_span( - tcx.hir() - .opt_local_def_id(obligation.cause.body_id) - .unwrap_or_else(|| { - tcx.hir().body_owner_def_id(hir::BodyId { - hir_id: obligation.cause.body_id, - }) - }), - ); - - err.span_label(enclosing_scope_span, s.as_str()); - } - - self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err); - self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); - self.suggest_remove_reference(&obligation, &mut err, &trait_ref); - self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); - self.note_version_mismatch(&mut err, &trait_ref); - if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { - err.emit(); - return; - } - - // Try to report a help message - if !trait_ref.has_infer_types() - && self.predicate_can_apply(obligation.param_env, trait_ref) - { - // If a where-clause may be useful, remind the - // user that they can add it. - // - // don't display an on-unimplemented note, as - // these notes will often be of the form - // "the type `T` can't be frobnicated" - // which is somewhat confusing. - self.suggest_restricting_param_bound( - &mut err, - &trait_ref, - obligation.cause.body_id, - ); - } else { - if !have_alt_message { - // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(trait_ref); - self.report_similar_impl_candidates(impl_candidates, &mut err); - } - self.suggest_change_mut( - &obligation, - &mut err, - &trait_ref, - points_at_arg, - ); - } - - // If this error is due to `!: Trait` not implemented but `(): Trait` is - // implemented, and fallback has occurred, then it could be due to a - // variable that used to fallback to `()` now falling back to `!`. Issue a - // note informing about the change in behaviour. - if trait_predicate.skip_binder().self_ty().is_never() - && fallback_has_occurred - { - let predicate = trait_predicate.map_bound(|mut trait_pred| { - trait_pred.trait_ref.substs = self.tcx.mk_substs_trait( - self.tcx.mk_unit(), - &trait_pred.trait_ref.substs[1..], - ); - trait_pred - }); - let unit_obligation = Obligation { - predicate: ty::Predicate::Trait(predicate), - ..obligation.clone() - }; - if self.predicate_may_hold(&unit_obligation) { - err.note( - "the trait is implemented for `()`. \ - Possibly this error has been caused by changes to \ - Rust's type-inference algorithm \ - (see: https://github.com/rust-lang/rust/issues/48950 \ - for more info). Consider whether you meant to use the \ - type `()` here instead.", - ); - } - } - - err - } - - ty::Predicate::Subtype(ref predicate) => { - // Errors for Subtype predicates show up as - // `FulfillmentErrorCode::CodeSubtypeError`, - // not selection error. - span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) - } - - ty::Predicate::RegionOutlives(ref predicate) => { - let predicate = self.resolve_vars_if_possible(predicate); - let err = self - .region_outlives_predicate(&obligation.cause, &predicate) - .err() - .unwrap(); - struct_span_err!( - self.tcx.sess, - span, - E0279, - "the requirement `{}` is not satisfied (`{}`)", - predicate, - err, - ) - } - - ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - struct_span_err!( - self.tcx.sess, - span, - E0280, - "the requirement `{}` is not satisfied", - predicate - ) - } - - ty::Predicate::ObjectSafe(trait_def_id) => { - let violations = object_safety_violations(self.tcx, trait_def_id); - report_object_safety_error(self.tcx, span, trait_def_id, violations) - } - - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - let found_kind = self.closure_kind(closure_def_id, closure_substs).unwrap(); - let closure_span = self - .tcx - .sess - .source_map() - .def_span(self.tcx.hir().span_if_local(closure_def_id).unwrap()); - let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id).unwrap(); - let mut err = struct_span_err!( - self.tcx.sess, - closure_span, - E0525, - "expected a closure that implements the `{}` trait, \ - but this closure only implements `{}`", - kind, - found_kind - ); - - err.span_label( - closure_span, - format!("this closure implements `{}`, not `{}`", found_kind, kind), - ); - err.span_label( - obligation.cause.span, - format!("the requirement to implement `{}` derives from here", kind), - ); - - // Additional context information explaining why the closure only implements - // a particular trait. - if let Some(tables) = self.in_progress_tables { - let tables = tables.borrow(); - match (found_kind, tables.closure_kind_origins().get(hir_id)) { - (ty::ClosureKind::FnOnce, Some((span, name))) => { - err.span_label( - *span, - format!( - "closure is `FnOnce` because it moves the \ - variable `{}` out of its environment", - name - ), - ); - } - (ty::ClosureKind::FnMut, Some((span, name))) => { - err.span_label( - *span, - format!( - "closure is `FnMut` because it mutates the \ - variable `{}` here", - name - ), - ); - } - _ => {} - } - } - - err.emit(); - return; - } - - ty::Predicate::WellFormed(ty) => { - if !self.tcx.sess.opts.debugging_opts.chalk { - // WF predicates cannot themselves make - // errors. They can only block due to - // ambiguity; otherwise, they always - // degenerate into other obligations - // (which may fail). - span_bug!(span, "WF predicate not satisfied for {:?}", ty); - } else { - // FIXME: we'll need a better message which takes into account - // which bounds actually failed to hold. - self.tcx.sess.struct_span_err( - span, - &format!("the type `{}` is not well-formed (chalk)", ty), - ) - } - } - - ty::Predicate::ConstEvaluatable(..) => { - // Errors for `ConstEvaluatable` predicates show up as - // `SelectionError::ConstEvalFailure`, - // not `Unimplemented`. - span_bug!( - span, - "const-evaluatable requirement gave wrong error: `{:?}`", - obligation - ) - } - } - } - - OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { - let found_trait_ref = self.resolve_vars_if_possible(&*found_trait_ref); - let expected_trait_ref = self.resolve_vars_if_possible(&*expected_trait_ref); - - if expected_trait_ref.self_ty().references_error() { - return; - } - - let found_trait_ty = found_trait_ref.self_ty(); - - let found_did = match found_trait_ty.kind { - ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), - ty::Adt(def, _) => Some(def.did), - _ => None, - }; - - let found_span = found_did - .and_then(|did| self.tcx.hir().span_if_local(did)) - .map(|sp| self.tcx.sess.source_map().def_span(sp)); // the sp could be an fn def - - if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { - // We check closures twice, with obligations flowing in different directions, - // but we want to complain about them only once. - return; - } - - self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); - - let found = match found_trait_ref.skip_binder().substs.type_at(1).kind { - ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], - _ => vec![ArgKind::empty()], - }; - - let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); - let expected = match expected_ty.kind { - ty::Tuple(ref tys) => tys - .iter() - .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span))) - .collect(), - _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], - }; - - if found.len() == expected.len() { - self.report_closure_arg_mismatch( - span, - found_span, - found_trait_ref, - expected_trait_ref, - ) - } else { - let (closure_span, found) = found_did - .and_then(|did| self.tcx.hir().get_if_local(did)) - .map(|node| { - let (found_span, found) = self.get_fn_like_arguments(node); - (Some(found_span), found) - }) - .unwrap_or((found_span, found)); - - self.report_arg_count_mismatch( - span, - closure_span, - expected, - found, - found_trait_ty.is_closure(), - ) - } - } - - TraitNotObjectSafe(did) => { - let violations = object_safety_violations(self.tcx, did); - report_object_safety_error(self.tcx, span, did, violations) - } - - // already reported in the query - ConstEvalFailure(err) => { - if let ErrorHandled::TooGeneric = err { - // Silence this error, as it can be produced during intermediate steps - // when a constant is not yet able to be evaluated (but will be later). - return; - } - self.tcx.sess.delay_span_bug( - span, - &format!("constant in type had an ignored error: {:?}", err), - ); - return; - } - - Overflow => { - bug!("overflow should be handled before the `report_selection_error` path"); - } - }; - - self.note_obligation_cause(&mut err, obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); - - err.emit(); - } - - /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait - /// with the same path as `trait_ref`, a help message about - /// a probable version mismatch is added to `err` - fn note_version_mismatch( - &self, - err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) { - let get_trait_impl = |trait_def_id| { - let mut trait_impl = None; - self.tcx.for_each_relevant_impl(trait_def_id, trait_ref.self_ty(), |impl_def_id| { - if trait_impl.is_none() { - trait_impl = Some(impl_def_id); - } - }); - trait_impl - }; - let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); - let all_traits = self.tcx.all_traits(LOCAL_CRATE); - let traits_with_same_path: std::collections::BTreeSet<_> = all_traits - .iter() - .filter(|trait_def_id| **trait_def_id != trait_ref.def_id()) - .filter(|trait_def_id| self.tcx.def_path_str(**trait_def_id) == required_trait_path) - .collect(); - for trait_with_same_path in traits_with_same_path { - if let Some(impl_def_id) = get_trait_impl(*trait_with_same_path) { - let impl_span = self.tcx.def_span(impl_def_id); - err.span_help(impl_span, "trait impl with same name found"); - let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); - let crate_msg = format!( - "perhaps two different versions of crate `{}` are being used?", - trait_crate - ); - err.note(&crate_msg); - } - } - } - fn suggest_restricting_param_bound( + crate fn suggest_restricting_param_bound( &self, mut err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::PolyTraitRef<'_>, @@ -1318,7 +168,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a /// suggestion to borrow the initializer in order to use have a slice instead. - fn suggest_borrow_on_unsized_slice( + crate fn suggest_borrow_on_unsized_slice( &self, code: &ObligationCauseCode<'tcx>, err: &mut DiagnosticBuilder<'tcx>, @@ -1342,22 +192,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn mk_obligation_for_def_id( - &self, - def_id: DefId, - output_ty: Ty<'tcx>, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> PredicateObligation<'tcx> { - let new_trait_ref = - ty::TraitRef { def_id, substs: self.tcx.mk_substs_trait(output_ty, &[]) }; - Obligation::new(cause, param_env, new_trait_ref.to_predicate()) - } - /// Given a closure's `DefId`, return the given name of the closure. /// /// This doesn't account for reassignments, but it's only used for suggestions. - fn get_closure_name( + crate fn get_closure_name( &self, def_id: DefId, err: &mut DiagnosticBuilder<'_>, @@ -1395,7 +233,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// We tried to apply the bound to an `fn` or closure. Check whether calling it would /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. - fn suggest_fn_call( + crate fn suggest_fn_call( &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, @@ -1479,7 +317,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn suggest_add_reference_to_arg( + crate fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'tcx>, @@ -1548,7 +386,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, /// suggest removing these references until we reach a type that implements the trait. - fn suggest_remove_reference( + crate fn suggest_remove_reference( &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'tcx>, @@ -1608,7 +446,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Check if the trait bound is implemented for a different mutability and note it in the /// final error. - fn suggest_change_mut( + crate fn suggest_change_mut( &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'tcx>, @@ -1672,7 +510,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn suggest_semicolon_removal( + crate fn suggest_semicolon_removal( &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'tcx>, @@ -1705,7 +543,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn suggest_impl_trait( + crate fn suggest_impl_trait( &self, err: &mut DiagnosticBuilder<'tcx>, span: Span, @@ -1865,7 +703,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { false } - fn point_at_returns_when_relevant( + crate fn point_at_returns_when_relevant( &self, err: &mut DiagnosticBuilder<'tcx>, obligation: &PredicateObligation<'tcx>, @@ -2110,14 +948,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err } - fn report_closure_arg_mismatch( + crate fn report_closure_arg_mismatch( &self, span: Span, found_span: Option, expected_ref: ty::PolyTraitRef<'tcx>, found: ty::PolyTraitRef<'tcx>, ) -> DiagnosticBuilder<'tcx> { - fn build_fn_sig_string<'tcx>(tcx: TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> String { + crate fn build_fn_sig_string<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: &ty::TraitRef<'tcx>, + ) -> String { let inputs = trait_ref.substs.type_at(1); let sig = if let ty::Tuple(inputs) = inputs.kind { tcx.mk_fn_sig( @@ -2165,238 +1006,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } -pub fn recursive_type_with_infinite_size_error( - tcx: TyCtxt<'tcx>, - type_def_id: DefId, -) -> DiagnosticBuilder<'tcx> { - assert!(type_def_id.is_local()); - let span = tcx.hir().span_if_local(type_def_id).unwrap(); - let span = tcx.sess.source_map().def_span(span); - let mut err = struct_span_err!( - tcx.sess, - span, - E0072, - "recursive type `{}` has infinite size", - tcx.def_path_str(type_def_id) - ); - err.span_label(span, "recursive type has infinite size"); - err.help(&format!( - "insert indirection (e.g., a `Box`, `Rc`, or `&`) \ - at some point to make `{}` representable", - tcx.def_path_str(type_def_id) - )); - err -} - -pub fn report_object_safety_error( - tcx: TyCtxt<'tcx>, - span: Span, - trait_def_id: DefId, - violations: Vec, -) -> DiagnosticBuilder<'tcx> { - let trait_str = tcx.def_path_str(trait_def_id); - let span = tcx.sess.source_map().def_span(span); - let mut err = struct_span_err!( - tcx.sess, - span, - E0038, - "the trait `{}` cannot be made into an object", - trait_str - ); - err.span_label(span, format!("the trait `{}` cannot be made into an object", trait_str)); - - let mut reported_violations = FxHashSet::default(); - for violation in violations { - if reported_violations.insert(violation.clone()) { - match violation.span() { - Some(span) => err.span_label(span, violation.error_msg()), - None => err.note(&violation.error_msg()), - }; - } - } - - if tcx.sess.trait_methods_not_found.borrow().contains(&span) { - // Avoid emitting error caused by non-existing method (#58734) - err.cancel(); - } - - err -} - impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - fn maybe_report_ambiguity( - &self, - obligation: &PredicateObligation<'tcx>, - body_id: Option, - ) { - // Unable to successfully determine, probably means - // insufficient type information, but could mean - // ambiguous impls. The latter *ought* to be a - // coherence violation, so we don't report it here. - - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - let span = obligation.cause.span; - - debug!( - "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})", - predicate, obligation, body_id, obligation.cause.code, - ); - - // Ambiguity errors are often caused as fallout from earlier - // errors. So just ignore them if this infcx is tainted. - if self.is_tainted_by_errors() { - return; - } - - let mut err = match predicate { - ty::Predicate::Trait(ref data) => { - let trait_ref = data.to_poly_trait_ref(); - let self_ty = trait_ref.self_ty(); - debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref); - - if predicate.references_error() { - return; - } - // Typically, this ambiguity should only happen if - // there are unresolved type inference variables - // (otherwise it would suggest a coherence - // failure). But given #21974 that is not necessarily - // the case -- we can have multiple where clauses that - // are only distinguished by a region, which results - // in an ambiguity even when all types are fully - // known, since we don't dispatch based on region - // relationships. - - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we - // could just skip over all checks where the self-ty is an - // inference variable, but I was afraid that there might be an - // inference variable created, registered as an obligation, and - // then never forced by writeback, and hence by skipping here we'd - // be ignoring the fact that we don't KNOW the type works - // out. Though even that would probably be harmless, given that - // we're only talking about builtin traits, which are known to be - // inhabited. We used to check for `self.tcx.sess.has_errors()` to - // avoid inundating the user with unnecessary errors, but we now - // check upstream for type errors and dont add the obligations to - // begin with in those cases. - if self - .tcx - .lang_items() - .sized_trait() - .map_or(false, |sized_id| sized_id == trait_ref.def_id()) - { - self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0282).emit(); - return; - } - let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0283); - err.note(&format!("cannot resolve `{}`", predicate)); - if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { - self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); - } else if let ( - Ok(ref snippet), - ObligationCauseCode::BindingObligation(ref def_id, _), - ) = - (self.tcx.sess.source_map().span_to_snippet(span), &obligation.cause.code) - { - let generics = self.tcx.generics_of(*def_id); - if !generics.params.is_empty() && !snippet.ends_with('>') { - // FIXME: To avoid spurious suggestions in functions where type arguments - // where already supplied, we check the snippet to make sure it doesn't - // end with a turbofish. Ideally we would have access to a `PathSegment` - // instead. Otherwise we would produce the following output: - // - // error[E0283]: type annotations needed - // --> $DIR/issue-54954.rs:3:24 - // | - // LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); - // | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - // | | - // | cannot infer type - // | help: consider specifying the type argument - // | in the function call: - // | `Tt::const_val::<[i8; 123]>::` - // ... - // LL | const fn const_val() -> usize { - // | --------- - required by this bound in `Tt::const_val` - // | - // = note: cannot resolve `_: Tt` - - err.span_suggestion( - span, - &format!( - "consider specifying the type argument{} in the function call", - if generics.params.len() > 1 { "s" } else { "" }, - ), - format!( - "{}::<{}>", - snippet, - generics - .params - .iter() - .map(|p| p.name.to_string()) - .collect::>() - .join(", ") - ), - Applicability::HasPlaceholders, - ); - } - } - err - } - - ty::Predicate::WellFormed(ty) => { - // Same hacky approach as above to avoid deluging user - // with error messages. - if ty.references_error() || self.tcx.sess.has_errors() { - return; - } - self.need_type_info_err(body_id, span, ty, ErrorCode::E0282) - } - - ty::Predicate::Subtype(ref data) => { - if data.references_error() || self.tcx.sess.has_errors() { - // no need to overload user in such cases - return; - } - let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder(); - // both must be type variables, or the other would've been instantiated - assert!(a.is_ty_var() && b.is_ty_var()); - self.need_type_info_err(body_id, span, a, ErrorCode::E0282) - } - ty::Predicate::Projection(ref data) => { - let trait_ref = data.to_poly_trait_ref(self.tcx); - let self_ty = trait_ref.self_ty(); - if predicate.references_error() { - return; - } - let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0284); - err.note(&format!("cannot resolve `{}`", predicate)); - err - } - - _ => { - if self.tcx.sess.has_errors() { - return; - } - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0284, - "type annotations needed: cannot resolve `{}`", - predicate, - ); - err.span_label(span, &format!("cannot resolve `{}`", predicate)); - err - } - }; - self.note_obligation_cause(&mut err, obligation); - err.emit(); - } - - fn suggest_fully_qualified_path( + crate fn suggest_fully_qualified_path( &self, err: &mut DiagnosticBuilder<'_>, def_id: DefId, @@ -2420,76 +1031,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - /// Returns `true` if the trait predicate may apply for *some* assignment - /// to the type parameters. - fn predicate_can_apply( - &self, - param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitRef<'tcx>, - ) -> bool { - struct ParamToVarFolder<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - var_map: FxHashMap, Ty<'tcx>>, - } - - impl<'a, 'tcx> TypeFolder<'tcx> for ParamToVarFolder<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Param(ty::ParamTy { name, .. }) = ty.kind { - let infcx = self.infcx; - self.var_map.entry(ty).or_insert_with(|| { - infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeParameterDefinition(name, None), - span: DUMMY_SP, - }) - }) - } else { - ty.super_fold_with(self) - } - } - } - - self.probe(|_| { - let mut selcx = SelectionContext::new(self); - - let cleaned_pred = - pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); - - let cleaned_pred = super::project::normalize( - &mut selcx, - param_env, - ObligationCause::dummy(), - &cleaned_pred, - ) - .value; - - let obligation = - Obligation::new(ObligationCause::dummy(), param_env, cleaned_pred.to_predicate()); - - self.predicate_may_hold(&obligation) - }) - } - - fn note_obligation_cause( - &self, - err: &mut DiagnosticBuilder<'_>, - obligation: &PredicateObligation<'tcx>, - ) { - // First, attempt to add note to this error with an async-await-specific - // message, and fall back to regular note otherwise. - if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { - self.note_obligation_cause_code( - err, - &obligation.predicate, - &obligation.cause.code, - &mut vec![], - ); - } - } - /// Adds an async-await specific note to the diagnostic when the future does not implement /// an auto trait because of a captured type. /// @@ -2532,7 +1073,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// ``` /// /// Returns `true` if an async-await specific note was added to the diagnostic. - fn maybe_note_obligation_cause_for_async_await( + crate fn maybe_note_obligation_cause_for_async_await( &self, err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, @@ -2712,7 +1253,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Unconditionally adds the diagnostic note described in /// `maybe_note_obligation_cause_for_async_await`'s documentation comment. - fn note_obligation_cause_for_async_await( + crate fn note_obligation_cause_for_async_await( &self, err: &mut DiagnosticBuilder<'_>, target_span: Span, @@ -2838,7 +1379,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } - fn note_obligation_cause_code( + crate fn note_obligation_cause_code( &self, err: &mut DiagnosticBuilder<'_>, predicate: &T, @@ -3053,7 +1594,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) { + crate fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) { let current_limit = self.tcx.sess.recursion_limit.get(); let suggested_limit = current_limit * 2; err.help(&format!( @@ -3061,52 +1602,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { suggested_limit )); } - - fn is_recursive_obligation( - &self, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, - cause_code: &ObligationCauseCode<'tcx>, - ) -> bool { - if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { - let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); - - if obligated_types.iter().any(|ot| ot == &parent_trait_ref.skip_binder().self_ty()) { - return true; - } - } - false - } -} - -/// Summarizes information -#[derive(Clone)] -pub enum ArgKind { - /// An argument of non-tuple type. Parameters are (name, ty) - Arg(String, String), - - /// An argument of tuple type. For a "found" argument, the span is - /// the locationo in the source of the pattern. For a "expected" - /// argument, it will be None. The vector is a list of (name, ty) - /// strings for the components of the tuple. - Tuple(Option, Vec<(String, String)>), -} - -impl ArgKind { - fn empty() -> ArgKind { - ArgKind::Arg("_".to_owned(), "_".to_owned()) - } - - /// Creates an `ArgKind` from the expected type of an - /// argument. It has no name (`_`) and an optional source span. - pub fn from_expected_ty(t: Ty<'_>, span: Option) -> ArgKind { - match t.kind { - ty::Tuple(ref tys) => ArgKind::Tuple( - span, - tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::>(), - ), - _ => ArgKind::Arg("_".to_owned(), t.to_string()), - } - } } /// Suggest restricting a type param with a new bound. From 4a75ef91f37dd0bd5267a852fa05ee0a5547a62b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 14 Jan 2020 10:29:13 -0800 Subject: [PATCH 09/15] fix error code index comment --- src/librustc_error_codes/error_codes/E0746.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md index 538c9d720d71b..acf369d8e144f 100644 --- a/src/librustc_error_codes/error_codes/E0746.md +++ b/src/librustc_error_codes/error_codes/E0746.md @@ -12,7 +12,7 @@ impl T for S { } // Having the trait `T` as return type is invalid because bare traits do not -have a statically known size: +// have a statically known size: fn foo() -> dyn T { S(42) } From 5b36c187dcbb8a4b6555fe046194f2b6deb74230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 14 Jan 2020 13:18:06 -0800 Subject: [PATCH 10/15] review comments --- src/librustc/traits/error_reporting/mod.rs | 2 + .../traits/error_reporting/suggestions.rs | 243 +++++++++--------- src/librustc/traits/mod.rs | 14 +- src/librustc/ty/error.rs | 4 +- src/librustc_typeck/check/coercion.rs | 31 +-- src/librustc_typeck/check/mod.rs | 12 - .../coerce-expect-unsized-ascribed.stderr | 14 +- .../into-iter-no-impls-length-33.stderr | 14 +- src/test/ui/destructure-trait-ref.rs | 2 - src/test/ui/destructure-trait-ref.stderr | 10 +- src/test/ui/dst/dst-bad-assign-3.rs | 2 +- src/test/ui/dst/dst-bad-assign-3.stderr | 2 +- src/test/ui/dst/dst-bad-assign.rs | 2 +- src/test/ui/dst/dst-bad-assign.stderr | 2 +- src/test/ui/error-codes/E0746.stderr | 8 +- ...n-trait-return-should-be-impl-trait.stderr | 38 +-- src/test/ui/impl-trait/equality.stderr | 2 +- src/test/ui/issues/issue-58344.stderr | 4 +- .../lifetime-elision-return-type-trait.stderr | 2 +- .../feature-gate-never_type_fallback.stderr | 2 +- ...type-err-cause-on-impl-trait-return.stderr | 12 +- ...e-57673-ice-on-deref-of-boxed-trait.stderr | 2 +- 22 files changed, 206 insertions(+), 218 deletions(-) diff --git a/src/librustc/traits/error_reporting/mod.rs b/src/librustc/traits/error_reporting/mod.rs index f8329124851b0..17d7b75a7f7d6 100644 --- a/src/librustc/traits/error_reporting/mod.rs +++ b/src/librustc/traits/error_reporting/mod.rs @@ -1412,6 +1412,8 @@ pub fn suggest_constraining_type_param( false } +/// Collect all the returned expressions within the input expression. +/// Used to point at the return spans when we want to suggest some change to them. struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>); impl<'v> Visitor<'v> for ReturnsVisitor<'v> { diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs index 389edfa071794..0a9747b631e87 100644 --- a/src/librustc/traits/error_reporting/suggestions.rs +++ b/src/librustc/traits/error_reporting/suggestions.rs @@ -4,6 +4,7 @@ use super::{ }; use crate::infer::InferCtxt; +use crate::traits::object_safety::object_safety_violations; use crate::ty::TypeckTables; use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable}; @@ -543,6 +544,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if + /// applicable and signal that the error has been expanded appropriately and needs to be + /// emitted. crate fn suggest_impl_trait( &self, err: &mut DiagnosticBuilder<'tcx>, @@ -550,9 +554,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, trait_ref: &ty::Binder>, ) -> bool { - if let ObligationCauseCode::SizedReturnType = obligation.cause.code.peel_derives() { - } else { - return false; + match obligation.cause.code.peel_derives() { + // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`. + ObligationCauseCode::SizedReturnType => {} + _ => return false, } let hir = self.tcx.hir(); @@ -565,12 +570,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let body = hir.body(*body_id); let trait_ref = self.resolve_vars_if_possible(trait_ref); let ty = trait_ref.skip_binder().self_ty(); - if let ty::Dynamic(..) = ty.kind { - } else { + let is_object_safe; + match ty.kind { + ty::Dynamic(predicates, _) => { + // The `dyn Trait` is not object safe, do not suggest `Box`. + is_object_safe = predicates.principal_def_id().map_or(true, |def_id| { + !object_safety_violations(self.tcx, def_id).is_empty() + }) + } // We only want to suggest `impl Trait` to `dyn Trait`s. // For example, `fn foo() -> str` needs to be filtered out. - return false; + _ => return false, } + + let ret_ty = if let hir::FunctionRetTy::Return(ret_ty) = sig.decl.output { + ret_ty + } else { + return false; + }; + // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for // cases like `fn foo() -> (dyn Trait, i32) {}`. // 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> { let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); - if let hir::FunctionRetTy::Return(ret_ty) = sig.decl.output { - let mut all_returns_conform_to_trait = true; - let mut all_returns_have_same_type = true; - let mut last_ty = None; - if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { - let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); - if let ty::Dynamic(predicates, _) = &ty_ret_ty.kind { - for predicate in predicates.iter() { - for expr in &visitor.0 { - if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { - if let Some(ty) = last_ty { - all_returns_have_same_type &= ty == returned_ty; - } - last_ty = Some(returned_ty); - - let param_env = ty::ParamEnv::empty(); - let pred = predicate.with_self_ty(self.tcx, returned_ty); - let obligation = - Obligation::new(cause.clone(), param_env, pred); - all_returns_conform_to_trait &= - self.predicate_may_hold(&obligation); - } - } - } - } - } else { - // We still want to verify whether all the return types conform to each other. + let mut all_returns_conform_to_trait = true; + let mut all_returns_have_same_type = true; + let mut last_ty = None; + if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { + let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); + let param_env = ty::ParamEnv::empty(); + if let ty::Dynamic(predicates, _) = &ty_ret_ty.kind { for expr in &visitor.0 { if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { - if let Some(ty) = last_ty { - all_returns_have_same_type &= ty == returned_ty; - } + all_returns_have_same_type &= + Some(returned_ty) == last_ty || last_ty.is_none(); last_ty = Some(returned_ty); + for predicate in predicates.iter() { + let pred = predicate.with_self_ty(self.tcx, returned_ty); + let obl = Obligation::new(cause.clone(), param_env, pred); + all_returns_conform_to_trait &= self.predicate_may_hold(&obl); + } + } + } + } + } else { + // We still want to verify whether all the return types conform to each other. + for expr in &visitor.0 { + if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { + if let Some(ty) = last_ty { + all_returns_have_same_type &= ty == returned_ty; } + last_ty = Some(returned_ty); } } + } + let (snippet, last_ty) = if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = ( + // Verify that we're dealing with a return `dyn Trait` ret_ty.span.overlaps(span), &ret_ty.kind, self.tcx.sess.source_map().span_to_snippet(ret_ty.span), + // If any of the return types does not conform to the trait, then we can't + // suggest `impl Trait` nor trait objects, it is a type mismatch error. all_returns_conform_to_trait, last_ty, ) { - err.code = Some(error_code!(E0746)); - err.set_primary_message( - "return type cannot have a bare trait because it must be `Sized`", + (snippet, last_ty) + } else { + return false; + }; + err.code(error_code!(E0746)); + err.set_primary_message("return type cannot have an unboxed trait object"); + err.children.clear(); + let impl_trait_msg = "for information on `impl Trait`, see \ + "; + let trait_obj_msg = "for information on trait objects, see \ + "; + let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); + let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] }; + if all_returns_have_same_type { + // Suggest `-> impl Trait`. + err.span_suggestion( + ret_ty.span, + &format!( + "return `impl {1}` instead, as all return paths are of type `{}`, \ + which implements `{1}`", + last_ty, trait_obj, + ), + format!("impl {}", trait_obj), + Applicability::MachineApplicable, + ); + err.note(impl_trait_msg); + } else { + if is_object_safe { + // Suggest `-> Box` and `Box::new(returned_value)`. + // Get all the return values and collect their span and suggestion. + let mut suggestions = visitor + .0 + .iter() + .map(|expr| { + ( + expr.span, + format!( + "Box::new({})", + self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap() + ), + ) + }) + .collect::>(); + // Add the suggestion for the return type. + suggestions.push(( + ret_ty.span, + format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet), + )); + err.multipart_suggestion( + "return a trait object instead", + suggestions, + Applicability::MaybeIncorrect, ); - err.children.clear(); - let impl_trait_msg = "for information on `impl Trait`, see \ - "; - let trait_obj_msg = "for information on trait objects, see \ - "; - let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); - let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] }; - if all_returns_have_same_type { - err.span_suggestion( - ret_ty.span, - &format!( - "you can use the `impl Trait` feature \ - in the return type because all the return paths are of type \ - `{}`, which implements `dyn {}`", - last_ty, trait_obj, - ), - format!("impl {}", trait_obj), - Applicability::MachineApplicable, - ); - err.note(impl_trait_msg); - } else { - let mut suggestions = visitor - .0 - .iter() - .map(|expr| { - ( - expr.span, - format!( - "Box::new({})", - self.tcx - .sess - .source_map() - .span_to_snippet(expr.span) - .unwrap() - ), - ) - }) - .collect::>(); - suggestions.push(( - ret_ty.span, - format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet), - )); - err.multipart_suggestion( - "if the performance implications are acceptable, you can return a \ - trait object", - suggestions, - Applicability::MaybeIncorrect, - ); - err.span_help( - visitor.0.iter().map(|expr| expr.span).collect::>(), - &format!( - "if all the returned values were of the same type you could use \ - `impl {}` as the return type", - trait_obj, - ), - ); - err.help( - "alternatively, you can always create a new `enum` with a variant \ - for each returned type", - ); - err.note(impl_trait_msg); - err.note(trait_obj_msg); - } - return true; + } else { + err.note(&format!( + "if trait `{}` was object safe, you could return a trait object", + trait_obj, + )); } + err.note(&format!( + "if all the returned values were of the same type you could use \ + `impl {}` as the return type", + trait_obj, + )); + err.note(impl_trait_msg); + err.note(trait_obj_msg); + err.note("you can create a new `enum` with a variant for each returned type"); } + return true; } false } @@ -708,9 +724,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'tcx>, obligation: &PredicateObligation<'tcx>, ) { - if let ObligationCauseCode::SizedReturnType = obligation.cause.code.peel_derives() { - } else { - return; + match obligation.cause.code.peel_derives() { + ObligationCauseCode::SizedReturnType => {} + _ => return, } let hir = self.tcx.hir(); @@ -726,10 +742,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); for expr in &visitor.0 { if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { - err.span_label( - expr.span, - &format!("this returned value is of type `{}`", returned_ty), - ); + let ty = self.resolve_vars_if_possible(&returned_ty); + err.span_label(expr.span, &format!("this returned value is of type `{}`", ty)); } } } @@ -1685,9 +1699,8 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> { } fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - match ex.kind { - hir::ExprKind::Ret(Some(ex)) => self.0.push(ex), - _ => {} + if let hir::ExprKind::Ret(Some(ex)) = ex.kind { + self.0.push(ex); } hir::intravisit::walk_expr(self, ex); } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index f68711c06205b..b4998d4486f09 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -155,8 +155,8 @@ pub struct ObligationCause<'tcx> { pub code: ObligationCauseCode<'tcx>, } -impl<'tcx> ObligationCause<'tcx> { - pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span { +impl ObligationCause<'_> { + pub fn span(&self, tcx: TyCtxt<'_>) -> Span { match self.code { ObligationCauseCode::CompareImplMethodObligation { .. } | ObligationCauseCode::MainFunctionType @@ -1172,13 +1172,13 @@ impl<'tcx> ObligationCause<'tcx> { } impl<'tcx> ObligationCauseCode<'tcx> { + // Return the base obligation, ignoring derived obligations. pub fn peel_derives(&self) -> &Self { - match self { - BuiltinDerivedObligation(cause) | ImplDerivedObligation(cause) => { - cause.parent_code.peel_derives() - } - _ => self, + let mut base_cause = self; + while let BuiltinDerivedObligation(cause) | ImplDerivedObligation(cause) = base_cause { + base_cause = &cause.parent_code; } + base_cause } } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index f7612874e05b6..217ca0ca3f6f5 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -244,9 +244,9 @@ impl<'tcx> ty::TyS<'tcx> { ty::FnPtr(_) => "fn pointer".into(), ty::Dynamic(ref inner, ..) => { if let Some(principal) = inner.principal() { - format!("trait `{}`", tcx.def_path_str(principal.def_id())).into() + format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() } else { - "trait".into() + "trait object".into() } } ty::Closure(..) => "closure".into(), diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 77f16fb79141d..66499b1753fdd 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -50,6 +50,7 @@ //! sort of a minor point so I've opted to leave it for later -- after all, //! we may want to adjust precisely when coercions occur. +use crate::astconv::AstConv; use crate::check::{FnCtxt, Needs}; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc::infer::{Coercion, InferOk, InferResult}; @@ -1245,7 +1246,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expression.map(|expr| (expr, blk_id)), ); if !fcx.tcx.features().unsized_locals { - unsized_return = fcx.is_unsized_return(blk_id); + unsized_return = self.is_return_ty_unsized(fcx, blk_id); } } ObligationCauseCode::ReturnValue(id) => { @@ -1260,7 +1261,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ); if !fcx.tcx.features().unsized_locals { let id = fcx.tcx.hir().get_parent_node(id); - unsized_return = fcx.is_unsized_return(id); + unsized_return = self.is_return_ty_unsized(fcx, id); } } _ => { @@ -1290,15 +1291,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { .filter(|e| fcx.is_assign_to_bool(e, self.expected_ty())) .is_some(); - if unsized_return { - fcx.tcx.sess.delay_span_bug( - cause.span, - &format!( - "elided E0308 in favor of more detailed E0277 or E0746: {:?}", - cause.code - ), - ); - } err.emit_unless(assign_to_bool || unsized_return); self.final_ty = Some(fcx.tcx.types.err); @@ -1365,10 +1357,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { "...is found to be `{}` here", fcx.resolve_vars_with_obligations(expected), )); - err.note( - "`impl Trait` as a return type requires that all the returned values must have \ - the same type", - ); + err.note("to return `impl Trait`, all returned values must be of the same type"); let snippet = fcx .tcx .sess @@ -1397,6 +1386,18 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err } + fn is_return_ty_unsized(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { + if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) { + if let hir::FunctionRetTy::Return(ty) = fn_decl.output { + let ty = AstConv::ast_ty_to_ty(fcx, ty); + if let ty::Dynamic(..) = ty.kind { + return true; + } + } + } + false + } + pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> { if let Some(final_ty) = self.final_ty { final_ty diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 8f531ea6199e1..baf9ae1ac2911 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4964,18 +4964,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn is_unsized_return(&self, blk_id: hir::HirId) -> bool { - if let Some((fn_decl, _)) = self.get_fn_decl(blk_id) { - if let hir::FunctionRetTy::Return(ty) = fn_decl.output { - let ty = AstConv::ast_ty_to_ty(self, ty); - if let ty::Dynamic(..) = ty.kind { - return true; - } - } - } - false - } - /// A possible error is to forget to add a return type that is needed: /// /// ``` diff --git a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr index 303d83d342625..44e5c6a99f727 100644 --- a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr +++ b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr @@ -29,7 +29,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:13:13 | LL | let _ = box { |x| (x as u8) }: Box _>; - | ^^^^^^^^^^^^^^^^^^^^^ expected trait `std::ops::Fn`, found closure + | ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::ops::Fn`, found closure | = note: expected struct `std::boxed::Box u8>` found struct `std::boxed::Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:19: 13:32]>` @@ -38,7 +38,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:14:13 | LL | let _ = box if true { false } else { true }: Box; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::fmt::Debug`, found `bool` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::fmt::Debug`, found `bool` | = note: expected struct `std::boxed::Box` found struct `std::boxed::Box` @@ -47,7 +47,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:15:13 | LL | let _ = box match true { true => 'a', false => 'b' }: Box; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::fmt::Debug`, found `char` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::fmt::Debug`, found `char` | = note: expected struct `std::boxed::Box` found struct `std::boxed::Box` @@ -83,7 +83,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:21:13 | LL | let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _; - | ^^^^^^^^^^^^^^^^^^ expected trait `std::ops::Fn`, found closure + | ^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::ops::Fn`, found closure | = note: expected reference `&dyn std::ops::Fn(i32) -> u8` found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:21:16: 21:29]` @@ -92,7 +92,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:22:13 | LL | let _ = &if true { false } else { true }: &dyn Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::fmt::Debug`, found `bool` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::fmt::Debug`, found `bool` | = note: expected reference `&dyn std::fmt::Debug` found reference `&bool` @@ -101,7 +101,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:23:13 | LL | let _ = &match true { true => 'a', false => 'b' }: &dyn Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::fmt::Debug`, found `char` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::fmt::Debug`, found `char` | = note: expected reference `&dyn std::fmt::Debug` found reference `&char` @@ -119,7 +119,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:26:13 | LL | let _ = Box::new(|x| (x as u8)): Box _>; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::ops::Fn`, found closure + | ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::ops::Fn`, found closure | = note: expected struct `std::boxed::Box _>` found struct `std::boxed::Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:22: 26:35]>` diff --git a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr index e615e10bd5f5f..4869f48363447 100644 --- a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr +++ b/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr @@ -13,7 +13,7 @@ LL | pub fn no_iterator() -> impl Iterator { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` LL | LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` + | ------------------------- this returned value is of type `std::array::IntoIter` | = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -33,7 +33,7 @@ LL | pub fn no_double_ended_iterator() -> impl DoubleEndedIterator { | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` LL | LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` + | ------------------------- this returned value is of type `std::array::IntoIter` | = note: required because of the requirements on the impl of `std::iter::DoubleEndedIterator` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -53,7 +53,7 @@ LL | pub fn no_exact_size_iterator() -> impl ExactSizeIterator { | ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` LL | LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` + | ------------------------- this returned value is of type `std::array::IntoIter` | = note: required because of the requirements on the impl of `std::iter::ExactSizeIterator` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -73,7 +73,7 @@ LL | pub fn no_fused_iterator() -> impl FusedIterator { | ^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` LL | LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` + | ------------------------- this returned value is of type `std::array::IntoIter` | = note: required because of the requirements on the impl of `std::iter::FusedIterator` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -93,7 +93,7 @@ LL | pub fn no_trusted_len() -> impl TrustedLen { | ^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` LL | LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` + | ------------------------- this returned value is of type `std::array::IntoIter` | = note: required because of the requirements on the impl of `std::iter::TrustedLen` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -113,7 +113,7 @@ LL | pub fn no_clone() -> impl Clone { | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` LL | LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` + | ------------------------- this returned value is of type `std::array::IntoIter` | = note: required because of the requirements on the impl of `std::clone::Clone` for `std::array::IntoIter` = note: the return type of a function must have a statically known size @@ -133,7 +133,7 @@ LL | pub fn no_debug() -> impl Debug { | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` LL | LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter<_, _: usize>` + | ------------------------- this returned value is of type `std::array::IntoIter` | = note: required because of the requirements on the impl of `std::fmt::Debug` for `std::array::IntoIter` = note: the return type of a function must have a statically known size diff --git a/src/test/ui/destructure-trait-ref.rs b/src/test/ui/destructure-trait-ref.rs index fb92196b2bd56..34e7cad935aeb 100644 --- a/src/test/ui/destructure-trait-ref.rs +++ b/src/test/ui/destructure-trait-ref.rs @@ -33,12 +33,10 @@ fn main() { //~^ ERROR mismatched types //~| expected trait object `dyn T` //~| found reference `&_` - //~| expected trait `T`, found reference let &&&x = &(&1isize as &dyn T); //~^ ERROR mismatched types //~| expected trait object `dyn T` //~| found reference `&_` - //~| expected trait `T`, found reference let box box x = box 1isize as Box; //~^ ERROR mismatched types //~| expected trait object `dyn T` diff --git a/src/test/ui/destructure-trait-ref.stderr b/src/test/ui/destructure-trait-ref.stderr index c78166f411d28..f99bf2ffdc9d4 100644 --- a/src/test/ui/destructure-trait-ref.stderr +++ b/src/test/ui/destructure-trait-ref.stderr @@ -22,31 +22,31 @@ error[E0308]: mismatched types LL | let &&x = &1isize as &dyn T; | ^^ | | - | expected trait `T`, found reference + | expected trait object `dyn T`, found reference | help: you can probably remove the explicit borrow: `x` | = note: expected trait object `dyn T` found reference `&_` error[E0308]: mismatched types - --> $DIR/destructure-trait-ref.rs:37:11 + --> $DIR/destructure-trait-ref.rs:36:11 | LL | let &&&x = &(&1isize as &dyn T); | ^^ | | - | expected trait `T`, found reference + | expected trait object `dyn T`, found reference | help: you can probably remove the explicit borrow: `x` | = note: expected trait object `dyn T` found reference `&_` error[E0308]: mismatched types - --> $DIR/destructure-trait-ref.rs:42:13 + --> $DIR/destructure-trait-ref.rs:40:13 | LL | let box box x = box 1isize as Box; | ^^^^^ ------------------------ this expression has type `std::boxed::Box` | | - | expected trait `T`, found struct `std::boxed::Box` + | expected trait object `dyn T`, found struct `std::boxed::Box` | = note: expected trait object `dyn T` found struct `std::boxed::Box<_>` diff --git a/src/test/ui/dst/dst-bad-assign-3.rs b/src/test/ui/dst/dst-bad-assign-3.rs index e3b621b909a0c..d05b3937c998c 100644 --- a/src/test/ui/dst/dst-bad-assign-3.rs +++ b/src/test/ui/dst/dst-bad-assign-3.rs @@ -32,7 +32,7 @@ pub fn main() { let z: Box = Box::new(Bar1 {f: 36}); f5.2 = Bar1 {f: 36}; //~^ ERROR mismatched types - //~| expected trait `ToBar`, found struct `Bar1` + //~| expected trait object `dyn ToBar`, found struct `Bar1` //~| expected trait object `dyn ToBar` //~| found struct `Bar1` //~| ERROR the size for values of type diff --git a/src/test/ui/dst/dst-bad-assign-3.stderr b/src/test/ui/dst/dst-bad-assign-3.stderr index dc03f38e10387..0b6f9df2d83ee 100644 --- a/src/test/ui/dst/dst-bad-assign-3.stderr +++ b/src/test/ui/dst/dst-bad-assign-3.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/dst-bad-assign-3.rs:33:12 | LL | f5.2 = Bar1 {f: 36}; - | ^^^^^^^^^^^^ expected trait `ToBar`, found struct `Bar1` + | ^^^^^^^^^^^^ expected trait object `dyn ToBar`, found struct `Bar1` | = note: expected trait object `dyn ToBar` found struct `Bar1` diff --git a/src/test/ui/dst/dst-bad-assign.rs b/src/test/ui/dst/dst-bad-assign.rs index ed94242f5bfd0..496e01ae00532 100644 --- a/src/test/ui/dst/dst-bad-assign.rs +++ b/src/test/ui/dst/dst-bad-assign.rs @@ -34,7 +34,7 @@ pub fn main() { let z: Box = Box::new(Bar1 {f: 36}); f5.ptr = Bar1 {f: 36}; //~^ ERROR mismatched types - //~| expected trait `ToBar`, found struct `Bar1` + //~| expected trait object `dyn ToBar`, found struct `Bar1` //~| expected trait object `dyn ToBar` //~| found struct `Bar1` //~| ERROR the size for values of type diff --git a/src/test/ui/dst/dst-bad-assign.stderr b/src/test/ui/dst/dst-bad-assign.stderr index 8031f162482e3..434c460759fb4 100644 --- a/src/test/ui/dst/dst-bad-assign.stderr +++ b/src/test/ui/dst/dst-bad-assign.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/dst-bad-assign.rs:35:14 | LL | f5.ptr = Bar1 {f: 36}; - | ^^^^^^^^^^^^ expected trait `ToBar`, found struct `Bar1` + | ^^^^^^^^^^^^ expected trait object `dyn ToBar`, found struct `Bar1` | = note: expected trait object `dyn ToBar` found struct `Bar1` diff --git a/src/test/ui/error-codes/E0746.stderr b/src/test/ui/error-codes/E0746.stderr index 1c88ce64749ae..e7a8fd304cabe 100644 --- a/src/test/ui/error-codes/E0746.stderr +++ b/src/test/ui/error-codes/E0746.stderr @@ -1,23 +1,23 @@ -error[E0746]: return type cannot have a bare trait because it must be `Sized` +error[E0746]: return type cannot have an unboxed trait object --> $DIR/E0746.rs:8:13 | LL | fn foo() -> dyn Trait { Struct } | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: you can use the `impl Trait` feature in the return type because all the return paths are of type `Struct`, which implements `dyn Trait` +help: return `impl Trait` instead, as all return paths are of type `Struct`, which implements `Trait` | LL | fn foo() -> impl Trait { Struct } | ^^^^^^^^^^ -error[E0746]: return type cannot have a bare trait because it must be `Sized` +error[E0746]: return type cannot have an unboxed trait object --> $DIR/E0746.rs:11:13 | LL | fn bar() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: you can use the `impl Trait` feature in the return type because all the return paths are of type `{integer}`, which implements `dyn Trait` +help: return `impl Trait` instead, as all return paths are of type `{integer}`, which implements `Trait` | LL | fn bar() -> impl Trait { | ^^^^^^^^^^ diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index ff7438e9affc1..3d0707c091644 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:7:35 | LL | fn fuz() -> (usize, Trait) { (42, Struct) } - | ^^^^^^ expected trait `Trait`, found struct `Struct` + | ^^^^^^ expected trait object `dyn Trait`, found struct `Struct` | = note: expected trait object `(dyn Trait + 'static)` found struct `Struct` @@ -24,7 +24,7 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:10:39 | LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } - | ^^^^^^ expected trait `Trait`, found struct `Struct` + | ^^^^^^ expected trait object `dyn Trait`, found struct `Struct` | = note: expected trait object `(dyn Trait + 'static)` found struct `Struct` @@ -42,26 +42,26 @@ LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } = note: required because it appears within the type `(usize, (dyn Trait + 'static))` = note: the return type of a function must have a statically known size -error[E0746]: return type cannot have a bare trait because it must be `Sized` +error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:13:13 | LL | fn bap() -> Trait { Struct } | ^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: you can use the `impl Trait` feature in the return type because all the return paths are of type `Struct`, which implements `dyn Trait` +help: return `impl Trait` instead, as all return paths are of type `Struct`, which implements `Trait` | LL | fn bap() -> impl Trait { Struct } | ^^^^^^^^^^ -error[E0746]: return type cannot have a bare trait because it must be `Sized` +error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:15:13 | LL | fn ban() -> dyn Trait { Struct } | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: you can use the `impl Trait` feature in the return type because all the return paths are of type `Struct`, which implements `dyn Trait` +help: return `impl Trait` instead, as all return paths are of type `Struct`, which implements `Trait` | LL | fn ban() -> impl Trait { Struct } | ^^^^^^^^^^ @@ -76,40 +76,26 @@ LL | fn bak() -> dyn Trait { unimplemented!() } = note: to learn more, visit = note: the return type of a function must have a statically known size -error[E0746]: return type cannot have a bare trait because it must be `Sized` +error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:19:13 | LL | fn bal() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | -help: if all the returned values were of the same type you could use `impl Trait` as the return type - --> $DIR/dyn-trait-return-should-be-impl-trait.rs:23:5 - | -LL | return Struct; - | ^^^^^^ -LL | } -LL | 42 - | ^^ - = help: alternatively, you can always create a new `enum` with a variant for each returned type + = note: if trait `Trait` was object safe, you could return a trait object + = note: if all the returned values were of the same type you could use `impl Trait` as the return type = note: for information on `impl Trait`, see = note: for information on trait objects, see -help: if the performance implications are acceptable, you can return a trait object - | -LL | fn bal() -> Box { -LL | if true { -LL | return Box::new(Struct); -LL | } -LL | Box::new(42) - | + = note: you can create a new `enum` with a variant for each returned type -error[E0746]: return type cannot have a bare trait because it must be `Sized` +error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:27:13 | LL | fn bat() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | = note: for information on `impl Trait`, see -help: you can use the `impl Trait` feature in the return type because all the return paths are of type `{integer}`, which implements `dyn Trait` +help: return `impl Trait` instead, as all return paths are of type `{integer}`, which implements `Trait` | LL | fn bat() -> impl Trait { | ^^^^^^^^^^ diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index 215b6d52918ab..be8653d1689f8 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -10,7 +10,7 @@ LL | } LL | 0_u32 | ^^^^^ expected `i32`, found `u32` | - = note: `impl Trait` as a return type requires that all the returned values must have the same type + = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` = note: for information on `impl Trait`, see = note: for information on trait objects, see diff --git a/src/test/ui/issues/issue-58344.stderr b/src/test/ui/issues/issue-58344.stderr index 9b07dbd7ab69c..e0c196e518ba3 100644 --- a/src/test/ui/issues/issue-58344.stderr +++ b/src/test/ui/issues/issue-58344.stderr @@ -5,7 +5,7 @@ LL | ) -> Either>::Output>, impl Trait<` is not implemented for `impl Trait<::Output>` ... LL | add_generic(value, 1u32) - | ------------------------ this returned value is of type `Either>::Output>, impl Trait<<_ as std::ops::Add<_>>::Output>>` + | ------------------------ this returned value is of type `Either::Output>, impl Trait<::Output>>` | = note: the return type of a function must have a statically known size @@ -16,7 +16,7 @@ LL | ) -> Either>::Output>, impl Trait<` is not implemented for `impl Trait<::Output>` ... LL | add_generic(value, 1u32) - | ------------------------ this returned value is of type `Either>::Output>, impl Trait<<_ as std::ops::Add<_>>::Output>>` + | ------------------------ this returned value is of type `Either::Output>, impl Trait<::Output>>` | = note: the return type of a function must have a statically known size diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr index 49fa11c35aef8..e43fb6d0edfb9 100644 --- a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr +++ b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr @@ -5,7 +5,7 @@ LL | fn foo() -> impl Future> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Future` is not implemented for `std::result::Result<(), _>` LL | LL | Ok(()) - | ------ this returned value is of type `std::result::Result<_, _>` + | ------ this returned value is of type `std::result::Result<(), _>` | = note: the return type of a function must have a statically known size diff --git a/src/test/ui/never_type/feature-gate-never_type_fallback.stderr b/src/test/ui/never_type/feature-gate-never_type_fallback.stderr index 88bfed2b54742..77288f1badac5 100644 --- a/src/test/ui/never_type/feature-gate-never_type_fallback.stderr +++ b/src/test/ui/never_type/feature-gate-never_type_fallback.stderr @@ -5,7 +5,7 @@ LL | fn should_ret_unit() -> impl T { | ^^^^^^ the trait `T` is not implemented for `()` LL | LL | panic!() - | -------- this returned value is of type `_` + | -------- this returned value is of type `()` | = note: the return type of a function must have a statically known size = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) diff --git a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr index 9db5250e4d876..2c0425e718abc 100644 --- a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr @@ -10,7 +10,7 @@ LL | } LL | 1u32 | ^^^^ expected `i32`, found `u32` | - = note: `impl Trait` as a return type requires that all the returned values must have the same type + = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -27,7 +27,7 @@ LL | } else { LL | return 1u32; | ^^^^ expected `i32`, found `u32` | - = note: `impl Trait` as a return type requires that all the returned values must have the same type + = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -44,7 +44,7 @@ LL | } else { LL | 1u32 | ^^^^ expected `i32`, found `u32` | - = note: `impl Trait` as a return type requires that all the returned values must have the same type + = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -73,7 +73,7 @@ LL | 0 => return 0i32, LL | _ => 1u32, | ^^^^ expected `i32`, found `u32` | - = note: `impl Trait` as a return type requires that all the returned values must have the same type + = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -92,7 +92,7 @@ LL | | _ => 2u32, LL | | } | |_____^ expected `i32`, found `u32` | - = note: `impl Trait` as a return type requires that all the returned values must have the same type + = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -109,7 +109,7 @@ LL | return 0i32; LL | 1u32 | ^^^^ expected `i32`, found `u32` | - = note: `impl Trait` as a return type requires that all the returned values must have the same type + = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` = note: for information on `impl Trait`, see = note: for information on trait objects, see diff --git a/src/test/ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.stderr b/src/test/ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.stderr index 14c09ade7dde3..a656b20c23ec3 100644 --- a/src/test/ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.stderr +++ b/src/test/ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | fn ice(x: Box>) { | - possibly return type missing here? LL | *x - | ^^ expected `()`, found trait `std::iter::Iterator` + | ^^ expected `()`, found trait object `dyn std::iter::Iterator` | = note: expected unit type `()` found trait object `(dyn std::iter::Iterator + 'static)` From 509cb33dbcdb631625288f72c359a35dde6524b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 14 Jan 2020 14:49:30 -0800 Subject: [PATCH 11/15] review comments --- src/librustc/traits/error_reporting/suggestions.rs | 9 +++------ src/librustc_typeck/check/coercion.rs | 1 + src/test/ui/impl-trait/equality.stderr | 1 + .../point-to-type-err-cause-on-impl-trait-return.stderr | 6 ++++++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs index 0a9747b631e87..c2f562b4bc7b2 100644 --- a/src/librustc/traits/error_reporting/suggestions.rs +++ b/src/librustc/traits/error_reporting/suggestions.rs @@ -624,12 +624,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } else { // We still want to verify whether all the return types conform to each other. for expr in &visitor.0 { - if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { - if let Some(ty) = last_ty { - all_returns_have_same_type &= ty == returned_ty; - } - last_ty = Some(returned_ty); - } + let returned_ty = tables.node_type_opt(expr.hir_id); + all_returns_have_same_type &= last_ty == returned_ty || last_ty.is_none(); + last_ty = returned_ty; } } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 66499b1753fdd..8aa2cb5034224 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1372,6 +1372,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { &snippet[5..] )); } + err.help("alternatively, create a new `enum` with a variant for each returned type"); let impl_trait_msg = "for information on `impl Trait`, see \ "; diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index be8653d1689f8..ceb32dd4cd33e 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -12,6 +12,7 @@ LL | 0_u32 | = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` + = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see = note: for information on trait objects, see diff --git a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr index 2c0425e718abc..87daab5ca7a22 100644 --- a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr @@ -12,6 +12,7 @@ LL | 1u32 | = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` + = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -29,6 +30,7 @@ LL | return 1u32; | = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` + = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -46,6 +48,7 @@ LL | 1u32 | = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` + = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -75,6 +78,7 @@ LL | _ => 1u32, | = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` + = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -94,6 +98,7 @@ LL | | } | = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` + = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see = note: for information on trait objects, see @@ -111,6 +116,7 @@ LL | 1u32 | = note: to return `impl Trait`, all returned values must be of the same type = help: you can instead return a trait object using `Box` + = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see = note: for information on trait objects, see From c305ac31c09fdd5078fa0e69e718b4da10d9e354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 Jan 2020 09:55:56 -0800 Subject: [PATCH 12/15] Fix error index test --- src/librustc_error_codes/error_codes/E0746.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md index acf369d8e144f..2df27bcf0bf09 100644 --- a/src/librustc_error_codes/error_codes/E0746.md +++ b/src/librustc_error_codes/error_codes/E0746.md @@ -2,7 +2,8 @@ Return types cannot be `dyn Trait`s as they must be `Sized`. Erroneous code example: -```compile_fail,E0746 +```compile_fail,E0277 +# // FIXME: after E0746 is in beta, change the above trait T { fn bar(&self); } From d7a62124018ce8438caeedca203d39997f130b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 Jan 2020 11:14:05 -0800 Subject: [PATCH 13/15] review comments --- src/librustc/traits/error_reporting/mod.rs | 32 --- .../traits/error_reporting/suggestions.rs | 270 +++++++++--------- src/librustc/traits/mod.rs | 2 +- src/librustc_error_codes/error_codes/E0746.md | 10 +- src/librustc_typeck/check/coercion.rs | 67 +++-- ...n-trait-return-should-be-impl-trait.stderr | 11 +- src/test/ui/impl-trait/equality.stderr | 4 +- ...type-err-cause-on-impl-trait-return.stderr | 24 +- 8 files changed, 204 insertions(+), 216 deletions(-) diff --git a/src/librustc/traits/error_reporting/mod.rs b/src/librustc/traits/error_reporting/mod.rs index 17d7b75a7f7d6..db3173989ac60 100644 --- a/src/librustc/traits/error_reporting/mod.rs +++ b/src/librustc/traits/error_reporting/mod.rs @@ -25,7 +25,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::Visitor; use rustc_span::source_map::SourceMap; use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; @@ -1411,34 +1410,3 @@ pub fn suggest_constraining_type_param( } false } - -/// Collect all the returned expressions within the input expression. -/// Used to point at the return spans when we want to suggest some change to them. -struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>); - -impl<'v> Visitor<'v> for ReturnsVisitor<'v> { - type Map = rustc::hir::map::Map<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<'_, Self::Map> { - hir::intravisit::NestedVisitorMap::None - } - - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - match ex.kind { - hir::ExprKind::Ret(Some(ex)) => self.0.push(ex), - _ => {} - } - hir::intravisit::walk_expr(self, ex); - } - - fn visit_body(&mut self, body: &'v hir::Body<'v>) { - if body.generator_kind().is_none() { - if let hir::ExprKind::Block(block, None) = body.value.kind { - if let Some(expr) = block.expr { - self.0.push(expr); - } - } - } - hir::intravisit::walk_body(self, body); - } -} diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs index c2f562b4bc7b2..7c1b1041c34c3 100644 --- a/src/librustc/traits/error_reporting/suggestions.rs +++ b/src/librustc/traits/error_reporting/suggestions.rs @@ -563,157 +563,159 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let hir = self.tcx.hir(); let parent_node = hir.get_parent_node(obligation.cause.body_id); let node = hir.find(parent_node); - if let Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(sig, _, body_id), .. + let (sig, body_id) = if let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(sig, _, body_id), + .. })) = node { - let body = hir.body(*body_id); - let trait_ref = self.resolve_vars_if_possible(trait_ref); - let ty = trait_ref.skip_binder().self_ty(); - let is_object_safe; - match ty.kind { - ty::Dynamic(predicates, _) => { - // The `dyn Trait` is not object safe, do not suggest `Box`. - is_object_safe = predicates.principal_def_id().map_or(true, |def_id| { - !object_safety_violations(self.tcx, def_id).is_empty() - }) - } - // We only want to suggest `impl Trait` to `dyn Trait`s. - // For example, `fn foo() -> str` needs to be filtered out. - _ => return false, + (sig, body_id) + } else { + return false; + }; + let body = hir.body(*body_id); + let trait_ref = self.resolve_vars_if_possible(trait_ref); + let ty = trait_ref.skip_binder().self_ty(); + let is_object_safe = match ty.kind { + ty::Dynamic(predicates, _) => { + // If the `dyn Trait` is not object safe, do not suggest `Box`. + predicates + .principal_def_id() + .map_or(true, |def_id| object_safety_violations(self.tcx, def_id).is_empty()) } + // We only want to suggest `impl Trait` to `dyn Trait`s. + // For example, `fn foo() -> str` needs to be filtered out. + _ => return false, + }; - let ret_ty = if let hir::FunctionRetTy::Return(ret_ty) = sig.decl.output { - ret_ty - } else { - return false; - }; - - // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for - // cases like `fn foo() -> (dyn Trait, i32) {}`. - // Recursively look for `TraitObject` types and if there's only one, use that span to - // suggest `impl Trait`. - - // Visit to make sure there's a single `return` type to suggest `impl Trait`, - // otherwise suggest using `Box` or an enum. - let mut visitor = ReturnsVisitor(vec![]); - visitor.visit_body(&body); - - let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); + let ret_ty = if let hir::FunctionRetTy::Return(ret_ty) = sig.decl.output { + ret_ty + } else { + return false; + }; - let mut all_returns_conform_to_trait = true; - let mut all_returns_have_same_type = true; - let mut last_ty = None; - if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { - let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); - let param_env = ty::ParamEnv::empty(); - if let ty::Dynamic(predicates, _) = &ty_ret_ty.kind { - for expr in &visitor.0 { - if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { - all_returns_have_same_type &= - Some(returned_ty) == last_ty || last_ty.is_none(); - last_ty = Some(returned_ty); - for predicate in predicates.iter() { - let pred = predicate.with_self_ty(self.tcx, returned_ty); - let obl = Obligation::new(cause.clone(), param_env, pred); - all_returns_conform_to_trait &= self.predicate_may_hold(&obl); - } + // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for + // cases like `fn foo() -> (dyn Trait, i32) {}`. + // Recursively look for `TraitObject` types and if there's only one, use that span to + // suggest `impl Trait`. + + // Visit to make sure there's a single `return` type to suggest `impl Trait`, + // otherwise suggest using `Box` or an enum. + let mut visitor = ReturnsVisitor(vec![]); + visitor.visit_body(&body); + + let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); + + let mut all_returns_conform_to_trait = true; + let mut all_returns_have_same_type = true; + let mut last_ty = None; + if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { + let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); + let param_env = ty::ParamEnv::empty(); + if let ty::Dynamic(predicates, _) = &ty_ret_ty.kind { + for expr in &visitor.0 { + if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { + all_returns_have_same_type &= + Some(returned_ty) == last_ty || last_ty.is_none(); + last_ty = Some(returned_ty); + for predicate in predicates.iter() { + let pred = predicate.with_self_ty(self.tcx, returned_ty); + let obl = Obligation::new(cause.clone(), param_env, pred); + all_returns_conform_to_trait &= self.predicate_may_hold(&obl); } } } - } else { - // We still want to verify whether all the return types conform to each other. - for expr in &visitor.0 { - let returned_ty = tables.node_type_opt(expr.hir_id); - all_returns_have_same_type &= last_ty == returned_ty || last_ty.is_none(); - last_ty = returned_ty; - } } + } else { + // We still want to verify whether all the return types conform to each other. + for expr in &visitor.0 { + let returned_ty = tables.node_type_opt(expr.hir_id); + all_returns_have_same_type &= last_ty == returned_ty || last_ty.is_none(); + last_ty = returned_ty; + } + } - let (snippet, last_ty) = - if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = ( - // Verify that we're dealing with a return `dyn Trait` - ret_ty.span.overlaps(span), - &ret_ty.kind, - self.tcx.sess.source_map().span_to_snippet(ret_ty.span), - // If any of the return types does not conform to the trait, then we can't - // suggest `impl Trait` nor trait objects, it is a type mismatch error. - all_returns_conform_to_trait, - last_ty, - ) { - (snippet, last_ty) - } else { - return false; - }; - err.code(error_code!(E0746)); - err.set_primary_message("return type cannot have an unboxed trait object"); - err.children.clear(); - let impl_trait_msg = "for information on `impl Trait`, see \ - "; - let trait_obj_msg = "for information on trait objects, see \ - "; - let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); - let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] }; - if all_returns_have_same_type { - // Suggest `-> impl Trait`. - err.span_suggestion( + let (snippet, last_ty) = + if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = ( + // Verify that we're dealing with a return `dyn Trait` + ret_ty.span.overlaps(span), + &ret_ty.kind, + self.tcx.sess.source_map().span_to_snippet(ret_ty.span), + // If any of the return types does not conform to the trait, then we can't + // suggest `impl Trait` nor trait objects, it is a type mismatch error. + all_returns_conform_to_trait, + last_ty, + ) { + (snippet, last_ty) + } else { + return false; + }; + err.code(error_code!(E0746)); + err.set_primary_message("return type cannot have an unboxed trait object"); + err.children.clear(); + let impl_trait_msg = "for information on `impl Trait`, see \ + "; + let trait_obj_msg = "for information on trait objects, see \ + "; + let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); + let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] }; + if all_returns_have_same_type { + // Suggest `-> impl Trait`. + err.span_suggestion( + ret_ty.span, + &format!( + "return `impl {1}` instead, as all return paths are of type `{}`, \ + which implements `{1}`", + last_ty, trait_obj, + ), + format!("impl {}", trait_obj), + Applicability::MachineApplicable, + ); + err.note(impl_trait_msg); + } else { + if is_object_safe { + // Suggest `-> Box` and `Box::new(returned_value)`. + // Get all the return values and collect their span and suggestion. + let mut suggestions = visitor + .0 + .iter() + .map(|expr| { + ( + expr.span, + format!( + "Box::new({})", + self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap() + ), + ) + }) + .collect::>(); + // Add the suggestion for the return type. + suggestions.push(( ret_ty.span, - &format!( - "return `impl {1}` instead, as all return paths are of type `{}`, \ - which implements `{1}`", - last_ty, trait_obj, - ), - format!("impl {}", trait_obj), - Applicability::MachineApplicable, + format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet), + )); + err.multipart_suggestion( + "return a trait object instead", + suggestions, + Applicability::MaybeIncorrect, ); - err.note(impl_trait_msg); } else { - if is_object_safe { - // Suggest `-> Box` and `Box::new(returned_value)`. - // Get all the return values and collect their span and suggestion. - let mut suggestions = visitor - .0 - .iter() - .map(|expr| { - ( - expr.span, - format!( - "Box::new({})", - self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap() - ), - ) - }) - .collect::>(); - // Add the suggestion for the return type. - suggestions.push(( - ret_ty.span, - format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet), - )); - err.multipart_suggestion( - "return a trait object instead", - suggestions, - Applicability::MaybeIncorrect, - ); - } else { - err.note(&format!( - "if trait `{}` was object safe, you could return a trait object", - trait_obj, - )); - } err.note(&format!( - "if all the returned values were of the same type you could use \ - `impl {}` as the return type", + "if trait `{}` was object safe, you could return a trait object", trait_obj, )); - err.note(impl_trait_msg); - err.note(trait_obj_msg); - err.note("you can create a new `enum` with a variant for each returned type"); } - return true; + err.note(trait_obj_msg); + err.note(&format!( + "if all the returned values were of the same type you could use \ + `impl {}` as the return type", + trait_obj, + )); + err.note(impl_trait_msg); + err.note("you can create a new `enum` with a variant for each returned type"); } - false + true } crate fn point_at_returns_when_relevant( @@ -1686,6 +1688,8 @@ pub fn suggest_constraining_type_param( false } +/// Collect all the returned expressions within the input expression. +/// Used to point at the return spans when we want to suggest some change to them. struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>); impl<'v> Visitor<'v> for ReturnsVisitor<'v> { diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index b4998d4486f09..2e5da2b038254 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -1171,7 +1171,7 @@ impl<'tcx> ObligationCause<'tcx> { } } -impl<'tcx> ObligationCauseCode<'tcx> { +impl ObligationCauseCode<'_> { // Return the base obligation, ignoring derived obligations. pub fn peel_derives(&self) -> &Self { let mut base_cause = self; diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md index 2df27bcf0bf09..041061f3380c1 100644 --- a/src/librustc_error_codes/error_codes/E0746.md +++ b/src/librustc_error_codes/error_codes/E0746.md @@ -12,8 +12,8 @@ impl T for S { fn bar(&self) {} } -// Having the trait `T` as return type is invalid because bare traits do not -// have a statically known size: +// Having the trait `T` as return type is invalid because +// bare trait objects do not have a statically known size: fn foo() -> dyn T { S(42) } @@ -32,15 +32,15 @@ If there is a single type involved, you can use [`impl Trait`]: # fn bar(&self) {} # } // The compiler will select `S(usize)` as the materialized return type of this -// function, but callers will only be able to access associated items from `T`. +// function, but callers will only know that the return type implements `T`. fn foo() -> impl T { S(42) } ``` If there are multiple types involved, the only way you care to interact with -them is through the trait's interface and having to rely on dynamic dispatch is -acceptable, then you can use [trait objects] with `Box`, or other container +them is through the trait's interface, and having to rely on dynamic dispatch +is acceptable, then you can use [trait objects] with `Box`, or other container types like `Rc` or `Arc`: ``` diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 8aa2cb5034224..768e532fa3b91 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -68,7 +68,7 @@ use rustc_error_codes::*; use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_span; +use rustc_span::{self, Span}; use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; use smallvec::{smallvec, SmallVec}; @@ -1352,39 +1352,48 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } if let (Some(sp), Some(return_sp)) = (fcx.ret_coercion_span.borrow().as_ref(), return_sp) { - err.span_label(return_sp, "expected because this return type..."); - err.span_label( *sp, format!( - "...is found to be `{}` here", - fcx.resolve_vars_with_obligations(expected), - )); - err.note("to return `impl Trait`, all returned values must be of the same type"); - let snippet = fcx - .tcx - .sess - .source_map() - .span_to_snippet(return_sp) - .unwrap_or_else(|_| "dyn Trait".to_string()); - let mut snippet_iter = snippet.split_whitespace(); - let has_impl = snippet_iter.next().map_or(false, |s| s == "impl"); - if has_impl { - err.help(&format!( - "you can instead return a trait object using `Box`", - &snippet[5..] - )); - } - err.help("alternatively, create a new `enum` with a variant for each returned type"); - let impl_trait_msg = "for information on `impl Trait`, see \ + self.add_impl_trait_explanation(&mut err, fcx, expected, *sp, return_sp); + } + err + } + + fn add_impl_trait_explanation<'a>( + &self, + err: &mut DiagnosticBuilder<'a>, + fcx: &FnCtxt<'a, 'tcx>, + expected: Ty<'tcx>, + sp: Span, + return_sp: Span, + ) { + err.span_label(return_sp, "expected because this return type..."); + err.span_label( + sp, + format!("...is found to be `{}` here", fcx.resolve_vars_with_obligations(expected)), + ); + let impl_trait_msg = "for information on `impl Trait`, see \ "; - let trait_obj_msg = "for information on trait objects, see \ + let trait_obj_msg = "for information on trait objects, see \ "; - err.note(impl_trait_msg); - if has_impl { - err.note(trait_obj_msg); - } + err.note("to return `impl Trait`, all returned values must be of the same type"); + err.note(impl_trait_msg); + let snippet = fcx + .tcx + .sess + .source_map() + .span_to_snippet(return_sp) + .unwrap_or_else(|_| "dyn Trait".to_string()); + let mut snippet_iter = snippet.split_whitespace(); + let has_impl = snippet_iter.next().map_or(false, |s| s == "impl"); + if has_impl { + err.help(&format!( + "you can instead return a trait object using `Box`", + &snippet[5..] + )); + err.note(trait_obj_msg); } - err + err.help("alternatively, create a new `enum` with a variant for each returned type"); } fn is_return_ty_unsized(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index 3d0707c091644..ac101f8f3ce36 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -82,11 +82,18 @@ error[E0746]: return type cannot have an unboxed trait object LL | fn bal() -> dyn Trait { | ^^^^^^^^^ doesn't have a size known at compile-time | - = note: if trait `Trait` was object safe, you could return a trait object + = note: for information on trait objects, see = note: if all the returned values were of the same type you could use `impl Trait` as the return type = note: for information on `impl Trait`, see - = note: for information on trait objects, see = note: you can create a new `enum` with a variant for each returned type +help: return a trait object instead + | +LL | fn bal() -> Box { +LL | if true { +LL | return Box::new(Struct); +LL | } +LL | Box::new(42) + | error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:27:13 diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index ceb32dd4cd33e..a399fadbc5db5 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -11,10 +11,10 @@ LL | 0_u32 | ^^^^^ expected `i32`, found `u32` | = note: to return `impl Trait`, all returned values must be of the same type - = help: you can instead return a trait object using `Box` - = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see + = help: you can instead return a trait object using `Box` = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type error[E0277]: cannot add `impl Foo` to `u32` --> $DIR/equality.rs:24:11 diff --git a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr index 87daab5ca7a22..9859c73b7b18b 100644 --- a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr @@ -11,10 +11,10 @@ LL | 1u32 | ^^^^ expected `i32`, found `u32` | = note: to return `impl Trait`, all returned values must be of the same type - = help: you can instead return a trait object using `Box` - = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see + = help: you can instead return a trait object using `Box` = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:13:16 @@ -29,10 +29,10 @@ LL | return 1u32; | ^^^^ expected `i32`, found `u32` | = note: to return `impl Trait`, all returned values must be of the same type - = help: you can instead return a trait object using `Box` - = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see + = help: you can instead return a trait object using `Box` = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:22:9 @@ -47,10 +47,10 @@ LL | 1u32 | ^^^^ expected `i32`, found `u32` | = note: to return `impl Trait`, all returned values must be of the same type - = help: you can instead return a trait object using `Box` - = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see + = help: you can instead return a trait object using `Box` = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type error[E0308]: `if` and `else` have incompatible types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:31:9 @@ -77,10 +77,10 @@ LL | _ => 1u32, | ^^^^ expected `i32`, found `u32` | = note: to return `impl Trait`, all returned values must be of the same type - = help: you can instead return a trait object using `Box` - = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see + = help: you can instead return a trait object using `Box` = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:45:5 @@ -97,10 +97,10 @@ LL | | } | |_____^ expected `i32`, found `u32` | = note: to return `impl Trait`, all returned values must be of the same type - = help: you can instead return a trait object using `Box` - = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see + = help: you can instead return a trait object using `Box` = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:59:13 @@ -115,10 +115,10 @@ LL | 1u32 | ^^^^ expected `i32`, found `u32` | = note: to return `impl Trait`, all returned values must be of the same type - = help: you can instead return a trait object using `Box` - = help: alternatively, create a new `enum` with a variant for each returned type = note: for information on `impl Trait`, see + = help: you can instead return a trait object using `Box` = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type error: aborting due to 7 previous errors From 00e262689599a6a753bbf7ce8786e07ed100d238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 Jan 2020 15:49:54 -0800 Subject: [PATCH 14/15] Account for object safety when suggesting `Box` --- .../traits/error_reporting/suggestions.rs | 4 +- src/librustc_hir/hir.rs | 7 +++ src/librustc_typeck/check/coercion.rs | 53 +++++++++++++++---- ...n-trait-return-should-be-impl-trait.stderr | 2 +- src/test/ui/impl-trait/equality.stderr | 2 +- ...safe-trait-in-return-position-dyn-trait.rs | 35 ++++++++++++ ...-trait-in-return-position-dyn-trait.stderr | 21 ++++++++ ...afe-trait-in-return-position-impl-trait.rs | 46 ++++++++++++++++ ...trait-in-return-position-impl-trait.stderr | 39 ++++++++++++++ ...type-err-cause-on-impl-trait-return.stderr | 12 ++--- 10 files changed, 202 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.rs create mode 100644 src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr create mode 100644 src/test/ui/impl-trait/object-unsafe-trait-in-return-position-impl-trait.rs create mode 100644 src/test/ui/impl-trait/object-unsafe-trait-in-return-position-impl-trait.stderr diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs index 7c1b1041c34c3..c3af063d518f0 100644 --- a/src/librustc/traits/error_reporting/suggestions.rs +++ b/src/librustc/traits/error_reporting/suggestions.rs @@ -696,11 +696,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet), )); err.multipart_suggestion( - "return a trait object instead", + "return a boxed trait object instead", suggestions, Applicability::MaybeIncorrect, ); } else { + // This is currently not possible to trigger because E0038 takes precedence, but + // leave it in for completeness in case anything changes in an earlier stage. err.note(&format!( "if trait `{}` was object safe, you could return a trait object", trait_obj, diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 550e3654d0800..5c1d600c837c4 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -377,6 +377,13 @@ pub enum GenericBound<'hir> { } impl GenericBound<'_> { + pub fn trait_def_id(&self) -> Option { + match self { + GenericBound::Trait(data, _) => Some(data.trait_ref.trait_def_id()), + _ => None, + } + } + pub fn span(&self) -> Span { match self { &GenericBound::Trait(ref t, ..) => t.span, diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 768e532fa3b91..a32fbff7bfe2d 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -55,6 +55,7 @@ use crate::check::{FnCtxt, Needs}; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc::infer::{Coercion, InferOk, InferResult}; use rustc::session::parse::feature_err; +use rustc::traits::object_safety_violations; use rustc::traits::{self, ObligationCause, ObligationCauseCode}; use rustc::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, @@ -68,8 +69,8 @@ use rustc_error_codes::*; use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_span::{self, Span}; use rustc_span::symbol::sym; +use rustc_span::{self, Span}; use rustc_target::spec::abi::Abi; use smallvec::{smallvec, SmallVec}; use std::ops::Deref; @@ -1311,7 +1312,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err); let mut pointing_at_return_type = false; - let mut return_sp = None; + let mut fn_output = None; // Verify that this is a tail expression of a function, otherwise the // label pointing out the cause for the type coercion will be wrong @@ -1348,11 +1349,11 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ); } if !pointing_at_return_type { - return_sp = Some(fn_decl.output.span()); // `impl Trait` return type + fn_output = Some(&fn_decl.output); // `impl Trait` return type } } - if let (Some(sp), Some(return_sp)) = (fcx.ret_coercion_span.borrow().as_ref(), return_sp) { - self.add_impl_trait_explanation(&mut err, fcx, expected, *sp, return_sp); + if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.borrow().as_ref(), fn_output) { + self.add_impl_trait_explanation(&mut err, fcx, expected, *sp, fn_output); } err } @@ -1363,8 +1364,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx: &FnCtxt<'a, 'tcx>, expected: Ty<'tcx>, sp: Span, - return_sp: Span, + fn_output: &hir::FunctionRetTy<'_>, ) { + let return_sp = fn_output.span(); err.span_label(return_sp, "expected because this return type..."); err.span_label( sp, @@ -1386,11 +1388,42 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { .unwrap_or_else(|_| "dyn Trait".to_string()); let mut snippet_iter = snippet.split_whitespace(); let has_impl = snippet_iter.next().map_or(false, |s| s == "impl"); + // Only suggest `Box` if `Trait` in `impl Trait` is object safe. + let mut is_object_safe = false; + if let hir::FunctionRetTy::Return(ty) = fn_output { + // Get the return type. + if let hir::TyKind::Def(..) = ty.kind { + let ty = AstConv::ast_ty_to_ty(fcx, ty); + // Get the `impl Trait`'s `DefId`. + if let ty::Opaque(def_id, _) = ty.kind { + let hir_id = fcx.tcx.hir().as_local_hir_id(def_id).unwrap(); + // Get the `impl Trait`'s `Item` so that we can get its trait bounds and + // get the `Trait`'s `DefId`. + if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) = + fcx.tcx.hir().expect_item(hir_id).kind + { + // Are of this `impl Trait`'s traits object safe? + is_object_safe = bounds.iter().all(|bound| { + bound.trait_def_id().map_or(false, |def_id| { + object_safety_violations(fcx.tcx, def_id).is_empty() + }) + }) + } + } + } + }; if has_impl { - err.help(&format!( - "you can instead return a trait object using `Box`", - &snippet[5..] - )); + if is_object_safe { + err.help(&format!( + "you can instead return a boxed trait object using `Box`", + &snippet[5..] + )); + } else { + err.help(&format!( + "if the trait `{}` were object safe, you could return a boxed trait object", + &snippet[5..] + )); + } err.note(trait_obj_msg); } err.help("alternatively, create a new `enum` with a variant for each returned type"); diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index ac101f8f3ce36..977a7ef0e0244 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -86,7 +86,7 @@ LL | fn bal() -> dyn Trait { = note: if all the returned values were of the same type you could use `impl Trait` as the return type = note: for information on `impl Trait`, see = note: you can create a new `enum` with a variant for each returned type -help: return a trait object instead +help: return a boxed trait object instead | LL | fn bal() -> Box { LL | if true { diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index a399fadbc5db5..9178358b60a9c 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -12,7 +12,7 @@ LL | 0_u32 | = note: to return `impl Trait`, all returned values must be of the same type = note: for information on `impl Trait`, see - = help: you can instead return a trait object using `Box` + = help: if the trait `Foo` were object safe, you could return a boxed trait object = note: for information on trait objects, see = help: alternatively, create a new `enum` with a variant for each returned type diff --git a/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.rs b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.rs new file mode 100644 index 0000000000000..ab3086c78b3a1 --- /dev/null +++ b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.rs @@ -0,0 +1,35 @@ +#![allow(bare_trait_objects)] +trait NotObjectSafe { + fn foo() -> Self; +} + +struct A; +struct B; + +impl NotObjectSafe for A { + fn foo() -> Self { + A + } +} + +impl NotObjectSafe for B { + fn foo() -> Self { + B + } +} + +fn car() -> dyn NotObjectSafe { //~ ERROR the trait `NotObjectSafe` cannot be made into an object + if true { + return A; + } + B +} + +fn cat() -> Box { //~ ERROR the trait `NotObjectSafe` cannot be made into an + if true { + return Box::new(A); + } + Box::new(B) +} + +fn main() {} diff --git a/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr new file mode 100644 index 0000000000000..0c8d267c13434 --- /dev/null +++ b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr @@ -0,0 +1,21 @@ +error[E0038]: the trait `NotObjectSafe` cannot be made into an object + --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:21:1 + | +LL | fn foo() -> Self; + | --- associated function `foo` has no `self` parameter +... +LL | fn car() -> dyn NotObjectSafe { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NotObjectSafe` cannot be made into an object + +error[E0038]: the trait `NotObjectSafe` cannot be made into an object + --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:1 + | +LL | fn foo() -> Self; + | --- associated function `foo` has no `self` parameter +... +LL | fn cat() -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NotObjectSafe` cannot be made into an object + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-impl-trait.rs b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-impl-trait.rs new file mode 100644 index 0000000000000..503515013b9ab --- /dev/null +++ b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-impl-trait.rs @@ -0,0 +1,46 @@ +trait NotObjectSafe { + fn foo() -> Self; +} + +trait ObjectSafe { + fn bar(&self); +} + +struct A; +struct B; + +impl NotObjectSafe for A { + fn foo() -> Self { + A + } +} + +impl NotObjectSafe for B { + fn foo() -> Self { + B + } +} + +impl ObjectSafe for A { + fn bar(&self) {} +} + +impl ObjectSafe for B { + fn bar(&self) {} +} + +fn can() -> impl NotObjectSafe { + if true { + return A; + } + B //~ ERROR mismatched types +} + +fn cat() -> impl ObjectSafe { + if true { + return A; + } + B //~ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-impl-trait.stderr b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-impl-trait.stderr new file mode 100644 index 0000000000000..dd4260fbe4f91 --- /dev/null +++ b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-impl-trait.stderr @@ -0,0 +1,39 @@ +error[E0308]: mismatched types + --> $DIR/object-unsafe-trait-in-return-position-impl-trait.rs:36:5 + | +LL | fn can() -> impl NotObjectSafe { + | ------------------ expected because this return type... +LL | if true { +LL | return A; + | - ...is found to be `A` here +LL | } +LL | B + | ^ expected struct `A`, found struct `B` + | + = note: to return `impl Trait`, all returned values must be of the same type + = note: for information on `impl Trait`, see + = help: if the trait `NotObjectSafe` were object safe, you could return a boxed trait object + = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type + +error[E0308]: mismatched types + --> $DIR/object-unsafe-trait-in-return-position-impl-trait.rs:43:5 + | +LL | fn cat() -> impl ObjectSafe { + | --------------- expected because this return type... +LL | if true { +LL | return A; + | - ...is found to be `A` here +LL | } +LL | B + | ^ expected struct `A`, found struct `B` + | + = note: to return `impl Trait`, all returned values must be of the same type + = note: for information on `impl Trait`, see + = help: you can instead return a boxed trait object using `Box` + = note: for information on trait objects, see + = help: alternatively, create a new `enum` with a variant for each returned type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr index 9859c73b7b18b..b663cccbeef0f 100644 --- a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr @@ -12,7 +12,7 @@ LL | 1u32 | = note: to return `impl Trait`, all returned values must be of the same type = note: for information on `impl Trait`, see - = help: you can instead return a trait object using `Box` + = help: you can instead return a boxed trait object using `Box` = note: for information on trait objects, see = help: alternatively, create a new `enum` with a variant for each returned type @@ -30,7 +30,7 @@ LL | return 1u32; | = note: to return `impl Trait`, all returned values must be of the same type = note: for information on `impl Trait`, see - = help: you can instead return a trait object using `Box` + = help: you can instead return a boxed trait object using `Box` = note: for information on trait objects, see = help: alternatively, create a new `enum` with a variant for each returned type @@ -48,7 +48,7 @@ LL | 1u32 | = note: to return `impl Trait`, all returned values must be of the same type = note: for information on `impl Trait`, see - = help: you can instead return a trait object using `Box` + = help: you can instead return a boxed trait object using `Box` = note: for information on trait objects, see = help: alternatively, create a new `enum` with a variant for each returned type @@ -78,7 +78,7 @@ LL | _ => 1u32, | = note: to return `impl Trait`, all returned values must be of the same type = note: for information on `impl Trait`, see - = help: you can instead return a trait object using `Box` + = help: you can instead return a boxed trait object using `Box` = note: for information on trait objects, see = help: alternatively, create a new `enum` with a variant for each returned type @@ -98,7 +98,7 @@ LL | | } | = note: to return `impl Trait`, all returned values must be of the same type = note: for information on `impl Trait`, see - = help: you can instead return a trait object using `Box` + = help: you can instead return a boxed trait object using `Box` = note: for information on trait objects, see = help: alternatively, create a new `enum` with a variant for each returned type @@ -116,7 +116,7 @@ LL | 1u32 | = note: to return `impl Trait`, all returned values must be of the same type = note: for information on `impl Trait`, see - = help: you can instead return a trait object using `Box` + = help: you can instead return a boxed trait object using `Box` = note: for information on trait objects, see = help: alternatively, create a new `enum` with a variant for each returned type From 029a9c625371e756d93024efd3deb7636a90f8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 16 Jan 2020 11:32:50 -0800 Subject: [PATCH 15/15] review comments --- .../traits/error_reporting/suggestions.rs | 49 +++++++++---------- src/librustc_error_codes/error_codes/E0746.md | 2 +- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs index c3af063d518f0..bf6891214ace1 100644 --- a/src/librustc/traits/error_reporting/suggestions.rs +++ b/src/librustc/traits/error_reporting/suggestions.rs @@ -605,34 +605,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap(); - let mut all_returns_conform_to_trait = true; - let mut all_returns_have_same_type = true; - let mut last_ty = None; - if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { - let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); - let param_env = ty::ParamEnv::empty(); - if let ty::Dynamic(predicates, _) = &ty_ret_ty.kind { - for expr in &visitor.0 { - if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) { - all_returns_have_same_type &= - Some(returned_ty) == last_ty || last_ty.is_none(); - last_ty = Some(returned_ty); - for predicate in predicates.iter() { - let pred = predicate.with_self_ty(self.tcx, returned_ty); - let obl = Obligation::new(cause.clone(), param_env, pred); - all_returns_conform_to_trait &= self.predicate_may_hold(&obl); - } + let mut ret_types = visitor.0.iter().filter_map(|expr| tables.node_type_opt(expr.hir_id)); + let (last_ty, all_returns_have_same_type) = + ret_types.clone().fold((None, true), |(last_ty, mut same), returned_ty| { + same &= last_ty.map_or(true, |ty| ty == returned_ty); + (Some(returned_ty), same) + }); + let all_returns_conform_to_trait = + if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) { + match ty_ret_ty.kind { + ty::Dynamic(predicates, _) => { + let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); + let param_env = ty::ParamEnv::empty(); + ret_types.all(|returned_ty| { + predicates.iter().all(|predicate| { + let pred = predicate.with_self_ty(self.tcx, returned_ty); + let obl = Obligation::new(cause.clone(), param_env, pred); + self.predicate_may_hold(&obl) + }) + }) } + _ => true, } - } - } else { - // We still want to verify whether all the return types conform to each other. - for expr in &visitor.0 { - let returned_ty = tables.node_type_opt(expr.hir_id); - all_returns_have_same_type &= last_ty == returned_ty || last_ty.is_none(); - last_ty = returned_ty; - } - } + } else { + true + }; let (snippet, last_ty) = if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = ( diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md index 041061f3380c1..16b2722f0eac2 100644 --- a/src/librustc_error_codes/error_codes/E0746.md +++ b/src/librustc_error_codes/error_codes/E0746.md @@ -13,7 +13,7 @@ impl T for S { } // Having the trait `T` as return type is invalid because -// bare trait objects do not have a statically known size: +// unboxed trait objects do not have a statically known size: fn foo() -> dyn T { S(42) }