Skip to content

Commit 95a4016

Browse files
committed
Auto merge of #45384 - mikhail-m1:mir_add_false_edges_terminator_kind, r=arielb1
add TerminatorKind::FalseEdges and use it in matches impl #45184 and fixes #45043 right way. False edges unexpectedly affects uninitialized variables analysis in MIR borrowck.
2 parents 2278506 + 7d87054 commit 95a4016

File tree

21 files changed

+447
-48
lines changed

21 files changed

+447
-48
lines changed

src/librustc/ich/impls_mir.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ for mir::Terminator<'gcx> {
6262
mir::TerminatorKind::Drop { .. } |
6363
mir::TerminatorKind::DropAndReplace { .. } |
6464
mir::TerminatorKind::Yield { .. } |
65-
mir::TerminatorKind::Call { .. } => false,
65+
mir::TerminatorKind::Call { .. } |
66+
mir::TerminatorKind::FalseEdges { .. } => false,
6667
};
6768

6869
if hash_spans_unconditionally {
@@ -210,6 +211,12 @@ for mir::TerminatorKind<'gcx> {
210211
target.hash_stable(hcx, hasher);
211212
cleanup.hash_stable(hcx, hasher);
212213
}
214+
mir::TerminatorKind::FalseEdges { ref real_target, ref imaginary_targets } => {
215+
real_target.hash_stable(hcx, hasher);
216+
for target in imaginary_targets {
217+
target.hash_stable(hcx, hasher);
218+
}
219+
}
213220
}
214221
}
215222
}

src/librustc/mir/mod.rs

+28-4
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,11 @@ pub enum TerminatorKind<'tcx> {
682682

683683
/// Indicates the end of the dropping of a generator
684684
GeneratorDrop,
685+
686+
FalseEdges {
687+
real_target: BasicBlock,
688+
imaginary_targets: Vec<BasicBlock>
689+
},
685690
}
686691

687692
impl<'tcx> Terminator<'tcx> {
@@ -731,6 +736,11 @@ impl<'tcx> TerminatorKind<'tcx> {
731736
}
732737
Assert { target, cleanup: Some(unwind), .. } => vec![target, unwind].into_cow(),
733738
Assert { ref target, .. } => slice::ref_slice(target).into_cow(),
739+
FalseEdges { ref real_target, ref imaginary_targets } => {
740+
let mut s = vec![*real_target];
741+
s.extend_from_slice(imaginary_targets);
742+
s.into_cow()
743+
}
734744
}
735745
}
736746

@@ -757,7 +767,12 @@ impl<'tcx> TerminatorKind<'tcx> {
757767
vec![target]
758768
}
759769
Assert { ref mut target, cleanup: Some(ref mut unwind), .. } => vec![target, unwind],
760-
Assert { ref mut target, .. } => vec![target]
770+
Assert { ref mut target, .. } => vec![target],
771+
FalseEdges { ref mut real_target, ref mut imaginary_targets } => {
772+
let mut s = vec![real_target];
773+
s.extend(imaginary_targets.iter_mut());
774+
s
775+
}
761776
}
762777
}
763778
}
@@ -874,7 +889,8 @@ impl<'tcx> TerminatorKind<'tcx> {
874889
}
875890

876891
write!(fmt, ")")
877-
}
892+
},
893+
FalseEdges { .. } => write!(fmt, "falseEdges")
878894
}
879895
}
880896

@@ -910,7 +926,12 @@ impl<'tcx> TerminatorKind<'tcx> {
910926
}
911927
Assert { cleanup: None, .. } => vec!["".into()],
912928
Assert { .. } =>
913-
vec!["success".into_cow(), "unwind".into_cow()]
929+
vec!["success".into_cow(), "unwind".into_cow()],
930+
FalseEdges { ref imaginary_targets, .. } => {
931+
let mut l = vec!["real".into()];
932+
l.resize(imaginary_targets.len() + 1, "imaginary".into());
933+
l
934+
}
914935
}
915936
}
916937
}
@@ -1878,6 +1899,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
18781899
Resume => Resume,
18791900
Return => Return,
18801901
Unreachable => Unreachable,
1902+
FalseEdges { real_target, ref imaginary_targets } =>
1903+
FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() }
18811904
};
18821905
Terminator {
18831906
source_info: self.source_info,
@@ -1917,7 +1940,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
19171940
Resume |
19181941
Return |
19191942
GeneratorDrop |
1920-
Unreachable => false
1943+
Unreachable |
1944+
FalseEdges { .. } => false
19211945
}
19221946
}
19231947
}

