use crate::mir::interpret::Scalar;
use crate::ty::{self, Ty, TyCtxt};
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use smallvec::{smallvec, SmallVec};
use super::{
AssertMessage, BasicBlock, InlineAsmOperand, Operand, Place, SourceInfo, Successors,
SuccessorsMut,
};
pub use rustc_ast::Mutability;
use rustc_macros::HashStable;
use rustc_span::Span;
use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter, Write};
use std::iter;
use std::slice;
pub use super::query::*;
#[derive(Debug, Clone, TyEncodable, TyDecodable, HashStable, PartialEq)]
pub struct SwitchTargets {
values: SmallVec<[u128; 1]>,
targets: SmallVec<[BasicBlock; 2]>,
}
impl SwitchTargets {
pub fn new(targets: impl Iterator<Item = (u128, BasicBlock)>, otherwise: BasicBlock) -> Self {
let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
targets.push(otherwise);
Self { values, targets }
}
pub fn static_if(value: u128, then: BasicBlock, else_: BasicBlock) -> Self {
Self { values: smallvec![value], targets: smallvec![then, else_] }
}
pub fn otherwise(&self) -> BasicBlock {
*self.targets.last().unwrap()
}
pub fn iter(&self) -> SwitchTargetsIter<'_> {
SwitchTargetsIter { inner: self.values.iter().zip(self.targets.iter()) }
}
pub fn all_targets(&self) -> &[BasicBlock] {
&self.targets
}
pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
&mut self.targets
}
}
pub struct SwitchTargetsIter<'a> {
inner: iter::Zip<slice::Iter<'a, u128>, slice::Iter<'a, BasicBlock>>,
}
impl<'a> Iterator for SwitchTargetsIter<'a> {
type Item = (u128, BasicBlock);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(val, bb)| (*val, *bb))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
#[derive(Clone, TyEncodable, TyDecodable, HashStable, PartialEq)]
pub enum TerminatorKind<'tcx> {
Goto { target: BasicBlock },
SwitchInt {
discr: Operand<'tcx>,
switch_ty: Ty<'tcx>,
targets: SwitchTargets,
},
Resume,
Abort,
Return,
Unreachable,
Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> },
DropAndReplace {
place: Place<'tcx>,
value: Operand<'tcx>,
target: BasicBlock,
unwind: Option<BasicBlock>,
},
Call {
func: Operand<'tcx>,
args: Vec<Operand<'tcx>>,
destination: Option<(Place<'tcx>, BasicBlock)>,
cleanup: Option<BasicBlock>,
from_hir_call: bool,
fn_span: Span,
},
Assert {
cond: Operand<'tcx>,
expected: bool,
msg: AssertMessage<'tcx>,
target: BasicBlock,
cleanup: Option<BasicBlock>,
},
Yield {
value: Operand<'tcx>,
resume: BasicBlock,
resume_arg: Place<'tcx>,
drop: Option<BasicBlock>,
},
GeneratorDrop,
FalseEdge {
real_target: BasicBlock,
imaginary_target: BasicBlock,
},
FalseUnwind {
real_target: BasicBlock,
unwind: Option<BasicBlock>,
},
InlineAsm {
template: &'tcx [InlineAsmTemplatePiece],
operands: Vec<InlineAsmOperand<'tcx>>,
options: InlineAsmOptions,
line_spans: &'tcx [Span],
destination: Option<BasicBlock>,
},
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct Terminator<'tcx> {
pub source_info: SourceInfo,
pub kind: TerminatorKind<'tcx>,
}
impl<'tcx> Terminator<'tcx> {
pub fn successors(&self) -> Successors<'_> {
self.kind.successors()
}
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
self.kind.successors_mut()
}
pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
self.kind.unwind()
}
pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
self.kind.unwind_mut()
}
}
impl<'tcx> TerminatorKind<'tcx> {
pub fn if_(
tcx: TyCtxt<'tcx>,
cond: Operand<'tcx>,
t: BasicBlock,
f: BasicBlock,
) -> TerminatorKind<'tcx> {
TerminatorKind::SwitchInt {
discr: cond,
switch_ty: tcx.types.bool,
targets: SwitchTargets::static_if(0, f, t),
}
}
pub fn successors(&self) -> Successors<'_> {
use self::TerminatorKind::*;
match *self {
Resume
| Abort
| GeneratorDrop
| Return
| Unreachable
| Call { destination: None, cleanup: None, .. }
| InlineAsm { destination: None, .. } => None.into_iter().chain(&[]),
Goto { target: ref t }
| Call { destination: None, cleanup: Some(ref t), .. }
| Call { destination: Some((_, ref t)), cleanup: None, .. }
| Yield { resume: ref t, drop: None, .. }
| DropAndReplace { target: ref t, unwind: None, .. }
| Drop { target: ref t, unwind: None, .. }
| Assert { target: ref t, cleanup: None, .. }
| FalseUnwind { real_target: ref t, unwind: None }
| InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]),
Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. }
| Yield { resume: ref t, drop: Some(ref u), .. }
| DropAndReplace { target: ref t, unwind: Some(ref u), .. }
| Drop { target: ref t, unwind: Some(ref u), .. }
| Assert { target: ref t, cleanup: Some(ref u), .. }
| FalseUnwind { real_target: ref t, unwind: Some(ref u) } => {
Some(t).into_iter().chain(slice::from_ref(u))
}
SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets[..]),
FalseEdge { ref real_target, ref imaginary_target } => {
Some(real_target).into_iter().chain(slice::from_ref(imaginary_target))
}
}
}
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
use self::TerminatorKind::*;
match *self {
Resume
| Abort
| GeneratorDrop
| Return
| Unreachable
| Call { destination: None, cleanup: None, .. }
| InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []),
Goto { target: ref mut t }
| Call { destination: None, cleanup: Some(ref mut t), .. }
| Call { destination: Some((_, ref mut t)), cleanup: None, .. }
| Yield { resume: ref mut t, drop: None, .. }
| DropAndReplace { target: ref mut t, unwind: None, .. }
| Drop { target: ref mut t, unwind: None, .. }
| Assert { target: ref mut t, cleanup: None, .. }
| FalseUnwind { real_target: ref mut t, unwind: None }
| InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []),
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. }
| Yield { resume: ref mut t, drop: Some(ref mut u), .. }
| DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
| Drop { target: ref mut t, unwind: Some(ref mut u), .. }
| Assert { target: ref mut t, cleanup: Some(ref mut u), .. }
| FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => {
Some(t).into_iter().chain(slice::from_mut(u))
}
SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets[..]),
FalseEdge { ref mut real_target, ref mut imaginary_target } => {
Some(real_target).into_iter().chain(slice::from_mut(imaginary_target))
}
}
}
pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
match *self {
TerminatorKind::Goto { .. }
| TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::InlineAsm { .. } => None,
TerminatorKind::Call { cleanup: ref unwind, .. }
| TerminatorKind::Assert { cleanup: ref unwind, .. }
| TerminatorKind::DropAndReplace { ref unwind, .. }
| TerminatorKind::Drop { ref unwind, .. }
| TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind),
}
}
pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
match *self {
TerminatorKind::Goto { .. }
| TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::InlineAsm { .. } => None,
TerminatorKind::Call { cleanup: ref mut unwind, .. }
| TerminatorKind::Assert { cleanup: ref mut unwind, .. }
| TerminatorKind::DropAndReplace { ref mut unwind, .. }
| TerminatorKind::Drop { ref mut unwind, .. }
| TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind),
}
}
}
impl<'tcx> Debug for TerminatorKind<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
self.fmt_head(fmt)?;
let successor_count = self.successors().count();
let labels = self.fmt_successor_labels();
assert_eq!(successor_count, labels.len());
match successor_count {
0 => Ok(()),
1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
_ => {
write!(fmt, " -> [")?;
for (i, target) in self.successors().enumerate() {
if i > 0 {
write!(fmt, ", ")?;
}
write!(fmt, "{}: {:?}", labels[i], target)?;
}
write!(fmt, "]")
}
}
}
}
impl<'tcx> TerminatorKind<'tcx> {
pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
use self::TerminatorKind::*;
match self {
Goto { .. } => write!(fmt, "goto"),
SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr),
Return => write!(fmt, "return"),
GeneratorDrop => write!(fmt, "generator_drop"),
Resume => write!(fmt, "resume"),
Abort => write!(fmt, "abort"),
Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value),
Unreachable => write!(fmt, "unreachable"),
Drop { place, .. } => write!(fmt, "drop({:?})", place),
DropAndReplace { place, value, .. } => {
write!(fmt, "replace({:?} <- {:?})", place, value)
}
Call { func, args, destination, .. } => {
if let Some((destination, _)) = destination {
write!(fmt, "{:?} = ", destination)?;
}
write!(fmt, "{:?}(", func)?;
for (index, arg) in args.iter().enumerate() {
if index > 0 {
write!(fmt, ", ")?;
}
write!(fmt, "{:?}", arg)?;
}
write!(fmt, ")")
}
Assert { cond, expected, msg, .. } => {
write!(fmt, "assert(")?;
if !expected {
write!(fmt, "!")?;
}
write!(fmt, "{:?}, ", cond)?;
msg.fmt_assert_args(fmt)?;
write!(fmt, ")")
}
FalseEdge { .. } => write!(fmt, "falseEdge"),
FalseUnwind { .. } => write!(fmt, "falseUnwind"),
InlineAsm { template, ref operands, options, .. } => {
write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
for op in operands {
write!(fmt, ", ")?;
let print_late = |&late| if late { "late" } else { "" };
match op {
InlineAsmOperand::In { reg, value } => {
write!(fmt, "in({}) {:?}", reg, value)?;
}
InlineAsmOperand::Out { reg, late, place: Some(place) } => {
write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
}
InlineAsmOperand::Out { reg, late, place: None } => {
write!(fmt, "{}out({}) _", print_late(late), reg)?;
}
InlineAsmOperand::InOut {
reg,
late,
in_value,
out_place: Some(out_place),
} => {
write!(
fmt,
"in{}out({}) {:?} => {:?}",
print_late(late),
reg,
in_value,
out_place
)?;
}
InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
}
InlineAsmOperand::Const { value } => {
write!(fmt, "const {:?}", value)?;
}
InlineAsmOperand::SymFn { value } => {
write!(fmt, "sym_fn {:?}", value)?;
}
InlineAsmOperand::SymStatic { def_id } => {
write!(fmt, "sym_static {:?}", def_id)?;
}
}
}
write!(fmt, ", options({:?}))", options)
}
}
}
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
use self::TerminatorKind::*;
match *self {
Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
Goto { .. } => vec!["".into()],
SwitchInt { ref targets, switch_ty, .. } => ty::tls::with(|tcx| {
let param_env = ty::ParamEnv::empty();
let switch_ty = tcx.lift(switch_ty).unwrap();
let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
targets
.values
.iter()
.map(|&u| {
ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty)
.to_string()
.into()
})
.chain(iter::once("otherwise".into()))
.collect()
}),
Call { destination: Some(_), cleanup: Some(_), .. } => {
vec!["return".into(), "unwind".into()]
}
Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()],
Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()],
Call { destination: None, cleanup: None, .. } => vec![],
Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
Yield { drop: None, .. } => vec!["resume".into()],
DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => {
vec!["return".into()]
}
DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => {
vec!["return".into(), "unwind".into()]
}
Assert { cleanup: None, .. } => vec!["".into()],
Assert { .. } => vec!["success".into(), "unwind".into()],
FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
FalseUnwind { unwind: None, .. } => vec!["real".into()],
InlineAsm { destination: Some(_), .. } => vec!["".into()],
InlineAsm { destination: None, .. } => vec![],
}
}
}