target.hash_stable(hcx, hasher);
}
}
+ mir::TerminatorKind::FalseUnwind { ref real_target, ref unwind } => {
+ real_target.hash_stable(hcx, hasher);
+ unwind.hash_stable(hcx, hasher);
+ }
}
}
}
/// Indicates the end of the dropping of a generator
GeneratorDrop,
+ /// A block where control flow only ever takes one real path, but borrowck
+ /// needs to be more conservative.
FalseEdges {
+ /// The target normal control flow will take
real_target: BasicBlock,
- imaginary_targets: Vec<BasicBlock>
+ /// The list of blocks control flow could conceptually take, but won't
+ /// in practice
+ imaginary_targets: Vec<BasicBlock>,
+ },
+ /// A terminator for blocks that only take one path in reality, but where we
+ /// reserve the right to unwind in borrowck, even if it won't happen in practice.
+ /// This can arise in infinite loops with no function calls for example.
+ FalseUnwind {
+ /// The target normal control flow will take
+ real_target: BasicBlock,
+ /// The imaginary cleanup block link. This particular path will never be taken
+ /// in practice, but in order to avoid fragility we want to always
+ /// consider it in borrowck. We don't want to accept programs which
+ /// pass borrowck only when panic=abort or some assertions are disabled
+ /// due to release vs. debug mode builds. This needs to be an Option because
+ /// of the remove_noop_landing_pads and no_landing_pads passes
+ unwind: Option<BasicBlock>,
},
}
s.extend_from_slice(imaginary_targets);
s.into_cow()
}
+ FalseUnwind { real_target: t, unwind: Some(u) } => vec![t, u].into_cow(),
+ FalseUnwind { real_target: ref t, unwind: None } => slice::from_ref(t).into_cow(),
}
}
s.extend(imaginary_targets.iter_mut());
s
}
+ FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => vec![t, u],
+ FalseUnwind { ref mut real_target, unwind: None } => vec![real_target],
}
}
TerminatorKind::Call { cleanup: ref mut unwind, .. } |
TerminatorKind::Assert { cleanup: ref mut unwind, .. } |
TerminatorKind::DropAndReplace { ref mut unwind, .. } |
- TerminatorKind::Drop { ref mut unwind, .. } => {
+ TerminatorKind::Drop { ref mut unwind, .. } |
+ TerminatorKind::FalseUnwind { ref mut unwind, .. } => {
Some(unwind)
}
}
write!(fmt, ")")
},
- FalseEdges { .. } => write!(fmt, "falseEdges")
+ FalseEdges { .. } => write!(fmt, "falseEdges"),
+ FalseUnwind { .. } => write!(fmt, "falseUnwind"),
}
}
l.resize(imaginary_targets.len() + 1, "imaginary".into());
l
}
+ FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
+ FalseUnwind { unwind: None, .. } => vec!["real".into()],
}
}
}
Return => Return,
Unreachable => Unreachable,
FalseEdges { real_target, ref imaginary_targets } =>
- FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() }
+ FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() },
+ FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
};
Terminator {
source_info: self.source_info,
Return |
GeneratorDrop |
Unreachable |
- FalseEdges { .. } => false
+ FalseEdges { .. } |
+ FalseUnwind { .. } => false
}
}
}
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 } => {
+ TerminatorKind::FalseEdges { real_target, ref imaginary_targets} => {
self.visit_branch(block, real_target);
for target in imaginary_targets {
self.visit_branch(block, *target);
}
}
+
+ TerminatorKind::FalseUnwind { real_target, unwind } => {
+ self.visit_branch(block, real_target);
+ if let Some(unwind) = unwind {
+ self.visit_branch(block, unwind);
+ }
+ }
}
}
TerminatorKind::Goto { target: _ }
| TerminatorKind::Abort
| TerminatorKind::Unreachable
- | TerminatorKind::FalseEdges { .. } => {
+ | TerminatorKind::FalseEdges { real_target: _, imaginary_targets: _ }
+ | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
// no data used, thus irrelevant to borrowck
}
}
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
- | TerminatorKind::FalseEdges { .. } => {
+ | TerminatorKind::FalseEdges { .. }
+ | TerminatorKind::FalseUnwind { .. } => {
// no checks needed for these
}
self.assert_iscleanup(mir, block_data, *target, is_cleanup);
}
}
+ TerminatorKind::FalseUnwind {
+ real_target,
+ unwind
+ } => {
+ self.assert_iscleanup(mir, block_data, real_target, is_cleanup);
+ if let Some(unwind) = unwind {
+ if is_cleanup {
+ span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind");
+ }
+ self.assert_iscleanup(mir, block_data, unwind, true);
+ }
+ }
}
}
TerminatorKind::FalseEdges {
real_target: block,
imaginary_targets:
- vec![candidate.next_candidate_pre_binding_block]});
+ vec![candidate.next_candidate_pre_binding_block],
+ });
self.bind_matched_candidate(block, candidate.bindings);
TerminatorKind::FalseEdges {
real_target: otherwise,
imaginary_targets:
- vec![candidate.next_candidate_pre_binding_block] });
+ vec![candidate.next_candidate_pre_binding_block],
+ });
Some(otherwise)
} else {
self.cfg.terminate(block, candidate_source_info,
mir::TerminatorKind::Yield {..} |
mir::TerminatorKind::Goto {..} |
mir::TerminatorKind::FalseEdges {..} |
+ mir::TerminatorKind::FalseUnwind {..} |
mir::TerminatorKind::Unreachable => {}
}
}
self.propagate_bits_into_entry_set_for(in_out, changed, target);
}
}
+ mir::TerminatorKind::FalseUnwind { ref real_target, unwind } => {
+ self.propagate_bits_into_entry_set_for(in_out, changed, real_target);
+ if let Some(ref unwind) = unwind {
+ if !self.dead_unwinds.contains(&bb) {
+ self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
+ }
+ }
+ }
}
}
TerminatorKind::Abort |
TerminatorKind::GeneratorDrop |
TerminatorKind::FalseEdges { .. } |
+ TerminatorKind::FalseUnwind { .. } |
TerminatorKind::Unreachable => { }
TerminatorKind::Return => {
Resume => unimplemented!(),
Abort => unimplemented!(),
FalseEdges { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"),
+ FalseUnwind { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"),
Unreachable => return err!(Unreachable),
}
mir::TerminatorKind::Assert { .. } => {}
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Yield { .. } |
- mir::TerminatorKind::FalseEdges { .. } => bug!(),
+ mir::TerminatorKind::FalseEdges { .. } |
+ mir::TerminatorKind::FalseUnwind { .. } => bug!(),
}
self.super_terminator_kind(block, kind, location);
TerminatorKind::Abort |
TerminatorKind::Return |
TerminatorKind::Unreachable |
- TerminatorKind::FalseEdges { .. } => {
+ TerminatorKind::FalseEdges { .. } |
+ TerminatorKind::FalseUnwind { .. } => {
// safe (at least as emitted during MIR construction)
}
*target = self.update_target(*target);
}
}
+ TerminatorKind::FalseUnwind { real_target: _ , unwind: _ } =>
+ // see the ordering of passes in the optimized_mir query.
+ bug!("False unwinds should have been removed before inlining")
}
}
TerminatorKind::GeneratorDrop |
TerminatorKind::Yield { .. } |
TerminatorKind::Unreachable |
- TerminatorKind::FalseEdges { .. } => None,
+ TerminatorKind::FalseEdges { .. } |
+ TerminatorKind::FalseUnwind { .. } => None,
TerminatorKind::Return => {
// Check for unused values. This usually means
TerminatorKind::Goto { .. } |
TerminatorKind::Resume |
TerminatorKind::SwitchInt { .. } |
- TerminatorKind::FalseEdges { .. } => {
+ TerminatorKind::FalseEdges { .. } |
+ TerminatorKind::FalseUnwind { .. } => {
terminator.successors().iter().all(|succ| {
nop_landing_pads.contains(succ.index())
})
TerminatorKind::FalseEdges { real_target, .. } => {
TerminatorKind::Goto { target: real_target }
},
+ TerminatorKind::FalseUnwind { real_target, .. } => {
+ TerminatorKind::Goto { target: real_target }
+ },
_ => continue
};
}
TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop",
TerminatorKind::Yield { .. } => "TerminatorKind::Yield",
TerminatorKind::FalseEdges { .. } => "TerminatorKind::FalseEdges",
+ TerminatorKind::FalseUnwind { .. } => "TerminatorKind::FalseUnwind",
}, kind);
self.super_terminator_kind(block, kind, location);
}
TerminatorKind::Unreachable |
TerminatorKind::SwitchInt { .. } |
TerminatorKind::Yield { .. } |
- TerminatorKind::FalseEdges { .. } => {
+ TerminatorKind::FalseEdges { .. } |
+ TerminatorKind::FalseUnwind { .. } => {
/* nothing to do */
}
TerminatorKind::Call { cleanup: unwind, .. } |
cleanup);
}
mir::TerminatorKind::GeneratorDrop |
- mir::TerminatorKind::Yield { .. } |
- mir::TerminatorKind::FalseEdges { .. } => bug!("generator ops in trans"),
+ mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"),
+ mir::TerminatorKind::FalseEdges { .. } |
+ mir::TerminatorKind::FalseUnwind { .. } => bug!("borrowck false edges in trans"),
}
}