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.
This commit is contained in:
commit
95a401609f
@ -62,7 +62,8 @@ for mir::Terminator<'gcx> {
|
||||
mir::TerminatorKind::Drop { .. } |
|
||||
mir::TerminatorKind::DropAndReplace { .. } |
|
||||
mir::TerminatorKind::Yield { .. } |
|
||||
mir::TerminatorKind::Call { .. } => false,
|
||||
mir::TerminatorKind::Call { .. } |
|
||||
mir::TerminatorKind::FalseEdges { .. } => false,
|
||||
};
|
||||
|
||||
if hash_spans_unconditionally {
|
||||
@ -210,6 +211,12 @@ for mir::TerminatorKind<'gcx> {
|
||||
target.hash_stable(hcx, hasher);
|
||||
cleanup.hash_stable(hcx, hasher);
|
||||
}
|
||||
mir::TerminatorKind::FalseEdges { ref real_target, ref imaginary_targets } => {
|
||||
real_target.hash_stable(hcx, hasher);
|
||||
for target in imaginary_targets {
|
||||
target.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -682,6 +682,11 @@ pub enum TerminatorKind<'tcx> {
|
||||
|
||||
/// Indicates the end of the dropping of a generator
|
||||
GeneratorDrop,
|
||||
|
||||
FalseEdges {
|
||||
real_target: BasicBlock,
|
||||
imaginary_targets: Vec<BasicBlock>
|
||||
},
|
||||
}
|
||||
|
||||
impl<'tcx> Terminator<'tcx> {
|
||||
@ -731,6 +736,11 @@ impl<'tcx> TerminatorKind<'tcx> {
|
||||
}
|
||||
Assert { target, cleanup: Some(unwind), .. } => vec![target, unwind].into_cow(),
|
||||
Assert { ref target, .. } => slice::ref_slice(target).into_cow(),
|
||||
FalseEdges { ref real_target, ref imaginary_targets } => {
|
||||
let mut s = vec![*real_target];
|
||||
s.extend_from_slice(imaginary_targets);
|
||||
s.into_cow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,7 +767,12 @@ impl<'tcx> TerminatorKind<'tcx> {
|
||||
vec![target]
|
||||
}
|
||||
Assert { ref mut target, cleanup: Some(ref mut unwind), .. } => vec![target, unwind],
|
||||
Assert { ref mut target, .. } => vec![target]
|
||||
Assert { ref mut target, .. } => vec![target],
|
||||
FalseEdges { ref mut real_target, ref mut imaginary_targets } => {
|
||||
let mut s = vec![real_target];
|
||||
s.extend(imaginary_targets.iter_mut());
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -874,7 +889,8 @@ impl<'tcx> TerminatorKind<'tcx> {
|
||||
}
|
||||
|
||||
write!(fmt, ")")
|
||||
}
|
||||
},
|
||||
FalseEdges { .. } => write!(fmt, "falseEdges")
|
||||
}
|
||||
}
|
||||
|
||||
@ -910,7 +926,12 @@ impl<'tcx> TerminatorKind<'tcx> {
|
||||
}
|
||||
Assert { cleanup: None, .. } => vec!["".into()],
|
||||
Assert { .. } =>
|
||||
vec!["success".into_cow(), "unwind".into_cow()]
|
||||
vec!["success".into_cow(), "unwind".into_cow()],
|
||||
FalseEdges { ref imaginary_targets, .. } => {
|
||||
let mut l = vec!["real".into()];
|
||||
l.resize(imaginary_targets.len() + 1, "imaginary".into());
|
||||
l
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1878,6 +1899,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
|
||||
Resume => Resume,
|
||||
Return => Return,
|
||||
Unreachable => Unreachable,
|
||||
FalseEdges { real_target, ref imaginary_targets } =>
|
||||
FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() }
|
||||
};
|
||||
Terminator {
|
||||
source_info: self.source_info,
|
||||
@ -1917,7 +1940,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
|
||||
Resume |
|
||||
Return |
|
||||
GeneratorDrop |
|
||||
Unreachable => false
|
||||
Unreachable |
|
||||
FalseEdges { .. } => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -486,8 +486,15 @@ macro_rules! make_mir_visitor {
|
||||
self.visit_operand(value, source_location);
|
||||
self.visit_branch(block, resume);
|
||||
drop.map(|t| self.visit_branch(block, t));
|
||||
|
||||
}
|
||||
|
||||
TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => {
|
||||
self.visit_branch(block, real_target);
|
||||
for target in imaginary_targets {
|
||||
self.visit_branch(block, *target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,7 +364,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Unreachable => {
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::FalseEdges { .. } => {
|
||||
// no data used, thus irrelevant to borrowck
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ use rustc::mir::*;
|
||||
use rustc::hir;
|
||||
use hair::*;
|
||||
use syntax::ast::{Name, NodeId};
|
||||
use syntax_pos::{DUMMY_SP, Span};
|
||||
use syntax_pos::Span;
|
||||
|
||||
// helper functions, broken out by category:
|
||||
mod simplify;
|
||||
@ -54,11 +54,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
(body, scope.unwrap_or(self.visibility_scope))
|
||||
}).collect();
|
||||
|
||||
// create binding start block for link them by false edges
|
||||
let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len());
|
||||
let pre_binding_blocks: Vec<_> = (0..candidate_count + 1)
|
||||
.map(|_| self.cfg.start_new_block()).collect();
|
||||
|
||||
// assemble a list of candidates: there is one candidate per
|
||||
// pattern, which means there may be more than one candidate
|
||||
// *per arm*. These candidates are kept sorted such that the
|
||||
// highest priority candidate comes first in the list.
|
||||
// (i.e. same order as in source)
|
||||
|
||||
let candidates: Vec<_> =
|
||||
arms.iter()
|
||||
.enumerate()
|
||||
@ -66,17 +72,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
arm.patterns.iter()
|
||||
.map(move |pat| (arm_index, pat, arm.guard.clone()))
|
||||
})
|
||||
.map(|(arm_index, pattern, guard)| {
|
||||
.zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1)))
|
||||
.map(|((arm_index, pattern, guard),
|
||||
(pre_binding_block, next_candidate_pre_binding_block))| {
|
||||
Candidate {
|
||||
span: pattern.span,
|
||||
match_pairs: vec![MatchPair::new(discriminant_lvalue.clone(), pattern)],
|
||||
bindings: vec![],
|
||||
guard,
|
||||
arm_index,
|
||||
pre_binding_block: *pre_binding_block,
|
||||
next_candidate_pre_binding_block: *next_candidate_pre_binding_block,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let outer_source_info = self.source_info(span);
|
||||
self.cfg.terminate(*pre_binding_blocks.last().unwrap(),
|
||||
outer_source_info, TerminatorKind::Unreachable);
|
||||
|
||||
// this will generate code to test discriminant_lvalue and
|
||||
// branch to the appropriate arm block
|
||||
let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
|
||||
@ -148,7 +162,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)],
|
||||
bindings: vec![],
|
||||
guard: None,
|
||||
arm_index: 0, // since we don't call `match_candidates`, this field is unused
|
||||
|
||||
// since we don't call `match_candidates`, next fields is unused
|
||||
arm_index: 0,
|
||||
pre_binding_block: block,
|
||||
next_candidate_pre_binding_block: block
|
||||
};
|
||||
|
||||
// Simplify the candidate. Since the pattern is irrefutable, this should
|
||||
@ -278,6 +296,10 @@ pub struct Candidate<'pat, 'tcx:'pat> {
|
||||
|
||||
// ...and then we branch to arm with this index.
|
||||
arm_index: usize,
|
||||
|
||||
// ...and the blocks for add false edges between candidates
|
||||
pre_binding_block: BasicBlock,
|
||||
next_candidate_pre_binding_block: BasicBlock,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -398,17 +420,43 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
|
||||
debug!("match_candidates: {:?} candidates fully matched", fully_matched);
|
||||
let mut unmatched_candidates = candidates.split_off(fully_matched);
|
||||
for (index, candidate) in candidates.into_iter().enumerate() {
|
||||
|
||||
let fully_matched_with_guard =
|
||||
candidates.iter().take_while(|c| c.guard.is_some()).count();
|
||||
|
||||
let unreachable_candidates = if fully_matched_with_guard + 1 < candidates.len() {
|
||||
candidates.split_off(fully_matched_with_guard + 1)
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
for candidate in candidates {
|
||||
// If so, apply any bindings, test the guard (if any), and
|
||||
// branch to the arm.
|
||||
let is_last = index == fully_matched - 1;
|
||||
if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks,
|
||||
candidate, is_last) {
|
||||
if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks, candidate) {
|
||||
block = b;
|
||||
} else {
|
||||
// if None is returned, then any remaining candidates
|
||||
// are unreachable (at least not through this path).
|
||||
return vec![];
|
||||
// Link them with false edges.
|
||||
debug!("match_candidates: add false edges for unreachable {:?} and unmatched {:?}",
|
||||
unreachable_candidates, unmatched_candidates);
|
||||
for candidate in unreachable_candidates {
|
||||
let source_info = self.source_info(candidate.span);
|
||||
let target = self.cfg.start_new_block();
|
||||
if let Some(otherwise) = self.bind_and_guard_matched_candidate(target,
|
||||
arm_blocks,
|
||||
candidate) {
|
||||
self.cfg.terminate(otherwise, source_info, TerminatorKind::Unreachable);
|
||||
}
|
||||
}
|
||||
|
||||
if unmatched_candidates.is_empty() {
|
||||
return vec![]
|
||||
} else {
|
||||
let target = self.cfg.start_new_block();
|
||||
return self.match_candidates(span, arm_blocks, unmatched_candidates, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -423,9 +471,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
self.test_candidates(span, arm_blocks, &unmatched_candidates, block);
|
||||
|
||||
// If the target candidates were exhaustive, then we are done.
|
||||
if otherwise.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
// But for borrowck continue build decision tree.
|
||||
|
||||
// If all candidates were sorted into `target_candidates` somewhere, then
|
||||
// the initial set was inexhaustive.
|
||||
@ -666,17 +712,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
fn bind_and_guard_matched_candidate<'pat>(&mut self,
|
||||
mut block: BasicBlock,
|
||||
arm_blocks: &mut ArmBlocks,
|
||||
candidate: Candidate<'pat, 'tcx>,
|
||||
is_last_arm: bool)
|
||||
candidate: Candidate<'pat, 'tcx>)
|
||||
-> Option<BasicBlock> {
|
||||
debug!("bind_and_guard_matched_candidate(block={:?}, candidate={:?})",
|
||||
block, candidate);
|
||||
|
||||
debug_assert!(candidate.match_pairs.is_empty());
|
||||
|
||||
self.bind_matched_candidate(block, candidate.bindings);
|
||||
|
||||
let arm_block = arm_blocks.blocks[candidate.arm_index];
|
||||
let candidate_source_info = self.source_info(candidate.span);
|
||||
|
||||
self.cfg.terminate(block, candidate_source_info,
|
||||
TerminatorKind::Goto { target: candidate.pre_binding_block });
|
||||
|
||||
block = self.cfg.start_new_block();
|
||||
self.cfg.terminate(candidate.pre_binding_block, candidate_source_info,
|
||||
TerminatorKind::FalseEdges {
|
||||
real_target: block,
|
||||
imaginary_targets:
|
||||
vec![candidate.next_candidate_pre_binding_block]});
|
||||
|
||||
self.bind_matched_candidate(block, candidate.bindings);
|
||||
|
||||
if let Some(guard) = candidate.guard {
|
||||
// the block to branch to if the guard fails; if there is no
|
||||
@ -684,30 +740,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
let guard = self.hir.mirror(guard);
|
||||
let source_info = self.source_info(guard.span);
|
||||
let cond = unpack!(block = self.as_local_operand(block, guard));
|
||||
let otherwise = self.cfg.start_new_block();
|
||||
|
||||
let false_edge_block = self.cfg.start_new_block();
|
||||
self.cfg.terminate(block, source_info,
|
||||
TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));
|
||||
Some(otherwise)
|
||||
} else if !is_last_arm {
|
||||
// Add always true guard in case of more than one arm
|
||||
// it creates false edges and allow MIR borrowck detects errors
|
||||
// FIXME(#45184) -- permit "false edges"
|
||||
let source_info = self.source_info(candidate.span);
|
||||
let true_expr = Expr {
|
||||
temp_lifetime: None,
|
||||
ty: self.hir.tcx().types.bool,
|
||||
span: DUMMY_SP,
|
||||
kind: ExprKind::Literal{literal: self.hir.true_literal()},
|
||||
};
|
||||
let cond = unpack!(block = self.as_local_operand(block, true_expr));
|
||||
TerminatorKind::if_(self.hir.tcx(), cond, arm_block,
|
||||
false_edge_block));
|
||||
|
||||
let otherwise = self.cfg.start_new_block();
|
||||
self.cfg.terminate(block, source_info,
|
||||
TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));
|
||||
self.cfg.terminate(false_edge_block, source_info,
|
||||
TerminatorKind::FalseEdges {
|
||||
real_target: otherwise,
|
||||
imaginary_targets:
|
||||
vec![candidate.next_candidate_pre_binding_block] });
|
||||
Some(otherwise)
|
||||
} else {
|
||||
let source_info = self.source_info(candidate.span);
|
||||
self.cfg.terminate(block, source_info,
|
||||
TerminatorKind::Goto { target: arm_block });
|
||||
self.cfg.terminate(block, candidate_source_info,
|
||||
TerminatorKind::Goto { target: arm_block });
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -598,6 +598,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
bindings: candidate.bindings.clone(),
|
||||
guard: candidate.guard.clone(),
|
||||
arm_index: candidate.arm_index,
|
||||
pre_binding_block: candidate.pre_binding_block,
|
||||
next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,6 +661,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
bindings: candidate.bindings.clone(),
|
||||
guard: candidate.guard.clone(),
|
||||
arm_index: candidate.arm_index,
|
||||
pre_binding_block: candidate.pre_binding_block,
|
||||
next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -721,6 +721,12 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
|
||||
}
|
||||
}
|
||||
mir::TerminatorKind::FalseEdges { ref real_target, ref imaginary_targets } => {
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, real_target);
|
||||
for target in imaginary_targets {
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,6 +305,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
|
||||
TerminatorKind::Goto { target: _ } |
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::FalseEdges { .. } |
|
||||
TerminatorKind::Unreachable => { }
|
||||
|
||||
TerminatorKind::Return => {
|
||||
|
@ -73,7 +73,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::Unreachable => {
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::FalseEdges { .. } => {
|
||||
// safe (at least as emitted during MIR construction)
|
||||
}
|
||||
|
||||
|
@ -720,6 +720,12 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
TerminatorKind::Unreachable => { }
|
||||
TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_targets } => {
|
||||
*real_target = self.update_target(*real_target);
|
||||
for target in imaginary_targets {
|
||||
*target = self.update_target(*target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,8 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Yield { .. } |
|
||||
TerminatorKind::SwitchInt { .. } => {
|
||||
TerminatorKind::SwitchInt { .. } |
|
||||
TerminatorKind::FalseEdges { .. } => {
|
||||
/* nothing to do */
|
||||
},
|
||||
TerminatorKind::Call { cleanup: ref mut unwind, .. } |
|
||||
|
@ -317,7 +317,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Yield { .. } |
|
||||
TerminatorKind::Unreachable => None,
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::FalseEdges { .. } => None,
|
||||
|
||||
TerminatorKind::Return => {
|
||||
// Check for unused values. This usually means
|
||||
|
@ -61,6 +61,9 @@ impl MirPass for SimplifyBranches {
|
||||
}), expected, .. } if cond == expected => {
|
||||
TerminatorKind::Goto { target: target }
|
||||
},
|
||||
TerminatorKind::FalseEdges { real_target, .. } => {
|
||||
TerminatorKind::Goto { target: real_target }
|
||||
},
|
||||
_ => continue
|
||||
};
|
||||
}
|
||||
|
@ -441,7 +441,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::Drop { .. } => {
|
||||
TerminatorKind::Drop { .. } |
|
||||
TerminatorKind::FalseEdges { .. } => {
|
||||
// no checks needed for these
|
||||
}
|
||||
|
||||
@ -685,6 +686,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
||||
self.assert_iscleanup(mir, block, cleanup, true);
|
||||
}
|
||||
}
|
||||
TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => {
|
||||
self.assert_iscleanup(mir, block, real_target, is_cleanup);
|
||||
for target in imaginary_targets {
|
||||
self.assert_iscleanup(mir, block, *target, is_cleanup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +121,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
|
||||
TerminatorKind::Assert { .. } => "TerminatorKind::Assert",
|
||||
TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop",
|
||||
TerminatorKind::Yield { .. } => "TerminatorKind::Yield",
|
||||
TerminatorKind::FalseEdges { .. } => "TerminatorKind::FalseEdges",
|
||||
}, kind);
|
||||
self.super_terminator_kind(block, kind, location);
|
||||
}
|
||||
|
@ -228,7 +228,8 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::SwitchInt { .. } |
|
||||
TerminatorKind::Yield { .. } => {
|
||||
TerminatorKind::Yield { .. } |
|
||||
TerminatorKind::FalseEdges { .. } => {
|
||||
/* nothing to do */
|
||||
}
|
||||
TerminatorKind::Call { cleanup: unwind, .. } |
|
||||
|
@ -583,7 +583,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
|
||||
cleanup);
|
||||
}
|
||||
mir::TerminatorKind::GeneratorDrop |
|
||||
mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"),
|
||||
mir::TerminatorKind::Yield { .. } |
|
||||
mir::TerminatorKind::FalseEdges { .. } => bug!("generator ops in trans"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,7 +623,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
|
||||
mir::TerminatorKind::Unreachable |
|
||||
mir::TerminatorKind::Assert { .. } => {}
|
||||
mir::TerminatorKind::GeneratorDrop |
|
||||
mir::TerminatorKind::Yield { .. } => bug!(),
|
||||
mir::TerminatorKind::Yield { .. } |
|
||||
mir::TerminatorKind::FalseEdges { .. } => bug!(),
|
||||
}
|
||||
|
||||
self.super_terminator_kind(block, kind, location);
|
||||
|
24
src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs
Normal file
24
src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
//compile-flags: -Z emit-end-regions -Z borrowck-mir
|
||||
|
||||
fn foo(_:String) {}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let my_str = "hello".to_owned();
|
||||
match Some(42) {
|
||||
Some(_) if { drop(my_str); false } => {}
|
||||
Some(_) => {}
|
||||
None => { foo(my_str); } //~ ERROR (Mir) [E0381]
|
||||
}
|
||||
}
|
@ -11,6 +11,24 @@
|
||||
// revisions: ast mir
|
||||
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
|
||||
|
||||
enum Foo {
|
||||
A(i32),
|
||||
B
|
||||
}
|
||||
|
||||
fn match_enum() {
|
||||
let mut foo = Foo::B;
|
||||
let p = &mut foo;
|
||||
let _ = match foo {
|
||||
Foo::B => 1, //[mir]~ ERROR (Mir) [E0503]
|
||||
_ => 2,
|
||||
Foo::A(x) => x //[ast]~ ERROR [E0503]
|
||||
//[mir]~^ ERROR (Ast) [E0503]
|
||||
//[mir]~| ERROR (Mir) [E0503]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let mut x = 1;
|
||||
let _x = &mut x;
|
||||
|
236
src/test/mir-opt/match_false_edges.rs
Normal file
236
src/test/mir-opt/match_false_edges.rs
Normal file
@ -0,0 +1,236 @@
|
||||
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z emit-end-regions -Z borrowck-mir
|
||||
|
||||
fn guard() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn guard2(_:i32) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn full_tested_match() {
|
||||
let _ = match Some(42) {
|
||||
Some(x) if guard() => (1, x),
|
||||
Some(y) => (2, y),
|
||||
None => (3, 3),
|
||||
};
|
||||
}
|
||||
|
||||
fn full_tested_match2() {
|
||||
let _ = match Some(42) {
|
||||
Some(x) if guard() => (1, x),
|
||||
None => (3, 3),
|
||||
Some(y) => (2, y),
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = match Some(1) {
|
||||
Some(_w) if guard() => 1,
|
||||
_x => 2,
|
||||
Some(y) if guard2(y) => 3,
|
||||
_z => 4,
|
||||
};
|
||||
}
|
||||
|
||||
// END RUST SOURCE
|
||||
//
|
||||
// START rustc.node17.SimplifyBranches-initial.before.mir
|
||||
// bb0: {
|
||||
// ...
|
||||
// _2 = std::option::Option<i32>::Some(const 42i32,);
|
||||
// _5 = discriminant(_2);
|
||||
// switchInt(_5) -> [0isize: bb5, otherwise: bb3];
|
||||
// }
|
||||
// bb1: { // arm1
|
||||
// StorageLive(_7);
|
||||
// _7 = _3;
|
||||
// _1 = (const 1i32, _7);
|
||||
// StorageDead(_7);
|
||||
// goto -> bb11;
|
||||
// }
|
||||
// bb2: { // binding3(empty) and arm3
|
||||
// _1 = (const 3i32, const 3i32);
|
||||
// goto -> bb11;
|
||||
// }
|
||||
// bb3: {
|
||||
// falseEdges -> [real: bb7, imaginary: bb4]; //pre_binding1
|
||||
// }
|
||||
// bb4: {
|
||||
// falseEdges -> [real: bb10, imaginary: bb5]; //pre_binding2
|
||||
// }
|
||||
// bb5: {
|
||||
// falseEdges -> [real: bb2, imaginary: bb6]; //pre_binding3
|
||||
// }
|
||||
// bb6: {
|
||||
// unreachable;
|
||||
// }
|
||||
// bb7: { // binding1 and guard
|
||||
// StorageLive(_3);
|
||||
// _3 = ((_2 as Some).0: i32);
|
||||
// StorageLive(_6);
|
||||
// _6 = const guard() -> bb8;
|
||||
// }
|
||||
// bb8: { // end of guard
|
||||
// switchInt(_6) -> [0u8: bb9, otherwise: bb1];
|
||||
// }
|
||||
// bb9: { // to pre_binding2
|
||||
// falseEdges -> [real: bb4, imaginary: bb4];
|
||||
// }
|
||||
// bb10: { // bindingNoLandingPads.before.mir2 and arm2
|
||||
// StorageLive(_4);
|
||||
// _4 = ((_2 as Some).0: i32);
|
||||
// StorageLive(_8);
|
||||
// _8 = _4;
|
||||
// _1 = (const 2i32, _8);
|
||||
// StorageDead(_8);
|
||||
// goto -> bb11;
|
||||
// }
|
||||
// bb11: {
|
||||
// ...
|
||||
// return;
|
||||
// }
|
||||
// END rustc.node17.SimplifyBranches-initial.before.mir
|
||||
//
|
||||
// START rustc.node42.SimplifyBranches-initial.before.mir
|
||||
// bb0: {
|
||||
// ...
|
||||
// _2 = std::option::Option<i32>::Some(const 42i32,);
|
||||
// _5 = discriminant(_2);
|
||||
// switchInt(_5) -> [0isize: bb4, otherwise: bb3];
|
||||
// }
|
||||
// bb1: { // arm1
|
||||
// StorageLive(_7);
|
||||
// _7 = _3;
|
||||
// _1 = (const 1i32, _7);
|
||||
// StorageDead(_7);
|
||||
// goto -> bb11;
|
||||
// }
|
||||
// bb2: { // binding3(empty) and arm3
|
||||
// _1 = (const 3i32, const 3i32);
|
||||
// goto -> bb11;
|
||||
// }
|
||||
// bb3: {
|
||||
// falseEdges -> [real: bb7, imaginary: bb4]; //pre_binding1
|
||||
// }
|
||||
// bb4: {
|
||||
// falseEdges -> [real: bb2, imaginary: bb5]; //pre_binding2
|
||||
// }
|
||||
// bb5: {
|
||||
// falseEdges -> [real: bb10, imaginary: bb6]; //pre_binding3
|
||||
// }
|
||||
// bb6: {
|
||||
// unreachable;
|
||||
// }
|
||||
// bb7: { // binding1 and guard
|
||||
// StorageLive(_3);
|
||||
// _3 = ((_2 as Some).0: i32);
|
||||
// StorageLive(_6);
|
||||
// _6 = const guard() -> bb8;
|
||||
// }
|
||||
// bb8: { // end of guard
|
||||
// switchInt(_6) -> [0u8: bb9, otherwise: bb1];
|
||||
// }
|
||||
// bb9: { // to pre_binding2
|
||||
// falseEdges -> [real: bb5, imaginary: bb4];
|
||||
// }
|
||||
// bb10: { // binding2 and arm2
|
||||
// StorageLive(_4);
|
||||
// _4 = ((_2 as Some).0: i32);
|
||||
// StorageLive(_8);
|
||||
// _8 = _4;
|
||||
// _1 = (const 2i32, _8);
|
||||
// StorageDead(_8);
|
||||
// goto -> bb11;
|
||||
// }
|
||||
// bb11: {
|
||||
// ...
|
||||
// return;
|
||||
// }
|
||||
// END rustc.node42.SimplifyBranches-initial.before.mir
|
||||
//
|
||||
// START rustc.node67.SimplifyBranches-initial.before.mir
|
||||
// bb0: {
|
||||
// ...
|
||||
// _2 = std::option::Option<i32>::Some(const 1i32,);
|
||||
// _7 = discriminant(_2);
|
||||
// switchInt(_7) -> [1isize: bb3, otherwise: bb4];
|
||||
// }
|
||||
// bb1: { // arm1
|
||||
// _1 = const 1i32;
|
||||
// goto -> bb16;
|
||||
// }
|
||||
// bb2: { // arm3
|
||||
// _1 = const 3i32;
|
||||
// goto -> bb16;
|
||||
// }
|
||||
//
|
||||
// bb3: {
|
||||
// falseEdges -> [real: bb8, imaginary: bb4]; //pre_binding1
|
||||
// }
|
||||
// bb4: {
|
||||
// falseEdges -> [real: bb11, imaginary: bb5]; //pre_binding2
|
||||
// }
|
||||
// bb5: {
|
||||
// falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding3
|
||||
// }
|
||||
// bb6: {
|
||||
// falseEdges -> [real: bb15, imaginary: bb7]; //pre_binding4
|
||||
// }
|
||||
// bb7: {
|
||||
// unreachable;
|
||||
// }
|
||||
// bb8: { // binding1: Some(w) if guard()
|
||||
// StorageLive(_3);
|
||||
// _3 = ((_2 as Some).0: i32);
|
||||
// StorageLive(_8);
|
||||
// _8 = const guard() -> bb9;
|
||||
// }
|
||||
// bb9: { //end of guard
|
||||
// switchInt(_8) -> [0u8: bb10, otherwise: bb1];
|
||||
// }
|
||||
// bb10: { // to pre_binding2
|
||||
// falseEdges -> [real: bb4, imaginary: bb4];
|
||||
// }
|
||||
// bb11: { // binding2 & arm2
|
||||
// StorageLive(_4);
|
||||
// _4 = _2;
|
||||
// _1 = const 2i32;
|
||||
// goto -> bb16;
|
||||
// }
|
||||
// bb12: { // binding3: Some(y) if guard2(y)
|
||||
// StorageLive(_5);
|
||||
// _5 = ((_2 as Some).0: i32);
|
||||
// StorageLive(_10);
|
||||
// StorageLive(_11);
|
||||
// _11 = _5;
|
||||
// _10 = const guard2(_11) -> bb13;
|
||||
// }
|
||||
// bb13: { // end of guard2
|
||||
// StorageDead(_11);
|
||||
// switchInt(_10) -> [0u8: bb14, otherwise: bb2];
|
||||
// }
|
||||
// bb14: { // to pre_binding4
|
||||
// falseEdges -> [real: bb6, imaginary: bb6];
|
||||
// }
|
||||
// bb15: { // binding4 & arm4
|
||||
// StorageLive(_6);
|
||||
// _6 = _2;
|
||||
// _1 = const 4i32;
|
||||
// goto -> bb16;
|
||||
// }
|
||||
// bb16: {
|
||||
// ...
|
||||
// return;
|
||||
// }
|
||||
// END rustc.node67.SimplifyBranches-initial.before.mir
|
Loading…
Reference in New Issue
Block a user