Skip to content

Commit 7b150a1

Browse files
committed
Don't use fake wildcards when we can get the failure block directly
This commit too was obtained by repeatedly inlining and simplifying.
1 parent c0c6c32 commit 7b150a1

File tree

3 files changed

+125
-143
lines changed

3 files changed

+125
-143
lines changed

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

+59-50
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
329329
&scrutinee_place,
330330
match_start_span,
331331
&mut candidates,
332+
false,
332333
);
333334

334335
self.lower_match_arms(
@@ -691,6 +692,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
691692
&initializer,
692693
irrefutable_pat.span,
693694
&mut [&mut candidate],
695+
false,
694696
);
695697
self.bind_pattern(
696698
self.source_info(irrefutable_pat.span),
@@ -1215,52 +1217,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12151217
///
12161218
/// Modifies `candidates` to store the bindings and type ascriptions for
12171219
/// that candidate.
1220+
///
1221+
/// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
1222+
/// or not (for `let` and `match`). In the refutable case we return the block to which we branch
1223+
/// on failure.
12181224
fn lower_match_tree<'pat>(
12191225
&mut self,
12201226
block: BasicBlock,
12211227
scrutinee_span: Span,
12221228
scrutinee_place_builder: &PlaceBuilder<'tcx>,
12231229
match_start_span: Span,
12241230
candidates: &mut [&mut Candidate<'pat, 'tcx>],
1225-
) {
1226-
// See the doc comment on `match_candidates` for why we have an
1227-
// otherwise block. Match checking will ensure this is actually
1228-
// unreachable.
1231+
refutable: bool,
1232+
) -> BasicBlock {
1233+
// See the doc comment on `match_candidates` for why we have an otherwise block.
12291234
let otherwise_block = self.cfg.start_new_block();
12301235

12311236
// This will generate code to test scrutinee_place and branch to the appropriate arm block
12321237
self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates);
12331238

1234-
let source_info = self.source_info(scrutinee_span);
1235-
1236-
// Matching on a `scrutinee_place` with an uninhabited type doesn't
1237-
// generate any memory reads by itself, and so if the place "expression"
1238-
// contains unsafe operations like raw pointer dereferences or union
1239-
// field projections, we wouldn't know to require an `unsafe` block
1240-
// around a `match` equivalent to `std::intrinsics::unreachable()`.
1241-
// See issue #47412 for this hole being discovered in the wild.
1242-
//
1243-
// HACK(eddyb) Work around the above issue by adding a dummy inspection
1244-
// of `scrutinee_place`, specifically by applying `ReadForMatch`.
1245-
//
1246-
// NOTE: ReadForMatch also checks that the scrutinee is initialized.
1247-
// This is currently needed to not allow matching on an uninitialized,
1248-
// uninhabited value. If we get never patterns, those will check that
1249-
// the place is initialized, and so this read would only be used to
1250-
// check safety.
1251-
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
1252-
1253-
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
1254-
self.cfg.push_fake_read(
1255-
otherwise_block,
1256-
source_info,
1257-
cause_matched_place,
1258-
scrutinee_place,
1259-
);
1260-
}
1261-
1262-
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
1263-
12641239
// Link each leaf candidate to the `false_edge_start_block` of the next one.
12651240
let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
12661241
for candidate in candidates {
@@ -1272,6 +1247,46 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12721247
previous_candidate = Some(leaf_candidate);
12731248
});
12741249
}
1250+
1251+
if refutable {
1252+
// In refutable cases there's always at least one candidate, and we want a false edge to
1253+
// the failure block.
1254+
previous_candidate.as_mut().unwrap().next_candidate_start_block = Some(otherwise_block)
1255+
} else {
1256+
// Match checking ensures `otherwise_block` is actually unreachable in irrefutable
1257+
// cases.
1258+
let source_info = self.source_info(scrutinee_span);
1259+
1260+
// Matching on a `scrutinee_place` with an uninhabited type doesn't
1261+
// generate any memory reads by itself, and so if the place "expression"
1262+
// contains unsafe operations like raw pointer dereferences or union
1263+
// field projections, we wouldn't know to require an `unsafe` block
1264+
// around a `match` equivalent to `std::intrinsics::unreachable()`.
1265+
// See issue #47412 for this hole being discovered in the wild.
1266+
//
1267+
// HACK(eddyb) Work around the above issue by adding a dummy inspection
1268+
// of `scrutinee_place`, specifically by applying `ReadForMatch`.
1269+
//
1270+
// NOTE: ReadForMatch also checks that the scrutinee is initialized.
1271+
// This is currently needed to not allow matching on an uninitialized,
1272+
// uninhabited value. If we get never patterns, those will check that
1273+
// the place is initialized, and so this read would only be used to
1274+
// check safety.
1275+
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
1276+
1277+
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
1278+
self.cfg.push_fake_read(
1279+
otherwise_block,
1280+
source_info,
1281+
cause_matched_place,
1282+
scrutinee_place,
1283+
);
1284+
}
1285+
1286+
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
1287+
}
1288+
1289+
otherwise_block
12751290
}
12761291

