diff --git a/sway-ast/src/item/item_storage.rs b/sway-ast/src/item/item_storage.rs index ea923faee73..4c9cd025147 100644 --- a/sway-ast/src/item/item_storage.rs +++ b/sway-ast/src/item/item_storage.rs @@ -3,18 +3,41 @@ use crate::priv_prelude::*; #[derive(Clone, Debug, Serialize)] pub struct ItemStorage { pub storage_token: StorageToken, - pub fields: Braces, CommaToken>>, + pub entries: Braces, CommaToken>>, } impl Spanned for ItemStorage { fn span(&self) -> Span { - Span::join(self.storage_token.span(), &self.fields.span()) + Span::join(self.storage_token.span(), &self.entries.span()) } } #[derive(Clone, Debug, Serialize)] + +pub struct StorageEntry { + pub name: Ident, + pub namespace: Option>, CommaToken>>>, + pub field: Option, +} + +impl Spanned for StorageEntry { + fn span(&self) -> Span { + if let Some(namespace) = &self.namespace { + Span::join(self.name.span(), &namespace.span()) + } else if let Some(field) = &self.field { + Span::join(self.name.span(), &field.span()) + } else { + self.name.span() + } + } +} + +#[derive(Clone, Debug, Serialize)] + pub struct StorageField { pub name: Ident, + pub in_token: Option, + pub key_expr: Option, pub colon_token: ColonToken, pub ty: Ty, pub eq_token: EqToken, diff --git a/sway-ast/src/lib.rs b/sway-ast/src/lib.rs index 1b78f6be597..b1030cf040d 100644 --- a/sway-ast/src/lib.rs +++ b/sway-ast/src/lib.rs @@ -37,7 +37,7 @@ pub use crate::{ item_enum::ItemEnum, item_fn::ItemFn, item_impl::{ItemImpl, ItemImplItem}, - item_storage::{ItemStorage, StorageField}, + item_storage::{ItemStorage, StorageEntry, StorageField}, item_struct::ItemStruct, item_trait::{ItemTrait, ItemTraitItem, Traits}, item_type_alias::ItemTypeAlias, diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index e433f9ee6e5..e2e9439ec69 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -2,7 +2,7 @@ use super::{ compile::compile_function, convert::*, lexical_map::LexicalMap, - storage::{add_to_b256, get_storage_key}, + storage::{add_to_b256, get_storage_field_id, get_storage_key}, types::*, }; use crate::{ @@ -11,7 +11,7 @@ use crate::{ compile_constant_expression, compile_constant_expression_to_constant, }, language::{ - ty::{self, ProjectionKind, TyConstantDecl, TyExpressionVariant}, + ty::{self, ProjectionKind, TyConstantDecl, TyExpressionVariant, TyStorageField}, *, }, metadata::MetadataManager, @@ -28,7 +28,6 @@ use sway_types::{ ident::Ident, integer_bits::IntegerBits, span::{Span, Spanned}, - state::StateIndex, u256::U256, Named, }; @@ -588,9 +587,22 @@ impl<'eng> FnCompiler<'eng> { Ok(TerminatorValue::new(val, context)) } ty::TyExpressionVariant::StorageAccess(access) => { - let span_md_idx = md_mgr.span_to_md(context, &access.span()); - let ns = access.namespace.as_ref().map(|ns| ns.as_str()); - self.compile_storage_access(context, ns, &access.ix, &access.fields, span_md_idx) + let span_md_idx: Option = md_mgr.span_to_md(context, &access.span()); + let key = TyStorageField::get_key_expression_const( + &access.key_expression.clone().map(|v| *v), + self.engines, + context, + md_mgr, + self.module, + )?; + self.compile_storage_access( + context, + access.storage_field_names.clone(), + access.struct_field_names.clone(), + key, + &access.fields, + span_md_idx, + ) } ty::TyExpressionVariant::IntrinsicFunction(kind) => { self.compile_intrinsic_function(context, md_mgr, kind, ast_expr.span.clone()) @@ -3726,8 +3738,9 @@ impl<'eng> FnCompiler<'eng> { fn compile_storage_access( &mut self, context: &mut Context, - ns: Option<&str>, - ix: &StateIndex, + storage_field_names: Vec, + struct_field_names: Vec, + key: Option, fields: &[ty::TyStorageAccessDescriptor], span_md_idx: Option, ) -> Result { @@ -3752,7 +3765,15 @@ impl<'eng> FnCompiler<'eng> { // Do the actual work. This is a recursive function because we want to drill down // to load each primitive type in the storage field in its own storage slot. - self.compile_storage_read(context, ns, ix, &field_idcs, &base_type, span_md_idx) + self.compile_storage_read( + context, + storage_field_names, + struct_field_names, + key, + &field_idcs, + &base_type, + span_md_idx, + ) } #[allow(clippy::too_many_arguments)] @@ -3854,11 +3875,13 @@ impl<'eng> FnCompiler<'eng> { Ok(TerminatorValue::new(val, context)) } + #[allow(clippy::too_many_arguments)] fn compile_storage_read( &mut self, context: &mut Context, - ns: Option<&str>, - ix: &StateIndex, + storage_field_names: Vec, + struct_field_names: Vec, + key: Option, indices: &[u64], base_type: &Type, span_md_idx: Option, @@ -3895,7 +3918,10 @@ impl<'eng> FnCompiler<'eng> { // plus the offset, in number of slots, computed above. The offset within this // particular slot is the remaining offset, in words. ( - add_to_b256(get_storage_key::(ns, ix, &[]), offset_in_slots), + add_to_b256( + get_storage_key(storage_field_names.clone(), key.clone()), + offset_in_slots, + ), offset_remaining, ) }; @@ -3950,7 +3976,7 @@ impl<'eng> FnCompiler<'eng> { .add_metadatum(context, span_md_idx); // Store the field identifier as the third field in the `StorageKey` struct - let unique_field_id = get_storage_key(ns, ix, indices); // use the indices to get a field id that is unique even for zero-sized values that live in the same slot + let unique_field_id = get_storage_field_id(storage_field_names, struct_field_names); // use the struct_field_names to get a field id that is unique even for zero-sized values that live in the same slot let field_id = convert_literal_to_value(context, &Literal::B256(unique_field_id.into())) .add_metadatum(context, span_md_idx); let gep_2_val = diff --git a/sway-core/src/ir_generation/storage.rs b/sway-core/src/ir_generation/storage.rs index 2f0bda0ee38..14fd2b8ef00 100644 --- a/sway-core/src/ir_generation/storage.rs +++ b/sway-core/src/ir_generation/storage.rs @@ -8,7 +8,7 @@ use sway_ir::{ context::Context, irtype::Type, }; -use sway_types::state::StateIndex; +use sway_types::u256::U256; /// Determines how values that are less then a word in length /// has to be padded to word boundary when in structs or enums. @@ -19,20 +19,61 @@ enum InByte8Padding { Left, } -/// Hands out storage keys using a state index and a list of subfield indices. -/// Basically returns sha256("storage____..") -/// or sha256("storage_____..") -pub(super) fn get_storage_key(namespace: Option<&str>, ix: &StateIndex, indices: &[T]) -> Bytes32 -where - T: std::fmt::Display, -{ +/// Hands out storage keys using storage field names or an existing key. +/// Basically returns sha256("storage::::.") +/// or key if defined. +pub(super) fn get_storage_key(storage_field_names: Vec, key: Option) -> Bytes32 { + if let Some(key) = key { + return key.to_be_bytes().into(); + } + + Hasher::hash(get_storage_key_string(storage_field_names)) +} + +pub fn get_storage_key_string(storage_field_names: Vec) -> String { + if storage_field_names.len() == 1 { + format!( + "{}{}{}", + sway_utils::constants::STORAGE_DOMAIN, + sway_utils::constants::STORAGE_FIELD_SEPARATOR, + storage_field_names.last().unwrap(), + ) + } else { + format!( + "{}{}{}{}{}", + sway_utils::constants::STORAGE_DOMAIN, + sway_utils::constants::STORAGE_NAMESPACE_SEPARATOR, + storage_field_names + .iter() + .take(storage_field_names.len() - 1) + .cloned() + .collect::>() + .join(sway_utils::constants::STORAGE_NAMESPACE_SEPARATOR), + sway_utils::constants::STORAGE_FIELD_SEPARATOR, + storage_field_names.last().unwrap(), + ) + } +} + +/// Hands out unique storage field ids using storage field names and struct field names. +/// Basically returns sha256("storage::::...") +pub(super) fn get_storage_field_id( + storage_field_names: Vec, + struct_field_names: Vec, +) -> Bytes32 { let data = format!( - "{}{}{}", - sway_utils::constants::STORAGE_DOMAIN_SEPARATOR, - namespace.map(|ns| format!("{ns}_")).unwrap_or("".into()), - ix.to_usize(), + "{}{}", + get_storage_key_string(storage_field_names), + if struct_field_names.is_empty() { + "".to_string() + } else { + format!( + "{}{}", + sway_utils::constants::STRUCT_FIELD_SEPARATOR, + struct_field_names.join(sway_utils::constants::STRUCT_FIELD_SEPARATOR), + ) + } ); - let data = indices.iter().fold(data, |acc, i| format!("{acc}_{i}")); Hasher::hash(data) } @@ -64,22 +105,21 @@ pub(super) fn add_to_b256(x: Bytes32, y: u64) -> Bytes32 { pub fn serialize_to_storage_slots( constant: &Constant, context: &Context, - ix: &StateIndex, - ns: Option<&str>, + storage_field_names: Vec, + key: Option, ty: &Type, - indices: &[usize], ) -> Vec { match &constant.value { ConstantValue::Undef => vec![], // If not being a part of an aggregate, single byte values like `bool`, `u8`, and unit // are stored as a byte at the beginning of the storage slot. ConstantValue::Unit if ty.is_unit(context) => vec![StorageSlot::new( - get_storage_key(ns, ix, indices), + get_storage_key(storage_field_names, key), Bytes32::new([0; 32]), )], ConstantValue::Bool(b) if ty.is_bool(context) => { vec![StorageSlot::new( - get_storage_key(ns, ix, indices), + get_storage_key(storage_field_names, key), Bytes32::new([ if *b { 1 } else { 0 }, 0, @@ -118,7 +158,7 @@ pub fn serialize_to_storage_slots( } ConstantValue::Uint(b) if ty.is_uint8(context) => { vec![StorageSlot::new( - get_storage_key(ns, ix, indices), + get_storage_key(storage_field_names, key), Bytes32::new([ *b as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -128,7 +168,7 @@ pub fn serialize_to_storage_slots( // Similarly, other uint values are stored at the beginning of the storage slot. ConstantValue::Uint(n) if ty.is_uint(context) => { vec![StorageSlot::new( - get_storage_key(ns, ix, indices), + get_storage_key(storage_field_names, key), Bytes32::new( n.to_be_bytes() .iter() @@ -142,13 +182,13 @@ pub fn serialize_to_storage_slots( } ConstantValue::U256(b) if ty.is_uint_of(context, 256) => { vec![StorageSlot::new( - get_storage_key(ns, ix, indices), + get_storage_key(storage_field_names, key), Bytes32::new(b.to_be_bytes()), )] } ConstantValue::B256(b) if ty.is_b256(context) => { vec![StorageSlot::new( - get_storage_key(ns, ix, indices), + get_storage_key(storage_field_names, key), Bytes32::new(b.to_be_bytes()), )] } @@ -185,7 +225,7 @@ pub fn serialize_to_storage_slots( ty.as_string(context) ); (0..(type_size_in_bytes + 31) / 32) - .map(|i| add_to_b256(get_storage_key(ns, ix, indices), i)) + .map(|i| add_to_b256(get_storage_key(storage_field_names.clone(), key.clone()), i)) .zip((0..packed.len() / 4).map(|i| { Bytes32::new( Vec::from_iter((0..4).flat_map(|j| *packed[4 * i + j])) diff --git a/sway-core/src/language/parsed/declaration/storage.rs b/sway-core/src/language/parsed/declaration/storage.rs index 0530a819aba..a7e3b663525 100644 --- a/sway-core/src/language/parsed/declaration/storage.rs +++ b/sway-core/src/language/parsed/declaration/storage.rs @@ -11,7 +11,7 @@ use sway_types::{ident::Ident, span::Span, Spanned}; /// All values in this struct are mutable and persistent among executions of the same contract deployment. pub struct StorageDeclaration { pub attributes: transform::AttributesMap, - pub fields: Vec, + pub entries: Vec, pub span: Span, pub storage_keyword: Ident, } @@ -20,7 +20,7 @@ impl EqWithEngines for StorageDeclaration {} impl PartialEqWithEngines for StorageDeclaration { fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool { self.attributes == other.attributes - && self.fields.eq(&other.fields, ctx) + && self.entries.eq(&other.entries, ctx) && self.span == other.span && self.storage_keyword == other.storage_keyword } @@ -32,6 +32,45 @@ impl Spanned for StorageDeclaration { } } +#[derive(Debug, Clone)] +pub struct StorageNamespace { + pub name: Ident, + pub entries: Vec>, +} + +impl EqWithEngines for StorageNamespace {} +impl PartialEqWithEngines for StorageNamespace { + fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool { + self.name.eq(&other.name) && self.entries.eq(&other.entries, ctx) + } +} + +#[derive(Debug, Clone)] +pub enum StorageEntry { + Namespace(StorageNamespace), + Field(StorageField), +} + +impl StorageEntry { + pub fn name(&self) -> Ident { + match self { + StorageEntry::Namespace(namespace) => namespace.name.clone(), + StorageEntry::Field(field) => field.name.clone(), + } + } +} + +impl EqWithEngines for StorageEntry {} +impl PartialEqWithEngines for StorageEntry { + fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool { + match (self, other) { + (StorageEntry::Namespace(n1), StorageEntry::Namespace(n2)) => n1.eq(n2, ctx), + (StorageEntry::Field(f1), StorageEntry::Field(f2)) => f1.eq(f2, ctx), + _ => false, + } + } +} + /// An individual field in a storage declaration. /// A type annotation _and_ initializer value must be provided. The initializer value must be a /// constant expression. For now, that basically means just a literal, but as constant folding @@ -39,6 +78,7 @@ impl Spanned for StorageDeclaration { #[derive(Debug, Clone)] pub struct StorageField { pub name: Ident, + pub key_expression: Option, pub attributes: transform::AttributesMap, pub type_argument: TypeArgument, pub span: Span, diff --git a/sway-core/src/language/parsed/expression/mod.rs b/sway-core/src/language/parsed/expression/mod.rs index b5e1977aa56..73ab3a33cbb 100644 --- a/sway-core/src/language/parsed/expression/mod.rs +++ b/sway-core/src/language/parsed/expression/mod.rs @@ -330,6 +330,7 @@ impl PartialEqWithEngines for ArrayIndexExpression { #[derive(Debug, Clone)] pub struct StorageAccessExpression { + pub namespace_names: Vec, pub field_names: Vec, pub storage_keyword_span: Span, } diff --git a/sway-core/src/language/ty/declaration/storage.rs b/sway-core/src/language/ty/declaration/storage.rs index d56ecb100e7..cb1e9c6b648 100644 --- a/sway-core/src/language/ty/declaration/storage.rs +++ b/sway-core/src/language/ty/declaration/storage.rs @@ -4,12 +4,12 @@ use sway_error::{ error::{CompileError, StructFieldUsageContext}, handler::{ErrorEmitted, Handler}, }; -use sway_types::{state::StateIndex, Ident, Named, Span, Spanned}; +use sway_types::{Ident, Named, Span, Spanned}; use crate::{ engine_threading::*, language::{ty::*, Visibility}, - transform::{self, AttributeKind}, + transform::{self}, type_system::*, Namespace, }; @@ -56,14 +56,6 @@ impl Spanned for TyStorageDecl { } impl TyStorageDecl { - pub(crate) fn storage_namespace(&self) -> Option { - self.attributes - .get(&AttributeKind::Namespace) - .and_then(|attrs| attrs.first()) - .and_then(|attr| attr.args.first()) - .map(|arg| arg.name.clone()) - } - /// Given a path that consists of `fields`, where the first field is one of the storage fields, /// find the type information of all the elements in the path and return it as a [TyStorageAccess]. /// @@ -73,11 +65,13 @@ impl TyStorageDecl { /// /// An error is returned if the above constraints are violated or if the access to the struct fields /// fails. E.g, if the struct field does not exists or is an inaccessible private field. + #[allow(clippy::too_many_arguments)] pub fn apply_storage_load( &self, handler: &Handler, engines: &Engines, namespace: &Namespace, + namespace_names: &[Ident], fields: &[Ident], storage_fields: &[TyStorageField], storage_keyword_span: Span, @@ -95,22 +89,30 @@ impl TyStorageDecl { "Having at least one element in the storage load is guaranteed by the grammar.", ); - let (ix, initial_field_type) = match storage_fields - .iter() - .enumerate() - .find(|(_, sf)| &sf.name == first_field) - { - Some((ix, TyStorageField { type_argument, .. })) => { - (StateIndex::new(ix), type_argument.type_id) - } - None => { - return Err(handler.emit_err(CompileError::StorageFieldDoesNotExist { - field_name: first_field.into(), - available_fields: storage_fields.iter().map(|sf| sf.name.clone()).collect(), - storage_decl_span: self.span(), - })); - } - }; + let (initial_field_type, initial_field_key, initial_field_name) = + match storage_fields.iter().find(|sf| { + &sf.name == first_field + && sf.namespace_names.len() == namespace_names.len() + && sf + .namespace_names + .iter() + .zip(namespace_names.iter()) + .all(|(n1, n2)| n1 == n2) + }) { + Some(TyStorageField { + type_argument, + key_expression, + name, + .. + }) => (type_argument.type_id, key_expression, name), + None => { + return Err(handler.emit_err(CompileError::StorageFieldDoesNotExist { + field_name: first_field.into(), + available_fields: storage_fields.iter().map(|sf| sf.name.clone()).collect(), + storage_decl_span: self.span(), + })); + } + }; access_descriptors.push(TyStorageAccessDescriptor { name: first_field.clone(), @@ -133,6 +135,8 @@ impl TyStorageDecl { _ => None, }; + let mut struct_field_names = vec![]; + for field in remaining_fields { match get_struct_decl(previous_field_type_id) { Some(struct_decl) => { @@ -161,6 +165,8 @@ impl TyStorageDecl { span: field.span(), }); + struct_field_names.push(field.as_str().to_string()); + previous_field = field; previous_field_type_id = current_field_type_id; } @@ -208,8 +214,13 @@ impl TyStorageDecl { Ok(( TyStorageAccess { fields: access_descriptors, - ix, - namespace: self.storage_namespace(), + key_expression: initial_field_key.clone().map(Box::new), + storage_field_names: namespace_names + .iter() + .map(|n| n.as_str().to_string()) + .chain(vec![initial_field_name.as_str().to_string()]) + .collect(), + struct_field_names, storage_keyword_span, }, return_type, @@ -247,6 +258,8 @@ impl Spanned for TyStorageField { #[derive(Clone, Debug)] pub struct TyStorageField { pub name: Ident, + pub namespace_names: Vec, + pub key_expression: Option, pub type_argument: TypeArgument, pub initializer: TyExpression, pub(crate) span: Span, @@ -257,6 +270,7 @@ impl EqWithEngines for TyStorageField {} impl PartialEqWithEngines for TyStorageField { fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool { self.name == other.name + && self.namespace_names.eq(&other.namespace_names) && self.type_argument.eq(&other.type_argument, ctx) && self.initializer.eq(&other.initializer, ctx) } @@ -266,6 +280,8 @@ impl HashWithEngines for TyStorageField { fn hash(&self, state: &mut H, engines: &Engines) { let TyStorageField { name, + namespace_names, + key_expression, type_argument, initializer, // these fields are not hashed because they aren't relevant/a @@ -274,6 +290,8 @@ impl HashWithEngines for TyStorageField { attributes: _, } = self; name.hash(state); + namespace_names.hash(state); + key_expression.hash(state, engines); type_argument.hash(state, engines); initializer.hash(state, engines); } diff --git a/sway-core/src/language/ty/expression/storage.rs b/sway-core/src/language/ty/expression/storage.rs index fe097923d90..127d64fd20b 100644 --- a/sway-core/src/language/ty/expression/storage.rs +++ b/sway-core/src/language/ty/expression/storage.rs @@ -1,24 +1,31 @@ use std::hash::{Hash, Hasher}; -use sway_types::{state::StateIndex, Ident, Span, Spanned}; +use sway_types::{Ident, Span, Spanned}; use crate::{engine_threading::*, type_system::TypeId}; +use super::TyExpression; + /// Describes the full storage access including all the subfields #[derive(Clone, Debug)] pub struct TyStorageAccess { pub fields: Vec, - pub(crate) namespace: Option, - pub(crate) ix: StateIndex, + pub storage_field_names: Vec, + pub struct_field_names: Vec, + pub key_expression: Option>, pub storage_keyword_span: Span, } impl EqWithEngines for TyStorageAccess {} impl PartialEqWithEngines for TyStorageAccess { fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool { - self.ix == other.ix - && self.fields.len() == other.fields.len() + self.fields.len() == other.fields.len() && self.fields.eq(&other.fields, ctx) + && self.storage_field_names.len() == other.storage_field_names.len() + && self.storage_field_names.eq(&other.storage_field_names) + && self.struct_field_names.len() == other.struct_field_names.len() + && self.struct_field_names.eq(&other.struct_field_names) + && self.key_expression.eq(&other.key_expression, ctx) } } @@ -26,13 +33,15 @@ impl HashWithEngines for TyStorageAccess { fn hash(&self, state: &mut H, engines: &Engines) { let TyStorageAccess { fields, - ix, - namespace, storage_keyword_span, + storage_field_names, + struct_field_names, + key_expression, } = self; fields.hash(state, engines); - ix.hash(state); - namespace.hash(state); + storage_field_names.hash(state); + struct_field_names.hash(state); + key_expression.hash(state, engines); storage_keyword_span.hash(state); } } diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs b/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs index a5698c83186..d241cabae3e 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs @@ -4,8 +4,8 @@ use sway_types::{BaseIdent, Ident, Named, Spanned}; use crate::{ decl_engine::{DeclEngineGet, DeclEngineInsert, DeclRef, ReplaceFunctionImplementingType}, language::{ - parsed, - ty::{self, FunctionDecl, TyDecl}, + parsed::{self, StorageEntry}, + ty::{self, FunctionDecl, TyDecl, TyStorageField}, CallPath, }, namespace::{IsExtendingExistingImpl, IsImplSelf}, @@ -433,43 +433,97 @@ impl TyDecl { parsed::Declaration::StorageDeclaration(decl_id) => { let parsed::StorageDeclaration { span, - fields, + entries, attributes, storage_keyword, } = engines.pe().get_storage(&decl_id).as_ref().clone(); - let mut fields_buf = Vec::with_capacity(fields.len()); - for parsed::StorageField { - name, - initializer, - mut type_argument, - attributes, - span: field_span, - .. - } in fields - { - type_argument.type_id = ctx.resolve_type( - handler, - type_argument.type_id, - &name.span(), - EnforceTypeArguments::Yes, - None, - )?; - - let mut ctx = ctx - .by_ref() - .with_type_annotation(type_argument.type_id) - .with_storage_declaration(); - let initializer = - ty::TyExpression::type_check(handler, ctx.by_ref(), &initializer)?; - - fields_buf.push(ty::TyStorageField { - name, - type_argument, - initializer, - span: field_span, - attributes, - }); + let mut fields_buf = vec![]; + fn type_check_storage_entries( + handler: &Handler, + mut ctx: TypeCheckContext, + entries: Vec, + fields_buf: &mut Vec, + namespace_names: Vec, + ) -> Result<(), ErrorEmitted> { + let engines = ctx.engines; + for entry in entries { + if let StorageEntry::Field(parsed::StorageField { + name, + key_expression, + initializer, + mut type_argument, + attributes, + span: field_span, + .. + }) = entry + { + type_argument.type_id = ctx.by_ref().resolve_type( + handler, + type_argument.type_id, + &name.span(), + EnforceTypeArguments::Yes, + None, + )?; + + let mut ctx = ctx + .by_ref() + .with_type_annotation(type_argument.type_id) + .with_storage_declaration(); + let initializer = + ty::TyExpression::type_check(handler, ctx.by_ref(), &initializer)?; + + let mut key_ty_expression = None; + if let Some(key_expression) = key_expression { + let mut key_ctx = ctx.with_type_annotation(engines.te().insert( + engines, + TypeInfo::B256, + None, + )); + + key_ty_expression = Some(ty::TyExpression::type_check( + handler, + key_ctx.by_ref(), + &key_expression, + )?); + } + + fields_buf.push(ty::TyStorageField { + name, + namespace_names: namespace_names.clone(), + key_expression: key_ty_expression, + type_argument, + initializer, + span: field_span, + attributes, + }); + } else if let StorageEntry::Namespace(namespace) = entry { + let mut new_namespace_names = namespace_names.clone(); + new_namespace_names.push(namespace.name); + type_check_storage_entries( + handler, + ctx.by_ref(), + namespace + .entries + .iter() + .map(|e| (**e).clone()) + .collect::>(), + fields_buf, + new_namespace_names, + )?; + } + } + + Ok(()) } + + type_check_storage_entries( + handler, + ctx.by_ref(), + entries, + &mut fields_buf, + vec![], + )?; + let decl = ty::TyStorageDecl { fields: fields_buf, span, diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs b/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs index 22e568ce153..c31fcc70566 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs @@ -1,9 +1,12 @@ +use std::collections::HashMap; + use crate::{ fuel_prelude::fuel_tx::StorageSlot, ir_generation::{ - const_eval::compile_constant_expression_to_constant, storage::serialize_to_storage_slots, + const_eval::compile_constant_expression_to_constant, + storage::{get_storage_key_string, serialize_to_storage_slots}, }, - language::ty, + language::ty::{self, TyExpression, TyStorageField}, metadata::MetadataManager, semantic_analysis::{ TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckFinalization, @@ -11,12 +14,14 @@ use crate::{ }, Engines, }; +use fuel_vm::fuel_tx::Bytes32; use sway_error::{ error::CompileError, handler::{ErrorEmitted, Handler}, + warning::CompileWarning, }; -use sway_ir::{Context, Module}; -use sway_types::state::StateIndex; +use sway_ir::{ConstantValue, Context, Module}; +use sway_types::{u256::U256, Spanned}; impl ty::TyStorageDecl { pub(crate) fn get_initialized_storage_slots( @@ -28,19 +33,46 @@ impl ty::TyStorageDecl { module: Module, ) -> Result, ErrorEmitted> { handler.scope(|handler| { + let mut slot_fields = HashMap::::new(); let storage_slots = self .fields .iter() - .enumerate() - .map(|(i, f)| { - f.get_initialized_storage_slots( - engines, - context, - md_mgr, - module, - self.storage_namespace().as_ref().map(|id| id.as_str()), - &StateIndex::new(i), - ) + .map(|f| { + let slots = f.get_initialized_storage_slots(engines, context, md_mgr, module); + + // Check if slot with same key was already used and throw warning. + if let Ok(slots) = slots.clone() { + for s in slots.into_iter() { + if let Some(old_field) = slot_fields.insert(*s.key(), f.clone()) { + handler.emit_warn(CompileWarning { + span: f.span(), + warning_content: + sway_error::warning::Warning::DuplicatedStorageKey { + key: format!("{:X} ", s.key()), + field1: get_storage_key_string( + old_field + .namespace_names + .iter() + .map(|i| i.as_str().to_string()) + .chain(vec![old_field + .name + .as_str() + .to_string()]) + .collect::>(), + ), + field2: get_storage_key_string( + f.namespace_names + .iter() + .map(|i| i.as_str().to_string()) + .chain(vec![f.name.as_str().to_string()]) + .collect::>(), + ), + }, + }) + } + } + } + slots }) .filter_map(|s| s.map_err(|e| handler.emit_err(e)).ok()) .flatten() @@ -58,9 +90,9 @@ impl ty::TyStorageField { context: &mut Context, md_mgr: &mut MetadataManager, module: Module, - ns: Option<&str>, - ix: &StateIndex, ) -> Result, CompileError> { + let key = + Self::get_key_expression_const(&self.key_expression, engines, context, md_mgr, module)?; compile_constant_expression_to_constant( engines, context, @@ -71,7 +103,50 @@ impl ty::TyStorageField { &self.initializer, true, ) - .map(|constant| serialize_to_storage_slots(&constant, context, ix, ns, &constant.ty, &[])) + .map(|constant| { + serialize_to_storage_slots( + &constant, + context, + self.namespace_names + .iter() + .map(|i| i.as_str().to_string()) + .chain(vec![self.name.as_str().to_string()]) + .collect(), + key, + &constant.ty, + ) + }) + } + + pub(crate) fn get_key_expression_const( + key_expression: &Option, + engines: &Engines, + context: &mut Context, + md_mgr: &mut MetadataManager, + module: Module, + ) -> Result, CompileError> { + if let Some(key_expression) = key_expression { + let const_key = compile_constant_expression_to_constant( + engines, + context, + md_mgr, + module, + None, + None, + key_expression, + true, + )?; + if let ConstantValue::B256(key) = const_key.value { + Ok(Some(key)) + } else { + Err(CompileError::Internal( + "Expected B256 key", + key_expression.span.clone(), + )) + } + } else { + Ok(None) + } } } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index a209fc56677..0d7667802ee 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -332,6 +332,7 @@ impl ty::TyExpression { Self::type_check_array_index(handler, ctx, prefix, index, span) } ExpressionKind::StorageAccess(StorageAccessExpression { + namespace_names, field_names, storage_keyword_span, }) => { @@ -342,6 +343,7 @@ impl ty::TyExpression { Self::type_check_storage_access( handler, ctx, + namespace_names, field_names, storage_keyword_span.clone(), &span, @@ -1118,6 +1120,7 @@ impl ty::TyExpression { fn type_check_storage_access( handler: &Handler, ctx: TypeCheckContext, + namespace_names: &[Ident], checkee: &[Ident], storage_keyword_span: Span, span: &Span, @@ -1146,6 +1149,7 @@ impl ty::TyExpression { handler, ctx.engines, ctx.namespace(), + namespace_names, checkee, &storage_fields, storage_keyword_span.clone(), diff --git a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs index 4e471633fd0..ea7b64602b3 100644 --- a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs +++ b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs @@ -94,11 +94,13 @@ impl Items { &self.symbols } + #[allow(clippy::too_many_arguments)] pub fn apply_storage_load( &self, handler: &Handler, engines: &Engines, namespace: &Namespace, + namespace_names: &[Ident], fields: &[Ident], storage_fields: &[ty::TyStorageField], storage_keyword_span: Span, @@ -110,6 +112,7 @@ impl Items { handler, engines, namespace, + namespace_names, fields, storage_fields, storage_keyword_span, diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index 735b625d7bd..9d74811182f 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -455,16 +455,10 @@ impl Dependencies { }) } Declaration::StorageDeclaration(decl_id) => { - let StorageDeclaration { fields, .. } = &*engines.pe().get_storage(decl_id); - self.gather_from_iter( - fields.iter(), - |deps, - StorageField { - ref type_argument, .. - }| { - deps.gather_from_type_argument(engines, type_argument) - }, - ) + let StorageDeclaration { entries, .. } = &*engines.pe().get_storage(decl_id); + self.gather_from_iter(entries.iter(), |deps, entry| { + deps.gather_from_storage_entry(engines, entry) + }) } Declaration::TypeAliasDeclaration(decl_id) => { let TypeAliasDeclaration { ty, .. } = &*engines.pe().get_type_alias(decl_id); @@ -473,6 +467,18 @@ impl Dependencies { } } + fn gather_from_storage_entry(self, engines: &Engines, entry: &StorageEntry) -> Self { + match entry { + StorageEntry::Namespace(namespace) => self + .gather_from_iter(namespace.entries.iter(), |deps, entry| { + deps.gather_from_storage_entry(engines, entry) + }), + StorageEntry::Field(field) => { + self.gather_from_type_argument(engines, &field.type_argument) + } + } + } + fn gather_from_constant_decl( self, engines: &Engines, diff --git a/sway-core/src/transform/attribute.rs b/sway-core/src/transform/attribute.rs index d8e7a4b64ca..46d2acde691 100644 --- a/sway-core/src/transform/attribute.rs +++ b/sway-core/src/transform/attribute.rs @@ -67,7 +67,6 @@ pub enum AttributeKind { Allow, Cfg, Deprecated, - Namespace, Fallback, } @@ -80,7 +79,7 @@ impl AttributeKind { Doc | DocComment | Storage | Inline | Test | Payable | Deprecated | Fallback => { (0, None) } - Allow | Cfg | Namespace => (1, Some(1)), + Allow | Cfg => (1, Some(1)), } } @@ -88,8 +87,7 @@ impl AttributeKind { pub fn expected_args_values(self, _arg_index: usize) -> Option> { use AttributeKind::*; match self { - Deprecated | Namespace | Doc | DocComment | Storage | Inline | Test | Payable - | Fallback => None, + Deprecated | Doc | DocComment | Storage | Inline | Test | Payable | Fallback => None, Allow => Some(vec![ ALLOW_DEAD_CODE_NAME.to_string(), ALLOW_DEPRECATED_NAME.to_string(), diff --git a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs index 86e9869a2d6..46049cdef2d 100644 --- a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs +++ b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs @@ -1003,37 +1003,51 @@ fn item_storage_to_storage_declaration( ) -> Result, ErrorEmitted> { let mut errors = Vec::new(); let span = item_storage.span(); - let fields: Vec = item_storage - .fields + let entries: Vec = item_storage + .entries .into_inner() .into_iter() - .map(|storage_field| { - let attributes = item_attrs_to_map(context, handler, &storage_field.attribute_list)?; + .map(|storage_entry| { + let attributes = item_attrs_to_map(context, handler, &storage_entry.attribute_list)?; if !cfg_eval(context, handler, &attributes, context.experimental)? { return Ok(None); } - Ok(Some(storage_field_to_storage_field( + Ok(Some(storage_entry_to_storage_entry( context, handler, engines, - storage_field.value, + storage_entry.value, attributes, )?)) }) - .filter_map_ok(|field| field) + .filter_map_ok(|entry| entry) .collect::>()?; - // Make sure each storage field is declared once - let mut names_of_fields = std::collections::HashSet::new(); - for v in fields.iter() { - if !names_of_fields.insert(v.name.clone()) { - errors.push(ConvertParseTreeError::DuplicateStorageField { - name: v.name.clone(), - span: v.name.span(), - }); + fn check_duplicate_names(entries: Vec, errors: &mut Vec) { + // Make sure each storage field is declared once + let mut names_of_fields = std::collections::HashSet::new(); + for v in entries { + if !names_of_fields.insert(v.name().clone()) { + errors.push(ConvertParseTreeError::DuplicateStorageField { + name: v.name().clone(), + span: v.name().span(), + }); + } + if let StorageEntry::Namespace(namespace) = v { + check_duplicate_names( + namespace + .entries + .iter() + .map(|e| (**e).clone()) + .collect::>(), + errors, + ); + } } } + check_duplicate_names(entries.clone(), &mut errors); + if let Some(errors) = emit_all(handler, errors) { return Err(errors); } @@ -1041,7 +1055,7 @@ fn item_storage_to_storage_declaration( let storage_declaration = StorageDeclaration { attributes, span, - fields, + entries, storage_keyword: item_storage.storage_token.into(), }; let storage_declaration = engines.pe().insert(storage_declaration); @@ -2160,11 +2174,15 @@ fn expr_to_expression( // Parent is `storage`. We found what we were looking for. Expr::Path(path_expr) if path_expr.root_opt.is_none() - && path_expr.suffix.is_empty() && path_expr.prefix.generics_opt.is_none() && path_expr.prefix.name.as_str() == "storage" => { break ExpressionKind::StorageAccess(StorageAccessExpression { + namespace_names: path_expr + .suffix + .iter() + .map(|s| s.1.name.clone()) + .collect(), field_names: idents.into_iter().rev().cloned().collect(), storage_keyword_span: path_expr.prefix.name.span(), }) @@ -2470,6 +2488,52 @@ fn op_call( }) } +fn storage_entry_to_storage_entry( + context: &mut Context, + handler: &Handler, + engines: &Engines, + storage_entry: sway_ast::StorageEntry, + attributes: AttributesMap, +) -> Result { + if let Some(storage_field) = storage_entry.field { + Ok(StorageEntry::Field(storage_field_to_storage_field( + context, + handler, + engines, + storage_field, + attributes, + )?)) + } else { + let mut entries = vec![]; + let namespace = storage_entry.namespace.unwrap(); + for entry in namespace + .into_inner() + .into_iter() + .flat_map(|storage_entry| { + let attributes = + item_attrs_to_map(context, handler, &storage_entry.attribute_list)?; + if !cfg_eval(context, handler, &attributes, context.experimental)? { + return Ok::, ErrorEmitted>(None); + } + Ok(Some(storage_entry_to_storage_entry( + context, + handler, + engines, + *storage_entry.value, + attributes, + )?)) + }) + .flatten() + { + entries.push(Box::new(entry)); + } + Ok(StorageEntry::Namespace(StorageNamespace { + name: storage_entry.name, + entries, + })) + } +} + fn storage_field_to_storage_field( context: &mut Context, handler: &Handler, @@ -2478,9 +2542,14 @@ fn storage_field_to_storage_field( attributes: AttributesMap, ) -> Result { let span = storage_field.span(); + let mut key_expr_opt = None; + if let Some(key_expr) = storage_field.key_expr { + key_expr_opt = Some(expr_to_expression(context, handler, engines, key_expr)?); + } let storage_field = StorageField { attributes, name: storage_field.name, + key_expression: key_expr_opt, type_argument: ty_to_type_argument(context, handler, engines, storage_field.ty)?, span, initializer: expr_to_expression(context, handler, engines, storage_field.initializer)?, @@ -4361,6 +4430,7 @@ fn assignable_to_expression( let field_names = field_names.into_iter().rev().cloned().collect(); Expression { kind: ExpressionKind::StorageAccess(StorageAccessExpression { + namespace_names: vec![], field_names, storage_keyword_span: storage_name.span(), }), @@ -4604,6 +4674,13 @@ fn item_attrs_to_map( let attrs = attr_decl.attribute.get().into_iter(); for attr in attrs { let name = attr.name.as_str(); + if name == NAMESPACE_ATTRIBUTE_NAME { + handler.emit_warn(CompileWarning { + span: attr_decl.span().clone(), + warning_content: Warning::NamespaceAttributeDeprecated, + }); + continue; + } if !VALID_ATTRIBUTE_NAMES.contains(&name) { handler.emit_warn(CompileWarning { span: attr_decl.span().clone(), @@ -4646,7 +4723,6 @@ fn item_attrs_to_map( ALLOW_ATTRIBUTE_NAME => Some(AttributeKind::Allow), CFG_ATTRIBUTE_NAME => Some(AttributeKind::Cfg), DEPRECATED_ATTRIBUTE_NAME => Some(AttributeKind::Deprecated), - NAMESPACE_ATTRIBUTE_NAME => Some(AttributeKind::Namespace), FALLBACK_ATTRIBUTE_NAME => Some(AttributeKind::Fallback), _ => None, } { diff --git a/sway-error/src/parser_error.rs b/sway-error/src/parser_error.rs index 170964db9c6..9f7d4e364f7 100644 --- a/sway-error/src/parser_error.rs +++ b/sway-error/src/parser_error.rs @@ -106,6 +106,8 @@ pub enum ParseErrorKind { ExpectedPathType, #[error("Expected ':'. Enum variants must be in the form `Variant: ()`, `Variant: `, or `Variant: (, ..., )`. E.g., `Foo: (), or `Bar: (bool, u32)`.")] MissingColonInEnumTypeField, + #[error("Expected storage key of type U256.")] + ExpectedStorageKeyU256, } #[derive(Debug, Error, Clone, PartialEq, Eq, Hash)] diff --git a/sway-error/src/warning.rs b/sway-error/src/warning.rs index e06825c6411..cef06a740cf 100644 --- a/sway-error/src/warning.rs +++ b/sway-error/src/warning.rs @@ -101,6 +101,7 @@ pub enum Warning { UnrecognizedAttribute { attrib_name: Ident, }, + NamespaceAttributeDeprecated, AttributeExpectedNumberOfArguments { attrib_name: Ident, received_args: usize, @@ -121,6 +122,11 @@ pub enum Warning { UsingDeprecated { message: String, }, + DuplicatedStorageKey { + key: String, + field1: String, + field2: String, + }, } impl fmt::Display for Warning { @@ -239,6 +245,13 @@ impl fmt::Display for Warning { ), MatchExpressionUnreachableArm { .. } => write!(f, "This match arm is unreachable."), UnrecognizedAttribute {attrib_name} => write!(f, "Unknown attribute: \"{attrib_name}\"."), + NamespaceAttributeDeprecated => write!(f, "Attribute namespace is deprecated.\n\ + You can use namespaces inside storage:\n\ + storage {{\n\ + \tmy_storage_namespace {{\n\ + \t\tfield: u64 = 1, \n\ + \t}}\n\ + }}"), AttributeExpectedNumberOfArguments {attrib_name, received_args, expected_min_len, expected_max_len } => write!( f, "Attribute: \"{attrib_name}\" expected {} argument(s) received {received_args}.", @@ -264,6 +277,7 @@ impl fmt::Display for Warning { You can enable the new behavior with the --experimental-private-modules flag, which will become the default behavior in a later release. More details are available in the related RFC: https://github.com/FuelLabs/sway-rfcs/blob/master/rfcs/0008-private-modules.md"), UsingDeprecated { message } => write!(f, "{}", message), + DuplicatedStorageKey { key, field1, field2 } => write!(f, "Two storage fields are using the same storage key.\nFirst field: {field1}\nSecond field: {field2}\nKey: {key}"), } } } diff --git a/sway-lsp/src/core/token.rs b/sway-lsp/src/core/token.rs index 1f4a72373ea..d869e87e0a4 100644 --- a/sway-lsp/src/core/token.rs +++ b/sway-lsp/src/core/token.rs @@ -8,8 +8,8 @@ use sway_core::{ AbiCastExpression, AmbiguousPathExpression, Declaration, DelineatedPathExpression, EnumVariant, Expression, FunctionApplicationExpression, FunctionParameter, IncludeStatement, MethodApplicationExpression, Scrutinee, StorageField, - StructExpression, StructExpressionField, StructField, StructScrutineeField, Supertrait, - TraitFn, UseStatement, + StorageNamespace, StructExpression, StructExpressionField, StructField, + StructScrutineeField, Supertrait, TraitFn, UseStatement, }, ty, }, @@ -44,6 +44,7 @@ pub enum AstToken { MethodApplicationExpression(MethodApplicationExpression), Scrutinee(Scrutinee), StorageField(StorageField), + StorageNamespace(StorageNamespace), StructExpression(StructExpression), StructExpressionField(StructExpressionField), StructField(StructField), diff --git a/sway-lsp/src/traverse/lexed_tree.rs b/sway-lsp/src/traverse/lexed_tree.rs index 9f0af530d56..f441321c500 100644 --- a/sway-lsp/src/traverse/lexed_tree.rs +++ b/sway-lsp/src/traverse/lexed_tree.rs @@ -9,7 +9,8 @@ use sway_ast::{ ExprTupleDescriptor, FnArg, FnArgs, FnSignature, IfCondition, IfExpr, ItemAbi, ItemConfigurable, ItemConst, ItemEnum, ItemFn, ItemImpl, ItemImplItem, ItemKind, ItemStorage, ItemStruct, ItemTrait, ItemTypeAlias, ItemUse, MatchBranchKind, ModuleKind, Pattern, - PatternStructField, Statement, StatementLet, StorageField, TraitType, Ty, TypeField, UseTree, + PatternStructField, Statement, StatementLet, StorageEntry, StorageField, TraitType, Ty, + TypeField, UseTree, }; use sway_core::language::{lexed::LexedProgram, HasSubmodules}; use sway_types::{Ident, Span, Spanned}; @@ -404,11 +405,26 @@ impl Parse for ItemStorage { fn parse(&self, ctx: &ParseContext) { insert_keyword(ctx, self.storage_token.span()); - self.fields + self.entries .get() .into_iter() .par_bridge() - .for_each(|field| field.value.parse(ctx)); + .for_each(|entry| entry.value.parse(ctx)); + } +} + +impl Parse for StorageEntry { + fn parse(&self, ctx: &ParseContext) { + if let Some(namespace) = &self.namespace { + namespace + .clone() + .into_inner() + .into_iter() + .par_bridge() + .for_each(|entry| entry.value.parse(ctx)); + } else if let Some(field) = &self.field { + field.parse(ctx); + } } } diff --git a/sway-lsp/src/traverse/parsed_tree.rs b/sway-lsp/src/traverse/parsed_tree.rs index 21cf83c954d..314b5210420 100644 --- a/sway-lsp/src/traverse/parsed_tree.rs +++ b/sway-lsp/src/traverse/parsed_tree.rs @@ -26,10 +26,10 @@ use sway_core::{ IncludeStatement, IntrinsicFunctionExpression, LazyOperatorExpression, MatchExpression, MethodApplicationExpression, MethodName, ParseModule, ParseProgram, ParseSubmodule, QualifiedPathType, ReassignmentExpression, ReassignmentTarget, RefExpression, - Scrutinee, StorageAccessExpression, StorageDeclaration, StorageField, - StructDeclaration, StructExpression, StructExpressionField, StructField, - StructScrutineeField, SubfieldExpression, Supertrait, TraitDeclaration, TraitFn, - TraitItem, TraitTypeDeclaration, TupleIndexExpression, TypeAliasDeclaration, + Scrutinee, StorageAccessExpression, StorageDeclaration, StorageEntry, StorageField, + StorageNamespace, StructDeclaration, StructExpression, StructExpressionField, + StructField, StructScrutineeField, SubfieldExpression, Supertrait, TraitDeclaration, + TraitFn, TraitItem, TraitTypeDeclaration, TupleIndexExpression, TypeAliasDeclaration, UseStatement, VariableDeclaration, WhileLoopExpression, }, CallPathTree, HasSubmodules, Literal, @@ -312,6 +312,7 @@ impl Parse for Expression { } ExpressionKind::StorageAccess(StorageAccessExpression { field_names, + namespace_names, storage_keyword_span, }) => { let storage_ident = Ident::new(storage_keyword_span.clone()); @@ -319,6 +320,15 @@ impl Parse for Expression { ctx.ident(&storage_ident), Token::from_parsed(AstToken::Ident(storage_ident), SymbolKind::Unknown), ); + adaptive_iter(namespace_names, |namespace_name| { + ctx.tokens.insert( + ctx.ident(namespace_name), + Token::from_parsed( + AstToken::Ident(namespace_name.clone()), + SymbolKind::Field, + ), + ); + }); adaptive_iter(field_names, |field_name| { ctx.tokens.insert( ctx.ident(field_name), @@ -885,11 +895,30 @@ impl Parse for ParsedDeclId { impl Parse for ParsedDeclId { fn parse(&self, ctx: &ParseContext) { let storage_decl = ctx.engines.pe().get_storage(self); - adaptive_iter(&storage_decl.fields, |field| field.parse(ctx)); + adaptive_iter(&storage_decl.entries, |entry| entry.parse(ctx)); storage_decl.attributes.parse(ctx); } } +impl Parse for StorageEntry { + fn parse(&self, ctx: &ParseContext) { + match self { + StorageEntry::Namespace(namespace) => namespace.parse(ctx), + StorageEntry::Field(field) => field.parse(ctx), + } + } +} + +impl Parse for StorageNamespace { + fn parse(&self, ctx: &ParseContext) { + ctx.tokens.insert( + ctx.ident(&self.name), + Token::from_parsed(AstToken::StorageNamespace(self.clone()), SymbolKind::Field), + ); + self.entries.iter().for_each(|entry| entry.parse(ctx)); + } +} + impl Parse for StorageField { fn parse(&self, ctx: &ParseContext) { ctx.tokens.insert( diff --git a/sway-parse/src/item/item_storage.rs b/sway-parse/src/item/item_storage.rs index 656226c7d5c..03640e2fd68 100644 --- a/sway-parse/src/item/item_storage.rs +++ b/sway-parse/src/item/item_storage.rs @@ -1,16 +1,49 @@ use crate::{Parse, ParseResult, Parser}; -use sway_ast::{ItemStorage, StorageField}; +use sway_ast::{ + attribute::Annotated, + keywords::{ColonToken, InToken}, + Braces, CommaToken, Expr, ItemStorage, Punctuated, StorageEntry, StorageField, +}; +use sway_types::BaseIdent; + +impl Parse for StorageEntry { + fn parse(parser: &mut Parser) -> ParseResult { + let name: BaseIdent = parser.parse()?; + let mut field = None; + let mut namespace = None; + if parser.peek::().is_some() || parser.peek::().is_some() { + let mut f: StorageField = parser.parse()?; + f.name = name.clone(); + field = Some(f); + } else { + let n: Braces>, CommaToken>> = parser.parse()?; + namespace = Some(n); + } + Ok(StorageEntry { + name, + namespace, + field, + }) + } +} impl Parse for StorageField { fn parse(parser: &mut Parser) -> ParseResult { - let name = parser.parse()?; + let name = BaseIdent::dummy(); // Name will be overridden in StorageEntry parse. + let in_token: Option = parser.take(); + let mut key_opt: Option = None; + if in_token.is_some() { + key_opt = Some(parser.parse()?); + } let colon_token = parser.parse()?; let ty = parser.parse()?; let eq_token = parser.parse()?; let initializer = parser.parse()?; Ok(StorageField { name, + in_token, + key_expr: key_opt, colon_token, ty, eq_token, @@ -22,10 +55,10 @@ impl Parse for StorageField { impl Parse for ItemStorage { fn parse(parser: &mut Parser) -> ParseResult { let storage_token = parser.parse()?; - let fields = parser.parse()?; + let entries = parser.parse()?; Ok(ItemStorage { storage_token, - fields, + entries, }) } } diff --git a/sway-types/src/constants.rs b/sway-types/src/constants.rs index a53c772f393..5cbbabe1d97 100644 --- a/sway-types/src/constants.rs +++ b/sway-types/src/constants.rs @@ -68,7 +68,6 @@ pub const VALID_ATTRIBUTE_NAMES: &[&str] = &[ ALLOW_ATTRIBUTE_NAME, CFG_ATTRIBUTE_NAME, DEPRECATED_ATTRIBUTE_NAME, - NAMESPACE_ATTRIBUTE_NAME, FALLBACK_ATTRIBUTE_NAME, ]; diff --git a/sway-types/src/lib.rs b/sway-types/src/lib.rs index f0f53752069..5d8e1cf95c1 100644 --- a/sway-types/src/lib.rs +++ b/sway-types/src/lib.rs @@ -20,8 +20,6 @@ pub use source_engine::*; pub mod span; pub use span::*; -pub mod state; - pub mod style; pub mod ast; diff --git a/sway-types/src/span.rs b/sway-types/src/span.rs index bf7eb181400..c757fb523da 100644 --- a/sway-types/src/span.rs +++ b/sway-types/src/span.rs @@ -239,6 +239,12 @@ pub trait Spanned { fn span(&self) -> Span; } +impl Spanned for Box { + fn span(&self) -> Span { + (**self).span() + } +} + #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct LineCol { pub line: usize, diff --git a/sway-types/src/state.rs b/sway-types/src/state.rs deleted file mode 100644 index 5a7ea4d038e..00000000000 --- a/sway-types/src/state.rs +++ /dev/null @@ -1,30 +0,0 @@ -/// Represents the position in a storage statement that a field was declared. -/// For example, in the following storage declaration, `foo` has [StateIndex] 0 and `bar` has -/// [StateIndex] 1. -/// -/// ```ignore -/// storage { -/// foo: u32 = 0, -/// bar: u32 = 0, -/// } -/// ``` -/// -/// The actual [StorageSlot] is calculated as the sha256 hash of the domain separator -/// [sway_utils::constants::STORAGE_DOMAIN_SEPARATOR] concatenated with the index. -/// -/// Here, `foo`'s [StorageSlot] is `sha256(format!("{}{}", STORAGE_DOMAIN_SEPARATOR, 0))` or -/// `F383B0CE51358BE57DAA3B725FE44ACDB2D880604E367199080B4379C41BB6ED`. -/// -/// `bar`'s [StorageSlot] is `sha256(format!("{}{}", STORAGE_DOMAIN_SEPARATOR, 1))` or -/// `DE9090CB50E71C2588C773487D1DA7066D0C719849A7E58DC8B6397A25C567C0`. -#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)] -pub struct StateIndex(usize); - -impl StateIndex { - pub fn new(raw: usize) -> Self { - StateIndex(raw) - } - pub fn to_usize(&self) -> usize { - self.0 - } -} diff --git a/sway-utils/src/constants.rs b/sway-utils/src/constants.rs index 30cf79ff401..00214945ed5 100644 --- a/sway-utils/src/constants.rs +++ b/sway-utils/src/constants.rs @@ -7,7 +7,10 @@ pub const USER_FORC_DIRECTORY: &str = ".forc"; pub const SRC_DIR: &str = "src"; pub const DEFAULT_NODE_URL: &str = "http://127.0.0.1:4000"; pub const LANGUAGE_NAME: &str = "Sway"; -pub const STORAGE_DOMAIN_SEPARATOR: &str = "storage_"; +pub const STORAGE_DOMAIN: &str = "storage"; +pub const STORAGE_NAMESPACE_SEPARATOR: &str = "::"; +pub const STORAGE_FIELD_SEPARATOR: &str = "."; +pub const STRUCT_FIELD_SEPARATOR: &str = "."; pub const LIB_ENTRY: &str = "lib.sw"; pub const MAIN_ENTRY: &str = "main.sw"; pub const FORC_INIT_MANIFEST_AUTHOR: &str = "FORC_INIT_MANIFEST_AUTHOR"; diff --git a/swayfmt/src/items/item_storage/mod.rs b/swayfmt/src/items/item_storage/mod.rs index ffbdeca3559..0fa131b55bc 100644 --- a/swayfmt/src/items/item_storage/mod.rs +++ b/swayfmt/src/items/item_storage/mod.rs @@ -10,9 +10,9 @@ use crate::{ CurlyBrace, }, }; -use std::fmt::Write; -use sway_ast::{keywords::Token, ItemStorage, StorageField}; -use sway_types::{ast::Delimiter, Spanned}; +use std::{collections::HashMap, fmt::Write}; +use sway_ast::{keywords::Token, ItemStorage, StorageEntry, StorageField}; +use sway_types::{ast::Delimiter, IdentUnique, Spanned}; #[cfg(test)] mod tests; @@ -33,7 +33,7 @@ impl Format for ItemStorage { // Add storage token write!(formatted_code, "{}", self.storage_token.span().as_str())?; - let fields = self.fields.get(); + let entries = self.entries.get(); // Handle opening brace Self::open_curly_brace(formatted_code, formatter)?; @@ -44,7 +44,7 @@ impl Format for ItemStorage { match formatter.config.structures.field_alignment { FieldAlignment::AlignFields(storage_field_align_threshold) => { writeln!(formatted_code)?; - let value_pairs = &fields + let value_pairs = &entries .value_separator_pairs .iter() // TODO: Handle annotations instead of stripping them @@ -52,16 +52,48 @@ impl Format for ItemStorage { .collect::>(); // In first iteration we are going to be collecting the lengths of the // struct fields. - let field_length: Vec = value_pairs - .iter() - .map(|(storage_field, _)| storage_field.name.as_str().len()) - .collect(); + let mut field_lengths: HashMap = + HashMap::::new(); + fn collect_field_lengths( + entry: &StorageEntry, + ident_size: usize, + current_ident: usize, + field_lengths: &mut HashMap, + ) { + if let Some(namespace) = &entry.namespace { + namespace.clone().into_inner().into_iter().for_each(|e| { + collect_field_lengths( + &e.value, + ident_size, + current_ident + ident_size, + field_lengths, + ) + }); + } else if let Some(storage_field) = &entry.field { + field_lengths.insert( + storage_field.name.clone().into(), + current_ident + storage_field.name.as_str().len(), + ); + } + } + let ident_size = formatter.config.whitespace.tab_spaces; + value_pairs.iter().for_each(|(storage_entry, _)| { + collect_field_lengths(storage_entry, ident_size, 0, &mut field_lengths) + }); + if let Some(final_value) = &entries.final_value_opt { + collect_field_lengths( + &final_value.value, + ident_size, + 0, + &mut field_lengths, + ); + } // Find the maximum length in the `field_length` vector that is still // smaller than `storage_field_align_threshold`. `max_valid_field_length`: // the length of the field that we are taking as a reference to align. let mut max_valid_field_length = 0; - field_length.iter().for_each(|length| { + field_lengths.iter().for_each(|(_, length)| { if *length > max_valid_field_length && *length < storage_field_align_threshold { @@ -69,47 +101,104 @@ impl Format for ItemStorage { } }); - let value_pairs_iter = value_pairs.iter().enumerate(); - for (field_index, (storage_field, comma_token)) in value_pairs_iter.clone() - { + fn format_entry( + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + entry: &StorageEntry, + field_lengths: &HashMap, + max_valid_field_length: usize, + ) -> Result<(), FormatterError> { write!(formatted_code, "{}", formatter.indent_to_str()?)?; - // Add name - storage_field.name.format(formatted_code, formatter)?; - - // `current_field_length`: the length of the current field that we are - // trying to format. - let current_field_length = field_length[field_index]; - if current_field_length < max_valid_field_length { - // We need to add alignment between `:` and `ty` - let mut required_alignment = - max_valid_field_length - current_field_length; - while required_alignment != 0 { - write!(formatted_code, " ")?; - required_alignment -= 1; + if let Some(namespace) = &entry.namespace { + entry.name.format(formatted_code, formatter)?; + ItemStorage::open_curly_brace(formatted_code, formatter)?; + writeln!(formatted_code)?; + + for (e, comma_token) in + namespace.clone().into_inner().value_separator_pairs + { + format_entry( + formatted_code, + formatter, + &e.value, + field_lengths, + max_valid_field_length, + )?; + writeln!(formatted_code, "{}", comma_token.ident().as_str())?; + } + if let Some(final_value) = + &namespace.clone().into_inner().final_value_opt + { + format_entry( + formatted_code, + formatter, + &final_value.value, + field_lengths, + max_valid_field_length, + )?; + writeln!(formatted_code)?; + } + + ItemStorage::close_curly_brace(formatted_code, formatter)?; + } else if let Some(storage_field) = &entry.field { + // Add name + storage_field.name.format(formatted_code, formatter)?; + + // `current_field_length`: the length of the current field that we are + // trying to format. + let current_field_length = field_lengths + .get(&storage_field.name.clone().into()) + .unwrap(); + if *current_field_length < max_valid_field_length { + // We need to add alignment between `:` and `ty` + let mut required_alignment = + max_valid_field_length - current_field_length; + while required_alignment != 0 { + write!(formatted_code, " ")?; + required_alignment -= 1; + } } + // Add `:`, `ty` & `CommaToken` + write!( + formatted_code, + " {} ", + storage_field.colon_token.ident().as_str(), + )?; + storage_field.ty.format(formatted_code, formatter)?; + write!( + formatted_code, + " {} ", + storage_field.eq_token.ident().as_str() + )?; + storage_field + .initializer + .format(formatted_code, formatter)?; } - // Add `:`, `ty` & `CommaToken` - write!( - formatted_code, - " {} ", - storage_field.colon_token.ident().as_str(), - )?; - storage_field.ty.format(formatted_code, formatter)?; - write!( + + Ok(()) + } + for (storage_entry, comma_token) in value_pairs.iter().clone() { + format_entry( formatted_code, - " {} ", - storage_field.eq_token.ident().as_str() + formatter, + storage_entry, + &field_lengths, + max_valid_field_length, )?; - storage_field - .initializer - .format(formatted_code, formatter)?; writeln!(formatted_code, "{}", comma_token.ident().as_str())?; } - if let Some(final_value) = &fields.final_value_opt { - final_value.format(formatted_code, formatter)?; + if let Some(final_value) = &entries.final_value_opt { + format_entry( + formatted_code, + formatter, + &final_value.value, + &field_lengths, + max_valid_field_length, + )?; + writeln!(formatted_code)?; } } - FieldAlignment::Off => fields.format(formatted_code, formatter)?, + FieldAlignment::Off => entries.format(formatted_code, formatter)?, } // Handle closing brace @@ -164,11 +253,25 @@ impl CurlyBrace for ItemStorage { impl LeafSpans for ItemStorage { fn leaf_spans(&self) -> Vec { let mut collected_spans = vec![ByteSpan::from(self.storage_token.span())]; - collected_spans.append(&mut self.fields.leaf_spans()); + collected_spans.append(&mut self.entries.leaf_spans()); collected_spans } } +impl LeafSpans for StorageEntry { + fn leaf_spans(&self) -> Vec { + if let Some(namespace) = &self.namespace { + let mut collected_spans = vec![ByteSpan::from(self.name.span())]; + collected_spans.append(&mut namespace.leaf_spans()); + collected_spans + } else if let Some(field) = &self.field { + field.leaf_spans() + } else { + vec![] + } + } +} + impl LeafSpans for StorageField { fn leaf_spans(&self) -> Vec { let mut collected_spans = vec![ByteSpan::from(self.name.span())]; diff --git a/swayfmt/src/utils/language/expr/mod.rs b/swayfmt/src/utils/language/expr/mod.rs index 03af3cabfb4..a65eb3bc527 100644 --- a/swayfmt/src/utils/language/expr/mod.rs +++ b/swayfmt/src/utils/language/expr/mod.rs @@ -977,13 +977,6 @@ fn get_field_width( // Leaf Spans -// TODO: Find a better way of handling Boxed version -impl LeafSpans for Box { - fn leaf_spans(&self) -> Vec { - expr_leaf_spans(self) - } -} - impl LeafSpans for Expr { fn leaf_spans(&self) -> Vec { expr_leaf_spans(self) diff --git a/swayfmt/src/utils/language/punctuated.rs b/swayfmt/src/utils/language/punctuated.rs index 0c42d346da3..852d4224efc 100644 --- a/swayfmt/src/utils/language/punctuated.rs +++ b/swayfmt/src/utils/language/punctuated.rs @@ -1,14 +1,20 @@ use crate::{ constants::RAW_MODIFIER, formatter::{shape::LineStyle, *}, - utils::map::byte_span::{ByteSpan, LeafSpans}, + utils::{ + map::byte_span::{ByteSpan, LeafSpans}, + CurlyBrace, + }, }; use std::fmt::Write; use sway_ast::{ - keywords::CommaToken, punctuated::Punctuated, ConfigurableField, StorageField, TypeField, + keywords::CommaToken, punctuated::Punctuated, ConfigurableField, ItemStorage, StorageEntry, + StorageField, TypeField, }; use sway_types::{ast::PunctKind, Ident, Spanned}; +use self::shape::ExprKind; + use super::expr::should_write_multiline; impl Format for Punctuated @@ -271,6 +277,49 @@ impl Format for StorageField { } } +impl Format for StorageEntry { + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + if let Some(field) = &self.field { + field.format(formatted_code, formatter)?; + } else if let Some(namespace) = &self.namespace { + self.name.format(formatted_code, formatter)?; + ItemStorage::open_curly_brace(formatted_code, formatter)?; + formatter.shape.code_line.update_expr_new_line(true); + formatter.with_shape( + formatter + .shape + .with_code_line_from(LineStyle::Multiline, ExprKind::Struct), + |formatter| -> Result<(), FormatterError> { + namespace + .clone() + .into_inner() + .format(formatted_code, formatter)?; + Ok(()) + }, + )?; + ItemStorage::close_curly_brace(formatted_code, formatter)?; + } + + Ok(()) + } +} + +impl Format for Box { + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + (**self).format(formatted_code, formatter)?; + + Ok(()) + } +} + impl Format for CommaToken { fn format( &self, diff --git a/swayfmt/src/utils/language/ty.rs b/swayfmt/src/utils/language/ty.rs index e8f7f8584d9..1d764e83bd7 100644 --- a/swayfmt/src/utils/language/ty.rs +++ b/swayfmt/src/utils/language/ty.rs @@ -177,9 +177,9 @@ impl Format for TyTupleDescriptor { } } -impl LeafSpans for Box { +impl LeafSpans for Box { fn leaf_spans(&self) -> Vec { - self.as_ref().leaf_spans() + (**self).leaf_spans() } } diff --git a/swayfmt/tests/mod.rs b/swayfmt/tests/mod.rs index b71f2f148c4..1cd3a4d9cc1 100644 --- a/swayfmt/tests/mod.rs +++ b/swayfmt/tests/mod.rs @@ -296,6 +296,7 @@ fn storage_without_alignment() { storage { var1: Type1=Type1{ foo: 8 }, var2: Type2=Type2{ bar: 9 }, + ns1 { var3: u64 = 1, ns2 { var4: u64 = 1 } }, } "#, r#"contract; @@ -311,6 +312,12 @@ struct Type2 { storage { var1: Type1 = Type1 { foo: 8 }, var2: Type2 = Type2 { bar: 9 }, + ns1 { + var3: u64 = 1, + ns2 { + var4: u64 = 1, + }, + }, } "#, ); @@ -333,6 +340,7 @@ struct Type2 { storage { long_var_name: Type1=Type1{ foo: 8 }, var2: Type2=Type2{ bar: 9 }, + ns1 { var3: u64 = 1, ns2 { var4: u64 = 1, }, }, var5: u64 = 1 } "#, r#"contract; @@ -348,6 +356,13 @@ struct Type2 { storage { long_var_name : Type1 = Type1 { foo: 8 }, var2 : Type2 = Type2 { bar: 9 }, + ns1 { + var3 : u64 = 1, + ns2 { + var4 : u64 = 1, + }, + }, + var5 : u64 = 1 } "#, &mut formatter, diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/Forc.lock new file mode 100644 index 00000000000..1e81cf8a690 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-F66B05B51B812E20" + +[[package]] +name = "deprecated_attribute" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/Forc.toml new file mode 100644 index 00000000000..69bfc30b4af --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "deprecated_attribute" +implicit-std = false + +[dependencies] +core = { path = "../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/src/main.sw new file mode 100644 index 00000000000..73a67d733cd --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/src/main.sw @@ -0,0 +1,7 @@ +contract; + +#[namespace(my_namespace)] +storage { + v:u64 = 1, +} + diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/test.toml new file mode 100644 index 00000000000..95ae0a24996 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated_attribute/test.toml @@ -0,0 +1,12 @@ +category = "compile" + +# check: $()#[namespace(my_namespace)] +# nextln: $()Attribute namespace is deprecated. +# nextln: $()You can use namespaces inside storage: +# nextln: $()storage { +# nextln: $()my_storage_namespace { +# nextln: $()field: u64 = 1, +# nextln: $()} +# nextln: $()} + +expected_warnings = 2 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/Forc.lock new file mode 100644 index 00000000000..78193118f40 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-40643AD438531682" + +[[package]] +name = "duplicated_storage_keys" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/Forc.toml new file mode 100644 index 00000000000..5f8ee5d6fc6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "duplicated_storage_keys" +implicit-std = false + +[dependencies] +core = { path = "../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/src/main.sw new file mode 100644 index 00000000000..54c8ccf3246 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/src/main.sw @@ -0,0 +1,13 @@ +contract; + +storage { + f1:u64 = 1, + f2 in 0x36389d1013642dcb070193fc48b0316e9dfdfef1860096dc5957e3eb44430b83: u64 = 2, + ns1 { + f3 in 0x5f4c20ce4bd128e5393a4c2b82007dac795fa0006d01acf8db4c42632bc680ca: u64 = 2, + }, + ns2 { + f4 in 0x5f4c20ce4bd128e5393a4c2b82007dac795fa0006d01acf8db4c42632bc680ca: u64 = 2, + } +} + diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/test.toml new file mode 100644 index 00000000000..8e5e7a86ef3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/duplicated_storage_keys/test.toml @@ -0,0 +1,13 @@ +category = "compile" + +# check: $()Two storage fields are using the same storage key. +# nextln: $()First field: storage.f1 +# nextln: $()Second field: storage.f2 +# nextln: $()Key: 36389D1013642DCB070193FC48B0316E9DFDFEF1860096DC5957E3EB44430B83 + +# check: $()Two storage fields are using the same storage key. +# nextln: $()First field: storage::ns1.f3 +# nextln: $()Second field: storage::ns2.f4 +# nextln: $()Key: 5F4C20CE4BD128E5393A4C2B82007DAC795FA0006D01ACF8DB4C42632BC680CA + +expected_warnings = 6 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/fallback_only/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/fallback_only/src/main.sw index e078e4ce4cb..56294ea407a 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/fallback_only/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/fallback_only/src/main.sw @@ -2,13 +2,14 @@ contract; use std::execution::run_external; -#[namespace(SRC1822)] storage { - target: ContractId = ContractId::zero(), + SRC1822 { + target: ContractId = ContractId::zero(), + } } #[fallback] #[storage(read)] fn fallback() { - run_external(storage.target.read()) + run_external(storage::SRC1822.target.read()) } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle.json index ec0a0a7addf..1ac3be1b41d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle.json @@ -199,10 +199,10 @@ ], "loggedTypes": [ { - "logId": "1515152261580153489", + "logId": "14454674236531057292", "loggedType": { "name": "", - "type": 9, + "type": 10, "typeArguments": null } }, @@ -221,6 +221,14 @@ "type": 1, "typeArguments": null } + }, + { + "logId": "1515152261580153489", + "loggedType": { + "name": "", + "type": 9, + "typeArguments": null + } } ], "messagesTypes": [], @@ -350,6 +358,12 @@ "type": "u64", "typeId": 9, "typeParameters": null + }, + { + "components": null, + "type": "u8", + "typeId": 10, + "typeParameters": null } ] -} +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle_new_encoding.json index 8e817c80160..1ac3be1b41d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle_new_encoding.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle_new_encoding.json @@ -199,10 +199,10 @@ ], "loggedTypes": [ { - "logId": "1515152261580153489", + "logId": "14454674236531057292", "loggedType": { "name": "", - "type": 9, + "type": 10, "typeArguments": null } }, @@ -221,6 +221,14 @@ "type": 1, "typeArguments": null } + }, + { + "logId": "1515152261580153489", + "loggedType": { + "name": "", + "type": 9, + "typeArguments": null + } } ], "messagesTypes": [], @@ -350,6 +358,12 @@ "type": "u64", "typeId": 9, "typeParameters": null + }, + { + "components": null, + "type": "u8", + "typeId": 10, + "typeParameters": null } ] } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_storage_slots_oracle.json index 8477a8235b2..b2063f1474b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_storage_slots_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_storage_slots_oracle.json @@ -1,54 +1,58 @@ [ { - "key": "02dac99c283f16bc91b74f6942db7f012699a2ad51272b15207b9cc14a70dbae", - "value": "6161000000000000000000000000000000000000000000000000000000000000" - }, - { - "key": "0a860bae2f483542169820f52be34ca40c507a967660cdc5941c15815204bc4d", - "value": "6161616161616161610000000000000000000000000000000000000000000000" + "key": "26a0f316674277151cdb647fc857710df30dca882c9c61a48e35790d5cb75159", + "value": "6161616161616161000000000000000000000000000000000000000000000000" }, { - "key": "6294951dcb0a9111a517be5cf4785670ff4e166fb5ab9c33b17e6881b48e964f", - "value": "6161610000000000000000000000000000000000000000000000000000000000" + "key": "2873dd8e90786169217d1dc180cdbeb5642b04daf3979c26776d8b8e93720c12", + "value": "6161616161610000000000000000000000000000000000000000000000000000" }, { - "key": "71217a24656901c411894bb65eb78a828dafa5a6844488ef5024eb5ac0cff79c", - "value": "0000000000000000000000000000000000000000000000000000000001234567" + "key": "323860f3257dc73a83c0b26b915b97575ef2cf8cc150988eb0b0fae2fe61ad77", + "value": "6100000000000000000000000000000000000000000000000000000000000000" }, { - "key": "7f91d1a929dce734e7f930bbb279ccfccdb5474227502ea8845815c74bd930a7", + "key": "3c025b285aab5e23d355e104b29cb021a7581c1485c4aee6f93c3a37569636ef", "value": "6161616161000000000000000000000000000000000000000000000000000000" }, { - "key": "8a89a0cce819e0426e565819a9a98711329087da5a802fb16edd223c47fa44ef", - "value": "6161616161610000000000000000000000000000000000000000000000000000" + "key": "533dcc8c8bc47b384931e0bdab958e580a48b65d633991b87ecf6f035a2ec53a", + "value": "0000000000000002000000000000000000000000000000000000000000000000" }, { - "key": "94b2b70d20da552763c7614981b2a4d984380d7ed4e54c01b28c914e79e44bd5", - "value": "6161616100000000000000000000000000000000000000000000000000000000" + "key": "67572eb5ab2c5a6c11d8a7217f617a380ad7adbe7e7fed0ffa18be2a11ae78fc", + "value": "6161000000000000000000000000000000000000000000000000000000000000" }, { - "key": "a9203bbb8366ca9d708705dce980acbb54d44fb753370ffe4c7d351b46b2abbc", - "value": "6161616161616100000000000000000000000000000000000000000000000000" + "key": "6c1f46e5e38cfa9767a552ab89186da5647f603b447e226eb8b0fdbdefb97f0e", + "value": "6161616161616161616100000000000000000000000000000000000000000000" }, { - "key": "b48b753af346966d0d169c0b2e3234611f65d5cfdb57c7b6e7cd6ca93707bee0", - "value": "6100000000000000000000000000000000000000000000000000000000000000" + "key": "933a534d4af4c376b0b569e8d8a2c62e635e26f403e124cb91d9c42e83d54373", + "value": "0100000000000000000000000000000000000000000000000000000000000000" }, { - "key": "c5e69153be998bc6f957aeb6f8fd46a0e9c5bc2d3dff421a73e02f64a3012fbb", - "value": "6161616161616161000000000000000000000000000000000000000000000000" + "key": "98ba41aa317c3ee2a207b8deaf0a3386a1705b2548777944f29d8a288999c460", + "value": "0000000000000000000000000000000000000000000000000000000001234567" }, { - "key": "c7e08cdde76020f08f4ce5c3257422ae67f9676992689b64b85f35aa58752d9e", + "key": "a605c78ce1b8f4b67230a55ea491e2b7c5f3d6099cbdcb67e8bf60c9678ae047", "value": "0000000000000000000000000000000000000000000000000000000001234567" }, { - "key": "d02e07f5a716bd3b6670aaf9a73352164e6b946c24db14f72005b7029e67d96a", - "value": "6161616161616161616100000000000000000000000000000000000000000000" + "key": "c4085ef85fb993a44237f78f083c0e96be21522fa8d00bd74b8b8d51b8aa2e8a", + "value": "6161616100000000000000000000000000000000000000000000000000000000" }, { - "key": "f383b0ce51358be57daa3b725fe44acdb2d880604e367199080b4379c41bb6ed", - "value": "0000000000000001000000000000000000000000000000000000000000000000" + "key": "d40c4527ee86df5ab69139820872760c294318505804f4327672d6ce7ee2e15f", + "value": "6161616161616100000000000000000000000000000000000000000000000000" + }, + { + "key": "dc7ac8a34653cb050eb9eeb0792ed909ecaf9df17158e8c2a44820cada45805b", + "value": "6161610000000000000000000000000000000000000000000000000000000000" + }, + { + "key": "eafb8114029f277e266bedc3d0b60a5576e8476bc97b96ea42482e20ad15e051", + "value": "6161616161616161610000000000000000000000000000000000000000000000" } ] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw index ae5b63dcc70..261bd8cb0e8 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw @@ -3,10 +3,13 @@ use std::{hash::*, storage::storage_api::{read, write}}; use basic_storage_abi::*; const C1 = 1; +const NS1_NS2_C1 = 2; const S5 = __to_str_array("aaaaa"); +const C1KEY: b256 = 0x933a534d4af4c376b0b569e8d8a2c62e635e26f403e124cb91d9c42e83d54373; + storage { - c1: u64 = C1, + c1 in C1KEY: u8 = C1, str0: str[0] = __to_str_array(""), str1: str[1] = __to_str_array("a"), str2: str[2] = __to_str_array("aa"), @@ -21,6 +24,11 @@ storage { const_u256: u256 = 0x0000000000000000000000000000000000000000000000000000000001234567u256, const_b256: b256 = 0x0000000000000000000000000000000000000000000000000000000001234567, + ns1 { + ns2 { + c1: u64 = NS1_NS2_C1, + } + } } impl BasicStorage for Contract { @@ -284,6 +292,12 @@ fn test_storage() { assert_eq(storage.const_b256.read(), 0x0000000000000000000000000000000000000000000000000000000001234567); storage.const_b256.write(0x0000000000000000000000000000000000000000000000000000000012345678); assert_eq(storage.const_b256.read(), 0x0000000000000000000000000000000000000000000000000000000012345678); + + assert_eq(storage::ns1::ns2.c1.read(), NS1_NS2_C1); + + assert_eq(storage.c1.slot(), C1KEY); + assert_eq(storage.const_b256.slot(), sha256("storage.const_b256")); + assert_eq(storage::ns1::ns2.c1.slot(), sha256("storage::ns1::ns2.c1")); } // If these comparisons are done inline just above then it blows out the register allocator due to diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/json_storage_slots_oracle.json index 800315b033d..27d01be9eb2 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/json_storage_slots_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/json_storage_slots_oracle.json @@ -1,6 +1,6 @@ [ { - "key": "f383b0ce51358be57daa3b725fe44acdb2d880604e367199080b4379c41bb6ed", + "key": "1f5327d8b018fe8a341aec49e087546980b521535a650107a53efdc5e7038723", "value": "0000000000000000000000000000000000000000000000000000000000000000" } ] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_storage_slots_oracle.json index e1e002af06f..24a5f2c6daf 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_storage_slots_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_storage_slots_oracle.json @@ -1,74 +1,74 @@ [ { - "key": "02dac99c283f16bc91b74f6942db7f012699a2ad51272b15207b9cc14a70dbae", - "value": "0100000000000000000000000000000000000000000000000000000000000000" + "key": "06ee74b45a12fd83e7df88440f9b3aef7a275760bf6742de13f2007316af87cb", + "value": "0000000000000001000000000000000200000000000000000000000000000000" }, { - "key": "6294951dcb0a9111a517be5cf4785670ff4e166fb5ab9c33b17e6881b48e964f", - "value": "0800000000000000000000000000000000000000000000000000000000000000" + "key": "06ee74b45a12fd83e7df88440f9b3aef7a275760bf6742de13f2007316af87cc", + "value": "0000000000000000000000000000000300000000000000040000000000000005" }, { - "key": "7f91d1a929dce734e7f930bbb279ccfccdb5474227502ea8845815c74bd930a7", - "value": "0000000000000020000000000000000000000000000000000000000000000000" + "key": "06ee74b45a12fd83e7df88440f9b3aef7a275760bf6742de13f2007316af87cd", + "value": "0000000000000000000000000000000000000000000000000000000000000006" }, { - "key": "8a89a0cce819e0426e565819a9a98711329087da5a802fb16edd223c47fa44ef", - "value": "0000000000000001000000000000000100000000000000020000000000000000" + "key": "06ee74b45a12fd83e7df88440f9b3aef7a275760bf6742de13f2007316af87ce", + "value": "0100000000000000070000000000000000000000000000080000000000000009" }, { - "key": "8a89a0cce819e0426e565819a9a98711329087da5a802fb16edd223c47fa44f0", - "value": "0000000000000000000000000000000000000000000000030100000000000000" + "key": "5dc4bb371cab39153d3503dc677662a2c02b616d1789ffc8c7f11cd6838a5b8d", + "value": "0000000000000020000000000000000000000000000000000000000000000000" }, { - "key": "8a89a0cce819e0426e565819a9a98711329087da5a802fb16edd223c47fa44f1", - "value": "0400000000000000000000000000000500000000000000060000000000000000" + "key": "9a1c37d22e51c475d6284f621e29ad5753c418595c5d7b24a7d1684f11f31adf", + "value": "0100000000000000000000000000000000000000000000000000000000000000" }, { - "key": "94b2b70d20da552763c7614981b2a4d984380d7ed4e54c01b28c914e79e44bd5", + "key": "a87982353688625e3011294e19aa0a5c027b02e2045c8df73c59cf2142416a37", "value": "0000000000000010000000000000000000000000000000000000000000000000" }, { - "key": "a9203bbb8366ca9d708705dce980acbb54d44fb753370ffe4c7d351b46b2abbc", + "key": "aa834639bd045ecb5b8644fda6256196e090c28df31665d3ab8516fb89fb39cd", "value": "0000000000000000000000000000000000000000000000000000000000000000" }, { - "key": "a9203bbb8366ca9d708705dce980acbb54d44fb753370ffe4c7d351b46b2abbd", + "key": "aa834639bd045ecb5b8644fda6256196e090c28df31665d3ab8516fb89fb39ce", "value": "0000000000000000000000000000000000000000000000000000000000000000" }, { - "key": "a9203bbb8366ca9d708705dce980acbb54d44fb753370ffe4c7d351b46b2abbe", + "key": "aa834639bd045ecb5b8644fda6256196e090c28df31665d3ab8516fb89fb39cf", "value": "0000000000000000000000000000000000000000000003090000000000000000" }, { - "key": "b48b753af346966d0d169c0b2e3234611f65d5cfdb57c7b6e7cd6ca93707bee0", - "value": "0000000000000001000000000000000200000000000000000000000000000000" + "key": "ae4a9d6171099362a9d5a398016d7bf8555a8ba706d68ef7c879d4a439c8a6d5", + "value": "4141414141414141414141414141414141414141414141414141414141414141" }, { - "key": "b48b753af346966d0d169c0b2e3234611f65d5cfdb57c7b6e7cd6ca93707bee1", - "value": "0000000000000000000000000000000300000000000000040000000000000005" + "key": "ae4a9d6171099362a9d5a398016d7bf8555a8ba706d68ef7c879d4a439c8a6d6", + "value": "4141414141414141000000000000000000000000000000000000000000000000" }, { - "key": "b48b753af346966d0d169c0b2e3234611f65d5cfdb57c7b6e7cd6ca93707bee2", - "value": "0000000000000000000000000000000000000000000000000000000000000006" + "key": "b9ffa363d401358f328bb44e635cded3208652b383e46c121afb23d599356f41", + "value": "0800000000000000000000000000000000000000000000000000000000000000" }, { - "key": "b48b753af346966d0d169c0b2e3234611f65d5cfdb57c7b6e7cd6ca93707bee3", - "value": "0100000000000000070000000000000000000000000000080000000000000009" + "key": "c979570128d5f52725e9a343a7f4992d8ed386d7c8cfd25f1c646c51c2ac6b4b", + "value": "0000000000000040000000000000000000000000000000000000000000000000" }, { - "key": "c5e69153be998bc6f957aeb6f8fd46a0e9c5bc2d3dff421a73e02f64a3012fbb", - "value": "4141414141414141414141414141414141414141414141414141414141414141" + "key": "d95f4c8d717d52323d34c1118b3f0598a5ec3cabae386887507cabd6dd546a43", + "value": "0101010101010101010101010101010101010101010101010101010101010101" }, { - "key": "c5e69153be998bc6f957aeb6f8fd46a0e9c5bc2d3dff421a73e02f64a3012fbc", - "value": "4141414141414141000000000000000000000000000000000000000000000000" + "key": "e153542f820f1e716d34120d0ea017c2fab2e2545c0556584bc7462f2dafb3bd", + "value": "0000000000000001000000000000000100000000000000020000000000000000" }, { - "key": "de9090cb50e71c2588c773487d1da7066d0c719849a7e58dc8b6397a25c567c0", - "value": "0101010101010101010101010101010101010101010101010101010101010101" + "key": "e153542f820f1e716d34120d0ea017c2fab2e2545c0556584bc7462f2dafb3be", + "value": "0000000000000000000000000000000000000000000000030100000000000000" }, { - "key": "f383b0ce51358be57daa3b725fe44acdb2d880604e367199080b4379c41bb6ed", - "value": "0000000000000040000000000000000000000000000000000000000000000000" + "key": "e153542f820f1e716d34120d0ea017c2fab2e2545c0556584bc7462f2dafb3bf", + "value": "0400000000000000000000000000000500000000000000060000000000000000" } ] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_configurable/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_configurable/json_storage_slots_oracle.json new file mode 100644 index 00000000000..53131c4aef8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_configurable/json_storage_slots_oracle.json @@ -0,0 +1,10 @@ +[ + { + "key": "f383b0ce51358be57daa3b725fe44acdb2d880604e367199080b4379c41bb6ed", + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "key": "f383b0ce51358be57daa3b725fe44acdb2d880604e367199080b4379c41bb6ee", + "value": "0000000000000000000000000000000000000000000000000000000000000000" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_enum_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_enum_contract/json_storage_slots_oracle.json index 11850ac61d2..569da2a8bea 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_enum_contract/json_storage_slots_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_enum_contract/json_storage_slots_oracle.json @@ -1,62 +1,62 @@ [ { - "key": "02dac99c283f16bc91b74f6942db7f012699a2ad51272b15207b9cc14a70dbae", - "value": "0000000000000002000000000000000000000000000000000000000000000000" + "key": "07692c5e306e34754c8507b83507787cd816e26e21a73f984267468c6a858c5f", + "value": "00000000000000000000000000000000000000000000000000000000000000ee" }, { - "key": "0a860bae2f483542169820f52be34ca40c507a967660cdc5941c15815204bc4d", + "key": "093917a687c53cb2abc027cec98a5bbf66c8ae7f5cd58a0880fc9c79617ce148", "value": "0000000000000001000000000000aa00000000000000bb00000000000000cc00" }, { - "key": "6294951dcb0a9111a517be5cf4785670ff4e166fb5ab9c33b17e6881b48e964f", + "key": "1381e00cddf07f6fb1d14cdcb956f38a498a20369226d4528826287fd98e6d33", "value": "0000000000000000000000000000000100000000000000000000000000000000" }, { - "key": "7f91d1a929dce734e7f930bbb279ccfccdb5474227502ea8845815c74bd930a7", - "value": "0000000000000002000000000000000000000000000000000000000000000000" + "key": "163670615ae62a8f957b50ab4ce2c32b22427e70577a54b971439c44f1c4f6ac", + "value": "0000000000000001000000000000bb0000000000000000000000000000000000" }, { - "key": "8a89a0cce819e0426e565819a9a98711329087da5a802fb16edd223c47fa44ef", - "value": "000000000000000000000000000000aa00000000000000000000000000000000" + "key": "306d92806670cbeca4a1dcd450149e8aac249b1446ab7a4360b511de83dc031c", + "value": "0000000000000001000000000000ab00000000000000bc00000000000000cd00" }, { - "key": "94b2b70d20da552763c7614981b2a4d984380d7ed4e54c01b28c914e79e44bd5", - "value": "000000000000000100000000000000cd00000000000000000000000000000000" + "key": "306d92806670cbeca4a1dcd450149e8aac249b1446ab7a4360b511de83dc031d", + "value": "000000000000de00000000000000ef0000000000000000000000000000000000" }, { - "key": "a9203bbb8366ca9d708705dce980acbb54d44fb753370ffe4c7d351b46b2abbc", - "value": "0000000000000001000000000000bb0000000000000000000000000000000000" + "key": "919b268af8b1d0e73ebd666050f0d8ca91c4a57c1f07de173500c1204f12b4b5", + "value": "0000000000000002000000000000000000000000000000000000000000000000" }, { - "key": "b48b753af346966d0d169c0b2e3234611f65d5cfdb57c7b6e7cd6ca93707bee0", - "value": "0000000000000000000000000000000100000000000000000000000000000000" + "key": "b1852fd03d24006ecb20b244b4a807a5cc1901e1615b4c2cf38ad968da5778a3", + "value": "0000000000000002000000000000000000000000000000000000000000000000" }, { - "key": "c5e69153be998bc6f957aeb6f8fd46a0e9c5bc2d3dff421a73e02f64a3012fbb", - "value": "00000000000000000000000000000000000000000000000000000000000000ee" + "key": "b791faa009af5f959cb5b1e527897b5445b819ad012dbcdb4e6747fc67a74abf", + "value": "000000000000000000000000000000ab00000000000000000000000000000000" }, { - "key": "c7e08cdde76020f08f4ce5c3257422ae67f9676992689b64b85f35aa58752d9e", - "value": "0000000000000001000000000000ab00000000000000bc00000000000000cd00" + "key": "ca79befa366adbeb06bd973bbb1c4edf732df97bfec0caf094cf9f83fee6dead", + "value": "0000000000000000000000000000000100000000000000000000000000000000" }, { - "key": "c7e08cdde76020f08f4ce5c3257422ae67f9676992689b64b85f35aa58752d9f", - "value": "000000000000de00000000000000ef0000000000000000000000000000000000" + "key": "d44c3a30232bc40cbe5f573493a6951cd072404880b4dde75e10903d0b0a5256", + "value": "000000000000000000000000000000aa00000000000000000000000000000000" }, { - "key": "d02e07f5a716bd3b6670aaf9a73352164e6b946c24db14f72005b7029e67d96a", - "value": "0000000000000000000000000000000000000000000000000000000000000000" + "key": "e487c22c16cab65d2a63a26de30fd41df1586b5abbad6746cacdf958aa539d49", + "value": "000000000000000000000000000000cd00000000000000000000000000000000" }, { - "key": "d02e07f5a716bd3b6670aaf9a73352164e6b946c24db14f72005b7029e67d96b", - "value": "000000000000000000000000000000ff00000000000000000000000000000000" + "key": "fb07f807a63ddfd1422b1cba4ed39940c50eade2002542e1faccea5c64d5b087", + "value": "000000000000000100000000000000cd00000000000000000000000000000000" }, { - "key": "de9090cb50e71c2588c773487d1da7066d0c719849a7e58dc8b6397a25c567c0", - "value": "000000000000000000000000000000ab00000000000000000000000000000000" + "key": "fdfd23cb7462d11986f91b4f130b7ddb400b7bbc388ad6754175d93c2b00bd25", + "value": "0000000000000000000000000000000000000000000000000000000000000000" }, { - "key": "f383b0ce51358be57daa3b725fe44acdb2d880604e367199080b4379c41bb6ed", - "value": "000000000000000000000000000000cd00000000000000000000000000000000" + "key": "fdfd23cb7462d11986f91b4f130b7ddb400b7bbc388ad6754175d93c2b00bd26", + "value": "000000000000000000000000000000ff00000000000000000000000000000000" } ] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_storage_slots_oracle.json index c9c6e3d56aa..0b71f7ba584 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_storage_slots_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_storage_slots_oracle.json @@ -1,54 +1,54 @@ [ { - "key": "1cabb882ad94b0e00c6603259a68473d72c37a5afa388235798523db451b7435", - "value": "6161616161616161000000000000000000000000000000000000000000000000" + "key": "156750d12322b9d55699e5f250927c45dbc5a716ed514e5d5c6e001c72743958", + "value": "6161616161616161610000000000000000000000000000000000000000000000" }, { - "key": "256f94e8eaff16138bb2ac71a6db9dad1b3cb5ae657b6bc556d73e410532ffb7", - "value": "6161616100000000000000000000000000000000000000000000000000000000" + "key": "1900d3c5816770f72962fcc1c2903deaef383aad2245f8dce3ea438edb50d5be", + "value": "0000000000000000000000000000000000000000000000000000000001234567" }, { - "key": "29cc227d3bdd730793f247e195056ba7aaf20fbc902c56c9d7707f79039b334a", - "value": "6161616161610000000000000000000000000000000000000000000000000000" + "key": "316435c84dec635a59eafe5c7bbe948529d9b334642e3042790ccc24a48d020f", + "value": "6161610000000000000000000000000000000000000000000000000000000000" }, { - "key": "52ddfc3aad75e658d4478fe4de4c1138528cb129ed2febf2db01a473ecc41ca7", - "value": "0000000000000001000000000000000000000000000000000000000000000000" + "key": "75c8aa025fcadccc0595011724f38d1c4502877ab67e96c2d5ac4e28c7c067d2", + "value": "6161616161000000000000000000000000000000000000000000000000000000" }, { - "key": "7c52302f5f44f5e018951099f0a570522595f6cc8ac6903f58697111dacf7fc6", + "key": "8d03529726cb560c58c7c256daa265105ab1657cc58f285a4e450ade5e3b99b9", "value": "6161616161616161616100000000000000000000000000000000000000000000" }, { - "key": "9ba84fa6a786b5b6695103aa5c585a53c13431aa0f0b42e6e8831c5f6df8ce51", + "key": "8e2eedb0198112b5758b3ca68ff81ccf74f35bde89f59324c6349834b7446d42", + "value": "6161616161610000000000000000000000000000000000000000000000000000" + }, + { + "key": "96f53fe759d3f6112e66216565e101a4e3ad50e0b67047a2b1bc71f58e838b3b", "value": "6161000000000000000000000000000000000000000000000000000000000000" }, { - "key": "9de8a8fb10d236d1afea87fe118df7d8181284e2fea439d3fec1c52afbd46b4e", - "value": "6161610000000000000000000000000000000000000000000000000000000000" + "key": "9cd3727531d07180cf222a86ab51e4d6933e82ffa5a846a51ee7554929eec408", + "value": "0000000000000001000000000000000000000000000000000000000000000000" }, { - "key": "9f5382d0db2482d4c07835d1acb1b8d62cb800f21ce04577b0544c299a900d86", + "key": "b48939b669452acd21e7e052008441cf16898ebecad0cf50ea5d1022cc91683d", "value": "6161616161616100000000000000000000000000000000000000000000000000" }, { - "key": "be6e48ba10d92b9a39255aa8cb2c4c8936a653793beeee86a1da5223a4c8504e", - "value": "6161616161000000000000000000000000000000000000000000000000000000" + "key": "c2cec376bf7389162e8706fbb83f4c7d322150bbae540c4d7a89711e86a54d0b", + "value": "6161616100000000000000000000000000000000000000000000000000000000" }, { - "key": "c709c6eda5d3ec0462d8f91208dbcbb8432422234cde38395e468fc2b9dab1a5", + "key": "c47aaa0a4460ad82387b70843b35804e6526ec4a4094793064abda38e789d70b", "value": "0000000000000000000000000000000000000000000000000000000001234567" }, { - "key": "e72b5126b101e06e36ff31629167a6b41c6f752daf41b308fae666ed6092be8d", - "value": "6161616161616161610000000000000000000000000000000000000000000000" + "key": "dbaf1317f4011ad87979853011781f2088cdd8422250f19f27a90b5ce8037cac", + "value": "6161616161616161000000000000000000000000000000000000000000000000" }, { - "key": "ec544af52f548dd8627a0a0417c3856feb6493ae7728d27bebf2849511c530e8", + "key": "f665e43dbe17620f6ef1e4beb22f9d4f1e2bb711e20c003a09dad812801f59e0", "value": "6100000000000000000000000000000000000000000000000000000000000000" - }, - { - "key": "f472175bd2f643d824b2270804e2c8c2b62bb461cf1e365686aa2ef8393357b1", - "value": "0000000000000000000000000000000000000000000000000000000001234567" } ] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw index 6091131ef11..934376a211b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw @@ -5,23 +5,24 @@ use basic_storage_abi::*; const C1 = 1; const S5 = __to_str_array("aaaaa"); -#[namespace(my_storage_namespace)] storage { - c1: u64 = C1, - str0: str[0] = __to_str_array(""), - str1: str[1] = __to_str_array("a"), - str2: str[2] = __to_str_array("aa"), - str3: str[3] = __to_str_array("aaa"), - str4: str[4] = __to_str_array("aaaa"), - str5: str[5] = S5, - str6: str[6] = __to_str_array("aaaaaa"), - str7: str[7] = __to_str_array("aaaaaaa"), - str8: str[8] = __to_str_array("aaaaaaaa"), - str9: str[9] = __to_str_array("aaaaaaaaa"), - str10: str[10] = __to_str_array("aaaaaaaaaa"), - - const_u256: u256 = 0x0000000000000000000000000000000000000000000000000000000001234567u256, - const_b256: b256 = 0x0000000000000000000000000000000000000000000000000000000001234567, + my_storage_namespace { + c1: u64 = C1, + str0: str[0] = __to_str_array(""), + str1: str[1] = __to_str_array("a"), + str2: str[2] = __to_str_array("aa"), + str3: str[3] = __to_str_array("aaa"), + str4: str[4] = __to_str_array("aaaa"), + str5: str[5] = S5, + str6: str[6] = __to_str_array("aaaaaa"), + str7: str[7] = __to_str_array("aaaaaaa"), + str8: str[8] = __to_str_array("aaaaaaaa"), + str9: str[9] = __to_str_array("aaaaaaaaa"), + str10: str[10] = __to_str_array("aaaaaaaaaa"), + + const_u256: u256 = 0x0000000000000000000000000000000000000000000000000000000001234567u256, + const_b256: b256 = 0x0000000000000000000000000000000000000000000000000000000001234567, + } } impl BasicStorage for Contract { @@ -261,30 +262,30 @@ fn test_storage() { _ => assert(false), } - assert(storage.str0.try_read().is_none()); - - assert_streq(storage.str1.read(), "a"); - assert_streq(storage.str2.read(), "aa"); - assert_streq(storage.str3.read(), "aaa"); - assert_streq(storage.str4.read(), "aaaa"); - assert_streq(storage.str5.read(), "aaaaa"); - assert_streq(storage.str6.read(), "aaaaaa"); - assert_streq(storage.str7.read(), "aaaaaaa"); - assert_streq(storage.str8.read(), "aaaaaaaa"); - assert_streq(storage.str9.read(), "aaaaaaaaa"); - assert_streq(storage.str10.read(), "aaaaaaaaaa"); - - assert_eq(storage.c1.read(), C1); - storage.c1.write(2); - assert_eq(storage.c1.read(), 2); + assert(storage::my_storage_namespace.str0.try_read().is_none()); + + assert_streq(storage::my_storage_namespace.str1.read(), "a"); + assert_streq(storage::my_storage_namespace.str2.read(), "aa"); + assert_streq(storage::my_storage_namespace.str3.read(), "aaa"); + assert_streq(storage::my_storage_namespace.str4.read(), "aaaa"); + assert_streq(storage::my_storage_namespace.str5.read(), "aaaaa"); + assert_streq(storage::my_storage_namespace.str6.read(), "aaaaaa"); + assert_streq(storage::my_storage_namespace.str7.read(), "aaaaaaa"); + assert_streq(storage::my_storage_namespace.str8.read(), "aaaaaaaa"); + assert_streq(storage::my_storage_namespace.str9.read(), "aaaaaaaaa"); + assert_streq(storage::my_storage_namespace.str10.read(), "aaaaaaaaaa"); + + assert_eq(storage::my_storage_namespace.c1.read(), C1); + storage::my_storage_namespace.c1.write(2); + assert_eq(storage::my_storage_namespace.c1.read(), 2); - assert_eq(storage.const_u256.read(), 0x0000000000000000000000000000000000000000000000000000000001234567u256); - storage.const_u256.write(0x0000000000000000000000000000000000000000000000000000000012345678u256); - assert_eq(storage.const_u256.read(), 0x0000000000000000000000000000000000000000000000000000000012345678u256); + assert_eq(storage::my_storage_namespace.const_u256.read(), 0x0000000000000000000000000000000000000000000000000000000001234567u256); + storage::my_storage_namespace.const_u256.write(0x0000000000000000000000000000000000000000000000000000000012345678u256); + assert_eq(storage::my_storage_namespace.const_u256.read(), 0x0000000000000000000000000000000000000000000000000000000012345678u256); - assert_eq(storage.const_b256.read(), 0x0000000000000000000000000000000000000000000000000000000001234567); - storage.const_b256.write(0x0000000000000000000000000000000000000000000000000000000012345678); - assert_eq(storage.const_b256.read(), 0x0000000000000000000000000000000000000000000000000000000012345678); + assert_eq(storage::my_storage_namespace.const_b256.read(), 0x0000000000000000000000000000000000000000000000000000000001234567); + storage::my_storage_namespace.const_b256.write(0x0000000000000000000000000000000000000000000000000000000012345678); + assert_eq(storage::my_storage_namespace.const_b256.read(), 0x0000000000000000000000000000000000000000000000000000000012345678); } // If these comparisons are done inline just above then it blows out the register allocator due to