Rollup merge of #120104 - Nadrieril:never-pat-diverges, r=compiler-errors
never_patterns: Count `!` bindings as diverging A binding that is a never pattern is not reachable, hence counts as diverging code. This allows in particular `fn foo(!: Void) -> SomeType {}` to typecheck. r? ``@compiler-errors``
This commit is contained in:
commit
042cc7269c
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
use crate::coercion::CoerceMany;
|
use crate::coercion::CoerceMany;
|
||||||
use crate::gather_locals::GatherLocalsVisitor;
|
use crate::gather_locals::GatherLocalsVisitor;
|
||||||
use crate::CoroutineTypes;
|
use crate::{CoroutineTypes, Diverges, FnCtxt};
|
||||||
use crate::FnCtxt;
|
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::intravisit::Visitor;
|
use rustc_hir::intravisit::Visitor;
|
||||||
@ -76,6 +75,12 @@ pub(super) fn check_fn<'a, 'tcx>(
|
|||||||
let ty: Option<&hir::Ty<'_>> = try { inputs_hir?.get(idx)? };
|
let ty: Option<&hir::Ty<'_>> = try { inputs_hir?.get(idx)? };
|
||||||
let ty_span = ty.map(|ty| ty.span);
|
let ty_span = ty.map(|ty| ty.span);
|
||||||
fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
|
fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
|
||||||
|
if param.pat.is_never_pattern() {
|
||||||
|
fcx.function_diverges_because_of_empty_arguments.set(Diverges::Always {
|
||||||
|
span: param.pat.span,
|
||||||
|
custom_note: Some("any code following a never pattern is unreachable"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Check that argument is Sized.
|
// Check that argument is Sized.
|
||||||
if !params_can_be_unsized {
|
if !params_can_be_unsized {
|
||||||
@ -105,6 +110,7 @@ pub(super) fn check_fn<'a, 'tcx>(
|
|||||||
hir::FnRetTy::Return(ty) => ty.span,
|
hir::FnRetTy::Return(ty) => ty.span,
|
||||||
};
|
};
|
||||||
fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::SizedReturnType);
|
fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::SizedReturnType);
|
||||||
|
fcx.is_whole_body.set(true);
|
||||||
fcx.check_return_expr(body.value, false);
|
fcx.check_return_expr(body.value, false);
|
||||||
|
|
||||||
// Finalize the return check by taking the LUB of the return types
|
// Finalize the return check by taking the LUB of the return types
|
||||||
|
@ -208,10 +208,10 @@ pub(super) fn check_expr_with_expectation_and_args(
|
|||||||
// without the final expr (e.g. `try { return; }`). We don't want to generate an
|
// without the final expr (e.g. `try { return; }`). We don't want to generate an
|
||||||
// unreachable_code lint for it since warnings for autogenerated code are confusing.
|
// unreachable_code lint for it since warnings for autogenerated code are confusing.
|
||||||
let is_try_block_generated_unit_expr = match expr.kind {
|
let is_try_block_generated_unit_expr = match expr.kind {
|
||||||
ExprKind::Call(_, args) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {
|
ExprKind::Call(_, [arg]) => {
|
||||||
args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock)
|
expr.span.is_desugaring(DesugaringKind::TryBlock)
|
||||||
|
&& arg.span.is_desugaring(DesugaringKind::TryBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -220,9 +220,16 @@ pub(super) fn check_expr_with_expectation_and_args(
|
|||||||
self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
|
self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide the outer diverging and has_errors flags.
|
// Whether a past expression diverges doesn't affect typechecking of this expression, so we
|
||||||
|
// reset `diverges` while checking `expr`.
|
||||||
let old_diverges = self.diverges.replace(Diverges::Maybe);
|
let old_diverges = self.diverges.replace(Diverges::Maybe);
|
||||||
|
|
||||||
|
if self.is_whole_body.replace(false) {
|
||||||
|
// If this expression is the whole body and the function diverges because of its
|
||||||
|
// arguments, we check this here to ensure the body is considered to diverge.
|
||||||
|
self.diverges.set(self.function_diverges_because_of_empty_arguments.get())
|
||||||
|
};
|
||||||
|
|
||||||
let ty = ensure_sufficient_stack(|| match &expr.kind {
|
let ty = ensure_sufficient_stack(|| match &expr.kind {
|
||||||
hir::ExprKind::Path(
|
hir::ExprKind::Path(
|
||||||
qpath @ (hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)),
|
qpath @ (hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)),
|
||||||
|
@ -1471,6 +1471,12 @@ pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
|
|||||||
/// Type check a `let` statement.
|
/// Type check a `let` statement.
|
||||||
pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
|
pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
|
||||||
self.check_decl(local.into());
|
self.check_decl(local.into());
|
||||||
|
if local.pat.is_never_pattern() {
|
||||||
|
self.diverges.set(Diverges::Always {
|
||||||
|
span: local.pat.span,
|
||||||
|
custom_note: Some("any code following a never pattern is unreachable"),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
|
pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
|
||||||
|
@ -103,6 +103,13 @@ pub struct FnCtxt<'a, 'tcx> {
|
|||||||
/// the diverges flag is set to something other than `Maybe`.
|
/// the diverges flag is set to something other than `Maybe`.
|
||||||
pub(super) diverges: Cell<Diverges>,
|
pub(super) diverges: Cell<Diverges>,
|
||||||
|
|
||||||
|
/// If one of the function arguments is a never pattern, this counts as diverging code. This
|
||||||
|
/// affect typechecking of the function body.
|
||||||
|
pub(super) function_diverges_because_of_empty_arguments: Cell<Diverges>,
|
||||||
|
|
||||||
|
/// Whether the currently checked node is the whole body of the function.
|
||||||
|
pub(super) is_whole_body: Cell<bool>,
|
||||||
|
|
||||||
pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
|
pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
|
||||||
|
|
||||||
pub(super) inh: &'a Inherited<'tcx>,
|
pub(super) inh: &'a Inherited<'tcx>,
|
||||||
@ -124,6 +131,8 @@ pub fn new(
|
|||||||
ret_coercion_span: Cell::new(None),
|
ret_coercion_span: Cell::new(None),
|
||||||
coroutine_types: None,
|
coroutine_types: None,
|
||||||
diverges: Cell::new(Diverges::Maybe),
|
diverges: Cell::new(Diverges::Maybe),
|
||||||
|
function_diverges_because_of_empty_arguments: Cell::new(Diverges::Maybe),
|
||||||
|
is_whole_body: Cell::new(false),
|
||||||
enclosing_breakables: RefCell::new(EnclosingBreakables {
|
enclosing_breakables: RefCell::new(EnclosingBreakables {
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
by_id: Default::default(),
|
by_id: Default::default(),
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
// edition: 2018
|
||||||
|
// known-bug: #120240
|
||||||
|
#![feature(never_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
|
// Divergence is not detected.
|
||||||
|
async fn async_never(!: Void) -> ! {} // gives an error
|
||||||
|
|
||||||
|
// Divergence is detected
|
||||||
|
async fn async_let(x: Void) -> ! {
|
||||||
|
let ! = x;
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/120240-async-fn-never-arg.rs:11:36
|
||||||
|
|
|
||||||
|
LL | async fn async_never(!: Void) -> ! {} // gives an error
|
||||||
|
| ^^ expected `!`, found `()`
|
||||||
|
|
|
||||||
|
= note: expected type `!`
|
||||||
|
found unit type `()`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
@ -0,0 +1,36 @@
|
|||||||
|
#![feature(never_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![deny(unreachable_patterns)]
|
||||||
|
#![deny(unreachable_code)]
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
|
fn never_arg(!: Void) -> u32 {
|
||||||
|
println!();
|
||||||
|
//~^ ERROR unreachable statement
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ref_never_arg(&!: &Void) -> u32 {
|
||||||
|
println!();
|
||||||
|
//~^ ERROR unreachable statement
|
||||||
|
}
|
||||||
|
|
||||||
|
fn never_let() -> u32 {
|
||||||
|
let ptr: *const Void = std::ptr::null();
|
||||||
|
unsafe {
|
||||||
|
let ! = *ptr;
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
//~^ ERROR unreachable statement
|
||||||
|
}
|
||||||
|
|
||||||
|
fn never_match() -> u32 {
|
||||||
|
let ptr: *const Void = std::ptr::null();
|
||||||
|
unsafe {
|
||||||
|
match *ptr { ! };
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
//~^ ERROR unreachable statement
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
error: unreachable statement
|
||||||
|
--> $DIR/diverge-causes-unreachable-code.rs:11:5
|
||||||
|
|
|
||||||
|
LL | fn never_arg(!: Void) -> u32 {
|
||||||
|
| - any code following a never pattern is unreachable
|
||||||
|
LL | println!();
|
||||||
|
| ^^^^^^^^^^ unreachable statement
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/diverge-causes-unreachable-code.rs:4:9
|
||||||
|
|
|
||||||
|
LL | #![deny(unreachable_code)]
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
= 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)
|
||||||
|
|
||||||
|
error: unreachable statement
|
||||||
|
--> $DIR/diverge-causes-unreachable-code.rs:16:5
|
||||||
|
|
|
||||||
|
LL | fn ref_never_arg(&!: &Void) -> u32 {
|
||||||
|
| -- any code following a never pattern is unreachable
|
||||||
|
LL | println!();
|
||||||
|
| ^^^^^^^^^^ unreachable statement
|
||||||
|
|
|
||||||
|
= 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)
|
||||||
|
|
||||||
|
error: unreachable statement
|
||||||
|
--> $DIR/diverge-causes-unreachable-code.rs:25:5
|
||||||
|
|
|
||||||
|
LL | let ! = *ptr;
|
||||||
|
| - any code following a never pattern is unreachable
|
||||||
|
LL | }
|
||||||
|
LL | println!();
|
||||||
|
| ^^^^^^^^^^ unreachable statement
|
||||||
|
|
|
||||||
|
= 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)
|
||||||
|
|
||||||
|
error: unreachable statement
|
||||||
|
--> $DIR/diverge-causes-unreachable-code.rs:34:5
|
||||||
|
|
|
||||||
|
LL | match *ptr { ! };
|
||||||
|
| ---------------- any code following this `match` expression is unreachable, as all arms diverge
|
||||||
|
LL | }
|
||||||
|
LL | println!();
|
||||||
|
| ^^^^^^^^^^ unreachable statement
|
||||||
|
|
|
||||||
|
= 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)
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
56
tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs
Normal file
56
tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#![feature(never_patterns)]
|
||||||
|
#![feature(let_chains)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![deny(unreachable_patterns)]
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
|
// Contrast with `./diverges.rs`: merely having an empty type around isn't enough to diverge.
|
||||||
|
|
||||||
|
fn wild_void(_: Void) -> u32 {}
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
|
||||||
|
fn wild_let() -> u32 {
|
||||||
|
let ptr: *const Void = std::ptr::null();
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
let _ = *ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wild_match() -> u32 {
|
||||||
|
let ptr: *const Void = std::ptr::null();
|
||||||
|
unsafe {
|
||||||
|
match *ptr {
|
||||||
|
_ => {} //~ ERROR: mismatched types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn binding_void(_x: Void) -> u32 {}
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
|
||||||
|
fn binding_let() -> u32 {
|
||||||
|
let ptr: *const Void = std::ptr::null();
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
let _x = *ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn binding_match() -> u32 {
|
||||||
|
let ptr: *const Void = std::ptr::null();
|
||||||
|
unsafe {
|
||||||
|
match *ptr {
|
||||||
|
_x => {} //~ ERROR: mismatched types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't confuse this with a `let !` statement.
|
||||||
|
fn let_chain(x: Void) -> u32 {
|
||||||
|
if let true = true && let ! = x {}
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
}
|
55
tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.stderr
Normal file
55
tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.stderr
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/diverges-not.rs:12:26
|
||||||
|
|
|
||||||
|
LL | fn wild_void(_: Void) -> u32 {}
|
||||||
|
| --------- ^^^ expected `u32`, found `()`
|
||||||
|
| |
|
||||||
|
| implicitly returns `()` as its body has no tail or `return` expression
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/diverges-not.rs:17:5
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | let _ = *ptr;
|
||||||
|
LL | | }
|
||||||
|
| |_____^ expected `u32`, found `()`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/diverges-not.rs:27:18
|
||||||
|
|
|
||||||
|
LL | _ => {}
|
||||||
|
| ^^ expected `u32`, found `()`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/diverges-not.rs:32:30
|
||||||
|
|
|
||||||
|
LL | fn binding_void(_x: Void) -> u32 {}
|
||||||
|
| ------------ ^^^ expected `u32`, found `()`
|
||||||
|
| |
|
||||||
|
| implicitly returns `()` as its body has no tail or `return` expression
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/diverges-not.rs:37:5
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | let _x = *ptr;
|
||||||
|
LL | | }
|
||||||
|
| |_____^ expected `u32`, found `()`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/diverges-not.rs:47:19
|
||||||
|
|
|
||||||
|
LL | _x => {}
|
||||||
|
| ^^ expected `u32`, found `()`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/diverges-not.rs:54:37
|
||||||
|
|
|
||||||
|
LL | if let true = true && let ! = x {}
|
||||||
|
| ^^ expected `u32`, found `()`
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
38
tests/ui/rfcs/rfc-0000-never_patterns/diverges.rs
Normal file
38
tests/ui/rfcs/rfc-0000-never_patterns/diverges.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// check-pass
|
||||||
|
// edition: 2018
|
||||||
|
#![feature(never_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![deny(unreachable_patterns)]
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
|
// A never pattern alone diverges.
|
||||||
|
|
||||||
|
fn never_arg(!: Void) -> ! {}
|
||||||
|
|
||||||
|
fn never_arg_returns_anything<T>(!: Void) -> T {}
|
||||||
|
|
||||||
|
fn ref_never_arg(&!: &Void) -> ! {}
|
||||||
|
|
||||||
|
fn never_let() -> ! {
|
||||||
|
let ptr: *const Void = std::ptr::null();
|
||||||
|
unsafe {
|
||||||
|
let ! = *ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn never_match() -> ! {
|
||||||
|
let ptr: *const Void = std::ptr::null();
|
||||||
|
unsafe {
|
||||||
|
match *ptr { ! };
|
||||||
|
}
|
||||||
|
// Ensures this typechecks because of divergence and not the type of the match expression.
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: divergence is not detected for async fns when the `!` is in the argument (#120240).
|
||||||
|
async fn async_let(x: Void) -> ! {
|
||||||
|
let ! = x;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user