12771292
/// The main match algorithm. It begins with a set of candidates
@@ -1978,21 +1993,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
19781993
) -> BlockAnd<()> {
19791994
let expr_span = self.thir[expr_id].span;
19801995
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
1981-
let wildcard = Pat::wildcard_from_ty(pat.ty);
19821996
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), pat, false, self);
1983-
let mut otherwise_candidate =
1984-
Candidate::new(expr_place_builder.clone(), &wildcard, false, self);
1985-
self.lower_match_tree(
1997+
let otherwise_block = self.lower_match_tree(
19861998
block,
19871999
pat.span,
19882000
&expr_place_builder,
19892001
pat.span,
1990-
&mut [&mut guard_candidate, &mut otherwise_candidate],
2002+
&mut [&mut guard_candidate],
2003+
true,
19912004
);
19922005
let expr_place = expr_place_builder.try_to_place(self);
19932006
let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
1994-
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
1995-
self.break_for_else(otherwise_post_guard_block, self.source_info(expr_span));
2007+
self.break_for_else(otherwise_block, self.source_info(expr_span));
19962008

19972009
if declare_bindings {
19982010
self.declare_bindings(source_scope, pat.span.to(span), pat, None, opt_expr_place);
@@ -2008,7 +2020,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
20082020
);
20092021

20102022
// If branch coverage is enabled, record this branch.
2011-
self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_post_guard_block);
2023+
self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_block);
20122024

20132025
post_guard_block.unit()
20142026
}
@@ -2470,15 +2482,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24702482
let else_block_span = self.thir[else_block].span;
24712483
let (matching, failure) = self.in_if_then_scope(*let_else_scope, else_block_span, |this| {
24722484
let scrutinee = unpack!(block = this.lower_scrutinee(block, init_id, initializer_span));
2473-
let pat = Pat { ty: pattern.ty, span: else_block_span, kind: PatKind::Wild };
2474-
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false, this);
24752485
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false, this);
2476-
this.lower_match_tree(
2486+
let failure_block = this.lower_match_tree(
24772487
block,
24782488
initializer_span,
24792489
&scrutinee,
24802490
pattern.span,
2481-
&mut [&mut candidate, &mut wildcard],
2491+
&mut [&mut candidate],
2492+
true,
24822493
);
24832494
// This block is for the matching case
24842495
let matching = this.bind_pattern(
@@ -2489,13 +2500,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24892500
None,
24902501
true,
24912502
);
2492-
// This block is for the failure case
2493-
let failure = wildcard.pre_binding_block.unwrap();
24942503

24952504
// If branch coverage is enabled, record this branch.
2496-
this.visit_coverage_conditional_let(pattern, matching, failure);
2505+
this.visit_coverage_conditional_let(pattern, matching, failure_block);
24972506

2498-
this.break_for_else(failure, this.source_info(initializer_span));
2507+
this.break_for_else(failure_block, this.source_info(initializer_span));
24992508
matching.unit()
25002509
});
25012510
matching.and(failure)

tests/mir-opt/building/issue_101867.main.built.after.mir

+6-15
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ fn main() -> () {
2727
StorageLive(_5);
2828
PlaceMention(_1);
2929
_6 = discriminant(_1);
30-
switchInt(move _6) -> [1: bb6, otherwise: bb4];
30+
switchInt(move _6) -> [1: bb4, otherwise: bb3];
3131
}
3232

3333
bb1: {
3434
StorageLive(_3);
3535
StorageLive(_4);
36-
_4 = begin_panic::<&str>(const "explicit panic") -> bb10;
36+
_4 = begin_panic::<&str>(const "explicit panic") -> bb8;
3737
}
3838

3939
bb2: {
@@ -43,40 +43,31 @@ fn main() -> () {
4343
}
4444

4545
bb3: {
46-
FakeRead(ForMatchedPlace(None), _1);
47-
unreachable;
46+
goto -> bb7;
4847
}
4948

5049
bb4: {
51-
goto -> bb9;
50+
falseEdge -> [real: bb6, imaginary: bb3];
5251
}
5352

5453
bb5: {
5554
goto -> bb3;
5655
}
5756

5857
bb6: {
59-
falseEdge -> [real: bb8, imaginary: bb4];
60-
}
61-
62-
bb7: {
63-
goto -> bb4;
64-
}
65-
66-
bb8: {
6758
_5 = ((_1 as Some).0: u8);
6859
_0 = const ();
6960
StorageDead(_5);
7061
StorageDead(_1);
7162
return;
7263
}
7364

74-
bb9: {
65+
bb7: {
7566
StorageDead(_5);
7667
goto -> bb1;
7768
}
7869

79-
bb10 (cleanup): {
70+
bb8 (cleanup): {
8071
resume;
8172
}
8273
}

0 commit comments

Comments
 (0)