Disallow arm bodies on never patterns

This commit is contained in:
Nadrieril 2023-11-27 04:08:09 +01:00
parent 06a8ed10b6
commit 70deb9a57f
8 changed files with 75 additions and 26 deletions

View File

@ -108,6 +108,11 @@ ast_lowering_misplaced_impl_trait =
ast_lowering_misplaced_relax_trait_bound = ast_lowering_misplaced_relax_trait_bound =
`?Trait` bounds are only permitted at the point where a type parameter is declared `?Trait` bounds are only permitted at the point where a type parameter is declared
ast_lowering_never_pattern_with_body =
a never pattern is always unreachable
.label = this will never be executed
.suggestion = remove this expression
ast_lowering_never_pattern_with_guard = ast_lowering_never_pattern_with_guard =
a guard on a never pattern will never be run a guard on a never pattern will never be run
.suggestion = remove this guard .suggestion = remove this guard

View File

@ -349,6 +349,15 @@ pub struct MatchArmWithNoBody {
pub suggestion: Span, pub suggestion: Span,
} }
#[derive(Diagnostic)]
#[diag(ast_lowering_never_pattern_with_body)]
pub struct NeverPatternWithBody {
#[primary_span]
#[label]
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(ast_lowering_never_pattern_with_guard)] #[diag(ast_lowering_never_pattern_with_guard)]
pub struct NeverPatternWithGuard { pub struct NeverPatternWithGuard {

View File

@ -2,7 +2,8 @@ use super::errors::{
AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks, AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters, BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody, FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign, NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
UnderscoreExprLhsAssign,
}; };
use super::ResolverAstLoweringExt; use super::ResolverAstLoweringExt;
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
@ -567,20 +568,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
let hir_id = self.next_id(); let hir_id = self.next_id();
let span = self.lower_span(arm.span); let span = self.lower_span(arm.span);
self.lower_attrs(hir_id, &arm.attrs); self.lower_attrs(hir_id, &arm.attrs);
let body = if let Some(body) = &arm.body { let is_never_pattern = pat.is_never_pattern();
// FIXME(never_patterns): Disallow never pattern with a body or guard let body = if let Some(body) = &arm.body
&& !is_never_pattern
{
self.lower_expr(body) self.lower_expr(body)
} else { } else {
if !pat.is_never_pattern() { // Either `body.is_none()` or `is_never_pattern` here.
self.tcx if !is_never_pattern {
.sess let suggestion = span.shrink_to_hi();
.emit_err(MatchArmWithNoBody { span, suggestion: span.shrink_to_hi() }); self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
} else if let Some(body) = &arm.body {
self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
guard = None;
} else if let Some(g) = &arm.guard { } else if let Some(g) = &arm.guard {
self.tcx.sess.emit_err(NeverPatternWithGuard { span: g.span }); self.tcx.sess.emit_err(NeverPatternWithGuard { span: g.span });
guard = None; guard = None;
} }
// An arm without a body, meant for never patterns.
// We add a fake `loop {}` arm body so that it typecks to `!`. // We add a fake `loop {}` arm body so that it typecks to `!`.
// FIXME(never_patterns): Desugar into a call to `unreachable_unchecked`. // FIXME(never_patterns): Desugar into a call to `unreachable_unchecked`.
let block = self.arena.alloc(hir::Block { let block = self.arena.alloc(hir::Block {

View File

@ -12,7 +12,7 @@ fn main() {
unsafe { unsafe {
let ptr: *const Void = NonNull::dangling().as_ptr(); let ptr: *const Void = NonNull::dangling().as_ptr();
match *ptr { match *ptr {
! => {} //~ ERROR `!` patterns are experimental ! //~ ERROR `!` patterns are experimental
} }
} }

View File

@ -18,7 +18,7 @@ LL | let (Ok(_x) | Err(&!)) = res.as_ref();
error[E0658]: `!` patterns are experimental error[E0658]: `!` patterns are experimental
--> $DIR/feature-gate-never_patterns.rs:15:13 --> $DIR/feature-gate-never_patterns.rs:15:13
| |
LL | ! => {} LL | !
| ^ | ^
| |
= note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information

View File

@ -12,6 +12,7 @@ macro_rules! never {
fn no_arms_or_guards(x: Void) { fn no_arms_or_guards(x: Void) {
match None::<Void> { match None::<Void> {
Some(!) => {} Some(!) => {}
//~^ ERROR a never pattern is always unreachable
None => {} None => {}
} }
match None::<Void> { match None::<Void> {
@ -21,10 +22,12 @@ fn no_arms_or_guards(x: Void) {
} }
match None::<Void> { match None::<Void> {
Some(!) if true => {} Some(!) if true => {}
//~^ ERROR a never pattern is always unreachable
None => {} None => {}
} }
match None::<Void> { match None::<Void> {
Some(never!()) => {}, Some(never!()) => {},
//~^ ERROR a never pattern is always unreachable
None => {} None => {}
} }
} }

View File

@ -1,8 +1,35 @@
error: a never pattern is always unreachable
--> $DIR/check.rs:14:20
|
LL | Some(!) => {}
| ^^
| |
| this will never be executed
| help: remove this expression
error: a guard on a never pattern will never be run error: a guard on a never pattern will never be run
--> $DIR/check.rs:18:20 --> $DIR/check.rs:19:20
| |
LL | Some(!) if true, LL | Some(!) if true,
| ^^^^ help: remove this guard | ^^^^ help: remove this guard
error: aborting due to 1 previous error error: a never pattern is always unreachable
--> $DIR/check.rs:24:28
|
LL | Some(!) if true => {}
| ^^
| |
| this will never be executed
| help: remove this expression
error: a never pattern is always unreachable
--> $DIR/check.rs:29:27
|
LL | Some(never!()) => {},
| ^^
| |
| this will never be executed
| help: remove this expression
error: aborting due to 4 previous errors

View File

@ -20,58 +20,58 @@ fn never_pattern_location(void: Void) {
// FIXME(never_patterns): Don't accept on a non-empty type. // FIXME(never_patterns): Don't accept on a non-empty type.
match Some(0) { match Some(0) {
None => {} None => {}
Some(!) => {} Some(!),
} }
// FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches. // FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
match () { match () {
() => {} () => {}
! => {} !,
} }
// FIXME(never_patterns): Don't accept even on an empty branch. // FIXME(never_patterns): Don't accept even on an empty branch.
match None::<Void> { match None::<Void> {
None => {} None => {}
! => {} !,
} }
// FIXME(never_patterns): Let alone if the emptiness is behind a reference. // FIXME(never_patterns): Let alone if the emptiness is behind a reference.
match None::<&Void> { match None::<&Void> {
None => {} None => {}
! => {} !,
} }
// Participate in match ergonomics. // Participate in match ergonomics.
match &void { match &void {
! => {} !
} }
match &&void { match &&void {
! => {} !
} }
match &&void { match &&void {
&! => {} &!
} }
match &None::<Void> { match &None::<Void> {
None => {} None => {}
Some(!) => {} Some(!)
} }
match None::<&Void> { match None::<&Void> {
None => {} None => {}
Some(!) => {} Some(!),
} }
// Accept on a composite empty type. // Accept on a composite empty type.
match None::<&(u32, Void)> { match None::<&(u32, Void)> {
None => {} None => {}
Some(&!) => {} Some(&!),
} }
// Accept on an simple empty type. // Accept on an simple empty type.
match None::<Void> { match None::<Void> {
None => {} None => {}
Some(!) => {} Some(!),
} }
match None::<&Void> { match None::<&Void> {
None => {} None => {}
Some(&!) => {} Some(&!),
} }
match None::<&(u32, Void)> { match None::<&(u32, Void)> {
None => {} None => {}
Some(&(_, !)) => {} Some(&(_, !)),
} }
} }
@ -89,7 +89,7 @@ fn never_and_bindings() {
// FIXME(never_patterns): A never pattern mustn't have bindings. // FIXME(never_patterns): A never pattern mustn't have bindings.
match x { match x {
Ok(_) => {} Ok(_) => {}
Err(&(_b, !)) => {} Err(&(_b, !)),
} }
match x { match x {
Ok(_a) | Err(&(_b, !)) => {} Ok(_a) | Err(&(_b, !)) => {}