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 =
`?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 =
a guard on a never pattern will never be run
.suggestion = remove this guard

View File

@ -349,6 +349,15 @@ pub struct MatchArmWithNoBody {
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)]
#[diag(ast_lowering_never_pattern_with_guard)]
pub struct NeverPatternWithGuard {

View File

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

View File

@ -12,7 +12,7 @@ fn main() {
unsafe {
let ptr: *const Void = NonNull::dangling().as_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
--> $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

View File

@ -12,6 +12,7 @@ macro_rules! never {
fn no_arms_or_guards(x: Void) {
match None::<Void> {
Some(!) => {}
//~^ ERROR a never pattern is always unreachable
None => {}
}
match None::<Void> {
@ -21,10 +22,12 @@ fn no_arms_or_guards(x: Void) {
}
match None::<Void> {
Some(!) if true => {}
//~^ ERROR a never pattern is always unreachable
None => {}
}
match None::<Void> {
Some(never!()) => {},
//~^ ERROR a never pattern is always unreachable
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
--> $DIR/check.rs:18:20
--> $DIR/check.rs:19:20
|
LL | Some(!) if true,
| ^^^^ 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.
match Some(0) {
None => {}
Some(!) => {}
Some(!),
}
// FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
match () {
() => {}
! => {}
!,
}
// FIXME(never_patterns): Don't accept even on an empty branch.
match None::<Void> {
None => {}
! => {}
!,
}
// FIXME(never_patterns): Let alone if the emptiness is behind a reference.
match None::<&Void> {
None => {}
! => {}
!,
}
// Participate in match ergonomics.
match &void {
! => {}
!
}
match &&void {
! => {}
!
}
match &&void {
&! => {}
&!
}
match &None::<Void> {
None => {}
Some(!) => {}
Some(!)
}
match None::<&Void> {
None => {}
Some(!) => {}
Some(!),
}
// Accept on a composite empty type.
match None::<&(u32, Void)> {
None => {}
Some(&!) => {}
Some(&!),
}
// Accept on an simple empty type.
match None::<Void> {
None => {}
Some(!) => {}
Some(!),
}
match None::<&Void> {
None => {}
Some(&!) => {}
Some(&!),
}
match None::<&(u32, Void)> {
None => {}
Some(&(_, !)) => {}
Some(&(_, !)),
}
}
@ -89,7 +89,7 @@ fn never_and_bindings() {
// FIXME(never_patterns): A never pattern mustn't have bindings.
match x {
Ok(_) => {}
Err(&(_b, !)) => {}
Err(&(_b, !)),
}
match x {
Ok(_a) | Err(&(_b, !)) => {}