diff --git a/language/move-model/src/ast.rs b/language/move-model/src/ast.rs index 67f9edb4182a2..98596f18454b8 100644 --- a/language/move-model/src/ast.rs +++ b/language/move-model/src/ast.rs @@ -60,6 +60,21 @@ pub struct SpecFunDecl { pub is_recursive: RefCell>, } +// ================================================================================================= +/// # Attributes + +#[derive(Debug, Clone)] +pub enum AttributeValue { + Value(NodeId, Value), + Name(NodeId, Option, Symbol), +} + +#[derive(Debug, Clone)] +pub enum Attribute { + Apply(NodeId, Symbol, Vec), + Assign(NodeId, Symbol, AttributeValue), +} + // ================================================================================================= /// # Conditions diff --git a/language/move-model/src/builder/model_builder.rs b/language/move-model/src/builder/model_builder.rs index 146dcc8ce2c94..eb2195d729419 100644 --- a/language/move-model/src/builder/model_builder.rs +++ b/language/move-model/src/builder/model_builder.rs @@ -16,7 +16,7 @@ use move_compiler::{expansion::ast as EA, parser::ast as PA, shared::NumericalAd use move_symbol_pool::Symbol as MoveStringSymbol; use crate::{ - ast::{ModuleName, Operation, QualifiedSymbol, Spec, Value}, + ast::{Attribute, ModuleName, Operation, QualifiedSymbol, Spec, Value}, builder::spec_builtins, model::{ FunId, FunctionVisibility, GlobalEnv, Loc, ModuleId, QualifiedId, SpecFunId, SpecVarId, @@ -107,6 +107,7 @@ pub(crate) struct StructEntry { pub is_resource: bool, pub type_params: Vec<(Symbol, Type)>, pub fields: Option>, + pub attributes: Vec, } /// A declaration of a function. @@ -120,6 +121,7 @@ pub(crate) struct FunEntry { pub params: Vec<(Symbol, Type)>, pub result_type: Type, pub is_pure: bool, + pub attributes: Vec, } #[derive(Debug, Clone)] @@ -237,6 +239,7 @@ impl<'env> ModelBuilder<'env> { pub fn define_struct( &mut self, loc: Loc, + attributes: Vec, name: QualifiedSymbol, module_id: ModuleId, struct_id: StructId, @@ -246,6 +249,7 @@ impl<'env> ModelBuilder<'env> { ) { let entry = StructEntry { loc, + attributes, module_id, struct_id, is_resource, @@ -262,6 +266,7 @@ impl<'env> ModelBuilder<'env> { pub fn define_fun( &mut self, loc: Loc, + attributes: Vec, name: QualifiedSymbol, module_id: ModuleId, fun_id: FunId, @@ -272,6 +277,7 @@ impl<'env> ModelBuilder<'env> { ) { let entry = FunEntry { loc, + attributes, module_id, fun_id, visibility, diff --git a/language/move-model/src/builder/module_builder.rs b/language/move-model/src/builder/module_builder.rs index 7ffa3119ff6b9..180744e94a687 100644 --- a/language/move-model/src/builder/module_builder.rs +++ b/language/move-model/src/builder/module_builder.rs @@ -26,9 +26,9 @@ use move_ir_types::{ast::ConstantName, location::Spanned}; use crate::{ ast::{ - Condition, ConditionKind, Exp, ExpData, GlobalInvariant, ModuleName, Operation, - PropertyBag, PropertyValue, QualifiedSymbol, Spec, SpecBlockInfo, SpecBlockTarget, - SpecFunDecl, SpecVarDecl, Value, + Attribute, AttributeValue, Condition, ConditionKind, Exp, ExpData, GlobalInvariant, + ModuleName, Operation, PropertyBag, PropertyValue, QualifiedSymbol, Spec, SpecBlockInfo, + SpecBlockTarget, SpecFunDecl, SpecVarDecl, Value, }, builder::{ exp_translator::ExpTranslator, @@ -157,7 +157,8 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { self.decl_ana(&module_def, &compiled_module, &source_map); self.def_ana(&module_def, function_infos); self.collect_spec_block_infos(&module_def); - self.populate_env_from_result(loc, compiled_module, source_map); + let attrs = self.translate_attributes(&module_def.attributes); + self.populate_env_from_result(loc, attrs, compiled_module, source_map); } } @@ -244,6 +245,76 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { } } +/// # Attribute Analysis + +impl<'env, 'translator> ModuleBuilder<'env, 'translator> { + pub fn translate_attributes(&mut self, attrs: &EA::Attributes) -> Vec { + attrs + .iter() + .map(|(_, _, attr)| self.translate_attribute(attr)) + .collect() + } + + pub fn translate_attribute(&mut self, attr: &EA::Attribute) -> Attribute { + let node_id = self + .parent + .env + .new_node(self.parent.to_loc(&attr.loc), Type::Tuple(vec![])); + match &attr.value { + EA::Attribute_::Name(n) => { + let sym = self.symbol_pool().make(n.value.as_str()); + Attribute::Apply(node_id, sym, vec![]) + } + EA::Attribute_::Parameterized(n, vs) => { + let sym = self.symbol_pool().make(n.value.as_str()); + Attribute::Apply(node_id, sym, self.translate_attributes(vs)) + } + EA::Attribute_::Assigned(n, v) => { + let value_node_id = self + .parent + .env + .new_node(self.parent.to_loc(&v.loc), Type::Tuple(vec![])); + let v = match &v.value { + EA::AttributeValue_::Value(val) => { + let val = + if let Some((val, _)) = ExpTranslator::new(self).translate_value(val) { + val + } else { + // Error reported + Value::Bool(false) + }; + AttributeValue::Value(value_node_id, val) + } + EA::AttributeValue_::ModuleAccess(macc) => match macc.value { + EA::ModuleAccess_::Name(n) => AttributeValue::Name( + value_node_id, + None, + self.symbol_pool().make(n.value.as_str()), + ), + EA::ModuleAccess_::ModuleAccess(mident, n) => { + let addr_bytes = self.parent.resolve_address( + &self.parent.to_loc(&macc.loc), + &mident.value.address, + ); + let module_name = ModuleName::from_address_bytes_and_name( + addr_bytes, + self.symbol_pool() + .make(mident.value.module.0.value.as_str()), + ); + AttributeValue::Name( + value_node_id, + Some(module_name), + self.symbol_pool().make(n.value.as_str()), + ) + } + }, + }; + Attribute::Assign(node_id, self.symbol_pool().make(n.value.as_str()), v) + } + } + } +} + /// # Declaration Analysis impl<'env, 'translator> ModuleBuilder<'env, 'translator> { @@ -296,6 +367,7 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { fn decl_ana_struct(&mut self, name: &PA::StructName, def: &EA::StructDefinition) { let qsym = self.qualified_by_module_from_name(&name.0); let struct_id = StructId::new(qsym.symbol); + let attrs = self.translate_attributes(&def.attributes); let is_resource = // TODO migrate to abilities def.abilities.has_ability_(PA::Ability_::Key) || ( @@ -307,6 +379,7 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { et.analyze_and_add_type_params(def.type_parameters.iter().map(|param| ¶m.name)); et.parent.parent.define_struct( et.to_loc(&def.loc), + attrs, qsym, et.parent.module_id, struct_id, @@ -319,6 +392,7 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { fn decl_ana_fun(&mut self, name: &PA::FunctionName, def: &EA::Function) { let qsym = self.qualified_by_module_from_name(&name.0); let fun_id = FunId::new(qsym.symbol); + let attrs = self.translate_attributes(&def.attributes); let mut et = ExpTranslator::new(self); et.enter_scope(); let type_params = et.analyze_and_add_type_params( @@ -336,6 +410,7 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { let loc = et.to_loc(&def.loc); et.parent.parent.define_fun( loc.clone(), + attrs, qsym.clone(), et.parent.module_id, fun_id, @@ -2989,6 +3064,7 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { fn populate_env_from_result( &mut self, loc: Loc, + attributes: Vec, module: CompiledModule, source_map: SourceMap, ) { @@ -3015,6 +3091,7 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { def_idx, name, entry.loc.clone(), + entry.attributes.clone(), struct_spec, ), )) @@ -3052,6 +3129,7 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { def_idx, name, entry.loc.clone(), + entry.attributes.clone(), arg_names, type_arg_names, fun_spec, @@ -3084,6 +3162,7 @@ impl<'env, 'translator> ModuleBuilder<'env, 'translator> { .collect(); self.parent.env.add( loc, + attributes, module, source_map, named_constants, diff --git a/language/move-model/src/lib.rs b/language/move-model/src/lib.rs index 847851796d912..69d63574ef4fa 100644 --- a/language/move-model/src/lib.rs +++ b/language/move-model/src/lib.rs @@ -309,8 +309,14 @@ pub fn run_bytecode_model_builder<'a>( let name = m.identifier_at(m.struct_handle_at(def.struct_handle).name); let symbol = env.symbol_pool().make(name.as_str()); let struct_id = StructId::new(symbol); - let data = - env.create_move_struct_data(m, def_idx, symbol, Loc::default(), Spec::default()); + let data = env.create_move_struct_data( + m, + def_idx, + symbol, + Loc::default(), + Vec::default(), + Spec::default(), + ); module_data.struct_data.insert(struct_id, data); module_data.struct_idx_to_id.insert(def_idx, struct_id); } diff --git a/language/move-model/src/model.rs b/language/move-model/src/model.rs index 3561fced16de9..68dcb7a787caa 100644 --- a/language/move-model/src/model.rs +++ b/language/move-model/src/model.rs @@ -70,6 +70,7 @@ use crate::{ }; // import and re-expose symbols +use crate::ast::Attribute; use move_binary_format::file_format::CodeOffset; pub use move_binary_format::file_format::{AbilitySet, Visibility as FunctionVisibility}; @@ -991,6 +992,7 @@ impl GlobalEnv { pub fn add( &mut self, loc: Loc, + attributes: Vec, module: CompiledModule, source_map: SourceMap, named_constants: BTreeMap, @@ -1060,6 +1062,7 @@ impl GlobalEnv { module_spec, source_map, loc, + attributes, spec_block_infos, used_modules: Default::default(), friend_modules: Default::default(), @@ -1090,6 +1093,7 @@ impl GlobalEnv { def_idx: FunctionDefinitionIndex, name: Symbol, loc: Loc, + attributes: Vec, arg_names: Vec, type_arg_names: Vec, spec: Spec, @@ -1098,6 +1102,7 @@ impl GlobalEnv { FunctionData { name, loc, + attributes, def_idx, handle_idx, arg_names, @@ -1117,6 +1122,7 @@ impl GlobalEnv { def_idx: StructDefinitionIndex, name: Symbol, loc: Loc, + attributes: Vec, spec: Spec, ) -> StructData { let handle_idx = module.struct_def_at(def_idx).struct_handle; @@ -1142,6 +1148,7 @@ impl GlobalEnv { StructData { name, loc, + attributes, info, field_data, spec, @@ -1179,6 +1186,7 @@ impl GlobalEnv { StructData { name: self.ghost_memory_name(var_name), loc, + attributes: Default::default(), info: StructInfo::Generated { spec_var: var_id }, field_data, spec: Spec::default(), @@ -1625,6 +1633,9 @@ pub struct ModuleData { /// Id of this module in the global env. pub id: ModuleId, + /// Attributes attached to this module. + attributes: Vec, + /// Module byte code. pub module: CompiledModule, @@ -1685,6 +1696,7 @@ impl ModuleData { module_spec: Spec::default(), source_map: SourceMap::new(MoveIrLoc::new(FileHash::empty(), 0, 0), None), loc: Loc::default(), + attributes: Default::default(), spec_block_infos: vec![], used_modules: Default::default(), friend_modules: Default::default(), @@ -1724,6 +1736,11 @@ impl<'env> ModuleEnv<'env> { self.data.loc.clone() } + /// Returns the attributes of this module. + pub fn get_attributes(&self) -> &[Attribute] { + &self.data.attributes + } + /// Returns full name as a string. pub fn get_full_name_str(&self) -> String { self.get_name().display_full(self.symbol_pool()).to_string() @@ -2285,6 +2302,10 @@ pub struct StructData { /// The location of this struct. loc: Loc, + /// Attributes attached to this structure. + attributes: Vec, + + /// List of function argument names. Not in bytecode but obtained from AST. /// Information about this struct. info: StructInfo, @@ -2360,6 +2381,11 @@ impl<'env> StructEnv<'env> { self.data.loc.clone() } + /// Returns the attributes of this struct. + pub fn get_attributes(&self) -> &[Attribute] { + &self.data.attributes + } + /// Get documentation associated with this struct. pub fn get_doc(&self) -> &str { self.module_env.env.get_doc(&self.data.loc) @@ -2794,6 +2820,9 @@ pub struct FunctionData { /// The handle index of this function in its module. handle_idx: FunctionHandleIndex, + /// Attributes attached to this function. + attributes: Vec, + /// List of function argument names. Not in bytecode but obtained from AST. arg_names: Vec, @@ -2822,6 +2851,7 @@ impl FunctionData { FunctionData { name, loc: Loc::default(), + attributes: Vec::default(), def_idx, handle_idx, arg_names: vec![], @@ -2895,6 +2925,11 @@ impl<'env> FunctionEnv<'env> { self.data.loc.clone() } + /// Returns the attributes of this function. + pub fn get_attributes(&self) -> &[Attribute] { + &self.data.attributes + } + /// Returns the location of the specification block of this function. If the function has /// none, returns that of the function itself. pub fn get_spec_loc(&self) -> Loc {