Auto merge of #118527 - Nadrieril:never_patterns_parse, r=compiler-errors
never_patterns: Parse match arms with no body
Never patterns are meant to signal unreachable cases, and thus don't take bodies:
```rust
let ptr: *const Option<!> = ...;
match *ptr {
None => { foo(); }
Some(!),
}
```
This PR makes rustc accept the above, and enforces that an arm has a body xor is a never pattern. This affects parsing of match arms even with the feature off, so this is delicate. (Plus this is my first non-trivial change to the parser).
~~The last commit is optional; it introduces a bit of churn to allow the new suggestions to be machine-applicable. There may be a better solution? I'm not sure.~~ EDIT: I removed that commit
r? `@compiler-errors`
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
use super::errors::{
|
||||
AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
|
||||
BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
|
||||
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd,
|
||||
NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign,
|
||||
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
|
||||
NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
|
||||
UnderscoreExprLhsAssign,
|
||||
};
|
||||
use super::ResolverAstLoweringExt;
|
||||
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
|
||||
@@ -549,7 +550,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
|
||||
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
||||
let pat = self.lower_pat(&arm.pat);
|
||||
let guard = arm.guard.as_ref().map(|cond| {
|
||||
let mut guard = arm.guard.as_ref().map(|cond| {
|
||||
if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
|
||||
hir::Guard::IfLet(self.arena.alloc(hir::Let {
|
||||
hir_id: self.next_id(),
|
||||
@@ -564,14 +565,43 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
});
|
||||
let hir_id = self.next_id();
|
||||
let span = self.lower_span(arm.span);
|
||||
self.lower_attrs(hir_id, &arm.attrs);
|
||||
hir::Arm {
|
||||
hir_id,
|
||||
pat,
|
||||
guard,
|
||||
body: self.lower_expr(&arm.body),
|
||||
span: self.lower_span(arm.span),
|
||||
}
|
||||
let is_never_pattern = pat.is_never_pattern();
|
||||
let body = if let Some(body) = &arm.body
|
||||
&& !is_never_pattern
|
||||
{
|
||||
self.lower_expr(body)
|
||||
} else {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
stmts: &[],
|
||||
expr: None,
|
||||
hir_id: self.next_id(),
|
||||
rules: hir::BlockCheckMode::DefaultBlock,
|
||||
span,
|
||||
targeted_by_break: false,
|
||||
});
|
||||
self.arena.alloc(hir::Expr {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ExprKind::Loop(block, None, hir::LoopSource::Loop, span),
|
||||
span,
|
||||
})
|
||||
};
|
||||
hir::Arm { hir_id, pat, guard, body, span }
|
||||
}
|
||||
|
||||
/// Lower an `async` construct to a coroutine that implements `Future`.
|
||||
|
||||
Reference in New Issue
Block a user