1
Fork 0

lower let-else in MIR instead

This commit is contained in:
Ding Xiang Fei 2022-06-02 22:39:47 +08:00
parent 38b72154de
commit 6c529ded86
No known key found for this signature in database
GPG key ID: 3CD748647EEF6359
71 changed files with 421 additions and 264 deletions

View file

@ -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 });
@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|this| {
let scope = (*init_scope, source_info);
this.in_scope(scope, *lint_level, |this| {
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| {

View file

@ -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);
@ -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
@ -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()
}
}

View file

@ -48,7 +48,7 @@ impl<'tcx> Cx<'tcx> {
.filter_map(|(index, stmt)| {
let hir_id = stmt.hir_id;
let opt_dxn_ext = self.region_scope_tree.opt_destruction_scope(hir_id.local_id);
match stmt.kind {
match &stmt.kind {
hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
let stmt = Stmt {
kind: StmtKind::Expr {
@ -66,7 +66,7 @@ impl<'tcx> Cx<'tcx> {
// ignore for purposes of the MIR
None
}
hir::StmtKind::Local(ref local) => {
hir::StmtKind::Local(local, els) => {
let remainder_scope = region::Scope {
id: block_id,
data: region::ScopeData::Remainder(region::FirstStatementIndex::new(
@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> {
)),
};
let else_block = els.map(|els| self.mirror_block(els));
let mut pattern = self.pattern_from_hir(local.pat);
debug!(?pattern);
@ -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,

View file

@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{
};
use rustc_session::Session;
use rustc_span::source_map::Spanned;
use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span};
use rustc_span::{BytePos, Span};
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
let body_id = match def_id.as_local() {
@ -75,8 +75,11 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
}
}
fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
intravisit::walk_local(self, loc);
fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) {
intravisit::walk_local(self, loc, els);
if let Some(init) = &loc.init && els.is_some() {
self.check_let(&loc.pat, &init, loc.span);
}
let (msg, sp) = match loc.source {
hir::LocalSource::Normal => ("local binding", Some(loc.span)),
@ -84,7 +87,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
};
self.check_irrefutable(&loc.pat, msg, sp);
if els.is_none() {
self.check_irrefutable(&loc.pat, msg, sp);
}
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
@ -1125,17 +1130,16 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
}) if Some(*hir_id) == pat_id => {
return LetSource::IfLetGuard;
}
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => {
let expn_data = span.ctxt().outer_expn_data();
if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind {
return LetSource::LetElse(expn_data.call_site);
}
}
_ => {}
}
let parent_parent = hir.get_parent_node(parent);
let parent_parent_node = hir.get(parent_parent);
if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_, Some(_)), span, .. }) =
parent_parent_node
{
return LetSource::LetElse(*span);
}
let parent_parent_parent = hir.get_parent_node(parent_parent);
let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);