src/librustc/mir/visit.rs

+7
Original file line numberDiff line numberDiff line change
@@ -486,8 +486,15 @@ macro_rules! make_mir_visitor {
486486
self.visit_operand(value, source_location);
487487
self.visit_branch(block, resume);
488488
drop.map(|t| self.visit_branch(block, t));
489+
489490
}
490491

492+
TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => {
493+
self.visit_branch(block, real_target);
494+
for target in imaginary_targets {
495+
self.visit_branch(block, *target);
496+
}
497+
}
491498
}
492499
}
493500

src/librustc_mir/borrow_check.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
364364
TerminatorKind::Resume |
365365
TerminatorKind::Return |
366366
TerminatorKind::GeneratorDrop |
367-
TerminatorKind::Unreachable => {
367+
TerminatorKind::Unreachable |
368+
TerminatorKind::FalseEdges { .. } => {
368369
// no data used, thus irrelevant to borrowck
369370
}
370371
}

src/librustc_mir/build/matches/mod.rs

+83-35
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc::mir::*;
2121
use rustc::hir;
2222
use hair::*;
2323
use syntax::ast::{Name, NodeId};
24-
use syntax_pos::{DUMMY_SP, Span};
24+
use syntax_pos::Span;
2525

2626
// helper functions, broken out by category:
2727
mod simplify;
@@ -54,29 +54,43 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5454
(body, scope.unwrap_or(self.visibility_scope))
5555
}).collect();
5656

57+
// create binding start block for link them by false edges
58+
let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len());
59+
let pre_binding_blocks: Vec<_> = (0..candidate_count + 1)
60+
.map(|_| self.cfg.start_new_block()).collect();
61+
5762
// assemble a list of candidates: there is one candidate per
5863
// pattern, which means there may be more than one candidate
5964
// *per arm*. These candidates are kept sorted such that the
6065
// highest priority candidate comes first in the list.
6166
// (i.e. same order as in source)
67+
6268
let candidates: Vec<_> =
6369
arms.iter()
6470
.enumerate()
6571
.flat_map(|(arm_index, arm)| {
6672
arm.patterns.iter()
6773
.map(move |pat| (arm_index, pat, arm.guard.clone()))
6874
})
69-
.map(|(arm_index, pattern, guard)| {
75+
.zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1)))
76+
.map(|((arm_index, pattern, guard),
77+
(pre_binding_block, next_candidate_pre_binding_block))| {
7078
Candidate {
7179
span: pattern.span,
7280
match_pairs: vec![MatchPair::new(discriminant_lvalue.clone(), pattern)],
7381
bindings: vec![],
7482
guard,
7583
arm_index,
84+
pre_binding_block: *pre_binding_block,
85+
next_candidate_pre_binding_block: *next_candidate_pre_binding_block,
7686
}
7787
})
7888
.collect();
7989

90+
let outer_source_info = self.source_info(span);
91+
self.cfg.terminate(*pre_binding_blocks.last().unwrap(),
92+
outer_source_info, TerminatorKind::Unreachable);
93+
8094
// this will generate code to test discriminant_lvalue and
8195
// branch to the appropriate arm block
8296
let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
@@ -148,7 +162,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
148162
match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)],
149163
bindings: vec![],
150164
guard: None,
151-
arm_index: 0, // since we don't call `match_candidates`, this field is unused
165+
166+
// since we don't call `match_candidates`, next fields is unused
167+
arm_index: 0,
168+
pre_binding_block: block,
169+
next_candidate_pre_binding_block: block
152170
};
153171

