Skip to content

Commit be29ccb

Browse files
committed
Never pattern in function arguments diverges
1 parent 25df572 commit be29ccb

File tree

8 files changed

+236
-8
lines changed

8 files changed

+236
-8
lines changed

compiler/rustc_hir_typeck/src/check.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use std::cell::RefCell;
22

33
use crate::coercion::CoerceMany;
44
use crate::gather_locals::GatherLocalsVisitor;
5-
use crate::CoroutineTypes;
6-
use crate::FnCtxt;
5+
use crate::{CoroutineTypes, Diverges, FnCtxt};
76
use rustc_hir as hir;
87
use rustc_hir::def::DefKind;
98
use rustc_hir::intravisit::Visitor;
@@ -76,6 +75,13 @@ pub(super) fn check_fn<'a, 'tcx>(
7675
let ty: Option<&hir::Ty<'_>> = try { inputs_hir?.get(idx)? };
7776
let ty_span = ty.map(|ty| ty.span);
7877
fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
78+
if param.pat.is_never_pattern() {
79+
fcx.diverges.set(Diverges::Always {
80+
span: param.pat.span,
81+
custom_note: Some("any code following a never pattern is unreachable"),
82+
});
83+
fcx.is_whole_body_and_arguments_diverge.set(true);
84+
}
7985

8086
// Check that argument is Sized.
8187
if !params_can_be_unsized {

compiler/rustc_hir_typeck/src/expr.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
215215
_ => false,
216216
};
217217

218-
// Warn for expressions after diverging siblings.
219-
if !is_try_block_generated_unit_expr {
220-
self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
221-
}
218+
let old_diverges = if self.is_whole_body_and_arguments_diverge.replace(false) {
219+
self.diverges.get()
220+
} else {
221+
// Warn for expressions after diverging siblings.
222+
if !is_try_block_generated_unit_expr {
223+
self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
224+
}
222225

223-
// Hide the outer diverging and has_errors flags.
224-
let old_diverges = self.diverges.replace(Diverges::Maybe);
226+
// Hide the outer diverging flag.
227+
self.diverges.replace(Diverges::Maybe)
228+
};
225229

226230
let ty = ensure_sufficient_stack(|| match &expr.kind {
227231
hir::ExprKind::Path(

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ pub struct FnCtxt<'a, 'tcx> {
103103
/// the diverges flag is set to something other than `Maybe`.
104104
pub(super) diverges: Cell<Diverges>,
105105

106+
/// Whether the currently checked node is the whole body of the function, and one of the
107+
/// function arguments is a never pattern. This makes the whole body of the function diverge.
108+
pub(super) is_whole_body_and_arguments_diverge: Cell<bool>,
109+
106110
pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
107111

108112
pub(super) inh: &'a Inherited<'tcx>,
@@ -124,6 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
124128
ret_coercion_span: Cell::new(None),
125129
coroutine_types: None,
126130
diverges: Cell::new(Diverges::Maybe),
131+
is_whole_body_and_arguments_diverge: Cell::new(false),
127132
enclosing_breakables: RefCell::new(EnclosingBreakables {
128133
stack: Vec::new(),
129134
by_id: Default::default(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![feature(never_patterns)]
2+
#![allow(incomplete_features)]
3+
#![deny(unreachable_patterns)]
4+
#![deny(unreachable_code)]
5+
6+
fn main() {}
7+
8+
enum Void {}
9+
10+
fn never_arg(!: Void) -> u32 {
11+
println!();
12+
//~^ ERROR unreachable statement
13+
}
14+
15+
fn ref_never_arg(&!: &Void) -> u32 {
16+
println!();
17+
//~^ ERROR unreachable statement
18+
}
19+
20+
//fn never_let() -> u32 {
21+
// let ptr: *const Void = std::ptr::null();
22+
// unsafe {
23+
// let ! = *ptr;
24+
// }
25+
// println!();
26+
//}
27+
28+
fn never_match() -> u32 {
29+
let ptr: *const Void = std::ptr::null();
30+
unsafe {
31+
match *ptr { ! };
32+
}
33+
println!();
34+
//~^ ERROR unreachable statement
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: unreachable statement
2+
--> $DIR/diverge-causes-unreachable-code.rs:11:5
3+
|
4+
LL | fn never_arg(!: Void) -> u32 {
5+
| - any code following a never pattern is unreachable
6+
LL | println!();
7+
| ^^^^^^^^^^ unreachable statement
8+
|
9+
note: the lint level is defined here
10+
--> $DIR/diverge-causes-unreachable-code.rs:4:9
11+
|
12+
LL | #![deny(unreachable_code)]
13+
| ^^^^^^^^^^^^^^^^
14+
= 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)
15+
16+
error: unreachable statement
17+
--> $DIR/diverge-causes-unreachable-code.rs:16:5
18+
|
19+
LL | fn ref_never_arg(&!: &Void) -> u32 {
20+
| -- any code following a never pattern is unreachable
21+
LL | println!();
22+
| ^^^^^^^^^^ unreachable statement
23+
|
24+
= 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)
25+
26+
error: unreachable statement
27+
--> $DIR/diverge-causes-unreachable-code.rs:33:5
28+
|
29+
LL | match *ptr { ! };
30+
| ---------------- any code following this `match` expression is unreachable, as all arms diverge
31+
LL | }
32+
LL | println!();
33+
| ^^^^^^^^^^ unreachable statement
34+
|
35+
= 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)
36+
37+
error: aborting due to 3 previous errors
38+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#![feature(never_patterns)]
2+
#![feature(let_chains)]
3+
#![allow(incomplete_features)]
4+
#![deny(unreachable_patterns)]
5+
6+
fn main() {}
7+
8+
enum Void {}
9+
10+
// Contrast with `./diverges.rs`: merely having an empty type around isn't enough to diverge.
11+
12+
fn wild_void(_: Void) -> u32 {}
13+
//~^ ERROR: mismatched types
14+
15+
fn wild_let() -> u32 {
16+
let ptr: *const Void = std::ptr::null();
17+
unsafe {
18+
//~^ ERROR: mismatched types
19+
let _ = *ptr;
20+
}
21+
}
22+
23+
fn wild_match() -> u32 {
24+
let ptr: *const Void = std::ptr::null();
25+
unsafe {
26+
match *ptr {
27+
_ => {} //~ ERROR: mismatched types
28+
}
29+
}
30+
}
31+
32+
fn binding_void(_x: Void) -> u32 {}
33+
//~^ ERROR: mismatched types
34+
35+
fn binding_let() -> u32 {
36+
let ptr: *const Void = std::ptr::null();
37+
unsafe {
38+
//~^ ERROR: mismatched types
39+
let _x = *ptr;
40+
}
41+
}
42+
43+
fn binding_match() -> u32 {
44+
let ptr: *const Void = std::ptr::null();
45+
unsafe {
46+
match *ptr {
47+
_x => {} //~ ERROR: mismatched types
48+
}
49+
}
50+
}
51+
52+
// Don't confuse this with a `let !` statement.
53+
fn let_chain(x: Void) -> u32 {
54+
if let true = true && let ! = x {}
55+
//~^ ERROR: mismatched types
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/diverges-not.rs:12:26
3+
|
4+
LL | fn wild_void(_: Void) -> u32 {}
5+
| --------- ^^^ expected `u32`, found `()`
6+
| |
7+
| implicitly returns `()` as its body has no tail or `return` expression
8+
9+
error[E0308]: mismatched types
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
20+
|
21+
LL | _ => {}
22+
| ^^ expected `u32`, found `()`
23+
24+
error[E0308]: mismatched types
25+
--> $DIR/diverges-not.rs:32:30
26+
|
27+
LL | fn binding_void(_x: Void) -> u32 {}
28+
| ------------ ^^^ expected `u32`, found `()`
29+
| |
30+
| implicitly returns `()` as its body has no tail or `return` expression
31+
32+
error[E0308]: mismatched types
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
43+
|
44+
LL | _x => {}
45+
| ^^ expected `u32`, found `()`
46+
47+
error[E0308]: mismatched types
48+
--> $DIR/diverges-not.rs:54:37
49+
|
50+
LL | if let true = true && let ! = x {}
51+
| ^^ expected `u32`, found `()`
52+
53+
error: aborting due to 7 previous errors
54+
55+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// check-pass
2+
#![feature(never_patterns)]
3+
#![allow(incomplete_features)]
4+
#![deny(unreachable_patterns)]
5+
6+
fn main() {}
7+
8+
enum Void {}
9+
10+
// A never pattern alone diverges.
11+
12+
fn never_arg(!: Void) -> u32 {}
13+
14+
fn ref_never_arg(&!: &Void) -> u32 {}
15+
16+
// fn never_let() -> u32 {
17+
// let ptr: *const Void = std::ptr::null();
18+
// unsafe {
19+
// let ! = *ptr;
20+
// }
21+
// }
22+
23+
fn never_match() -> u32 {
24+
let ptr: *const Void = std::ptr::null();
25+
unsafe {
26+
match *ptr { ! };
27+
}
28+
println!(); // Ensures this typechecks because of divergence.
29+
}

0 commit comments

Comments
 (0)