diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 90b974fb972c0..d0a76c207c716 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3517,7 +3517,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.diverges.set(Diverges::Maybe); self.has_errors.set(false); - let ty = self.check_expr_kind(expr, expected, needs); + let ty = self.check_expr_kind(expr, expected, needs, old_diverges); // Warn for non-block expressions with diverging children. match expr.node { @@ -3551,7 +3551,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_expr_kind(&self, expr: &'gcx hir::Expr, expected: Expectation<'tcx>, - needs: Needs) -> Ty<'tcx> { + needs: Needs, + prev_diverges: Diverges) + -> Ty<'tcx> { let tcx = self.tcx; let id = expr.id; match expr.node { @@ -3749,7 +3751,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { coerce.coerce(self, &cause, e, e_ty); } else { assert!(e_ty.is_nil()); - coerce.coerce_forced_unit(self, &cause, &mut |_| (), true); + // If we're breaking without a value, then the break + // implicitly carries a `()` value. Unless, that is, + // the function is already diverging. In this case, we + // don't need to supply any value (in which case, the + // value will be considered to have type `!`). + if !prev_diverges.always() { + coerce.coerce_forced_unit(self, &cause, &mut |_| (), true); + } } } else { // If `ctxt.coerce` is `None`, we can just ignore diff --git a/src/test/ui/loop-return-break.rs b/src/test/ui/loop-return-break.rs new file mode 100644 index 0000000000000..490d4ed70593e --- /dev/null +++ b/src/test/ui/loop-return-break.rs @@ -0,0 +1,45 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] +#![allow(unreachable_code)] + +fn main() { + // The `if false` expressions are simply to + // make sure we don't avoid checking everything + // simply because a few expressions are unreachable. + + if false { + let _: ! = { + loop { return } // ok + }; + } + + if false { + let _: ! = { + loop { return; break } // ok + }; + } + + if false { + let _: ! = { + // Here, the break (implicitly carrying the value `()`) + // occurs before the return, so it doesn't have the type + // `!` and should thus fail to type check. + loop { return break } //~ ERROR mismatched types + }; + } + + if false { + let _: ! = { + loop { break } //~ ERROR mismatched types + }; + } +} diff --git a/src/test/ui/loop-return-break.stderr b/src/test/ui/loop-return-break.stderr new file mode 100644 index 0000000000000..01375815e15c7 --- /dev/null +++ b/src/test/ui/loop-return-break.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/loop-return-break.rs:36:27 + | +LL | loop { return break } //~ ERROR mismatched types + | ^^^^^ expected (), found ! + | + = note: expected type `()` + found type `!` + +error[E0308]: mismatched types + --> $DIR/loop-return-break.rs:42:20 + | +LL | loop { break } //~ ERROR mismatched types + | ^^^^^ expected (), found ! + | + = note: expected type `()` + found type `!` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`.