Skip to content

Commit 7e37e36

Browse files
committed
Introduce lint DivergeReason.
1 parent 756cb48 commit 7e37e36

11 files changed

+90
-51
lines changed

compiler/rustc_hir_typeck/src/diverges.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ pub enum Diverges {
1414
/// others require a CFG to determine them.
1515
Maybe,
1616

17+
/// This expression is uninhabited, we want to
18+
/// emit a diagnostic but not pollute type checking.
19+
UninhabitedExpr(HirId, Span),
20+
21+
/// Same as `UninhabitedExpr` but with reachability
22+
/// warning already emitted.
23+
Warned,
24+
1725
/// Definitely known to diverge and therefore
1826
/// not reach the next sibling or its parent.
1927
Always(DivergeReason, Span),
@@ -55,16 +63,18 @@ impl ops::BitOrAssign for Diverges {
5563
impl Diverges {
5664
pub(super) fn is_always(self) -> bool {
5765
match self {
58-
Self::Maybe => false,
66+
Self::Maybe | Diverges::UninhabitedExpr(..) | Diverges::Warned => false,
5967
Self::Always(..) | Self::WarnedAlways => true,
6068
}
6169
}
6270

6371
fn ordinal(&self) -> u8 {
6472
match self {
6573
Self::Maybe => 0,
66-
Self::Always { .. } => 1,
67-
Self::WarnedAlways => 2,
74+
Self::UninhabitedExpr(..) => 1,
75+
Self::Warned => 2,
76+
Self::Always { .. } => 3,
77+
Self::WarnedAlways => 4,
6878
}
6979
}
7080
}

compiler/rustc_hir_typeck/src/expr.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -238,20 +238,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
238238
ExprKind::Field(..)
239239
if matches!(
240240
self.diverges.get(),
241-
Diverges::Always(DivergeReason::UninhabitedExpr(_), _)
241+
Diverges::UninhabitedExpr(_, _)
242242
) && self.ty_is_uninhabited(ty) => {}
243243
_ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"),
244244
}
245245

246-
if !self.diverges.get().is_always() {
246+
let cur_diverges = self.diverges.get();
247+
if !cur_diverges.is_always() {
247248
if ty.is_never() {
248249
// Any expression that produces a value of type `!` must have diverged.
249-
self.diverges.set(Diverges::Always(DivergeReason::Other, expr.span));
250+
self.diverges.set(cur_diverges | Diverges::Always(DivergeReason::Other, expr.span));
250251
} else if self.ty_is_uninhabited(ty) {
251252
// This expression produces a value of uninhabited type.
252253
// This means it has diverged somehow.
253254
self.diverges
254-
.set(Diverges::Always(DivergeReason::UninhabitedExpr(expr.hir_id), expr.span));
255+
.set(cur_diverges | Diverges::UninhabitedExpr(expr.hir_id, expr.span));
255256
}
256257
}
257258

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5151
/// Produces warning on the given node, if the current point in the
5252
/// function is unreachable, and there hasn't been another warning.
5353
pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) {
54-
let Diverges::Always(reason, orig_span) = self.diverges.get() else {
55-
return;
54+
let (reason, orig_span) = match self.diverges.get() {
55+
Diverges::UninhabitedExpr(hir_id, orig_span) => {
56+
(DivergeReason::UninhabitedExpr(hir_id), orig_span)
57+
}
58+
Diverges::Always(reason, orig_span) => (reason, orig_span),
59+
Diverges::Maybe | Diverges::Warned | Diverges::WarnedAlways => return,
5660
};
5761

5862
match span.desugaring_kind() {
@@ -74,9 +78,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7478
_ => {}
7579
}
7680

77-
// Don't warn twice.
78-
self.diverges.set(Diverges::WarnedAlways);
79-
8081
if matches!(reason, DivergeReason::UninhabitedExpr(_)) {
8182
if let Some(impl_of) = self.tcx.impl_of_method(self.body_id.to_def_id()) {
8283
if self.tcx.has_attr(impl_of, sym::automatically_derived) {
@@ -87,6 +88,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8788
}
8889
}
8990

91+
// Don't warn twice.
92+
self.diverges.set(match self.diverges.get() {
93+
Diverges::UninhabitedExpr(..) => Diverges::Warned,
94+
Diverges::Always(..) => Diverges::WarnedAlways,
95+
Diverges::Maybe | Diverges::Warned | Diverges::WarnedAlways => bug!(),
96+
});
97+
9098
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
9199

92100
let msg = format!("unreachable {kind}");

tests/ui/consts/let-irrefutable-pattern-ice-120337.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
//@ check-pass
12
#![feature(never_type)]
23
#[derive(Copy, Clone)]
34
pub enum E { A(!), }
45
pub union U { u: (), e: E, }
5-
pub const C: () = { //~ ERROR evaluation of constant value failed
6+
pub const C: () = {
67
let E::A(ref a) = unsafe { &(&U { u: () }).e};
78
};
89

tests/ui/consts/let-irrefutable-pattern-ice-120337.stderr

-12
This file was deleted.

tests/ui/match/match-no-arms-unreachable-after.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: unreachable statement
22
--> $DIR/match-no-arms-unreachable-after.rs:8:5
33
|
44
LL | match v { }
5-
| - this expression has type `Void`, which is uninhabited
5+
| ----------- any code following this expression is unreachable
66
LL | let x = 2;
77
| ^^^^^^^^^^ unreachable statement
88
|

tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.rs

+1
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ fn never_match() -> u32 {
3333
//~^ ERROR unreachable arm
3434
}
3535
println!();
36+
//~^ ERROR unreachable statement
3637
}

tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr

+12-1
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,16 @@ LL | match *ptr { ! };
4242
| |
4343
| this expression has type `Void`, which is uninhabited
4444

45-
error: aborting due to 4 previous errors
45+
error: unreachable statement
46+
--> $DIR/diverge-causes-unreachable-code.rs:34:5
47+
|
48+
LL | match *ptr { ! };
49+
| ---------------- any code following this `match` expression is unreachable, as all arms diverge
50+
LL | }
51+
LL | println!();
52+
| ^^^^^^^^^^ unreachable statement
53+
|
54+
= note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
55+
56+
error: aborting due to 5 previous errors
4657

tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ fn wild_void(_: Void) -> u32 {}
1515
fn wild_let() -> u32 {
1616
let ptr: *const Void = std::ptr::null();
1717
unsafe {
18+
//~^ ERROR: mismatched types
1819
let _ = *ptr;
1920
}
2021
}
@@ -34,8 +35,8 @@ fn binding_void(_x: Void) -> u32 {}
3435
fn binding_let() -> u32 {
3536
let ptr: *const Void = std::ptr::null();
3637
unsafe {
38+
//~^ ERROR: mismatched types
3739
let _x = *ptr;
38-
//~^ ERROR: cannot move
3940
}
4041
}
4142

tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.stderr

+24-22
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,49 @@ LL | fn wild_void(_: Void) -> u32 {}
77
| implicitly returns `()` as its body has no tail or `return` expression
88

99
error[E0308]: mismatched types
10-
--> $DIR/diverges-not.rs:26:18
10+
--> $DIR/diverges-not.rs:17:5
11+
|
12+
LL | / unsafe {
13+
LL | |
14+
LL | | let _ = *ptr;
15+
LL | | }
16+
| |_____^ expected `u32`, found `()`
17+
18+
error[E0308]: mismatched types
19+
--> $DIR/diverges-not.rs:27:18
1120
|
1221
LL | _ => {}
1322
| ^^ expected `u32`, found `()`
1423

1524
error[E0308]: mismatched types
16-
--> $DIR/diverges-not.rs:31:30
25+
--> $DIR/diverges-not.rs:32:30
1726
|
1827
LL | fn binding_void(_x: Void) -> u32 {}
1928
| ------------ ^^^ expected `u32`, found `()`
2029
| |
2130
| implicitly returns `()` as its body has no tail or `return` expression
2231

2332
error[E0308]: mismatched types
24-
--> $DIR/diverges-not.rs:46:19
33+
--> $DIR/diverges-not.rs:37:5
34+
|
35+
LL | / unsafe {
36+
LL | |
37+
LL | | let _x = *ptr;
38+
LL | | }
39+
| |_____^ expected `u32`, found `()`
40+
41+
error[E0308]: mismatched types
42+
--> $DIR/diverges-not.rs:47:19
2543
|
2644
LL | _x => {}
2745
| ^^ expected `u32`, found `()`
2846

2947
error[E0308]: mismatched types
30-
--> $DIR/diverges-not.rs:53:37
48+
--> $DIR/diverges-not.rs:54:37
3149
|
3250
LL | if let true = true && let ! = x {}
3351
| ^^ expected `u32`, found `()`
3452

35-
error[E0507]: cannot move out of `*ptr` which is behind a raw pointer
36-
--> $DIR/diverges-not.rs:37:18
37-
|
38-
LL | let _x = *ptr;
39-
| ^^^^ move occurs because `*ptr` has type `Void`, which does not implement the `Copy` trait
40-
|
41-
note: if `Void` implemented `Clone`, you could clone the value
42-
--> $DIR/diverges-not.rs:8:1
43-
|
44-
LL | enum Void {}
45-
| ^^^^^^^^^ consider implementing `Clone` for this type
46-
...
47-
LL | let _x = *ptr;
48-
| ---- you could clone this value
49-
50-
error: aborting due to 6 previous errors
53+
error: aborting due to 7 previous errors
5154

52-
Some errors have detailed explanations: E0308, E0507.
53-
For more information about an error, try `rustc --explain E0308`.
55+
For more information about this error, try `rustc --explain E0308`.

tests/ui/uninhabited/break-diverging-value.stderr

+17-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@ LL | fn loop_break_break() -> i32 {
66
| |
77
| implicitly returns `()` as its body has no tail or `return` expression
88

9+
error[E0308]: mismatched types
10+
--> $DIR/break-diverging-value.rs:33:25
11+
|
12+
LL | fn loop_break_void() -> i32 {
13+
| --------------- ^^^ expected `i32`, found `()`
14+
| |
15+
| implicitly returns `()` as its body has no tail or `return` expression
16+
17+
error[E0308]: mismatched types
18+
--> $DIR/break-diverging-value.rs:43:34
19+
|
20+
LL | fn loop_break_indirect_void() -> i32 {
21+
| ------------------------ ^^^ expected `i32`, found `()`
22+
| |
23+
| implicitly returns `()` as its body has no tail or `return` expression
24+
925
error[E0308]: mismatched types
1026
--> $DIR/break-diverging-value.rs:55:33
1127
|
@@ -14,6 +30,6 @@ LL | fn loop_break_private_void() -> i32 {
1430
| |
1531
| implicitly returns `()` as its body has no tail or `return` expression
1632

17-
error: aborting due to 2 previous errors
33+
error: aborting due to 4 previous errors
1834

1935
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)