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

Lower let-else in MIR #98574

Merged
merged 5 commits into from
Jul 13, 2022
Merged
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
95 changes: 22 additions & 73 deletions compiler/rustc_ast_lowering/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind};
use rustc_hir as hir;
use rustc_session::parse::feature_err;
use rustc_span::{sym, DesugaringKind};
use rustc_span::sym;

use smallvec::SmallVec;

Expand Down Expand Up @@ -36,21 +36,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
match s.kind {
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 @@ -101,10 +91,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let init = l.kind.init().map(|init| self.lower_expr(init));
let hir_id = self.lower_node_id(l.id);
let pat = self.lower_pat(&l.pat);
let els = if let LocalKind::InitElse(_, els) = &l.kind {
if !self.tcx.features().let_else {
feature_err(
&self.tcx.sess.parse_sess,
sym::let_else,
l.span,
"`let...else` statements are unstable",
)
.emit();
}
Some(self.lower_block(els, false))
} else {
None
};
let span = self.lower_span(l.span);
let source = hir::LocalSource::Normal;
self.lower_attrs(hir_id, &l.attrs);
self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source })
self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source })
}

fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
Expand All @@ -115,59 +119,4 @@ 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::Variable)));
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()))
};
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);
self.arena.alloc(self.expr_block(block, AttrVec::new()))
};
self.alias_attrs(let_expr.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,
span,
kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
});
if !self.tcx.features().let_else {
feature_err(
&self.tcx.sess.parse_sess,
sym::let_else,
local.span,
"`let...else` statements are unstable",
)
.emit();
}
if_expr
}
}
10 changes: 9 additions & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2146,7 +2146,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
debug_assert!(!a.is_empty());
self.attrs.insert(hir_id.local_id, a);
}
let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None };
let local = hir::Local {
hir_id,
init,
pat,
els: None,
source,
span: self.lower_span(span),
ty: None,
};
self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local)))
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,8 @@ pub struct Local<'hir> {
pub ty: Option<&'hir Ty<'hir>>,
/// Initializer expression to set the value, if any.
pub init: Option<&'hir Expr<'hir>>,
/// Else block for a `let...else` binding.
pub els: Option<&'hir Block<'hir>>,
pub hir_id: HirId,
pub span: Span,
/// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) {
walk_list!(visitor, visit_expr, &local.init);
visitor.visit_id(local.hir_id);
visitor.visit_pat(&local.pat);
if let Some(els) = local.els {
visitor.visit_block(els);
}
walk_list!(visitor, visit_ty, &local.ty);
}

Expand Down
18 changes: 15 additions & 3 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,12 @@ impl<'a> State<'a> {
self.ann.post(self, AnnNode::SubItem(ii.hir_id()))
}

pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) {
pub fn print_local(
&mut self,
init: Option<&hir::Expr<'_>>,
els: Option<&hir::Block<'_>>,
decl: impl Fn(&mut Self),
) {
self.space_if_not_bol();
self.ibox(INDENT_UNIT);
self.word_nbsp("let");
Expand All @@ -897,14 +902,21 @@ impl<'a> State<'a> {
self.word_space("=");
self.print_expr(init);
}

if let Some(els) = els {
self.nbsp();
self.word_space("else");
self.print_block(els);
}

self.end()
}

pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
self.maybe_print_comment(st.span.lo());
match st.kind {
hir::StmtKind::Local(loc) => {
self.print_local(loc.init, |this| this.print_local_decl(loc));
self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc));
}
hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)),
hir::StmtKind::Expr(expr) => {
Expand Down Expand Up @@ -1404,7 +1416,7 @@ impl<'a> State<'a> {

// Print `let _t = $init;`:
let temp = Ident::from_str("_t");
self.print_local(Some(init), |this| this.print_ident(temp));
self.print_local(Some(init), None, |this| this.print_ident(temp));
self.word(";");

// Print `_t`:
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ pub enum StmtKind<'tcx> {
/// `let pat: ty = <INIT>`
initializer: Option<ExprId>,

/// `let pat: ty = <INIT> else { <ELSE> }
else_block: Option<Block>,

/// The lint level for this `let` statement.
lint_level: LintLevel,
},
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/thir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,15 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm
init_scope: _,
ref pattern,
lint_level: _,
else_block,
} => {
if let Some(init) = initializer {
visitor.visit_expr(&visitor.thir()[*init]);
}
visitor.visit_pat(pattern);
if let Some(block) = else_block {
visitor.visit_block(block)
}
}
}
}
Expand Down
33 changes: 23 additions & 10 deletions compiler/rustc_mir_build/src/build/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ref pattern,
initializer,
lint_level,
else_block,
} => {
let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild);
this.block_context.push(BlockFrame::Statement { ignores_expr_result });
Expand All @@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|this| {
let scope = (*init_scope, source_info);
this.in_scope(scope, *lint_level, |this| {
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
this.declare_bindings(
visibility_scope,
remainder_span,
pattern,
ArmHasGuard(false),
Some((None, initializer_span)),
);
this.expr_into_pattern(block, pattern.clone(), init)
if let Some(else_block) = else_block {
this.ast_let_else(
block,
init,
initializer_span,
else_block,
visibility_scope,
remainder_span,
pattern,
)
} else {
this.declare_bindings(
visibility_scope,
remainder_span,
pattern,
ArmHasGuard(false),
Some((None, initializer_span)),
);
this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern
}
})
}
},
)
);
)
} else {
let scope = (*init_scope, source_info);
unpack!(this.in_scope(scope, *lint_level, |this| {
Expand Down
77 changes: 74 additions & 3 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1615,7 +1615,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// those N possible outcomes, create a (initially empty)
// vector of candidates. Those are the candidates that still
// apply if the test has that particular outcome.
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair);
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
target_candidates.resize_with(test.targets(), Default::default);

Expand All @@ -1635,8 +1635,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
// at least the first candidate ought to be tested
assert!(total_candidate_count > candidates.len());
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
debug!("untested_candidates: {}", candidates.len());
debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len());
debug!("test_candidates: untested_candidates: {}", candidates.len());

// HACK(matthewjasper) This is a closure so that we can let the test
// create its blocks before the rest of the match. This currently
Expand Down Expand Up @@ -2274,4 +2274,75 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
debug!("declare_binding: vars={:?}", locals);
self.var_indices.insert(var_id, locals);
}

pub(crate) fn ast_let_else(
&mut self,
mut block: BasicBlock,
init: &Expr<'tcx>,
initializer_span: Span,
else_block: &Block,
visibility_scope: Option<SourceScope>,
remainder_span: Span,
pattern: &Pat<'tcx>,
) -> BlockAnd<()> {
let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span));
let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
self.declare_bindings(
visibility_scope,
remainder_span,
pattern,
ArmHasGuard(false),
Some((None, initializer_span)),
);
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
let fake_borrow_temps = self.lower_match_tree(
block,
initializer_span,
pattern.span,
false,
&mut [&mut candidate, &mut wildcard],
);
// This block is for the matching case
let matching = self.bind_pattern(
self.source_info(pattern.span),
candidate,
None,
&fake_borrow_temps,
initializer_span,
None,
None,
None,
);
// This block is for the failure case
let failure = self.bind_pattern(
self.source_info(else_block.span),
wildcard,
None,
&fake_borrow_temps,
initializer_span,
None,
None,
None,
);
// This place is not really used because this destination place
// should never be used to take values at the end of the failure
// block.
let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() };
let failure_block;
unpack!(
failure_block = self.ast_block(
dummy_place,
failure,
else_block,
self.source_info(else_block.span),
)
);
self.cfg.terminate(
failure_block,
self.source_info(else_block.span),
TerminatorKind::Unreachable,
);
matching.unit()
}
}
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/thir/cx/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> {
)),
};

let else_block = local.els.map(|els| self.mirror_block(els));

let mut pattern = self.pattern_from_hir(local.pat);
debug!(?pattern);

Expand Down Expand Up @@ -110,6 +112,7 @@ impl<'tcx> Cx<'tcx> {
},
pattern,
initializer: local.init.map(|init| self.mirror_expr(init)),
else_block,
lint_level: LintLevel::Explicit(local.hir_id),
},
opt_destruction_scope: opt_dxn_ext,
Expand Down
Loading