Skip to content

Commit 762e81f

Browse files
NadrierilZalathar
authored andcommitted
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 7a09809 commit 762e81f

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

Lines changed: 59 additions & 50 deletions
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(
@@ -694,6 +695,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
694695
&initializer,
695696
irrefutable_pat.span,
696697
&mut [&mut candidate],
698+
false,
697699
);
698700
self.bind_pattern(
699701
self.source_info(irrefutable_pat.span),
@@ -1229,52 +1231,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12291231
///
12301232
/// Modifies `candidates` to store the bindings and type ascriptions for
12311233
/// that candidate.
1234+
///
1235+
/// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
1236+
/// or not (for `let` and `match`). In the refutable case we return the block to which we branch
1237+
/// on failure.
12321238
fn lower_match_tree<'pat>(
12331239
&mut self,
12341240
block: BasicBlock,
12351241
scrutinee_span: Span,
12361242
scrutinee_place_builder: &PlaceBuilder<'tcx>,
12371243
match_start_span: Span,
12381244
candidates: &mut [&mut Candidate<'pat, 'tcx>],
1239-
) {
1240-
// See the doc comment on `match_candidates` for why we have an
1241-
// otherwise block. Match checking will ensure this is actually
1242-
// unreachable.
1245+
refutable: bool,
1246+
) -> BasicBlock {
1247+
// See the doc comment on `match_candidates` for why we have an otherwise block.
12431248
let otherwise_block = self.cfg.start_new_block();
12441249

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

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

12911306
/// The main match algorithm. It begins with a set of candidates
@@ -1992,21 +2007,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
19922007
) -> BlockAnd<()> {
19932008
let expr_span = self.thir[expr_id].span;
19942009
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
1995-
let wildcard = Pat::wildcard_from_ty(pat.ty);
19962010
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), pat, false, self);
1997-
let mut otherwise_candidate =
1998-
Candidate::new(expr_place_builder.clone(), &wildcard, false, self);
1999-
self.lower_match_tree(
2011+
let otherwise_block = self.lower_match_tree(
20002012
block,
20012013
pat.span,
20022014
&expr_place_builder,
20032015
pat.span,
2004-
&mut [&mut guard_candidate, &mut otherwise_candidate],
2016+
&mut [&mut guard_candidate],
2017+
true,
20052018
);
20062019
let expr_place = expr_place_builder.try_to_place(self);
20072020
let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
2008-
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
2009-
self.break_for_else(otherwise_post_guard_block, self.source_info(expr_span));
2021+
self.break_for_else(otherwise_block, self.source_info(expr_span));
20102022

20112023
if declare_bindings {
20122024
self.declare_bindings(source_scope, pat.span.to(span), pat, None, opt_expr_place);
@@ -2022,7 +2034,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
20222034
);
20232035

20242036
// If branch coverage is enabled, record this branch.
2025-
self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_post_guard_block);
2037+
self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_block);
20262038

20272039
post_guard_block.unit()
20282040
}
@@ -2484,15 +2496,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24842496
let else_block_span = self.thir[else_block].span;
24852497
let (matching, failure) = self.in_if_then_scope(*let_else_scope, else_block_span, |this| {
24862498
let scrutinee = unpack!(block = this.lower_scrutinee(block, init_id, initializer_span));
2487-
let pat = Pat { ty: pattern.ty, span: else_block_span, kind: PatKind::Wild };
2488-
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false, this);
24892499
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false, this);
2490-
this.lower_match_tree(
2500+
let failure_block = this.lower_match_tree(
24912501
block,
24922502
initializer_span,
24932503
&scrutinee,
24942504
pattern.span,
2495-
&mut [&mut candidate, &mut wildcard],
2505+
&mut [&mut candidate],
2506+
true,
24962507
);
24972508
// This block is for the matching case
24982509
let matching = this.bind_pattern(
@@ -2503,13 +2514,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
25032514
None,
25042515
true,
25052516
);
2506-
// This block is for the failure case
2507-
let failure = wildcard.pre_binding_block.unwrap();
25082517

25092518
// If branch coverage is enabled, record this branch.
2510-
this.visit_coverage_conditional_let(pattern, matching, failure);
2519+
this.visit_coverage_conditional_let(pattern, matching, failure_block);
25112520

2512-
this.break_for_else(failure, this.source_info(initializer_span));
2521+
this.break_for_else(failure_block, this.source_info(initializer_span));
25132522
matching.unit()
25142523
});
25152524
matching.and(failure)

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

Lines changed: 6 additions & 15 deletions
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)