Skip to content

Commit 2039a9b

Browse files
committed
Improve divergence checking for loops
This change permits `break`s to be considered diverging (i.e. having type `!`) in diverging blocks, which allows constructions like `let _: ! = { loop { return; break } }`, relaxing the overly-restrictive divergence rules.
1 parent 30cae58 commit 2039a9b

File tree

3 files changed

+78
-3
lines changed

3 files changed

+78
-3
lines changed

src/librustc_typeck/check/mod.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3517,7 +3517,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
35173517
self.diverges.set(Diverges::Maybe);
35183518
self.has_errors.set(false);
35193519

3520-
let ty = self.check_expr_kind(expr, expected, needs);
3520+
let ty = self.check_expr_kind(expr, expected, needs, old_diverges);
35213521

35223522
// Warn for non-block expressions with diverging children.
35233523
match expr.node {
@@ -3551,7 +3551,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
35513551
fn check_expr_kind(&self,
35523552
expr: &'gcx hir::Expr,
35533553
expected: Expectation<'tcx>,
3554-
needs: Needs) -> Ty<'tcx> {
3554+
needs: Needs,
3555+
prev_diverges: Diverges)
3556+
-> Ty<'tcx> {
35553557
let tcx = self.tcx;
35563558
let id = expr.id;
35573559
match expr.node {
@@ -3749,7 +3751,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
37493751
coerce.coerce(self, &cause, e, e_ty);
37503752
} else {
37513753
assert!(e_ty.is_nil());
3752-
coerce.coerce_forced_unit(self, &cause, &mut |_| (), true);
3754+
// If we're breaking without a value, then the break
3755+
// implicitly carries a `()` value. Unless, that is,
3756+
// the function is already diverging. In this case, we
3757+
// don't need to supply any value (in which case, the
3758+
// value will be considered to have type `!`).
3759+
if !prev_diverges.always() {
3760+
coerce.coerce_forced_unit(self, &cause, &mut |_| (), true);
3761+
}
37533762
}
37543763
} else {
37553764
// If `ctxt.coerce` is `None`, we can just ignore

src/test/ui/loop-return-break.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(never_type)]
12+
#![allow(unreachable_code)]
13+
14+
fn main() {
15+
// The `if false` expressions are simply to
16+
// make sure we don't avoid checking everything
17+
// simply because a few expressions are unreachable.
18+
19+
if false {
20+
let _: ! = {
21+
loop { return } // ok
22+
};
23+
}
24+
25+
if false {
26+
let _: ! = {
27+
loop { return; break } // ok
28+
};
29+
}
30+
31+
if false {
32+
let _: ! = {
33+
// Here, the break (implicitly carrying the value `()`)
34+
// occurs before the return, so it doesn't have the type
35+
// `!` and should thus fail to type check.
36+
loop { return break } //~ ERROR mismatched types
37+
};
38+
}
39+
40+
if false {
41+
let _: ! = {
42+
loop { break } //~ ERROR mismatched types
43+
};
44+
}
45+
}

src/test/ui/loop-return-break.stderr

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/loop-return-break.rs:36:27
3+
|
4+
LL | loop { return break } //~ ERROR mismatched types
5+
| ^^^^^ expected (), found !
6+
|
7+
= note: expected type `()`
8+
found type `!`
9+
10+
error[E0308]: mismatched types
11+
--> $DIR/loop-return-break.rs:42:20
12+
|
13+
LL | loop { break } //~ ERROR mismatched types
14+
| ^^^^^ expected (), found !
15+
|
16+
= note: expected type `()`
17+
found type `!`
18+
19+
error: aborting due to 2 previous errors
20+
21+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)