Skip to content

Commit dd46b1b

Browse files
authored
Rollup merge of rust-lang#98574 - dingxiangfei2009:let-else-thir, r=oli-obk
Lower let-else in MIR This MR will switch to lower let-else statements in MIR building instead. To lower let-else in MIR, we build a mini-switch two branches. One branch leads to the matching case, and the other leads to the `else` block. This arrangement will allow temporary lifetime analysis running as-is so that the temporaries are properly extended according to the same rule applied to regular `let` statements. cc rust-lang#87335 Fix rust-lang#98672
2 parents 95e8b86 + 947cbda commit dd46b1b

File tree

33 files changed

+576
-265
lines changed

33 files changed

+576
-265
lines changed
+22-73
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
2-
use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
2+
use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind};
33
use rustc_hir as hir;
44
use rustc_session::parse::feature_err;
5-
use rustc_span::{sym, DesugaringKind};
5+
use rustc_span::sym;
66

77
use smallvec::SmallVec;
88

@@ -36,21 +36,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
3636
match s.kind {
3737
StmtKind::Local(ref local) => {
3838
let hir_id = self.lower_node_id(s.id);
39-
match &local.kind {
40-
LocalKind::InitElse(init, els) => {
41-
let e = self.lower_let_else(hir_id, local, init, els, tail);
42-
expr = Some(e);
43-
// remaining statements are in let-else expression
44-
break;
45-
}
46-
_ => {
47-
let local = self.lower_local(local);
48-
self.alias_attrs(hir_id, local.hir_id);
49-
let kind = hir::StmtKind::Local(local);
50-
let span = self.lower_span(s.span);
51-
stmts.push(hir::Stmt { hir_id, kind, span });
52-
}
53-
}
39+
let local = self.lower_local(local);
40+
self.alias_attrs(hir_id, local.hir_id);
41+
let kind = hir::StmtKind::Local(local);
42+
let span = self.lower_span(s.span);
43+
stmts.push(hir::Stmt { hir_id, kind, span });
5444
}
5545
StmtKind::Item(ref it) => {
5646
stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map(
@@ -101,10 +91,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
10191
let init = l.kind.init().map(|init| self.lower_expr(init));
10292
let hir_id = self.lower_node_id(l.id);
10393
let pat = self.lower_pat(&l.pat);
94+
let els = if let LocalKind::InitElse(_, els) = &l.kind {
95+
if !self.tcx.features().let_else {
96+
feature_err(
97+
&self.tcx.sess.parse_sess,
98+
sym::let_else,
99+
l.span,
100+
"`let...else` statements are unstable",
101+
)
102+
.emit();
103+
}
104+
Some(self.lower_block(els, false))
105+
} else {
106+
None
107+
};
104108
let span = self.lower_span(l.span);
105109
let source = hir::LocalSource::Normal;
106110
self.lower_attrs(hir_id, &l.attrs);
107-
self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source })
111+
self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source })
108112
}
109113

110114
fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
@@ -115,59 +119,4 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
115119
}
116120
}
117121
}
118-
119-
fn lower_let_else(
120-
&mut self,
121-
stmt_hir_id: hir::HirId,
122-
local: &Local,
123-
init: &Expr,
124-
els: &Block,
125-
tail: &[Stmt],
126-
) -> &'hir hir::Expr<'hir> {
127-
let ty = local
128-
.ty
129-
.as_ref()
130-
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable)));
131-
let span = self.lower_span(local.span);
132-
let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
133-
let init = self.lower_expr(init);
134-
let local_hir_id = self.lower_node_id(local.id);
135-
self.lower_attrs(local_hir_id, &local.attrs);
136-
let let_expr = {
137-
let lex = self.arena.alloc(hir::Let {
138-
hir_id: local_hir_id,
139-
pat: self.lower_pat(&local.pat),
140-
ty,
141-
init,
142-
span,
143-
});
144-
self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new()))
145-
};
146-
let then_expr = {
147-
let (stmts, expr) = self.lower_stmts(tail);
148-
let block = self.block_all(span, stmts, expr);
149-
self.arena.alloc(self.expr_block(block, AttrVec::new()))
150-
};
151-
let else_expr = {
152-
let block = self.lower_block(els, false);
153-
self.arena.alloc(self.expr_block(block, AttrVec::new()))
154-
};
155-
self.alias_attrs(let_expr.hir_id, local_hir_id);
156-
self.alias_attrs(else_expr.hir_id, local_hir_id);
157-
let if_expr = self.arena.alloc(hir::Expr {
158-
hir_id: stmt_hir_id,
159-
span,
160-
kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
161-
});
162-
if !self.tcx.features().let_else {
163-
feature_err(
164-
&self.tcx.sess.parse_sess,
165-
sym::let_else,
166-
local.span,
167-
"`let...else` statements are unstable",
168-
)
169-
.emit();
170-
}
171-
if_expr
172-
}
173122
}

