use std::collections::{BTreeMap, BTreeSet};
#[allow(unused_imports)]
use log::{debug, info, warn};
use num::BigUint;
use move_compiler::{expansion::ast as EA, parser::ast as PA, shared::NumericalAddress};
use crate::{
ast::{Attribute, ModuleName, Operation, QualifiedSymbol, Spec, Value},
builder::spec_builtins,
model::{
FunId, FunctionVisibility, GlobalEnv, Loc, ModuleId, QualifiedId, SpecFunId, SpecVarId,
StructId,
},
project_2nd,
symbol::Symbol,
ty::Type,
};
use codespan_reporting::diagnostic::Severity;
#[derive(Debug)]
pub(crate) struct ModelBuilder<'env> {
pub env: &'env mut GlobalEnv,
pub spec_fun_table: BTreeMap<QualifiedSymbol, Vec<SpecFunEntry>>,
pub spec_var_table: BTreeMap<QualifiedSymbol, SpecVarEntry>,
pub spec_schema_table: BTreeMap<QualifiedSymbol, SpecSchemaEntry>,
pub unused_schema_set: BTreeSet<QualifiedSymbol>,
pub struct_table: BTreeMap<QualifiedSymbol, StructEntry>,
pub reverse_struct_table: BTreeMap<(ModuleId, StructId), QualifiedSymbol>,
pub fun_table: BTreeMap<QualifiedSymbol, FunEntry>,
pub const_table: BTreeMap<QualifiedSymbol, ConstEntry>,
pub move_fun_call_graph: BTreeMap<QualifiedId<SpecFunId>, BTreeSet<QualifiedId<SpecFunId>>>,
}
#[derive(Debug, Clone)]
pub(crate) struct SpecFunEntry {
#[allow(dead_code)]
pub loc: Loc,
pub oper: Operation,
pub type_params: Vec<Type>,
pub arg_types: Vec<Type>,
pub result_type: Type,
}
#[derive(Debug, Clone)]
pub(crate) struct SpecVarEntry {
pub loc: Loc,
pub module_id: ModuleId,
#[allow(dead_code)]
pub var_id: SpecVarId,
pub type_params: Vec<(Symbol, Type)>,
pub type_: Type,
}
#[derive(Debug)]
pub(crate) struct SpecSchemaEntry {
pub loc: Loc,
#[allow(dead_code)]
pub name: QualifiedSymbol,
pub module_id: ModuleId,
pub type_params: Vec<(Symbol, Type)>,
pub vars: Vec<(Symbol, Type)>,
pub spec: Spec,
pub all_vars: BTreeMap<Symbol, LocalVarEntry>,
pub included_spec: Spec,
}
#[derive(Debug, Clone)]
pub(crate) struct StructEntry {
pub loc: Loc,
pub module_id: ModuleId,
pub struct_id: StructId,
#[allow(dead_code)]
pub is_resource: bool,
pub type_params: Vec<(Symbol, Type)>,
pub fields: Option<BTreeMap<Symbol, (usize, Type)>>,
pub attributes: Vec<Attribute>,
}
#[derive(Debug, Clone)]
pub(crate) struct FunEntry {
pub loc: Loc,
pub module_id: ModuleId,
#[allow(dead_code)]
pub fun_id: FunId,
pub visibility: FunctionVisibility,
pub is_entry: bool,
pub type_params: Vec<(Symbol, Type)>,
pub params: Vec<(Symbol, Type)>,
pub result_type: Type,
pub is_pure: bool,
pub attributes: Vec<Attribute>,
}
#[derive(Debug, Clone)]
pub(crate) struct ConstEntry {
pub loc: Loc,
pub ty: Type,
pub value: Value,
}
impl<'env> ModelBuilder<'env> {
pub fn new(env: &'env mut GlobalEnv) -> Self {
let mut translator = ModelBuilder {
env,
spec_fun_table: BTreeMap::new(),
spec_var_table: BTreeMap::new(),
spec_schema_table: BTreeMap::new(),
unused_schema_set: BTreeSet::new(),
struct_table: BTreeMap::new(),
reverse_struct_table: BTreeMap::new(),
fun_table: BTreeMap::new(),
const_table: BTreeMap::new(),
move_fun_call_graph: BTreeMap::new(),
};
spec_builtins::declare_spec_builtins(&mut translator);
translator
}
pub fn to_loc(&self, loc: &move_ir_types::location::Loc) -> Loc {
self.env.to_loc(loc)
}
pub fn error(&self, at: &Loc, msg: &str) {
self.env.error(at, msg)
}
pub fn error_with_notes(&self, at: &Loc, msg: &str, notes: Vec<String>) {
self.env.error_with_notes(at, msg, notes)
}
pub fn define_spec_fun(&mut self, name: QualifiedSymbol, entry: SpecFunEntry) {
self.spec_fun_table
.entry(name)
.or_insert_with(Vec::new)
.push(entry);
}
pub fn define_spec_var(
&mut self,
loc: &Loc,
name: QualifiedSymbol,
module_id: ModuleId,
var_id: SpecVarId,
type_params: Vec<(Symbol, Type)>,
type_: Type,
) {
let entry = SpecVarEntry {
loc: loc.clone(),
module_id,
var_id,
type_params,
type_,
};
if let Some(old) = self.spec_var_table.insert(name.clone(), entry) {
let var_name = name.display(self.env.symbol_pool());
self.error(loc, &format!("duplicate declaration of `{}`", var_name));
self.error(&old.loc, &format!("previous declaration of `{}`", var_name));
}
}
pub fn define_spec_schema(
&mut self,
loc: &Loc,
name: QualifiedSymbol,
module_id: ModuleId,
type_params: Vec<(Symbol, Type)>,
vars: Vec<(Symbol, Type)>,
) {
let entry = SpecSchemaEntry {
loc: loc.clone(),
name: name.clone(),
module_id,
type_params,
vars,
spec: Spec::default(),
all_vars: BTreeMap::new(),
included_spec: Spec::default(),
};
if let Some(old) = self.spec_schema_table.insert(name.clone(), entry) {
let schema_display = name.display(self.env.symbol_pool());
self.error(
loc,
&format!("duplicate declaration of `{}`", schema_display),
);
self.error(
&old.loc,
&format!("previous declaration of `{}`", schema_display),
);
}
self.unused_schema_set.insert(name);
}
pub fn define_struct(
&mut self,
loc: Loc,
attributes: Vec<Attribute>,
name: QualifiedSymbol,
module_id: ModuleId,
struct_id: StructId,
is_resource: bool,
type_params: Vec<(Symbol, Type)>,
fields: Option<BTreeMap<Symbol, (usize, Type)>>,
) {
let entry = StructEntry {
loc,
attributes,
module_id,
struct_id,
is_resource,
type_params,
fields,
};
assert!(self.struct_table.insert(name.clone(), entry).is_none());
self.reverse_struct_table
.insert((module_id, struct_id), name);
}
pub fn define_fun(
&mut self,
loc: Loc,
attributes: Vec<Attribute>,
name: QualifiedSymbol,
module_id: ModuleId,
fun_id: FunId,
visibility: FunctionVisibility,
is_entry: bool,
type_params: Vec<(Symbol, Type)>,
params: Vec<(Symbol, Type)>,
result_type: Type,
) {
let entry = FunEntry {
loc,
attributes,
module_id,
fun_id,
visibility,
is_entry,
type_params,
params,
result_type,
is_pure: false,
};
assert!(self.fun_table.insert(name, entry).is_none());
}
pub fn define_const(&mut self, name: QualifiedSymbol, entry: ConstEntry) {
assert!(self.const_table.insert(name, entry).is_none());
}
pub fn resolve_address(&self, loc: &Loc, addr: &EA::Address) -> NumericalAddress {
match addr {
EA::Address::Numerical(_, bytes) => bytes.value,
EA::Address::NamedUnassigned(name) => {
self.error(loc, &format!("Undeclared address `{}`", name));
NumericalAddress::DEFAULT_ERROR_ADDRESS
}
}
}
pub fn lookup_type(&self, loc: &Loc, name: &QualifiedSymbol) -> Type {
self.struct_table
.get(name)
.cloned()
.map(|e| Type::Struct(e.module_id, e.struct_id, project_2nd(&e.type_params)))
.unwrap_or_else(|| {
self.error(
loc,
&format!("undeclared `{}`", name.display_full(self.env.symbol_pool())),
);
Type::Error
})
}
pub fn warn_unused_schemas(&self) {
for name in &self.unused_schema_set {
let entry = self.spec_schema_table.get(name).expect("schema defined");
let schema_name = name.display_simple(self.env.symbol_pool()).to_string();
let module_env = self.env.get_module(entry.module_id);
if module_env.is_target() && !schema_name.starts_with("UNUSED") {
self.env.diag(
Severity::Note,
&entry.loc,
&format!("unused schema {}", name.display(self.env.symbol_pool())),
);
}
}
}
pub fn bin_op_symbol(&self, op: &PA::BinOp_) -> QualifiedSymbol {
QualifiedSymbol {
module_name: self.builtin_module(),
symbol: self.env.symbol_pool().make(op.symbol()),
}
}
pub fn unary_op_symbol(&self, op: &PA::UnaryOp_) -> QualifiedSymbol {
QualifiedSymbol {
module_name: self.builtin_module(),
symbol: self.env.symbol_pool().make(op.symbol()),
}
}
pub fn builtin_qualified_symbol(&self, name: &str) -> QualifiedSymbol {
QualifiedSymbol {
module_name: self.builtin_module(),
symbol: self.env.symbol_pool().make(name),
}
}
pub fn old_symbol(&self) -> Symbol {
self.env.symbol_pool().make("old")
}
pub fn assert_symbol(&self) -> Symbol {
self.env.symbol_pool().make("assert")
}
pub fn builtin_module(&self) -> ModuleName {
ModuleName::new(BigUint::default(), self.env.symbol_pool().make("$$"))
}
pub fn add_used_spec_fun(&mut self, qid: QualifiedId<SpecFunId>) {
self.env.used_spec_funs.insert(qid);
self.propagate_move_fun_usage(qid);
}
pub fn add_edge_to_move_fun_call_graph(
&mut self,
caller: QualifiedId<SpecFunId>,
callee: QualifiedId<SpecFunId>,
) {
self.move_fun_call_graph
.entry(caller)
.or_default()
.insert(callee);
}
pub fn propagate_move_fun_usage(&mut self, qid: QualifiedId<SpecFunId>) {
if let Some(neighbors) = self.move_fun_call_graph.get(&qid) {
neighbors.clone().iter().for_each(|n| {
if self.env.used_spec_funs.insert(*n) {
self.propagate_move_fun_usage(*n);
}
});
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct LocalVarEntry {
pub loc: Loc,
pub type_: Type,
pub operation: Option<Operation>,
pub temp_index: Option<usize>,
}