use std::collections::HashMap;
use std::fmt;
use std::u32;
use arena::SyncDroplessArena;
use log::Level;
use rustc::hir;
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::hir::{Mutability, Node};
use rustc::ty::{TyCtxt, TyKind, TypeAndMut, TyS};
use rustc_index::vec::{Idx, IndexVec};
use syntax::ast::IntTy;
use syntax::source_map::Span;
use crate::analysis::labeled_ty::{LabeledTy, LabeledTyCtxt};
use crate::command::CommandState;
use crate::context::HirMap;
use crate::type_map;
use crate::RefactorCtxt;
mod annot;
pub mod constraint;
mod context;
mod inst;
mod inter;
mod intra;
mod mono;
mod debug;
use self::annot::{handle_attrs, handle_marks};
use self::constraint::*;
use self::context::Ctxt;
use self::inst::find_instantiations;
use self::inter::InterCtxt;
use self::intra::IntraCtxt;
use self::mono::compute_all_mono_sigs;
use self::debug::*;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Var(pub u32);
impl Idx for Var {
fn new(idx: usize) -> Var {
assert!(idx as u32 as usize == idx);
Var(idx as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
impl Var {
fn next(self) -> Self {
Var(self.0 + 1)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PermVar {
Static(Var),
Sig(Var),
Inst(Var),
Local(Var),
}
pub type LTy<'lty, 'tcx> = LabeledTy<'lty, 'tcx, Option<PermVar>>;
type LFnSig<'lty, 'tcx> = FnSig<'lty, 'tcx, Option<PermVar>>;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct FnSig<'lty, 'tcx, L: 'lty> {
pub inputs: &'lty [LabeledTy<'lty, 'tcx, L>],
pub output: LabeledTy<'lty, 'tcx, L>,
pub is_variadic: bool,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub enum ConcretePerm {
Read,
Write,
Move,
}
impl<'lty, 'tcx, L: fmt::Debug> type_map::Signature<LabeledTy<'lty, 'tcx, L>>
for FnSig<'lty, 'tcx, L>
{
fn num_inputs(&self) -> usize {
self.inputs.len()
}
fn input(&self, idx: usize) -> LabeledTy<'lty, 'tcx, L> {
self.inputs[idx]
}
fn output(&self) -> LabeledTy<'lty, 'tcx, L> {
self.output
}
}
fn is_fn(hir_map: &hir::map::Map, def_id: DefId) -> bool {
let n = match hir_map.get_if_local(def_id) {
None => return false,
Some(n) => n,
};
match n {
Node::Item(i) => match i.kind {
hir::ItemKind::Fn(..) => true,
_ => false,
},
Node::ForeignItem(i) => match i.kind {
hir::ForeignItemKind::Fn(..) => true,
_ => false,
},
Node::TraitItem(i) => match i.kind {
hir::TraitItemKind::Method(..) => true,
_ => false,
},
Node::ImplItem(i) => match i.kind {
hir::ImplItemKind::Method(..) => true,
_ => false,
},
_ => false,
}
}
fn analyze_intra<'a, 'tcx, 'lty>(
cx: &mut Ctxt<'lty, 'tcx>,
hir_map: &HirMap<'a, 'tcx>,
tcx: TyCtxt<'tcx>,
) {
for &def_id in tcx.mir_keys(LOCAL_CRATE).iter() {
if !is_fn(hir_map, def_id) {
continue;
}
let mir = tcx.optimized_mir(def_id);
let mut local_cx = IntraCtxt::new(cx, def_id, mir);
local_cx.init();
for (bbid, bb) in mir.basic_blocks().iter_enumerated() {
local_cx.handle_basic_block(bbid, bb);
}
local_cx.finish();
}
}
fn analyze_externs<'a, 'tcx, 'lty>(cx: &mut Ctxt<'lty, 'tcx>, hir_map: &HirMap<'a, 'tcx>) {
for (def_id, func_summ) in cx.funcs_mut() {
if func_summ.cset_provided {
continue;
}
match hir_map.get_if_local(*def_id) {
Some(Node::ForeignItem(i)) => match i.kind {
hir::ForeignItemKind::Fn(..) => {}
_ => continue,
},
_ => continue,
}
for &input in func_summ.sig.inputs {
if let Some(p) = input.label {
match input.ty.kind {
TyKind::Ref(_, _, Mutability::Mutable) => {
func_summ.sig_cset.add(Perm::Concrete(ConcretePerm::Move), Perm::var(p));
}
TyKind::RawPtr(TypeAndMut{mutbl: Mutability::Mutable, ..}) => {
func_summ.sig_cset.add(Perm::Concrete(ConcretePerm::Move), Perm::var(p));
}
_ => {}
}
}
}
func_summ.cset_provided = true;
}
}
fn analyze_inter<'lty, 'tcx>(cx: &mut Ctxt<'lty, 'tcx>) {
let mut inter_cx = InterCtxt::new(cx);
inter_cx.process();
inter_cx.finish();
}
fn is_mut_t(ty: &TyS) -> bool {
if let TyKind::RawPtr(mut_ty) = ty.kind {
if mut_ty.mutbl == Mutability::Mutable {
if let TyKind::Param(param_ty) = mut_ty.ty.kind {
return param_ty.name.as_str() == "T";
}
}
}
false
}
fn register_std_constraints<'a, 'tcx, 'lty>(
ctxt: &mut Ctxt<'lty, 'tcx>,
tctxt: TyCtxt<'tcx>,
) {
for (def_id, func_summ) in ctxt.funcs_mut() {
let fn_name_path = tctxt.def_path(*def_id).to_string_no_crate();
if func_summ.sig.inputs.len() == 2 && fn_name_path == "::ptr[0]::{{impl}}[1]::offset[0]" {
let param0_is_mut_t = is_mut_t(func_summ.sig.inputs[0].ty);
let param1_is_isize = if let TyKind::Int(int_ty) = func_summ.sig.inputs[1].ty.kind {
int_ty == IntTy::Isize
} else {
false
};
let ret_is_mut_t = is_mut_t(func_summ.sig.output.ty);
if param0_is_mut_t && param1_is_isize && ret_is_mut_t {
func_summ.cset_provided = true;
func_summ.sig_cset.add(Perm::SigVar(Var(1)), Perm::SigVar(Var(0)));
}
}
}
}
pub fn analyze<'lty, 'a: 'lty, 'tcx: 'a>(
st: &CommandState,
dcx: &RefactorCtxt<'a, 'tcx>,
arena: &'lty SyncDroplessArena,
) -> AnalysisResult<'lty, 'tcx> {
let mut cx = Ctxt::new(dcx.ty_ctxt(), arena);
handle_attrs(&mut cx, st, dcx);
handle_marks(&mut cx, st, dcx);
analyze_intra(&mut cx, &dcx.hir_map(), dcx.ty_ctxt());
analyze_externs(&mut cx, &dcx.hir_map());
register_std_constraints(&mut cx, dcx.ty_ctxt());
analyze_inter(&mut cx);
compute_all_mono_sigs(&mut cx);
find_instantiations(&mut cx);
cx.into()
}
pub type VTy<'lty, 'tcx> = LabeledTy<'lty, 'tcx, Option<Var>>;
pub type VFnSig<'lty, 'tcx> = FnSig<'lty, 'tcx, Option<Var>>;
pub type PTy<'lty, 'tcx> = LabeledTy<'lty, 'tcx, Option<ConcretePerm>>;
pub type PFnSig<'lty, 'tcx> = FnSig<'lty, 'tcx, Option<ConcretePerm>>;
pub struct AnalysisResult<'lty, 'tcx> {
pub statics: HashMap<DefId, PTy<'lty, 'tcx>>,
pub funcs: HashMap<DefId, FunctionResult<'lty, 'tcx>>,
pub variants: HashMap<DefId, VariantResult>,
pub monos: HashMap<(DefId, usize), MonoResult>,
arena: &'lty SyncDroplessArena,
}
#[derive(Debug)]
pub struct FunctionResult<'lty, 'tcx: 'lty> {
pub sig: VFnSig<'lty, 'tcx>,
pub locals: HashMap<Span, VTy<'lty, 'tcx>>,
pub local_assign: IndexVec<Var, ConcretePerm>,
pub num_sig_vars: u32,
pub cset: ConstraintSet<'lty>,
pub variants: Option<Vec<DefId>>,
pub num_monos: usize,
}
#[derive(Debug)]
pub struct VariantResult {
pub func_id: DefId,
pub index: usize,
pub func_refs: Vec<FuncRef>,
}
#[derive(Debug)]
pub struct FuncRef {
pub def_id: DefId,
pub span: Option<Span>,
}
#[derive(Debug)]
pub struct MonoResult {
pub suffix: String,
pub assign: IndexVec<Var, ConcretePerm>,
pub callee_mono_idxs: Vec<usize>,
}
impl<'lty, 'tcx> AnalysisResult<'lty, 'tcx> {
pub fn fn_results(&self, id: DefId) -> (&FunctionResult<'lty, 'tcx>, &VariantResult) {
let vr = &self.variants[&id];
let fr = &self.funcs[&vr.func_id];
(fr, vr)
}
pub fn arena(&self) -> &'lty SyncDroplessArena {
self.arena
}
}
impl<'lty, 'tcx> From<Ctxt<'lty, 'tcx>> for AnalysisResult<'lty, 'tcx> {
fn from(cx: Ctxt<'lty, 'tcx>) -> AnalysisResult<'lty, 'tcx> {
let mut statics = HashMap::new();
let mut funcs = HashMap::new();
let mut variants = HashMap::new();
let mut monos = HashMap::new();
let perm_lcx = LabeledTyCtxt::new(cx.arena);
for (&def_id, <y) in cx.static_summ.iter() {
let pty = perm_lcx.relabel(lty, &mut |p| {
if let Some(PermVar::Static(v)) = *p {
Some(cx.static_assign[v])
} else {
None
}
});
statics.insert(def_id, pty);
}
let var_lcx = LabeledTyCtxt::new(cx.arena);
for def_id in cx.func_ids() {
let func = cx.get_func_summ(def_id);
let sig = {
let mut f = |p: &Option<_>| {
if let Some(PermVar::Sig(v)) = *p {
Some(v)
} else {
None
}
};
FnSig {
inputs: var_lcx.relabel_slice(func.sig.inputs, &mut f),
output: var_lcx.relabel(func.sig.output, &mut f),
is_variadic: func.sig.is_variadic,
}
};
let variant_ids = if func.variant_ids.len() == 1 {
None
} else {
Some(func.variant_ids.clone())
};
let mut f = |p: &Option<PermVar>| -> Option<Var> {
if let Some(PermVar::Local(v)) = *p {
Some(v)
} else {
None
}
};
let locals = func.locals
.iter()
.map(|(&span, lty)| (span, var_lcx.relabel(<y, &mut f)))
.collect();
funcs.insert(
def_id,
FunctionResult {
sig,
locals,
num_sig_vars: func.num_sig_vars,
cset: func.sig_cset.clone(),
variants: variant_ids,
num_monos: func.num_monos,
local_assign: func.local_assign.clone(),
},
);
for (idx, &var_id) in func.variant_ids.iter().enumerate() {
let variant = cx.get_variant_summ(var_id);
let func_refs = variant
.insts
.iter()
.map(|inst| FuncRef {
def_id: inst.callee,
span: inst.span,
})
.collect();
variants.insert(
var_id,
VariantResult {
func_id: def_id,
index: idx,
func_refs,
},
);
}
let mut suffixes = Vec::new();
if func.monos_provided {
} else if func.num_monos == 1 {
suffixes.push(String::new());
} else {
static SUFFIX_BASE: [&str; 3] = ["", "mut", "take"];
let mut suffix_count = [0, 0, 0];
let is_output = mono::infer_outputs(func);
for idx in 0..func.num_monos {
let mono = cx.get_mono_summ(def_id, idx);
let max_perm = is_output
.iter_enumerated()
.filter(|&(_, &out)| out)
.map(|(v, _)| mono.assign[v])
.max()
.unwrap_or(ConcretePerm::Read);
let idx = max_perm as usize;
suffix_count[idx] += 1;
let suffix = if suffix_count[idx] == 1 {
SUFFIX_BASE[idx].to_owned()
} else {
format!("{}{}", SUFFIX_BASE[idx], suffix_count[idx])
};
suffixes.push(suffix);
}
}
for idx in 0..func.num_monos {
let mono = cx.get_mono_summ(def_id, idx);
let suffix = if func.monos_provided {
mono.suffix.clone()
} else {
suffixes[idx].clone()
};
monos.insert(
(def_id, idx),
MonoResult {
suffix,
assign: mono.assign.clone(),
callee_mono_idxs: mono.callee_mono_idxs.clone(),
},
);
}
}
let Ctxt { arena, .. } = cx;
AnalysisResult {
statics,
funcs,
variants,
monos,
arena,
}
}
}
pub fn dump_results(dcx: &RefactorCtxt, results: &AnalysisResult) {
debug!("\n === summary ===");
let arena = SyncDroplessArena::default();
let new_lcx = LabeledTyCtxt::new(&arena);
let format_sig = |sig: VFnSig, assign: &IndexVec<Var, ConcretePerm>| {
let mut func = |p: &Option<_>| p.as_ref().map(|&v| assign[v]);
let inputs = new_lcx.relabel_slice(sig.inputs, &mut func);
let output = new_lcx.relabel(sig.output, &mut func);
format!("{:?} -> {:?}", pretty_slice(inputs), Pretty(output))
};
let path_str = |def_id| dcx.ty_ctxt().def_path(def_id).to_string_no_crate();
let mut ids = results.statics.keys().cloned().collect::<Vec<_>>();
ids.sort();
for id in ids {
let ty = results.statics[&id];
debug!("static {} :: {:?}", path_str(id), Pretty(ty));
}
let mut ids = results.funcs.keys().cloned().collect::<Vec<_>>();
ids.sort();
for id in ids {
let fr = &results.funcs[&id];
debug!("func {}:", path_str(id));
debug!(" sig constraints:");
if log_enabled!(Level::Debug) {
for &(a, b) in fr.cset.iter() {
debug!(" {:?} <= {:?}", a, b);
}
}
if let Some(ref var_ids) = fr.variants {
for (i, &var_id) in var_ids.iter().enumerate() {
debug!(" variant {}: {}", i, path_str(var_id));
let vr = &results.variants[&var_id];
for (j, func_ref) in vr.func_refs.iter().enumerate() {
let callee_fr = &results.funcs[&func_ref.def_id];
debug!(
" call #{}: {:?} :: {:?}",
j,
path_str(func_ref.def_id),
callee_fr.sig
);
debug!(" (at {:?})", func_ref.span);
}
}
} else {
debug!(" single variant");
let vr = &results.variants[&id];
for (j, func_ref) in vr.func_refs.iter().enumerate() {
let callee_fr = &results.funcs[&func_ref.def_id];
debug!(
" call #{}: {:?} :: {:?}",
j,
path_str(func_ref.def_id),
callee_fr.sig
);
debug!(" (at {:?})", func_ref.span);
}
}
for i in 0..fr.num_monos {
let mr = &results.monos[&(id, i)];
let var_id = fr.variants.as_ref().map_or(id, |vars| vars[i]);
let vr = &results.variants[&var_id];
debug!(
" mono #{} ({:?}): {}",
i,
mr.suffix,
format_sig(fr.sig, &mr.assign)
);
for (j, (func_ref, &mono_idx)) in vr
.func_refs
.iter()
.zip(mr.callee_mono_idxs.iter())
.enumerate()
{
let callee_fr = &results.funcs[&func_ref.def_id];
debug!(
" call #{}: {:?} #{} :: {}",
j,
path_str(func_ref.def_id),
mono_idx,
format_sig(
callee_fr.sig,
&results.monos[&(func_ref.def_id, mono_idx)].assign
)
);
debug!(" (at {:?})", func_ref.span);
}
}
}
}