use log::Level;
use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::ty::{Ty, TyKind};
use rustc_index::vec::IndexVec;
use rustc_target::abi::VariantIdx;
use syntax::source_map::{DUMMY_SP, Spanned};
use crate::analysis::labeled_ty::{LabeledTy, LabeledTyCtxt};
use super::constraint::{ConstraintSet, Perm};
use super::context::{Ctxt, Instantiation};
use super::{FnSig, LFnSig, LTy, PermVar, Var};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Label<'lty> {
None,
Ptr(Perm<'lty>),
FnDef(usize),
}
impl<'lty> Label<'lty> {
fn perm(&self) -> Perm<'lty> {
match *self {
Label::Ptr(p) => p,
_ => panic!("expected Label::Ptr"),
}
}
}
type ITy<'lty, 'tcx> = LabeledTy<'lty, 'tcx, Label<'lty>>;
type IFnSig<'lty, 'tcx> = FnSig<'lty, 'tcx, Label<'lty>>;
pub struct IntraCtxt<'c, 'lty, 'a: 'lty, 'tcx: 'a> {
cx: &'c mut Ctxt<'lty, 'tcx>,
ilcx: LabeledTyCtxt<'lty, Label<'tcx>>,
def_id: DefId,
mir: &'a Body<'tcx>,
bbid: BasicBlock,
stmt_idx: usize,
cset: ConstraintSet<'lty>,
local_tys: IndexVec<Local, Spanned<ITy<'lty, 'tcx>>>,
next_local_var: u32,
insts: Vec<Instantiation>,
next_inst_var: u32,
}
impl<'c, 'lty, 'a: 'lty, 'tcx: 'a> IntraCtxt<'c, 'lty, 'a, 'tcx> {
pub fn new(
cx: &'c mut Ctxt<'lty, 'tcx>,
def_id: DefId,
mir: &'a Body<'tcx>,
) -> IntraCtxt<'c, 'lty, 'a, 'tcx> {
let ilcx = LabeledTyCtxt::new(cx.arena);
IntraCtxt {
cx,
ilcx,
def_id,
mir,
bbid: START_BLOCK,
stmt_idx: !0,
cset: ConstraintSet::new(),
local_tys: IndexVec::new(),
next_local_var: 0,
insts: Vec::new(),
next_inst_var: 0,
}
}
fn enter_block(&mut self, bbid: BasicBlock) {
self.bbid = bbid;
self.stmt_idx = !0;
}
fn enter_stmt(&mut self, idx: usize) {
self.stmt_idx = idx;
}
pub fn init(&mut self) {
let sig = self.cx.variant_func_sig(self.def_id);
let sig = self.relabel_sig(sig);
for (l, decl) in self.mir.local_decls.iter_enumerated() {
let lty = if l.index() == 0 {
sig.output
} else if l.index() - 1 < self.mir.arg_count {
if l.index() == self.mir.arg_count && sig.is_variadic {
self.ilcx.label(decl.ty, &mut |_| Label::None)
} else {
sig.inputs[l.index() - 1]
}
} else {
self.local_ty(decl.ty)
};
let span = match &decl.local_info {
LocalInfo::User(ClearCrossCrate::Set(binding)) => Some(binding),
_ => None,
}.map(|binding| match binding {
BindingForm::Var(var) => var.pat_span,
_ => DUMMY_SP,
}).unwrap_or(DUMMY_SP);
self.local_tys.push(Spanned {
node: lty,
span,
});
}
self.cset = self.cx.variant_summ(self.def_id).1.inst_cset.clone();
}
fn relabel_ty(&mut self, lty: LTy<'lty, 'tcx>) -> ITy<'lty, 'tcx> {
self.ilcx.relabel(lty, &mut |&l| match l {
Some(pv) => Label::Ptr(Perm::var(pv)),
None => Label::None,
})
}
fn relabel_sig(&mut self, sig: LFnSig<'lty, 'tcx>) -> IFnSig<'lty, 'tcx> {
let mut f = |&l: &Option<_>| match l {
Some(pv) => Label::Ptr(Perm::var(pv)),
None => Label::None,
};
FnSig {
inputs: self.ilcx.relabel_slice(sig.inputs, &mut f),
output: self.ilcx.relabel(sig.output, &mut f),
is_variadic: sig.is_variadic,
}
}
pub fn finish(mut self) {
debug!(" original constraints:");
if log_enabled!(Level::Debug) {
for &(a, b) in self.cset.iter() {
debug!(" {:?} <= {:?}", a, b);
}
}
self.cset.remove_useless();
self.cset.simplify_min_lhs(self.cx.arena);
self.cset.retain_perms(self.cx.arena, |p| match p {
Perm::LocalVar(_) => false,
_ => true,
});
self.cset.simplify(self.cx.arena);
debug!(" simplified constraints:");
if log_enabled!(Level::Debug) {
for &(a, b) in self.cset.iter() {
debug!(" {:?} <= {:?}", a, b);
}
}
let mut f = |&l: &Label<'lty>| match l {
Label::Ptr(p) => p.as_var().into_iter().find(|pv| match pv {
PermVar::Local(_) => true,
_ => false,
}),
_ => None,
};
let relabeled_locals = self.local_tys
.raw
.iter()
.filter_map(|spanned_ity| {
if spanned_ity.span == DUMMY_SP {
None
} else {
Some((spanned_ity.span, self.cx.lcx.relabel(spanned_ity.node, &mut f)))
}
})
.collect();
let (func, var) = self.cx.variant_summ(self.def_id);
var.inst_cset = self.cset;
var.insts = self.insts;
func.locals = relabeled_locals;
}
fn local_ty(&mut self, ty: Ty<'tcx>) -> ITy<'lty, 'tcx> {
let Self {
ref mut cx,
ref mut ilcx,
ref mut next_local_var,
ref mut next_inst_var,
ref mut insts,
..
} = *self;
ilcx.label(ty, &mut |ty| match ty.kind {
TyKind::Ref(_, _, _) | TyKind::RawPtr(_) => {
let v = Var(*next_local_var);
*next_local_var += 1;
Label::Ptr(Perm::LocalVar(v))
}
TyKind::FnDef(def_id, _) => {
let (func, var) = cx.variant_summ(def_id);
let num_vars = func.num_sig_vars;
let inst_idx = insts.len();
insts.push(Instantiation {
callee: var.func_id,
span: None,
first_inst_var: *next_inst_var,
});
*next_inst_var += num_vars;
Label::FnDef(inst_idx)
}
_ => Label::None,
})
}
fn local_var_ty(&mut self, l: Local) -> ITy<'lty, 'tcx> {
self.local_tys[l].node
}
fn static_ty(&mut self, def_id: DefId) -> ITy<'lty, 'tcx> {
let lty = self.cx.static_ty(def_id);
self.relabel_ty(lty)
}
fn place_lty(&mut self, lv: &Place<'tcx>) -> (ITy<'lty, 'tcx>, Perm<'lty>) {
let (ty, perm, variant) = self.place_lty_downcast(lv);
assert!(variant.is_none(), "expected non-Downcast result");
(ty, perm)
}
fn place_lty_downcast(
&mut self,
lv: &Place<'tcx>,
) -> (ITy<'lty, 'tcx>, Perm<'lty>, Option<VariantIdx>) {
if !lv.projection.is_empty() {
let mut projection = lv.projection.to_vec();
let last_elem = projection.pop().unwrap();
let parent = Place {
base: lv.base.clone(),
projection: self.cx.tcx.intern_place_elems(&projection),
};
let (base_ty, base_perm, base_variant) = self.place_lty_downcast(&parent);
match last_elem {
ProjectionElem::Field(..) => {}
_ => assert!(base_variant.is_none(), "expected non-Downcast result"),
}
match last_elem {
ProjectionElem::Deref => (
base_ty.args[0],
self.cx.min_perm(base_perm, base_ty.label.perm()),
None,
),
ProjectionElem::Field(f, _) => (
self.field_lty(
base_ty,
base_variant.unwrap_or(VariantIdx::from_usize(0)),
f,
),
base_perm,
None,
),
ProjectionElem::Index(ref _index_op) => (base_ty.args[0], base_perm, None),
ProjectionElem::ConstantIndex { .. } => unimplemented!(),
ProjectionElem::Subslice { .. } => unimplemented!(),
ProjectionElem::Downcast(_, variant) => (base_ty, base_perm, Some(variant)),
}
} else {
match lv.base {
PlaceBase::Local(l) => (self.local_var_ty(l), Perm::move_(), None),
PlaceBase::Static(ref s) => match s.kind {
StaticKind::Static => (self.static_ty(s.def_id), Perm::move_(), None),
StaticKind::Promoted(ref _p, _) => {
let pty = lv.ty(self.mir, self.cx.tcx);
let ty = pty.ty;
(self.local_ty(ty), Perm::read(), None)
}
},
}
}
}
fn field_lty(&mut self, base_ty: ITy<'lty, 'tcx>, v: VariantIdx, f: Field) -> ITy<'lty, 'tcx> {
match base_ty.ty.kind {
TyKind::Adt(adt, _substs) => {
let field_def = &adt.variants[v].fields[f.index()];
let poly_ty = self.static_ty(field_def.did);
self.ilcx.subst(poly_ty, &base_ty.args)
}
TyKind::Tuple(_tys_) => base_ty.args[f.index()],
_ => unimplemented!(),
}
}
fn rvalue_lty(&mut self, rv: &Rvalue<'tcx>) -> (ITy<'lty, 'tcx>, Perm<'lty>) {
let ty = rv.ty(self.mir, self.cx.tcx);
match *rv {
Rvalue::Use(ref op) => self.operand_lty(op),
Rvalue::Repeat(ref op, _len) => {
let arr_ty = self.local_ty(ty);
let (op_ty, op_perm) = self.operand_lty(op);
self.propagate(arr_ty.args[0], op_ty, op_perm);
(arr_ty, Perm::move_())
}
Rvalue::Ref(_, _, ref lv) => {
let (ty, perm) = self.place_lty(lv);
let args = self.ilcx.mk_slice(&[ty]);
let ref_ty = self
.ilcx
.mk(rv.ty(self.mir, self.cx.tcx), args, Label::Ptr(perm));
(ref_ty, Perm::move_())
}
Rvalue::Len(_) => (self.local_ty(ty), Perm::move_()),
Rvalue::Cast(_, ref op, cast_raw_ty) => {
let cast_ty = self.local_ty(cast_raw_ty);
let (op_ty, op_perm) = self.operand_lty(op);
self.propagate(cast_ty, op_ty, Perm::move_());
(cast_ty, op_perm)
}
Rvalue::BinaryOp(op, ref a, ref _b) | Rvalue::CheckedBinaryOp(op, ref a, ref _b) => {
match op {
BinOp::Add
| BinOp::Sub
| BinOp::Mul
| BinOp::Div
| BinOp::Rem
| BinOp::BitXor
| BinOp::BitAnd
| BinOp::BitOr
| BinOp::Shl
| BinOp::Shr
| BinOp::Eq
| BinOp::Lt
| BinOp::Le
| BinOp::Ne
| BinOp::Ge
| BinOp::Gt => (self.local_ty(ty), Perm::move_()),
BinOp::Offset => self.operand_lty(a),
}
}
Rvalue::NullaryOp(_op, _ty) => unimplemented!(),
Rvalue::UnaryOp(op, ref _a) => match op {
UnOp::Not | UnOp::Neg => (self.local_ty(ty), Perm::move_()),
},
Rvalue::Discriminant(ref _lv) => unimplemented!(),
Rvalue::Aggregate(ref kind, ref ops) => match **kind {
AggregateKind::Array(_elem_ty) => {
let array_ty = self.local_ty(ty);
for op in ops {
let (op_ty, op_perm) = self.operand_lty(op);
self.propagate(array_ty.args[0], op_ty, op_perm);
}
(array_ty, Perm::move_())
}
AggregateKind::Tuple => {
let tuple_ty = self.local_ty(ty);
for (&elem_ty, op) in tuple_ty.args.iter().zip(ops.iter()) {
let (op_ty, op_perm) = self.operand_lty(op);
self.propagate(elem_ty, op_ty, op_perm);
}
(tuple_ty, Perm::move_())
}
AggregateKind::Adt(adt, disr, _substs, _annot, union_variant) => {
let adt_ty = self.local_ty(ty);
if let Some(union_variant) = union_variant {
assert!(ops.len() == 1);
let field_def_id = adt.non_enum_variant().fields[union_variant].did;
let poly_field_ty = self.static_ty(field_def_id);
let field_ty = self.ilcx.subst(poly_field_ty, adt_ty.args);
let (op_ty, op_perm) = self.operand_lty(&ops[0]);
self.propagate(field_ty, op_ty, op_perm);
} else {
for (i, op) in ops.iter().enumerate() {
let field_def_id = adt.variants[disr].fields[i].did;
let poly_field_ty = self.static_ty(field_def_id);
let field_ty = self.ilcx.subst(poly_field_ty, adt_ty.args);
let (op_ty, op_perm) = self.operand_lty(op);
self.propagate(field_ty, op_ty, op_perm);
}
}
(adt_ty, Perm::move_())
}
AggregateKind::Closure(_, _) => unimplemented!(),
AggregateKind::Generator(_, _, _) => unimplemented!(),
},
}
}
fn operand_lty(&mut self, op: &Operand<'tcx>) -> (ITy<'lty, 'tcx>, Perm<'lty>) {
match *op {
Operand::Copy(ref lv) => self.place_lty(lv),
Operand::Move(ref lv) => self.place_lty(lv),
Operand::Constant(ref c) => {
debug!("CONSTANT {:?}: type = {:?}", c, c.literal.ty);
let lty = self.local_ty(c.literal.ty);
if let Label::FnDef(inst_idx) = lty.label {
self.insts[inst_idx].span = Some(c.span);
}
(lty, Perm::move_())
}
}
}
fn propagate(&mut self, lhs: ITy<'lty, 'tcx>, rhs: ITy<'lty, 'tcx>, path_perm: Perm<'lty>) {
if let (Label::Ptr(l_perm), Label::Ptr(r_perm)) = (lhs.label, rhs.label) {
self.propagate_perm(l_perm, r_perm);
let l_perm_capped = self.cx.min_perm(l_perm, Perm::write());
self.propagate_perm(l_perm_capped, path_perm);
} else if let (Label::FnDef(l_inst), Label::FnDef(r_inst)) = (lhs.label, rhs.label) {
self.unify_inst_vars(l_inst, r_inst);
}
if lhs.args.len() == rhs.args.len() {
for (&l_arg, &r_arg) in lhs.args.iter().zip(rhs.args.iter()) {
self.propagate_eq(l_arg, r_arg);
}
}
}
fn propagate_eq(&mut self, lhs: ITy<'lty, 'tcx>, rhs: ITy<'lty, 'tcx>) {
if let (Label::Ptr(l_perm), Label::Ptr(r_perm)) = (lhs.label, rhs.label) {
self.propagate_perm(l_perm, r_perm);
self.propagate_perm(r_perm, l_perm);
} else if let (Label::FnDef(l_inst), Label::FnDef(r_inst)) = (lhs.label, rhs.label) {
self.unify_inst_vars(l_inst, r_inst);
}
if lhs.args.len() == rhs.args.len() {
for (&l_arg, &r_arg) in lhs.args.iter().zip(rhs.args.iter()) {
self.propagate_eq(l_arg, r_arg);
}
}
}
fn propagate_perm(&mut self, p1: Perm<'lty>, p2: Perm<'lty>) {
debug!("ADD: {:?} <= {:?}", p1, p2);
self.cset.add(p1, p2);
}
fn unify_inst_vars(&mut self, idx1: usize, idx2: usize) {
let (callee, first1, first2) = {
let inst1 = &self.insts[idx1];
let inst2 = &self.insts[idx2];
assert!(
inst1.callee == inst2.callee,
"impossible - tried to unify unequal FnDefs ({:?} != {:?})",
inst1.callee,
inst2.callee
);
if inst1.first_inst_var == inst2.first_inst_var {
return;
}
(inst1.callee, inst1.first_inst_var, inst2.first_inst_var)
};
let num_vars = self.cx.variant_summ(callee).0.num_sig_vars;
for offset in 0..num_vars {
let p1 = Perm::InstVar(Var(first1 + offset));
let p2 = Perm::InstVar(Var(first2 + offset));
self.propagate_perm(p1, p2);
self.propagate_perm(p2, p1);
}
}
fn ty_fn_sig(&mut self, ty: ITy<'lty, 'tcx>) -> IFnSig<'lty, 'tcx> {
match ty.ty.kind {
TyKind::FnDef(did, _substs) => {
let idx = expect!([ty.label] Label::FnDef(idx) => idx);
let var_base = self.insts[idx].first_inst_var;
let sig = self.cx.variant_func_sig(did);
let mut f = |p: &Option<_>| {
match *p {
Some(PermVar::Sig(v)) => Label::Ptr(Perm::InstVar(Var(var_base + v.0))),
Some(_) => panic!("found non-Sig PermVar in sig"),
None => Label::None,
}
};
let poly_inputs = self.ilcx.relabel_slice(sig.inputs, &mut f);
let poly_output = self.ilcx.relabel(sig.output, &mut f);
FnSig {
inputs: self.ilcx.subst_slice(poly_inputs, ty.args),
output: self.ilcx.subst(poly_output, ty.args),
is_variadic: sig.is_variadic,
}
}
TyKind::FnPtr(ty_sig) => FnSig {
inputs: &ty.args[..ty.args.len() - 1],
output: ty.args[ty.args.len() - 1],
is_variadic: ty_sig.skip_binder().c_variadic,
},
TyKind::Closure(_, _) => unimplemented!(),
_ => panic!("expected FnDef, FnPtr, or Closure"),
}
}
pub fn handle_basic_block(&mut self, bbid: BasicBlock, bb: &BasicBlockData<'tcx>) {
self.enter_block(bbid);
debug!(" {:?}", bbid);
for (idx, s) in bb.statements.iter().enumerate() {
self.enter_stmt(idx);
match s.kind {
StatementKind::Assign(box(ref lv, ref rv)) => {
let (lv_ty, lv_perm) = self.place_lty(lv);
let (rv_ty, rv_perm) = self.rvalue_lty(rv);
self.propagate(lv_ty, rv_ty, rv_perm);
self.propagate_perm(Perm::write(), lv_perm);
debug!(" {:?}: {:?}", lv, lv_ty);
debug!(" ^-- {:?}: {:?}", rv, rv_ty);
},
StatementKind::FakeRead(..) |
StatementKind::SetDiscriminant { .. } |
StatementKind::StorageLive(_) |
StatementKind::StorageDead(_) |
StatementKind::InlineAsm { .. } |
StatementKind::Retag { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {},
}
}
match bb.terminator().kind {
TerminatorKind::Goto { .. }
| TerminatorKind::FalseEdges { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Resume
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
| TerminatorKind::Assert { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::Abort => {}
TerminatorKind::DropAndReplace {
ref location,
ref value,
..
} => {
let (loc_ty, loc_perm) = self.place_lty(location);
let (val_ty, val_perm) = self.operand_lty(value);
self.propagate(loc_ty, val_ty, val_perm);
self.propagate_perm(Perm::write(), loc_perm);
debug!(" {:?}: {:?}", location, loc_ty);
debug!(" ^-- {:?}: {:?}", value, val_ty);
}
TerminatorKind::Call {
ref func,
ref args,
ref destination,
..
} => {
debug!(" call {:?}", func);
let (func_ty, _func_perm) = self.operand_lty(func);
debug!("fty = {:?}", func_ty);
let sig = self.ty_fn_sig(func_ty);
for (&sig_ty, arg) in sig.inputs.iter().zip(args.iter()) {
let (arg_ty, arg_perm) = self.operand_lty(arg);
self.propagate(sig_ty, arg_ty, arg_perm);
debug!(" (arg): {:?}", sig_ty);
debug!(" ^-- {:?}: {:?}", arg, arg_ty);
}
if let Some((ref dest, _)) = *destination {
let sig_ty = sig.output;
let (dest_ty, dest_perm) = self.place_lty(dest);
self.propagate(dest_ty, sig_ty, Perm::move_());
self.propagate_perm(Perm::write(), dest_perm);
debug!(" {:?}: {:?}", dest, dest_ty);
debug!(" ^-- (return): {:?}", sig_ty);
}
}
}
}
}