154172
// Simplify the candidate. Since the pattern is irrefutable, this should
@@ -278,6 +296,10 @@ pub struct Candidate<'pat, 'tcx:'pat> {
278296

279297
// ...and then we branch to arm with this index.
280298
arm_index: usize,
299+
300+
// ...and the blocks for add false edges between candidates
301+
pre_binding_block: BasicBlock,
302+
next_candidate_pre_binding_block: BasicBlock,
281303
}
282304

283305
#[derive(Clone, Debug)]
@@ -398,17 +420,43 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
398420
candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
399421
debug!("match_candidates: {:?} candidates fully matched", fully_matched);
400422
let mut unmatched_candidates = candidates.split_off(fully_matched);
401-
for (index, candidate) in candidates.into_iter().enumerate() {
423+
424+
let fully_matched_with_guard =
425+
candidates.iter().take_while(|c| c.guard.is_some()).count();
426+
427+
let unreachable_candidates = if fully_matched_with_guard + 1 < candidates.len() {
428+
candidates.split_off(fully_matched_with_guard + 1)
429+
} else {
430+
vec![]
431+
};
432+
433+
for candidate in candidates {
402434
// If so, apply any bindings, test the guard (if any), and
403435
// branch to the arm.
404-
let is_last = index == fully_matched - 1;
405-
if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks,
406-
candidate, is_last) {
436+
if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks, candidate) {
407437
block = b;
408438
} else {
409439
// if None is returned, then any remaining candidates
410440
// are unreachable (at least not through this path).
411-
return vec![];
441+
// Link them with false edges.
442+
debug!("match_candidates: add false edges for unreachable {:?} and unmatched {:?}",
443+
unreachable_candidates, unmatched_candidates);
444+
for candidate in unreachable_candidates {
445+
let source_info = self.source_info(candidate.span);
446+
let target = self.cfg.start_new_block();
447+
if let Some(otherwise) = self.bind_and_guard_matched_candidate(target,
448+
arm_blocks,
449+
candidate) {
450+
self.cfg.terminate(otherwise, source_info, TerminatorKind::Unreachable);
451+
}
452+
}
453+
454+
if unmatched_candidates.is_empty() {
455+
return vec![]
456+
} else {
457+
let target = self.cfg.start_new_block();
458+
return self.match_candidates(span, arm_blocks, unmatched_candidates, target);
459+
}
412460
}
413461
}
414462

@@ -423,9 +471,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
423471
self.test_candidates(span, arm_blocks, &unmatched_candidates, block);
424472

425473
// If the target candidates were exhaustive, then we are done.
426-
if otherwise.is_empty() {
427-
return vec![];
428-
}
474+
// But for borrowck continue build decision tree.
429475

