diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index a544b6dba89d6..9bcb54d05828a 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1979,6 +1979,15 @@ pub enum FunctionRetTy { Return(P), } +impl fmt::Display for FunctionRetTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Return(ref ty) => print::to_string(print::NO_ANN, |s| s.print_type(ty)).fmt(f), + DefaultReturn(_) => "()".fmt(f), + } + } +} + impl FunctionRetTy { pub fn span(&self) -> Span { match *self { diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 9a1f9784192c1..8b2be84942388 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1169,7 +1169,6 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> // `expression_ty` will be unit). // // Another example is `break` with no argument expression. - assert!(expression_ty.is_unit()); assert!(expression_ty.is_unit(), "if let hack without unit type"); fcx.at(cause, fcx.param_env) .eq_exp(label_expression_as_expected, expression_ty, self.merged_ty()) @@ -1210,13 +1209,14 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> (self.final_ty.unwrap_or(self.expected_ty), expression_ty) }; + let reason_label = "expected because of this statement"; let mut db; match cause.code { ObligationCauseCode::ReturnNoExpression => { db = struct_span_err!( fcx.tcx.sess, cause.span, E0069, "`return;` in a function whose return type is not `()`"); - db.span_label(cause.span, "return type is not ()"); + db.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id) => { db = fcx.report_mismatched_types(cause, expected, found, err); @@ -1234,9 +1234,19 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> cause.span, blk_id, ); + if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() { + if !sp.overlaps(cause.span) { + db.span_label(*sp, reason_label); + } + } } _ => { db = fcx.report_mismatched_types(cause, expected, found, err); + if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() { + if !sp.overlaps(cause.span) { + db.span_label(*sp, reason_label); + } + } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d670602c1fe96..9c82640af844e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -538,6 +538,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { err_count_on_creation: usize, ret_coercion: Option>>, + ret_coercion_span: RefCell>, yield_ty: Option>, @@ -1987,6 +1988,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { param_env, err_count_on_creation: inh.tcx.sess.err_count(), ret_coercion: None, + ret_coercion_span: RefCell::new(None), yield_ty: None, ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, ast::CRATE_NODE_ID)), @@ -4172,11 +4174,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { struct_span_err!(self.tcx.sess, expr.span, E0572, "return statement outside of function body").emit(); } else if let Some(ref e) = *expr_opt { + *self.ret_coercion_span.borrow_mut() = Some(e.span); self.check_return_expr(e); } else { let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); + *self.ret_coercion_span.borrow_mut() = Some(expr.span); let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); - coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); + if let Some((fn_decl, _)) = self.get_fn_decl(expr.id) { + coercion.coerce_forced_unit( + self, + &cause, + &mut |db| { + db.span_label( + fn_decl.output.span(), + format!( + "expected `{}` because of this return type", + fn_decl.output, + ), + ); + }, + true, + ); + } else { + coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); + } } tcx.types.never } diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 9aafb9fc549fc..ae2259d525898 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -334,6 +334,13 @@ impl Span { span.lo <= other.lo && other.hi <= span.hi } + /// Return `true` if `self` touches `other`. + pub fn overlaps(self, other: Span) -> bool { + let span = self.data(); + let other = other.data(); + span.lo < other.hi && other.lo < span.hi + } + /// Return true if the spans are equal with regards to the source text. /// /// Use this instead of `==` when either span could be generated code, diff --git a/src/test/ui/error-codes/E0069.stderr b/src/test/ui/error-codes/E0069.stderr index 0ba1ed456635f..12b778f42e22c 100644 --- a/src/test/ui/error-codes/E0069.stderr +++ b/src/test/ui/error-codes/E0069.stderr @@ -1,8 +1,10 @@ error[E0069]: `return;` in a function whose return type is not `()` --> $DIR/E0069.rs:12:5 | +LL | fn foo() -> u8 { + | -- expected `u8` because of this return type LL | return; - | ^^^^^^ return type is not () + | ^^^^^^ return type is not `()` error: aborting due to previous error diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index e277d4e28cb2b..f1d2071bbdb9e 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -1,6 +1,9 @@ error[E0308]: mismatched types --> $DIR/equality.rs:25:5 | +LL | return 1_i32; + | ----- expected because of this statement +LL | } LL | 0_u32 | ^^^^^ expected i32, found u32 | diff --git a/src/test/ui/ret-non-nil.stderr b/src/test/ui/ret-non-nil.stderr index 01f126bd11ea2..e0fdc8c67edf7 100644 --- a/src/test/ui/ret-non-nil.stderr +++ b/src/test/ui/ret-non-nil.stderr @@ -2,7 +2,9 @@ error[E0069]: `return;` in a function whose return type is not `()` --> $DIR/ret-non-nil.rs:15:19 | LL | fn g() -> isize { return; } - | ^^^^^^ return type is not () + | ----- ^^^^^^ return type is not `()` + | | + | expected `isize` because of this return type error: aborting due to previous error diff --git a/src/test/ui/return/return-unit-from-diverging.stderr b/src/test/ui/return/return-unit-from-diverging.stderr index 38d4ca37366ff..5a9f0877cc6b6 100644 --- a/src/test/ui/return/return-unit-from-diverging.stderr +++ b/src/test/ui/return/return-unit-from-diverging.stderr @@ -1,8 +1,10 @@ error[E0069]: `return;` in a function whose return type is not `()` --> $DIR/return-unit-from-diverging.rs:15:5 | +LL | fn fail() -> ! { + | - expected `!` because of this return type LL | return; //~ ERROR in a function whose return type is not - | ^^^^^^ return type is not () + | ^^^^^^ return type is not `()` error: aborting due to previous error