diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index ecbe8cc6aec..6bde4f2d8fa 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -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 diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index c6a4166f537..11bb559719b 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -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 { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4913f9cd12d..137ccb99346 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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 { diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs index 69e9f62abf0..ca5ce3b9489 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.rs +++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs @@ -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 } } diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index b7290eeb36d..2354a3b0476 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -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 for more information diff --git a/tests/ui/never_patterns/check.rs b/tests/ui/never_patterns/check.rs index 1f55ef11242..e298112244a 100644 --- a/tests/ui/never_patterns/check.rs +++ b/tests/ui/never_patterns/check.rs @@ -12,6 +12,7 @@ macro_rules! never { fn no_arms_or_guards(x: Void) { match None:: { Some(!) => {} + //~^ ERROR a never pattern is always unreachable None => {} } match None:: { @@ -21,10 +22,12 @@ fn no_arms_or_guards(x: Void) { } match None:: { Some(!) if true => {} + //~^ ERROR a never pattern is always unreachable None => {} } match None:: { Some(never!()) => {}, + //~^ ERROR a never pattern is always unreachable None => {} } } diff --git a/tests/ui/never_patterns/check.stderr b/tests/ui/never_patterns/check.stderr index a25ee1b0a2a..bfbc7a1b534 100644 --- a/tests/ui/never_patterns/check.stderr +++ b/tests/ui/never_patterns/check.stderr @@ -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 diff --git a/tests/ui/pattern/never_patterns.rs b/tests/ui/pattern/never_patterns.rs index fdba1b8e087..915f3e75e7b 100644 --- a/tests/ui/pattern/never_patterns.rs +++ b/tests/ui/pattern/never_patterns.rs @@ -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:: { 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:: { 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:: { 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, !)) => {}