430476
// If all candidates were sorted into `target_candidates` somewhere, then
431477
// the initial set was inexhaustive.
@@ -666,48 +712,50 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
666712
fn bind_and_guard_matched_candidate<'pat>(&mut self,
667713
mut block: BasicBlock,
668714
arm_blocks: &mut ArmBlocks,
669-
candidate: Candidate<'pat, 'tcx>,
670-
is_last_arm: bool)
715+
candidate: Candidate<'pat, 'tcx>)
671716
-> Option<BasicBlock> {
672717
debug!("bind_and_guard_matched_candidate(block={:?}, candidate={:?})",
673718
block, candidate);
674719

675720
debug_assert!(candidate.match_pairs.is_empty());
676721

677-
self.bind_matched_candidate(block, candidate.bindings);
678-
679722
let arm_block = arm_blocks.blocks[candidate.arm_index];
723+
let candidate_source_info = self.source_info(candidate.span);
724+
725+
self.cfg.terminate(block, candidate_source_info,
726+
TerminatorKind::Goto { target: candidate.pre_binding_block });
727+
728+
block = self.cfg.start_new_block();
729+
self.cfg.terminate(candidate.pre_binding_block, candidate_source_info,
730+
TerminatorKind::FalseEdges {
731+
real_target: block,
732+
imaginary_targets:
733+
vec![candidate.next_candidate_pre_binding_block]});
734+
735+
self.bind_matched_candidate(block, candidate.bindings);
680736

681737
if let Some(guard) = candidate.guard {
682738
// the block to branch to if the guard fails; if there is no
683739
// guard, this block is simply unreachable
684740
let guard = self.hir.mirror(guard);
685741
let source_info = self.source_info(guard.span);
686742
let cond = unpack!(block = self.as_local_operand(block, guard));
687-
let otherwise = self.cfg.start_new_block();
743+
744+
let false_edge_block = self.cfg.start_new_block();
688745
self.cfg.terminate(block, source_info,
689-
TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));
690-
Some(otherwise)
691-
} else if !is_last_arm {
692-
// Add always true guard in case of more than one arm
693-
// it creates false edges and allow MIR borrowck detects errors
694-
// FIXME(#45184) -- permit "false edges"
695-
let source_info = self.source_info(candidate.span);
696-
let true_expr = Expr {
697-
temp_lifetime: None,
698-
ty: self.hir.tcx().types.bool,
699-
span: DUMMY_SP,
700-
kind: ExprKind::Literal{literal: self.hir.true_literal()},
701-
};
702-
let cond = unpack!(block = self.as_local_operand(block, true_expr));
746+
TerminatorKind::if_(self.hir.tcx(), cond, arm_block,
747+
false_edge_block));
748+
703749
let otherwise = self.cfg.start_new_block();
704-
self.cfg.terminate(block, source_info,
705-
TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));
750+
self.cfg.terminate(false_edge_block, source_info,
751+
TerminatorKind::FalseEdges {
752+
real_target: otherwise,
753+
imaginary_targets:
754+
vec![candidate.next_candidate_pre_binding_block] });
706755
Some(otherwise)
707756
} else {
708-
let source_info = self.source_info(candidate.span);
709-
self.cfg.terminate(block, source_info,
710-
TerminatorKind::Goto { target: arm_block });
757+
self.cfg.terminate(block, candidate_source_info,
758+
TerminatorKind::Goto { target: arm_block });
711759
None
712760
}
713761
}

src/librustc_mir/build/matches/test.rs

+4
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
598598
bindings: candidate.bindings.clone(),
599599
guard: candidate.guard.clone(),
600600
arm_index: candidate.arm_index,
601+
pre_binding_block: candidate.pre_binding_block,
602+
next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
601603
}
602604
}
603605

@@ -659,6 +661,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
659661
bindings: candidate.bindings.clone(),
660662
guard: candidate.guard.clone(),
661663
arm_index: candidate.arm_index,
664+
pre_binding_block: candidate.pre_binding_block,
665+
next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
662666
}
663667
}
664668

src/librustc_mir/dataflow/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,12 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
721721
self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
722722
}
723723
}
724+
mir::TerminatorKind::FalseEdges { ref real_target, ref imaginary_targets } => {
725+
self.propagate_bits_into_entry_set_for(in_out, changed, real_target);
726+
for target in imaginary_targets {
727+
self.propagate_bits_into_entry_set_for(in_out, changed, target);
728+
}
729+
}
724730
}
725731
}
726732

src/librustc_mir/dataflow/move_paths/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
305305
TerminatorKind::Goto { target: _ } |
306306
TerminatorKind::Resume |
307307
TerminatorKind::GeneratorDrop |
308+
TerminatorKind::FalseEdges { .. } |
308309
TerminatorKind::Unreachable => { }
309310

310311
TerminatorKind::Return => {

src/librustc_mir/transform/check_unsafety.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
7373
TerminatorKind::GeneratorDrop |
7474
TerminatorKind::Resume |
7575
TerminatorKind::Return |
76-
TerminatorKind::Unreachable => {
76+
TerminatorKind::Unreachable |
77+
TerminatorKind::FalseEdges { .. } => {
7778
// safe (at least as emitted during MIR construction)
7879
}
7980

0 commit comments

Comments
 (0)