use rustc_middle::mir::Local;
use rustc_middle::ty::Ty;
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum PlaceBase {
Return,
Arg(usize),
Local(usize),
}
impl PlaceBase {
pub(crate) fn to_local(&self) -> Local {
match self {
PlaceBase::Return => Local::from_usize(0),
PlaceBase::Arg(n) => Local::from_usize(*n + 1),
PlaceBase::Local(n) => Local::from_usize(*n),
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum ContractProjection<'tcx> {
Field { index: usize, ty: Option<Ty<'tcx>> },
Downcast { variant_index: usize },
ForEach,
}
#[derive(Clone, Debug)]
pub(crate) struct ContractPlace<'tcx> {
pub base: PlaceBase,
pub projections: Vec<ContractProjection<'tcx>>,
}
impl<'tcx> ContractPlace<'tcx> {
pub(crate) fn local(base: usize, fields: Vec<(usize, Ty<'tcx>)>) -> Self {
Self {
base: if base == 0 {
PlaceBase::Return
} else {
PlaceBase::Local(base)
},
projections: fields
.into_iter()
.map(|(index, ty)| ContractProjection::Field {
index,
ty: Some(ty),
})
.collect(),
}
}
pub(crate) fn arg(index: usize) -> Self {
Self {
base: PlaceBase::Arg(index),
projections: Vec::new(),
}
}
pub(crate) fn local_base(&self) -> Option<usize> {
match self.base {
PlaceBase::Return => Some(0),
PlaceBase::Local(local) => Some(local),
PlaceBase::Arg(_) => None,
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum NumericBinOp {
Add,
Sub,
Mul,
Div,
Rem,
Min,
Max,
BitAnd,
BitOr,
BitXor,
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum NumericUnaryOp {
Not,
Neg,
}
#[derive(Clone, Debug)]
pub(crate) enum ContractExpr<'tcx> {
Place(ContractPlace<'tcx>),
Const(u128),
ConstParam {
index: u32,
name: String,
},
SizeOf(Ty<'tcx>),
AlignOf(Ty<'tcx>),
Len(Box<ContractExpr<'tcx>>),
IndexAccess {
slice: Box<ContractExpr<'tcx>>,
index: Box<ContractExpr<'tcx>>,
},
Binary {
op: NumericBinOp,
lhs: Box<ContractExpr<'tcx>>,
rhs: Box<ContractExpr<'tcx>>,
},
Unary {
op: NumericUnaryOp,
expr: Box<ContractExpr<'tcx>>,
},
If {
cond: Box<NumericPredicate<'tcx>>,
then_expr: Box<ContractExpr<'tcx>>,
else_expr: Box<ContractExpr<'tcx>>,
},
Unknown,
}
impl<'tcx> ContractExpr<'tcx> {
pub(crate) fn new_value(value: usize) -> Self {
Self::Const(value as u128)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum RelOp {
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
}
#[derive(Clone, Debug)]
pub(crate) struct NumericPredicate<'tcx> {
pub lhs: ContractExpr<'tcx>,
pub op: RelOp,
pub rhs: ContractExpr<'tcx>,
}
impl<'tcx> NumericPredicate<'tcx> {
pub(crate) fn new(lhs: ContractExpr<'tcx>, op: RelOp, rhs: ContractExpr<'tcx>) -> Self {
Self { lhs, op, rhs }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum PropertyKind {
Align,
Size,
NoPadding,
NonNull,
Allocated,
InBound,
NonOverlap,
ValidNum,
ValidString,
ValidCStr,
Init,
Unwrap,
Typed,
Owning,
Alias,
Alive,
Pinned,
NonVolatile,
Opened,
Null,
Trait,
Unreachable,
ValidTransmute,
SplitTransmute,
ContainNoType,
NoRawPtr,
NoInternalMut,
UniInternalMut,
AtomicUpdate,
RefSend,
Unknown,
}
#[derive(Clone, Debug)]
pub(crate) enum PropertyArg<'tcx> {
Ty(Ty<'tcx>),
Expr(ContractExpr<'tcx>),
Predicates(Vec<NumericPredicate<'tcx>>),
Ident(String),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum ContractKind {
Precond,
Hazard,
Option_,
}
#[derive(Clone, Debug)]
pub(crate) struct ContractOrigin {
pub name: String,
pub args: Vec<String>,
pub meaning: Option<String>,
}
#[derive(Clone, Debug)]
pub(crate) enum Property<'tcx> {
Atom(AtomProperty<'tcx>),
And(AndProperty<'tcx>),
Or(OrProperty<'tcx>),
}
#[derive(Clone, Debug)]
pub(crate) struct AtomProperty<'tcx> {
pub kind: PropertyKind,
pub args: Vec<PropertyArg<'tcx>>,
pub contract_kind: ContractKind,
pub for_each: Option<ContractPlace<'tcx>>,
pub origin: Option<ContractOrigin>,
}
#[derive(Clone, Debug)]
pub(crate) struct AndProperty<'tcx> {
pub conjuncts: Vec<Box<Property<'tcx>>>,
pub contract_kind: ContractKind,
pub origin: Option<ContractOrigin>,
}
#[derive(Clone, Debug)]
pub(crate) struct OrProperty<'tcx> {
pub disjuncts: Vec<Box<Property<'tcx>>>,
pub contract_kind: ContractKind,
pub origin: Option<ContractOrigin>,
}
impl<'tcx> Property<'tcx> {
pub(crate) fn new_atom(kind: PropertyKind, args: Vec<PropertyArg<'tcx>>) -> Self {
Self::Atom(AtomProperty {
kind,
args,
contract_kind: ContractKind::Precond,
for_each: None,
origin: None,
})
}
pub(crate) fn new_and(conjuncts: Vec<Property<'tcx>>) -> Self {
Self::And(AndProperty {
conjuncts: conjuncts.into_iter().map(Box::new).collect(),
contract_kind: ContractKind::Precond,
origin: None,
})
}
pub(crate) fn new_or(disjuncts: Vec<Property<'tcx>>) -> Self {
Self::Or(OrProperty {
disjuncts: disjuncts.into_iter().map(Box::new).collect(),
contract_kind: ContractKind::Precond,
origin: None,
})
}
pub(crate) fn conjunction(conjuncts: Vec<Property<'tcx>>) -> Self {
if conjuncts.len() == 1 {
conjuncts.into_iter().next().unwrap()
} else {
Self::new_and(conjuncts)
}
}
pub(crate) fn kind(&self) -> Option<PropertyKind> {
match self {
Property::Atom(a) => Some(a.kind),
Property::And(_) | Property::Or(_) => None,
}
}
pub(crate) fn args(&self) -> &[PropertyArg<'tcx>] {
match self {
Property::Atom(a) => &a.args,
Property::And(_) | Property::Or(_) => &[],
}
}
pub(crate) fn target_place(&self) -> Option<&ContractPlace<'tcx>> {
match self.args().first()? {
PropertyArg::Expr(ContractExpr::Place(cp)) => Some(cp),
PropertyArg::Expr(ContractExpr::IndexAccess { slice, .. }) => match slice.as_ref() {
ContractExpr::Place(cp) => Some(cp),
_ => None,
},
_ => None,
}
}
pub(crate) fn conjuncts(&self) -> &[Box<Property<'tcx>>] {
match self {
Property::And(a) => &a.conjuncts,
Property::Atom(_) | Property::Or(_) => &[],
}
}
pub(crate) fn disjuncts(&self) -> &[Box<Property<'tcx>>] {
match self {
Property::Or(o) => &o.disjuncts,
Property::Atom(_) | Property::And(_) => &[],
}
}
pub(crate) fn contract_kind(&self) -> ContractKind {
match self {
Property::Atom(a) => a.contract_kind,
Property::And(a) => a.contract_kind,
Property::Or(o) => o.contract_kind,
}
}
pub(crate) fn for_each(&self) -> Option<&ContractPlace<'tcx>> {
match self {
Property::Atom(a) => a.for_each.as_ref(),
Property::And(_) | Property::Or(_) => None,
}
}
pub(crate) fn origin(&self) -> Option<&ContractOrigin> {
match self {
Property::Atom(a) => a.origin.as_ref(),
Property::And(a) => a.origin.as_ref(),
Property::Or(o) => o.origin.as_ref(),
}
}
pub(crate) fn is_or(&self) -> bool {
matches!(self, Property::Or(_))
}
pub(crate) fn is_and(&self) -> bool {
matches!(self, Property::And(_))
}
pub(crate) fn apply_kind(&mut self, kind: Option<&str>) {
let target = match self {
Property::Atom(a) => &mut a.contract_kind,
Property::And(a) => &mut a.contract_kind,
Property::Or(o) => &mut o.contract_kind,
};
match kind {
Some("hazard") => *target = ContractKind::Hazard,
Some("option") => *target = ContractKind::Option_,
_ => {}
}
}
pub(crate) fn set_origin(&mut self, name: String, args: Vec<String>, meaning: Option<String>) {
let origin = ContractOrigin {
name,
args,
meaning,
};
match self {
Property::Atom(a) => a.origin = Some(origin),
Property::And(a) => a.origin = Some(origin),
Property::Or(o) => o.origin = Some(origin),
}
}
pub(crate) fn clear_origin(&mut self) {
match self {
Property::Atom(a) => a.origin = None,
Property::And(a) => a.origin = None,
Property::Or(o) => o.origin = None,
}
}
pub(crate) fn set_for_each(&mut self, place: Option<ContractPlace<'tcx>>) {
if let Property::Atom(a) = self {
a.for_each = place;
}
}
pub(crate) fn set_contract_kind(&mut self, k: ContractKind) {
match self {
Property::Atom(a) => a.contract_kind = k,
Property::And(a) => a.contract_kind = k,
Property::Or(o) => o.contract_kind = k,
}
}
}