compiler/rustc_ast_lowering/src/lib.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -2146,7 +2146,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
21462146
debug_assert!(!a.is_empty());
21472147
self.attrs.insert(hir_id.local_id, a);
21482148
}
2149-
let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None };
2149+
let local = hir::Local {
2150+
hir_id,
2151+
init,
2152+
pat,
2153+
els: None,
2154+
source,
2155+
span: self.lower_span(span),
2156+
ty: None,
2157+
};
21502158
self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local)))
21512159
}
21522160

compiler/rustc_hir/src/hir.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,8 @@ pub struct Local<'hir> {
13161316
pub ty: Option<&'hir Ty<'hir>>,
13171317
/// Initializer expression to set the value, if any.
13181318
pub init: Option<&'hir Expr<'hir>>,
1319+
/// Else block for a `let...else` binding.
1320+
pub els: Option<&'hir Block<'hir>>,
13191321
pub hir_id: HirId,
13201322
pub span: Span,
13211323
/// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop

compiler/rustc_hir/src/intravisit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) {
472472
walk_list!(visitor, visit_expr, &local.init);
473473
visitor.visit_id(local.hir_id);
474474
visitor.visit_pat(&local.pat);
475+
if let Some(els) = local.els {
476+
visitor.visit_block(els);
477+
}
475478
walk_list!(visitor, visit_ty, &local.ty);
476479
}
477480

compiler/rustc_hir_pretty/src/lib.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,12 @@ impl<'a> State<'a> {
883883
self.ann.post(self, AnnNode::SubItem(ii.hir_id()))
884884
}
885885

886-
pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) {
886+
pub fn print_local(
887+
&mut self,
888+
init: Option<&hir::Expr<'_>>,
889+
els: Option<&hir::Block<'_>>,
890+
decl: impl Fn(&mut Self),
891+
) {
887892
self.space_if_not_bol();
888893
self.ibox(INDENT_UNIT);
889894
self.word_nbsp("let");
@@ -897,14 +902,21 @@ impl<'a> State<'a> {
897902
self.word_space("=");
898903
self.print_expr(init);
899904
}
905+
906+
if let Some(els) = els {
907+
self.nbsp();
908+
self.word_space("else");
909+
self.print_block(els);
910+
}
911+
900912
self.end()
901913
}
902914

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

14051417
// Print `let _t = $init;`:
14061418
let temp = Ident::from_str("_t");
1407-
self.print_local(Some(init), |this| this.print_ident(temp));
1419+
self.print_local(Some(init), None, |this| this.print_ident(temp));
14081420
self.word(";");
14091421

14101422
// Print `_t`:

compiler/rustc_middle/src/thir.rs

+3
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ pub enum StmtKind<'tcx> {
182182
/// `let pat: ty = <INIT>`
183183
initializer: Option<ExprId>,
184184

185+
/// `let pat: ty = <INIT> else { <ELSE> }
186+
else_block: Option<Block>,
187+
185188
/// The lint level for this `let` statement.
186189
lint_level: LintLevel,
187190
},

compiler/rustc_middle/src/thir/visit.rs

+4
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,15 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm
167167
init_scope: _,
168168
ref pattern,
169169
lint_level: _,
170+
else_block,
170171
} => {
171172
if let Some(init) = initializer {
172173
visitor.visit_expr(&visitor.thir()[*init]);
173174
}
174175
visitor.visit_pat(pattern);
176+
if let Some(block) = else_block {
177+
visitor.visit_block(block)
178+
}
175179
}
176180
}
177181
}

compiler/rustc_mir_build/src/build/block.rs

