Skip to content

Commit

Permalink
Unrolled build for rust-lang#121619
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#121619 - RossSmyth:pfix_match, r=petrochenkov

Experimental feature postfix match

This has a basic experimental implementation for the RFC postfix match (rust-lang/rfcs#3295, rust-lang#121618). [Liaison is](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Postfix.20Match.20Liaison/near/423301844) ```@scottmcm``` with the lang team's [experimental feature gate process](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md).

This feature has had an RFC for a while, and there has been discussion on it for a while. It would probably be valuable to see it out in the field rather than continue discussing it. This feature also allows to see how popular postfix expressions like this are for the postfix macros RFC, as those will take more time to implement.

It is entirely implemented in the parser, so it should be relatively easy to remove if needed.

This PR is split in to 5 commits to ease review.

1. The implementation of the feature & gating.
2. Add a MatchKind field, fix uses, fix pretty.
3. Basic rustfmt impl, as rustfmt crashes upon seeing this syntax without a fix.
4. Add new MatchSource to HIR for Clippy & other HIR consumers
  • Loading branch information
rust-timer authored Mar 22, 2024
2 parents eff958c + 567c98b commit 4911756
Showing 34 changed files with 367 additions and 41 deletions.
11 changes: 10 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1437,7 +1437,7 @@ pub enum ExprKind {
/// `'label: loop { block }`
Loop(P<Block>, Option<Label>, Span),
/// A `match` block.
Match(P<Expr>, ThinVec<Arm>),
Match(P<Expr>, ThinVec<Arm>, MatchKind),
/// A closure (e.g., `move |a, b, c| a + b + c`).
Closure(Box<Closure>),
/// A block (`'label: { ... }`).
@@ -1762,6 +1762,15 @@ pub enum StrStyle {
Raw(u8),
}

/// The kind of match expression
#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
pub enum MatchKind {
/// match expr { ... }
Prefix,
/// expr.match { ... }
Postfix,
}

/// A literal in a meta item.
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
pub struct MetaItemLit {
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
@@ -1425,7 +1425,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
visit_opt(label, |label| vis.visit_label(label));
vis.visit_span(span);
}
ExprKind::Match(expr, arms) => {
ExprKind::Match(expr, arms, _kind) => {
vis.visit_expr(expr);
arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
}
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
@@ -923,7 +923,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
visit_opt!(visitor, visit_label, opt_label);
try_visit!(visitor.visit_block(block));
}
ExprKind::Match(subexpression, arms) => {
ExprKind::Match(subexpression, arms, _kind) => {
try_visit!(visitor.visit_expr(subexpression));
walk_list!(visitor, visit_arm, arms);
}
7 changes: 5 additions & 2 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
@@ -181,10 +181,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}),
ExprKind::TryBlock(body) => self.lower_expr_try_block(body),
ExprKind::Match(expr, arms) => hir::ExprKind::Match(
ExprKind::Match(expr, arms, kind) => hir::ExprKind::Match(
self.lower_expr(expr),
self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
hir::MatchSource::Normal,
match kind {
MatchKind::Prefix => hir::MatchSource::Normal,
MatchKind::Postfix => hir::MatchSource::Postfix,
},
),
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
ExprKind::Closure(box Closure {
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -564,6 +564,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental");

if !visitor.features.never_patterns {
if let Some(spans) = spans.get(&sym::never_patterns) {
20 changes: 15 additions & 5 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::pp::Breaks::Inconsistent;
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
use ast::ForLoopKind;
use ast::{ForLoopKind, MatchKind};
use itertools::{Itertools, Position};
use rustc_ast::ptr::P;
use rustc_ast::token;
@@ -589,12 +589,22 @@ impl<'a> State<'a> {
self.word_nbsp("loop");
self.print_block_with_attrs(blk, attrs);
}
ast::ExprKind::Match(expr, arms) => {
ast::ExprKind::Match(expr, arms, match_kind) => {
self.cbox(0);
self.ibox(0);
self.word_nbsp("match");
self.print_expr_as_cond(expr);
self.space();

match match_kind {
MatchKind::Prefix => {
self.word_nbsp("match");
self.print_expr_as_cond(expr);
self.space();
}
MatchKind::Postfix => {
self.print_expr_as_cond(expr);
self.word_nbsp(".match");
}
}

self.bopen();
self.print_inner_attributes_no_trailing_hardbreak(attrs);
for arm in arms {
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/assert/context.rs
Original file line number Diff line number Diff line change
@@ -245,7 +245,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
ExprKind::Let(_, local_expr, _, _) => {
self.manage_cond_expr(local_expr);
}
ExprKind::Match(local_expr, _) => {
ExprKind::Match(local_expr, ..) => {
self.manage_cond_expr(local_expr);
}
ExprKind::MethodCall(call) => {
Original file line number Diff line number Diff line change
@@ -132,7 +132,7 @@ fn cs_partial_cmp(
// Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354

if !tag_then_data
&& let ExprKind::Match(_, arms) = &mut expr1.kind
&& let ExprKind::Match(_, arms, _) = &mut expr1.kind
&& let Some(last) = arms.last_mut()
&& let PatKind::Wild = last.pat.kind
{
3 changes: 1 addition & 2 deletions compiler/rustc_builtin_macros/src/deriving/debug.rs
Original file line number Diff line number Diff line change
@@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::path_std;

use ast::EnumDef;
use rustc_ast::{self as ast, MetaItem};
use rustc_ast::{self as ast, EnumDef, MetaItem};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
4 changes: 2 additions & 2 deletions compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::base::ExtCtxt;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, PatKind, UnOp};
use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp};
use rustc_ast::{attr, token, util::literal};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -524,7 +524,7 @@ impl<'a> ExtCtxt<'a> {
}

pub fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: ThinVec<ast::Arm>) -> P<Expr> {
self.expr(span, ast::ExprKind::Match(arg, arms))
self.expr(span, ast::ExprKind::Match(arg, arms, MatchKind::Prefix))
}

pub fn expr_if(
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
@@ -559,6 +559,8 @@ declare_features! (
(unstable, offset_of_nested, "1.77.0", Some(120140)),
/// Allows using `#[optimize(X)]`.
(unstable, optimize_attribute, "1.34.0", Some(54882)),
/// Allows postfix match `expr.match { ... }`
(unstable, postfix_match, "CURRENT_RUSTC_VERSION", Some(121618)),
/// Allows macro attributes on expressions, statements and non-inline modules.
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
/// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
@@ -2015,6 +2015,8 @@ pub enum LocalSource {
pub enum MatchSource {
/// A `match _ { .. }`.
Normal,
/// A `expr.match { .. }`.
Postfix,
/// A desugared `for _ in _ { .. }` loop.
ForLoopDesugar,
/// A desugared `?` operator.
@@ -2031,6 +2033,7 @@ impl MatchSource {
use MatchSource::*;
match self {
Normal => "match",
Postfix => ".match",
ForLoopDesugar => "for",
TryDesugar(_) => "?",
AwaitDesugar => ".await",
4 changes: 2 additions & 2 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
@@ -865,7 +865,7 @@ trait UnusedDelimLint {
(iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
}

Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
Match(ref head, ..) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
let left = e.span.lo() + rustc_span::BytePos(5);
(head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
}
@@ -1133,7 +1133,7 @@ impl EarlyLintPass for UnusedParens {
}
return;
}
ExprKind::Match(ref _expr, ref arm) => {
ExprKind::Match(ref _expr, ref arm, _) => {
for a in arm {
if let Some(body) = &a.body {
self.check_unused_delims_expr(
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
@@ -481,6 +481,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
// when the iterator is an uninhabited type. unreachable_code will trigger instead.
hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
hir::MatchSource::ForLoopDesugar
| hir::MatchSource::Postfix
| hir::MatchSource::Normal
| hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
// Unreachable patterns in try and await expressions occur when one of
29 changes: 24 additions & 5 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ use crate::errors;
use crate::maybe_recover_from_interpolated_ty_qpath;
use ast::mut_visit::{noop_visit_expr, MutVisitor};
use ast::token::IdentIsRaw;
use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment};
use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment};
use core::mem;
use core::ops::ControlFlow;
use rustc_ast::ptr::P;
@@ -1379,6 +1379,13 @@ impl<'a> Parser<'a> {
return Ok(self.mk_await_expr(self_arg, lo));
}

// Post-fix match
if self.eat_keyword(kw::Match) {
let match_span = self.prev_token.span;
self.psess.gated_spans.gate(sym::postfix_match, match_span);
return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix);
}

let fn_span_lo = self.token.span;
let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;
self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]);
@@ -2894,8 +2901,20 @@ impl<'a> Parser<'a> {
/// Parses a `match ... { ... }` expression (`match` token already eaten).
fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> {
let match_span = self.prev_token.span;
let lo = self.prev_token.span;
let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;

self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix)
}

/// Parses the block of a `match expr { ... }` or a `expr.match { ... }`
/// expression. This is after the match token and scrutinee are eaten
fn parse_match_block(
&mut self,
lo: Span,
match_span: Span,
scrutinee: P<Expr>,
match_kind: MatchKind,
) -> PResult<'a, P<Expr>> {
if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
if self.token == token::Semi {
e.span_suggestion_short(
@@ -2938,15 +2957,15 @@ impl<'a> Parser<'a> {
});
return Ok(self.mk_expr_with_attrs(
span,
ExprKind::Match(scrutinee, arms),
ExprKind::Match(scrutinee, arms, match_kind),
attrs,
));
}
}
}
let hi = self.token.span;
self.bump();
Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs))
Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms, match_kind), attrs))
}

/// Attempt to recover from match arm body with statements and no surrounding braces.
@@ -3955,7 +3974,7 @@ impl MutVisitor for CondChecker<'_> {
| ExprKind::While(_, _, _)
| ExprKind::ForLoop { .. }
| ExprKind::Loop(_, _, _)
| ExprKind::Match(_, _)
| ExprKind::Match(_, _, _)
| ExprKind::Closure(_)
| ExprKind::Block(_, _)
| ExprKind::Gen(_, _, _)
2 changes: 1 addition & 1 deletion compiler/rustc_passes/src/check_const.rs
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ impl NonConstExpr {
Self::Match(TryDesugar(_)) => &[sym::const_try],

// All other expressions are allowed.
Self::Loop(Loop | While) | Self::Match(Normal | FormatArgs) => &[],
Self::Loop(Loop | While) | Self::Match(Normal | Postfix | FormatArgs) => &[],
};

Some(gates)
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -1335,6 +1335,7 @@ symbols! {
poll,
poll_next,
post_dash_lto: "post-lto",
postfix_match,
powerpc_target_feature,
powf128,
powf16,
22 changes: 22 additions & 0 deletions src/doc/unstable-book/src/language-features/postfix-match.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# `postfix-match`

`postfix-match` adds the feature for matching upon values postfix
the expressions that generate the values.

```rust,edition2021
#![feature(postfix_match)]
enum Foo {
Bar,
Baz
}
fn get_foo() -> Foo {
Foo::Bar
}
get_foo().match {
Foo::Bar => {},
Foo::Baz => panic!(),
}
```
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/redundant_else.rs
Original file line number Diff line number Diff line change
@@ -105,7 +105,7 @@ impl<'ast> Visitor<'ast> for BreakVisitor {
fn visit_expr(&mut self, expr: &'ast Expr) {
self.is_break = match expr.kind {
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
ExprKind::Match(_, ref arms) => arms.iter().all(|arm|
ExprKind::Match(_, ref arms, _) => arms.iter().all(|arm|
arm.body.is_none() || arm.body.as_deref().is_some_and(|body| self.check_expr(body))
),
ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
Original file line number Diff line number Diff line change
@@ -552,7 +552,7 @@ fn ident_difference_expr_with_base_location(
| (Gen(_, _, _), Gen(_, _, _))
| (Block(_, _), Block(_, _))
| (Closure(_), Closure(_))
| (Match(_, _), Match(_, _))
| (Match(_, _, _), Match(_, _, _))
| (Loop(_, _, _), Loop(_, _, _))
| (ForLoop { .. }, ForLoop { .. })
| (While(_, _, _), While(_, _, _))
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_utils/src/ast_utils.rs
Original file line number Diff line number Diff line change
@@ -198,7 +198,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
},
(AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
(Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
(Match(ls, la, lkind), Match(rs, ra, rkind)) => (lkind == rkind) && eq_expr(ls, rs) && over(la, ra, eq_arm),
(
Closure(box ast::Closure {
binder: lb,
8 changes: 4 additions & 4 deletions src/tools/rustfmt/src/expr.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ use std::cmp::min;

use itertools::Itertools;
use rustc_ast::token::{Delimiter, Lit, LitKind};
use rustc_ast::{ast, ptr, token, ForLoopKind};
use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind};
use rustc_span::{BytePos, Span};

use crate::chains::rewrite_chain;
@@ -170,8 +170,8 @@ pub(crate) fn format_expr(
}
}
}
ast::ExprKind::Match(ref cond, ref arms) => {
rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs)
ast::ExprKind::Match(ref cond, ref arms, kind) => {
rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs, kind)
}
ast::ExprKind::Path(ref qself, ref path) => {
rewrite_path(context, PathContext::Expr, qself, path, shape)
@@ -625,7 +625,7 @@ pub(crate) fn rewrite_cond(
shape: Shape,
) -> Option<String> {
match expr.kind {
ast::ExprKind::Match(ref cond, _) => {
ast::ExprKind::Match(ref cond, _, MatchKind::Prefix) => {
// `match `cond` {`
let cond_shape = match context.config.indent_style() {
IndentStyle::Visual => shape.shrink_left(6).and_then(|s| s.sub_width(2))?,
Loading

0 comments on commit 4911756

Please sign in to comment.