Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Introduce ast::StmtKind::LetElse to allow the usage of let_else with let_chains #93437

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,7 @@ pub struct Stmt {
impl Stmt {
pub fn tokens(&self) -> Option<&LazyTokenStream> {
match self.kind {
StmtKind::LetElse(ref let_else) => let_else.tokens.as_ref(),
StmtKind::Local(ref local) => local.tokens.as_ref(),
StmtKind::Item(ref item) => item.tokens.as_ref(),
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.tokens.as_ref(),
Expand Down Expand Up @@ -978,6 +979,8 @@ impl Stmt {

#[derive(Clone, Encodable, Decodable, Debug)]
pub enum StmtKind {
/// See [`LetElse`].
LetElse(P<LetElse>),
/// A local (let) binding.
Local(P<Local>),
/// An item definition.
Expand Down Expand Up @@ -1013,6 +1016,17 @@ pub enum MacStmtStyle {
NoBraces,
}

/// A `let .. && let && .. else { .. }` declaration.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct LetElse {
pub attrs: AttrVec,
pub expr: P<Expr>,
pub id: NodeId,
pub r#else: P<Block>,
pub span: Span,
pub tokens: Option<LazyTokenStream>,
}

/// Local represents a `let` statement, e.g., `let <pat>:<ty> = <expr>;`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Local {
Expand All @@ -1033,24 +1047,13 @@ pub enum LocalKind {
/// Local declaration with an initializer.
/// Example: `let x = y;`
Init(P<Expr>),
/// Local declaration with an initializer and an `else` clause.
/// Example: `let Some(x) = y else { return };`
InitElse(P<Expr>, P<Block>),
}

impl LocalKind {
pub fn init(&self) -> Option<&Expr> {
match self {
Self::Decl => None,
Self::Init(i) | Self::InitElse(i, _) => Some(i),
}
}

pub fn init_else_opt(&self) -> Option<(&Expr, Option<&Block>)> {
match self {
Self::Decl => None,
Self::Init(init) => Some((init, None)),
Self::InitElse(init, els) => Some((init, Some(els))),
Self::Init(i) => Some(i),
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_ast/src/ast_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::ptr::P;
use super::token::Nonterminal;
use super::tokenstream::LazyTokenStream;
use super::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant};
use super::{AssocItem, Expr, ForeignItem, Item, Local, MacCallStmt};
use super::{AssocItem, Expr, ForeignItem, Item, LetElse, Local, MacCallStmt};
use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility};
use super::{AttrVec, Attribute, Stmt, StmtKind};

Expand Down Expand Up @@ -104,6 +104,7 @@ impl AstLike for StmtKind {

fn attrs(&self) -> &[Attribute] {
match self {
StmtKind::LetElse(let_else) => let_else.attrs(),
StmtKind::Local(local) => local.attrs(),
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(),
StmtKind::Item(item) => item.attrs(),
Expand All @@ -114,6 +115,7 @@ impl AstLike for StmtKind {

fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
match self {
StmtKind::LetElse(let_else) => let_else.visit_attrs(f),
StmtKind::Local(local) => local.visit_attrs(f),
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
StmtKind::Item(item) => item.visit_attrs(f),
Expand All @@ -123,6 +125,7 @@ impl AstLike for StmtKind {
}
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
Some(match self {
StmtKind::LetElse(let_else) => &mut let_else.tokens,
StmtKind::Local(local) => &mut local.tokens,
StmtKind::Item(item) => &mut item.tokens,
StmtKind::Expr(expr) | StmtKind::Semi(expr) => &mut expr.tokens,
Expand Down Expand Up @@ -271,7 +274,7 @@ derive_has_tokens_and_attrs! {

derive_has_tokens_and_attrs! {
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
Local, MacCallStmt, Expr
LetElse, Local, MacCallStmt, Expr
}

// These ast nodes only support inert attributes, so they don't
Expand Down
22 changes: 18 additions & 4 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ pub trait MutVisitor: Sized {
noop_visit_parenthesized_parameter_data(p, self);
}

fn visit_let_else(&mut self, let_else: &mut P<LetElse>) {
noop_visit_let_else(let_else, self);
}

fn visit_local(&mut self, l: &mut P<Local>) {
noop_visit_local(l, self);
}
Expand Down Expand Up @@ -569,6 +573,16 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
vis.visit_span(span);
}

pub fn noop_visit_let_else<T: MutVisitor>(let_else: &mut P<LetElse>, vis: &mut T) {
let LetElse { attrs, expr, id, r#else, span, tokens } = let_else.deref_mut();
visit_thin_attrs(attrs, vis);
vis.visit_expr(expr);
vis.visit_id(id);
vis.visit_block(r#else);
vis.visit_span(span);
visit_lazy_tts(tokens, vis);
}

pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
let Local { id, pat, ty, kind, span, attrs, tokens } = local.deref_mut();
vis.visit_id(id);
Expand All @@ -579,10 +593,6 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
LocalKind::Init(init) => {
vis.visit_expr(init);
}
LocalKind::InitElse(init, els) => {
vis.visit_expr(init);
vis.visit_block(els);
}
}
vis.visit_span(span);
visit_thin_attrs(attrs, vis);
Expand Down Expand Up @@ -1408,6 +1418,10 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
vis: &mut T,
) -> SmallVec<[StmtKind; 1]> {
match kind {
StmtKind::LetElse(mut let_else) => smallvec![StmtKind::LetElse({
vis.visit_let_else(&mut let_else);
let_else
})],
StmtKind::Local(mut local) => smallvec![StmtKind::Local({
vis.visit_local(&mut local);
local
Expand Down
16 changes: 12 additions & 4 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ pub trait Visitor<'ast>: Sized {
fn visit_item(&mut self, i: &'ast Item) {
walk_item(self, i)
}
fn visit_let_else(&mut self, let_else: &'ast LetElse) {
walk_let_else(self, let_else)
}
fn visit_local(&mut self, l: &'ast Local) {
walk_local(self, l)
}
Expand Down Expand Up @@ -239,16 +242,20 @@ pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) {
walk_list!(visitor, visit_attribute, &krate.attrs);
}

pub fn walk_let_else<'a, V: Visitor<'a>>(visitor: &mut V, let_else: &'a LetElse) {
for attr in let_else.attrs.iter() {
visitor.visit_attribute(attr);
}
visitor.visit_expr(&let_else.expr);
visitor.visit_block(&let_else.r#else);
}

pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) {
for attr in local.attrs.iter() {
visitor.visit_attribute(attr);
}
visitor.visit_pat(&local.pat);
walk_list!(visitor, visit_ty, &local.ty);
if let Some((init, els)) = local.kind.init_else_opt() {
visitor.visit_expr(init);
walk_list!(visitor, visit_block, els);
}
}

pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, label: &'a Label) {
Expand Down Expand Up @@ -695,6 +702,7 @@ pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) {

pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) {
match statement.kind {
StmtKind::LetElse(ref let_else) => visitor.visit_let_else(let_else),
StmtKind::Local(ref local) => visitor.visit_local(local),
StmtKind::Item(ref item) => visitor.visit_item(item),
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr),
Expand Down
69 changes: 21 additions & 48 deletions compiler/rustc_ast_lowering/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
use rustc_ast::{AttrVec, Block, BlockCheckMode, LetElse, Local, Stmt, StmtKind};
use rustc_hir as hir;
use rustc_session::parse::feature_err;
use rustc_span::{sym, DesugaringKind};
Expand Down Expand Up @@ -34,23 +34,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let mut expr = None;
while let [s, tail @ ..] = ast_stmts {
match s.kind {
StmtKind::LetElse(ref let_else) => {
expr = Some(self.lower_let_else(let_else, tail));
// remaining statements are in let-else expression
break;
}
StmtKind::Local(ref local) => {
let hir_id = self.lower_node_id(s.id);
match &local.kind {
LocalKind::InitElse(init, els) => {
let e = self.lower_let_else(hir_id, local, init, els, tail);
expr = Some(e);
// remaining statements are in let-else expression
break;
}
_ => {
let local = self.lower_local(local);
self.alias_attrs(hir_id, local.hir_id);
let kind = hir::StmtKind::Local(local);
let span = self.lower_span(s.span);
stmts.push(hir::Stmt { hir_id, kind, span });
}
}
let local = self.lower_local(local);
self.alias_attrs(hir_id, local.hir_id);
let kind = hir::StmtKind::Local(local);
let span = self.lower_span(s.span);
stmts.push(hir::Stmt { hir_id, kind, span });
}
StmtKind::Item(ref it) => {
stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map(
Expand Down Expand Up @@ -116,54 +111,32 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}

fn lower_let_else(
&mut self,
stmt_hir_id: hir::HirId,
local: &Local,
init: &Expr,
els: &Block,
tail: &[Stmt],
) -> &'hir hir::Expr<'hir> {
let ty = local
.ty
.as_ref()
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
let span = self.lower_span(local.span);
let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
let init = self.lower_expr(init);
let local_hir_id = self.lower_node_id(local.id);
self.lower_attrs(local_hir_id, &local.attrs);
let let_expr = {
let lex = self.arena.alloc(hir::Let {
hir_id: local_hir_id,
pat: self.lower_pat(&local.pat),
ty,
init,
span,
});
self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new()))
};
fn lower_let_else(&mut self, let_else: &LetElse, tail: &[Stmt]) -> &'hir hir::Expr<'hir> {
let span = self.mark_span_with_reason(DesugaringKind::LetElse, let_else.span, None);
let local_hir_id = self.lower_node_id(let_else.id);
self.lower_attrs(local_hir_id, &let_else.attrs);
let cond = self.lower_expr(&let_else.expr);
let then_expr = {
let (stmts, expr) = self.lower_stmts(tail);
let block = self.block_all(span, stmts, expr);
self.arena.alloc(self.expr_block(block, AttrVec::new()))
};
let else_expr = {
let block = self.lower_block(els, false);
let block = self.lower_block(&let_else.r#else, false);
self.arena.alloc(self.expr_block(block, AttrVec::new()))
};
self.alias_attrs(let_expr.hir_id, local_hir_id);
self.alias_attrs(cond.hir_id, local_hir_id);
self.alias_attrs(else_expr.hir_id, local_hir_id);
let if_expr = self.arena.alloc(hir::Expr {
hir_id: stmt_hir_id,
hir_id: local_hir_id,
span,
kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
kind: hir::ExprKind::If(cond, then_expr, Some(else_expr)),
});
if !self.sess.features_untracked().let_else {
feature_err(
&self.sess.parse_sess,
sym::let_else,
local.span,
let_else.span,
"`let...else` statements are unstable",
)
.emit();
Expand Down
25 changes: 13 additions & 12 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,26 +1073,27 @@ impl<'a> State<'a> {
crate fn print_stmt(&mut self, st: &ast::Stmt) {
self.maybe_print_comment(st.span.lo());
match st.kind {
ast::StmtKind::LetElse(ref let_else) => {
self.print_outer_attributes(&let_else.attrs);
self.space_if_not_bol();
self.ibox(INDENT_UNIT);
self.word_nbsp("let");
self.print_expr(&let_else.expr);
self.cbox(INDENT_UNIT);
self.ibox(INDENT_UNIT);
self.word(" else ");
self.print_block(&let_else.r#else);
self.word(";");
self.end(); // `let` ibox
}
ast::StmtKind::Local(ref loc) => {
self.print_outer_attributes(&loc.attrs);
self.space_if_not_bol();
self.ibox(INDENT_UNIT);
self.word_nbsp("let");

self.ibox(INDENT_UNIT);
self.print_local_decl(loc);
self.end();
if let Some((init, els)) = loc.kind.init_else_opt() {
self.nbsp();
self.word_space("=");
self.print_expr(init);
if let Some(els) = els {
self.cbox(INDENT_UNIT);
self.ibox(INDENT_UNIT);
self.word(" else ");
self.print_block(els);
}
}
self.word(";");
self.end(); // `let` ibox
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,7 @@ impl InvocationCollectorNode for ast::Stmt {
StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)),
StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)),
StmtKind::Expr(..) => unreachable!(),
StmtKind::Local(..) | StmtKind::Empty => false,
StmtKind::LetElse(..) | StmtKind::Local(..) | StmtKind::Empty => false,
}
}
fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
impl EarlyLintPass for UnusedDocComment {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
let kind = match stmt.kind {
ast::StmtKind::Local(..) => "statements",
ast::StmtKind::LetElse(..) | ast::StmtKind::Local(..) => "statements",
// Disabled pending discussion in #78306
ast::StmtKind::Item(..) => return,
// expressions will be reported by `check_expr`.
Expand Down
7 changes: 2 additions & 5 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,11 +623,8 @@ trait UnusedDelimLint {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
match s.kind {
StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
if let Some((init, els)) = local.kind.init_else_opt() {
let ctx = match els {
None => UnusedDelimsCtx::AssignedValue,
Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
};
if let Some(init) = local.kind.init() {
let ctx = UnusedDelimsCtx::AssignedValue;
self.check_unused_delims_expr(cx, init, ctx, false, None, None);
}
}
Expand Down
Loading