+23-10
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
9999
ref pattern,
100100
initializer,
101101
lint_level,
102+
else_block,
102103
} => {
103104
let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild);
104105
this.block_context.push(BlockFrame::Statement { ignores_expr_result });
@@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
124125
|this| {
125126
let scope = (*init_scope, source_info);
126127
this.in_scope(scope, *lint_level, |this| {
127-
this.declare_bindings(
128-
visibility_scope,
129-
remainder_span,
130-
pattern,
131-
ArmHasGuard(false),
132-
Some((None, initializer_span)),
133-
);
134-
this.expr_into_pattern(block, pattern.clone(), init)
128+
if let Some(else_block) = else_block {
129+
this.ast_let_else(
130+
block,
131+
init,
132+
initializer_span,
133+
else_block,
134+
visibility_scope,
135+
remainder_span,
136+
pattern,
137+
)
138+
} else {
139+
this.declare_bindings(
140+
visibility_scope,
141+
remainder_span,
142+
pattern,
143+
ArmHasGuard(false),
144+
Some((None, initializer_span)),
145+
);
146+
this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern
147+
}
135148
})
136-
}
149+
},
137150
)
138-
);
151+
)
139152
} else {
140153
let scope = (*init_scope, source_info);
141154
unpack!(this.in_scope(scope, *lint_level, |this| {

compiler/rustc_mir_build/src/build/matches/mod.rs

+74-3
Original file line numberDiff line numberDiff line change
@@ -1615,7 +1615,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16151615
// those N possible outcomes, create a (initially empty)
16161616
// vector of candidates. Those are the candidates that still
16171617
// apply if the test has that particular outcome.
1618-
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
1618+
debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair);
16191619
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
16201620
target_candidates.resize_with(test.targets(), Default::default);
16211621

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

16411641
// HACK(matthewjasper) This is a closure so that we can let the test
16421642
// create its blocks before the rest of the match. This currently
@@ -2274,4 +2274,75 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22742274
debug!("declare_binding: vars={:?}", locals);
22752275
self.var_indices.insert(var_id, locals);
22762276
}
2277+
2278+
pub(crate) fn ast_let_else(
2279+
&mut self,
2280+
mut block: BasicBlock,
2281+
init: &Expr<'tcx>,
2282+
initializer_span: Span,
2283+
else_block: &Block,
2284+
visibility_scope: Option<SourceScope>,
2285+
remainder_span: Span,
2286+
pattern: &Pat<'tcx>,
2287+
) -> BlockAnd<()> {
2288+
let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span));
2289+
let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
2290+
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
2291+
self.declare_bindings(
2292+
visibility_scope,
2293+
remainder_span,
2294+
pattern,
2295+
ArmHasGuard(false),
2296+
Some((None, initializer_span)),
2297+
);
2298+
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
2299+
let fake_borrow_temps = self.lower_match_tree(
2300+
block,
2301+
initializer_span,
2302+
pattern.span,
2303+
false,
2304+
&mut [&mut candidate, &mut wildcard],
2305+
);
2306+
// This block is for the matching case
2307+
let matching = self.bind_pattern(
2308+
self.source_info(pattern.span),
2309+
candidate,
2310+
None,
2311+
&fake_borrow_temps,
2312+
initializer_span,
2313+
None,
2314+
None,
2315+
None,
2316+
);
2317+
// This block is for the failure case
2318+
let failure = self.bind_pattern(
2319+
self.source_info(else_block.span),
2320+
wildcard,
2321+
None,
2322+
&fake_borrow_temps,
2323+
initializer_span,
2324+
None,
2325+
None,
2326+
None,
2327+
);
2328+
// This place is not really used because this destination place
2329+
// should never be used to take values at the end of the failure
2330+
// block.
2331+
let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() };
2332+
let failure_block;
2333+
unpack!(
2334+
failure_block = self.ast_block(
2335+
dummy_place,
2336+
failure,
2337+
else_block,
2338+
self.source_info(else_block.span),
2339+
)
2340+
);
2341+
self.cfg.terminate(
2342+
failure_block,
2343+
self.source_info(else_block.span),
2344+
TerminatorKind::Unreachable,
2345+
);
2346+
matching.unit()
2347+
}
22772348
}

compiler/rustc_mir_build/src/thir/cx/block.rs

+3
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> {
7474
)),
7575
};
7676

77+
let else_block = local.els.map(|els| self.mirror_block(els));
78+
7779
let mut pattern = self.pattern_from_hir(local.pat);
7880
debug!(?pattern);
7981

@@ -110,6 +112,7 @@ impl<'tcx> Cx<'tcx> {
110112
},
111113
pattern,
112114
initializer: local.init.map(|init| self.mirror_expr(init)),
115+
else_block,
113116
lint_level: LintLevel::Explicit(local.hir_id),
114117
},
115118
opt_destruction_scope: opt_dxn_ext,

0 commit comments

Comments
 (0)