use std::collections::HashMap;
use std::collections::HashSet;
use arena::SyncDroplessArena;
use rustc::hir::def_id::DefId;
use rustc_index::vec::IndexVec;
use syntax::ast::*;
use syntax::source_map::DUMMY_SP;
use syntax::mut_visit::{self, MutVisitor};
use syntax::token::{self, Token, TokenKind, DelimToken};
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax::tokenstream::{TokenTree, TokenStream, DelimSpan};
use smallvec::{smallvec, SmallVec};
use crate::ast_manip::{MutVisitNodes, MutVisit};
use crate::ast_manip::fn_edit::flat_map_fns;
use crate::analysis::labeled_ty::LabeledTyCtxt;
use crate::analysis::ownership::{self, ConcretePerm, Var, PTy};
use crate::analysis::ownership::constraint::{ConstraintSet, Perm};
use crate::command::{CommandState, Registry, DriverCommand};
use crate::context::HirMap;
use crate::driver::{Phase};
use crate::RefactorCtxt;
use crate::type_map;
use c2rust_ast_builder::{mk, IntoSymbol};
pub fn register_commands(reg: &mut Registry) {
reg.register("ownership_annotate", |args| {
let label = args.get(0).map_or("target", |x| x).into_symbol();
Box::new(DriverCommand::new(Phase::Phase3, move |st, cx| {
do_annotate(st, cx, label);
}))
});
reg.register("ownership_split_variants", |args| {
let label = args.get(0).map_or("target", |x| x).into_symbol();
Box::new(DriverCommand::new(Phase::Phase3, move |st, cx| {
do_split_variants(st, cx, label);
}))
});
reg.register("ownership_mark_pointers", |_args| {
Box::new(DriverCommand::new(Phase::Phase3, move |st, cx| {
do_mark_pointers(st, cx);
}))
});
}
fn do_annotate(st: &CommandState,
cx: &RefactorCtxt,
label: Symbol) {
let arena = SyncDroplessArena::default();
let analysis = ownership::analyze(&st, &cx, &arena);
struct AnnotateFolder<'a, 'tcx: 'a> {
label: Symbol,
ana: ownership::AnalysisResult<'tcx, 'tcx>,
hir_map: HirMap<'a, 'tcx>,
st: &'a CommandState,
}
impl<'lty, 'a, 'tcx> AnnotateFolder<'a, 'tcx> {
fn static_attr_for(&self, id: NodeId) -> Option<Attribute> {
self.hir_map.opt_local_def_id_from_node_id(id)
.and_then(|def_id| self.ana.statics.get(&def_id))
.and_then(|&ty| build_static_attr(ty))
}
fn constraints_attr_for(&self, id: NodeId) -> Option<Attribute> {
self.hir_map.opt_local_def_id_from_node_id(id)
.and_then(|def_id| self.ana.funcs.get(&def_id))
.map(|fr| build_constraints_attr(&fr.cset))
}
fn push_mono_attrs_for(&self, id: NodeId, dest: &mut Vec<Attribute>) {
if let Some((def_id, (fr, vr))) = self.hir_map.opt_local_def_id_from_node_id(id)
.map(|def_id| (def_id, self.ana.fn_results(def_id))) {
if fr.num_sig_vars == 0 {
return;
}
if fr.variants.is_none() {
for idx in 0 .. fr.num_monos {
let mr = &self.ana.monos[&(def_id, idx)];
dest.push(build_mono_attr(&mr.suffix, &mr.assign));
}
} else {
let mr = &self.ana.monos[&(def_id, vr.index)];
dest.push(build_mono_attr(&mr.suffix, &mr.assign));
}
}
}
fn clean_attrs(&self, attrs: &mut Vec<Attribute>) {
attrs.retain(|a| {
match &*a.name_or_empty().as_str() {
"ownership_mono" |
"ownership_constraints" |
"ownership_static" => false,
_ => true,
}
});
}
}
impl<'lty, 'a, 'tcx> MutVisitor for AnnotateFolder<'a, 'tcx> {
fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
if !self.st.marked(i.id, self.label) {
return mut_visit::noop_flat_map_item(i, self);
}
mut_visit::noop_flat_map_item(i.map(|mut i| {
match i.kind {
ItemKind::Static(..) | ItemKind::Const(..) => {
self.clean_attrs(&mut i.attrs);
if let Some(attr) = self.static_attr_for(i.id) {
i.attrs.push(attr);
}
},
ItemKind::Fn(..) => {
self.clean_attrs(&mut i.attrs);
if let Some(attr) = self.constraints_attr_for(i.id) {
i.attrs.push(attr);
}
self.push_mono_attrs_for(i.id, &mut i.attrs);
},
_ => {},
}
i
}), self)
}
fn flat_map_impl_item(&mut self, i: ImplItem) -> SmallVec<[ImplItem; 1]> {
if !self.st.marked(i.id, self.label) {
return mut_visit::noop_flat_map_impl_item(i, self);
}
mut_visit::noop_flat_map_impl_item(i, self)
}
fn flat_map_struct_field(&mut self, mut sf: StructField) -> SmallVec<[StructField; 1]> {
if !self.st.marked(sf.id, self.label) {
return mut_visit::noop_flat_map_struct_field(sf, self);
}
self.clean_attrs(&mut sf.attrs);
if let Some(attr) = self.static_attr_for(sf.id) {
sf.attrs.push(attr);
}
mut_visit::noop_flat_map_struct_field(sf, self)
}
}
st.map_krate(|krate| {
krate.visit(&mut AnnotateFolder {
label: label,
ana: analysis,
hir_map: cx.hir_map(),
st: st,
})
});
}
fn build_static_attr(ty: PTy) -> Option<Attribute> {
let mut args = Vec::new();
ty.for_each_label(&mut |p| {
if let Some(p) = *p {
args.push(perm_token(p));
}
});
let args = delimited(args).into();
Some(make_attr("ownership_static", args))
}
fn build_constraints_attr(cset: &ConstraintSet) -> Attribute {
let mut args = Vec::new();
fn push_perm_tokens(p: Perm, dest: &mut Vec<TokenTree>) {
match p {
Perm::Concrete(p) => dest.push(perm_token(p)),
Perm::SigVar(v) => dest.push(ident_token(&format!("_{}", v.0))),
Perm::Min(ps) => {
let mut ts = Vec::new();
for (i, &p) in ps.iter().enumerate() {
if i > 0 {
ts.push(token(TokenKind::Comma));
}
push_perm_tokens(p, &mut ts);
}
dest.push(ident_token("min"));
dest.push(parens(ts));
},
_ => panic!("unexpected var kind in fn constraints"),
}
}
for (i, &(a, b)) in cset.iter().enumerate() {
if i > 0 {
args.push(token(TokenKind::Comma));
}
args.push(ident_token("le"));
let mut le_args = Vec::new();
push_perm_tokens(a, &mut le_args);
le_args.push(token(TokenKind::Comma));
push_perm_tokens(b, &mut le_args);
args.push(parens(le_args));
}
make_attr("ownership_constraints", delimited(args))
}
fn build_mono_attr(suffix: &str, assign: &IndexVec<Var, ConcretePerm>) -> Attribute {
let mut args = Vec::new();
args.push(str_token(suffix));
for &p in assign.iter() {
args.push(token(TokenKind::Comma));
args.push(perm_token(p));
}
make_attr("ownership_mono", delimited(args))
}
fn perm_token(p: ConcretePerm) -> TokenTree {
let name = match p {
ConcretePerm::Read => "READ",
ConcretePerm::Write => "WRITE",
ConcretePerm::Move => "MOVE",
};
ident_token(name)
}
fn ident_token(name: &str) -> TokenTree {
token(TokenKind::Ident(Symbol::intern(name), false))
}
fn str_token(s: &str) -> TokenTree {
token(TokenKind::Literal(token::Lit {
kind: token::LitKind::Str,
symbol: s.into_symbol(),
suffix: None,
}))
}
fn token(kind: TokenKind) -> TokenTree {
TokenTree::Token(Token{kind, span: DUMMY_SP})
}
fn parens(ts: Vec<TokenTree>) -> TokenTree {
TokenTree::Delimited(
DelimSpan::dummy(),
DelimToken::Paren,
ts.into_iter().collect::<TokenStream>(),
)
}
fn delimited(ts: Vec<TokenTree>) -> MacArgs {
MacArgs::Delimited(
DelimSpan::dummy(),
MacDelimiter::Parenthesis,
ts.into_iter().collect::<TokenStream>(),
)
}
fn make_attr(name: &str, args: MacArgs) -> Attribute {
Attribute {
id: AttrId(0),
style: AttrStyle::Outer,
kind: AttrKind::Normal(AttrItem {
path: mk().path(vec![name]),
args: args,
}),
span: DUMMY_SP,
}
}
fn build_variant_attr(group: &str) -> Attribute {
make_attr("ownership_variant_of", delimited(vec![str_token(group)]))
}
fn do_split_variants(st: &CommandState,
cx: &RefactorCtxt,
label: Symbol) {
let arena = SyncDroplessArena::default();
let ana = ownership::analyze(&st, &cx, &arena);
let mut span_fref_idx = HashMap::new();
for vr in ana.variants.values() {
for (idx, fref) in vr.func_refs.iter().enumerate() {
if let Some(span) = fref.span {
span_fref_idx.insert(span, idx);
}
}
}
let mut handled_spans = HashSet::new();
st.map_krate(|krate| {
flat_map_fns(krate, |fl| {
if !st.marked(fl.id, label) {
return smallvec![fl];
}
debug!("looking at {:?}", fl.ident);
let def_id = match_or!([cx.hir_map().opt_local_def_id_from_node_id(fl.id)]
Some(x) => x; return smallvec![fl]);
if !ana.variants.contains_key(&def_id) {
return smallvec![fl];
}
let (fr, vr) = ana.fn_results(def_id);
if fr.variants.is_some() {
return smallvec![fl];
}
let path_str = cx.ty_ctxt().def_path(def_id).to_string_no_crate();
let mut fls = SmallVec::with_capacity(fr.num_monos);
for mono_idx in 0 .. fr.num_monos {
let mr = &ana.monos[&(vr.func_id, mono_idx)];
let mut fl = fl.clone();
if !mr.suffix.is_empty() {
fl.ident = mk().ident(format!("{}_{}", fl.ident.name, mr.suffix));
}
fl.attrs.retain(|a| {
assert!(!a.check_name("ownership_variant_of".into_symbol()));
!a.check_name("ownership_mono".into_symbol()) &&
(!a.check_name("ownership_constraints".into_symbol()) || mono_idx == 0)
});
fl.attrs.push(build_mono_attr(&mr.suffix, &mr.assign));
fl.attrs.push(build_variant_attr(&path_str));
fl.block.as_mut().map(|b| MutVisitNodes::visit(b, |e: &mut P<Expr>| {
let fref_idx = match_or!([span_fref_idx.get(&e.span)]
Some(&x) => x; return);
handled_spans.insert(e.span);
let dest = vr.func_refs[fref_idx].def_id;
let dest_fr = &ana.funcs[&dest];
let dest_marked = cx.hir_map().as_local_node_id(dest)
.map_or(false, |id| st.marked(id, label));
if !dest_marked && dest_fr.variants.is_none() {
return;
}
let dest_mono_idx = mr.callee_mono_idxs[fref_idx];
let new_name = callee_new_name(cx, &ana, dest, dest_mono_idx);
rename_callee(e, &new_name);
}));
fls.push(fl);
}
fls
});
MutVisitNodes::visit(krate, |e: &mut P<Expr>| {
let fref_idx = match_or!([span_fref_idx.get(&e.span)]
Some(&x) => x; return);
if handled_spans.contains(&e.span) {
return;
}
let hir_id = cx.hir_map().node_to_hir_id(e.id);
let src = cx.hir_map().get_parent_item(hir_id);
let src = cx.hir_map().hir_to_node_id(src);
let src_def_id = cx.node_def_id(src);
let (src_fr, src_vr) = ana.fn_results(src_def_id);
let dest = src_vr.func_refs[fref_idx].def_id;
let dest_fr = &ana.funcs[&dest];
let dest_marked = cx.hir_map().as_local_node_id(dest)
.map_or(false, |id| st.marked(id, label));
if !dest_marked && dest_fr.variants.is_none() {
return;
}
let src_mono_idx =
if src_fr.variants.is_none() { 0 }
else { src_vr.index };
let src_mr = &ana.monos[&(src_vr.func_id, src_mono_idx)];
let dest_mono_idx = src_mr.callee_mono_idxs[fref_idx];
let new_name = callee_new_name(cx, &ana, dest, dest_mono_idx);
rename_callee(e, &new_name)
});
});
}
fn rename_callee(e: &mut P<Expr>, new_name: &str) {
match &mut e.kind {
ExprKind::Path(_, ref mut path) => {
let seg = path.segments.last_mut().unwrap();
seg.ident = mk().ident(new_name);
},
ExprKind::MethodCall(ref mut seg, _) => {
seg.ident = mk().ident(new_name);
},
_ => panic!("rename_callee: unexpected expr kind: {:?}", e),
}
}
fn callee_new_name(cx: &RefactorCtxt,
ana: &ownership::AnalysisResult,
dest: DefId,
dest_mono_idx: usize) -> String {
let fr = &ana.funcs[&dest];
if let Some(ref var_ids) = fr.variants {
let var_id = var_ids[dest_mono_idx];
cx.ty_ctxt().def_path(var_id).data
.last().unwrap().data.to_string()
} else {
let base_name = cx.ty_ctxt().def_path(dest).data
.last().unwrap().data.get_opt_name().unwrap();
let suffix = &ana.monos[&(dest, dest_mono_idx)].suffix;
if !suffix.is_empty() {
format!("{}_{}", base_name, suffix)
} else {
format!("{}", base_name)
}
}
}
fn do_mark_pointers(st: &CommandState, cx: &RefactorCtxt) {
let arena = SyncDroplessArena::default();
let ana = ownership::analyze(&st, &cx, &arena);
struct AnalysisTypeSource<'lty, 'tcx: 'lty> {
ana: &'lty ownership::AnalysisResult<'lty, 'tcx>,
hir_map: HirMap<'lty, 'tcx>,
}
impl<'lty, 'tcx> type_map::TypeSource for AnalysisTypeSource<'lty, 'tcx> {
type Type = ownership::PTy<'lty, 'tcx>;
type Signature = ownership::PFnSig<'lty, 'tcx>;
fn def_type(&mut self, did: DefId) -> Option<Self::Type> {
self.ana.statics.get(&did).cloned()
}
fn fn_sig(&mut self, did: DefId) -> Option<Self::Signature> {
let (fr, vr) = self.ana.fn_results(did);
if fr.variants.is_none() && fr.num_monos > 1 {
return None;
}
let mono_idx =
if fr.variants.is_none() { 0 }
else { vr.index };
let mr = &self.ana.monos[&(vr.func_id, mono_idx)];
let lcx = LabeledTyCtxt::new(self.ana.arena());
let sig = {
let mut f = |l: &Option<_>| {
if let Some(v) = *l {
Some(mr.assign[v])
} else {
None
}
};
ownership::FnSig {
inputs: lcx.relabel_slice(fr.sig.inputs, &mut f),
output: lcx.relabel(fr.sig.output, &mut f),
is_variadic: fr.sig.is_variadic,
}
};
Some(sig)
}
fn pat_type(&mut self, p: &Pat) -> Option<Self::Type> {
let hir_id = self.hir_map.opt_node_to_hir_id(p.id)?;
let fn_def_id = self.hir_map.get_parent_did(hir_id);
let f = self.ana.funcs.get(&fn_def_id)?;
let local_var = f.locals.get(&p.span)?;
let mut map_fn = |opt_var: &Option<Var>| -> Option<ConcretePerm> {
opt_var.map(|var| f.local_assign.get(var).copied()).flatten()
};
let lcx = LabeledTyCtxt::new(self.ana.arena());
Some(lcx.relabel(local_var, &mut map_fn))
}
fn closure_sig(&mut self, _did: DefId) -> Option<Self::Signature> { None }
}
let source = AnalysisTypeSource {
ana: &ana,
hir_map: cx.hir_map(),
};
let s_ref = "ref".into_symbol();
let s_mut = "mut".into_symbol();
let s_move = "move".into_symbol();
type_map::map_types(&cx.hir_map(), source, &st.krate(), |_source, ast_ty, lty| {
let p = match lty.label {
Some(x) => x,
None => return,
};
let label = match p {
ConcretePerm::Read => s_ref,
ConcretePerm::Write => s_mut,
ConcretePerm::Move => s_move,
};
st.add_mark(ast_ty.id, label);
});
}