diff --git a/docs/book/src/reference/attributes.md b/docs/book/src/reference/attributes.md index 4cee41cc041..6451a2a6ed3 100644 --- a/docs/book/src/reference/attributes.md +++ b/docs/book/src/reference/attributes.md @@ -4,6 +4,7 @@ Attributes are a form of metadata that can additionally instruct Sway compiler o Below is the list of attributes supported by the Sway compiler, ordered alphabetically: +- [ABI Name](#abi-name) - [Allow](#allow) - [Cfg](#cfg) - [Deprecated](#deprecated) @@ -15,6 +16,62 @@ Below is the list of attributes supported by the Sway compiler, ordered alphabet - [Storage](#payable) - [Test](#test) +## ABI Name + +The `#[abi_name]` attribute allows to specify the ABI name for an item. +This means that when an ABI JSON file is generated, the name that is output is the one specified +by the attribute. This can be useful to allow renaming items, while allowing for keeping backwards +compatibility at the contract ABI level. + +> **Note**: At the moment, only enum and struct types support the attribute. + +In the example that follows, we originally had `MyStruct` and `MyEnum` types, which we, later on, renamed to `MyStruct` and `MyEnum` in code. To keep the backward compatibility of the ABI, we annotate the types with the `#[abi_name]` attribute and give them the original names: + +```sway +contract; + +#[abi_name(name = "MyStruct")] +struct RenamedMyStruct {} + +#[abi_name(name = "MyEnum")] +enum RenamedMyEnum { + A: () +} + +abi MyAbi { + fn my_struct() -> RenamedMyStruct; + fn my_enum() -> RenamedMyEnum; +} + +impl MyAbi for Contract { + fn my_struct() -> RenamedMyStruct { RenamedMyStruct{} } + fn my_enum() -> RenamedMyEnum { RenamedMyEnum::A } +} +``` + +This generates the following JSON ABI: + +```json +{ + "concreteTypes": [ + { + "concreteTypeId": "215af2bca9e1aa8fec647dab22a0cd36c63ce5ed051a132d51323807e28c0d67", + "metadataTypeId": 1, + "type": "enum MyEnum" + }, + { + "concreteTypeId": "d31db280ac133d726851d8003bd2f06ec2d3fc76a46f1007d13914088fbd0791", + "type": "struct MyStruct" + } + ], + ... +} +``` + +We get the same JSON ABI output both before and after renaming the types, due to attributing them with +`#[abi_name(name = ...)]`, which forces them to be generated with their previous Sway names. +This means consumers of this contract will still get the original names, keeping compatibility at the ABI level. + ## Allow The `#[allow(...)]` attribute disables compiler checks so that certain warnings will go unreported. The following warnings can be disabled: diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 1c8d3fdd5cc..7fe97b6fb85 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -1790,6 +1790,7 @@ pub fn compile( panic_occurrences: &asm.panic_occurrences, abi_with_callpaths: true, type_ids_to_full_type_str: HashMap::::new(), + unique_names: HashMap::new(), }, engines, if experimental.new_encoding { diff --git a/sway-ast/src/attribute.rs b/sway-ast/src/attribute.rs index 98c5f15fdb4..ef805497431 100644 --- a/sway-ast/src/attribute.rs +++ b/sway-ast/src/attribute.rs @@ -52,6 +52,10 @@ pub const ERROR_TYPE_ATTRIBUTE_NAME: &str = "error_type"; pub const ERROR_ATTRIBUTE_NAME: &str = "error"; pub const ERROR_M_ARG_NAME: &str = "m"; +// Abi names. +pub const ABI_NAME_ATTRIBUTE_NAME: &str = "abi_name"; +pub const ABI_NAME_NAME_ARG_NAME: &str = "name"; + pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[ STORAGE_ATTRIBUTE_NAME, DOC_COMMENT_ATTRIBUTE_NAME, @@ -62,6 +66,7 @@ pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[ CFG_ATTRIBUTE_NAME, DEPRECATED_ATTRIBUTE_NAME, FALLBACK_ATTRIBUTE_NAME, + ABI_NAME_ATTRIBUTE_NAME, ]; /// An attribute declaration. Attribute declaration diff --git a/sway-core/src/abi_generation/abi_str.rs b/sway-core/src/abi_generation/abi_str.rs index d342904090e..decaaf27b38 100644 --- a/sway-core/src/abi_generation/abi_str.rs +++ b/sway-core/src/abi_generation/abi_str.rs @@ -1,6 +1,7 @@ -use sway_types::{integer_bits::IntegerBits, Named}; +use sway_error::handler::{ErrorEmitted, Handler}; +use sway_types::{integer_bits::IntegerBits, Ident, Named}; -use crate::{language::CallPath, Engines, GenericArgument, TypeId, TypeInfo}; +use crate::{language::CallPath, transform, Engines, GenericArgument, TypeId, TypeInfo}; #[derive(Clone)] pub struct AbiStrContext { @@ -14,14 +15,17 @@ impl TypeId { /// Gives back a string that represents the type, considering what it resolves to pub fn get_abi_type_str( &self, + handler: &Handler, ctx: &AbiStrContext, engines: &Engines, resolved_type_id: TypeId, - ) -> String { + ) -> Result { let type_engine = engines.te(); - let self_abi_str = type_engine.get(*self).abi_str(ctx, engines, true); + let self_abi_str = type_engine + .get(*self) + .abi_str(handler, ctx, engines, true)?; if self.is_generic_parameter(engines, resolved_type_id) { - format!("generic {}", self_abi_str) + Ok(format!("generic {}", self_abi_str)) } else { match ( &*type_engine.get(*self), @@ -30,9 +34,10 @@ impl TypeId { (TypeInfo::Custom { .. }, TypeInfo::Struct { .. }) | (TypeInfo::Custom { .. }, TypeInfo::Enum { .. }) => type_engine .get(resolved_type_id) - .abi_str(ctx, engines, true), + .abi_str(handler, ctx, engines, true), (_, TypeInfo::Alias { ty, .. }) => { - ty.type_id().get_abi_type_str(ctx, engines, ty.type_id()) + ty.type_id() + .get_abi_type_str(handler, ctx, engines, ty.type_id()) } (TypeInfo::Tuple(fields), TypeInfo::Tuple(resolved_fields)) => { assert_eq!(fields.len(), resolved_fields.len()); @@ -40,13 +45,15 @@ impl TypeId { .iter() .map(|f| { if ctx.abi_with_fully_specified_types { - type_engine.get(f.type_id()).abi_str(ctx, engines, false) + type_engine + .get(f.type_id()) + .abi_str(handler, ctx, engines, false) } else { - "_".to_string() + Ok("_".to_string()) } }) - .collect::>(); - format!("({})", field_strs.join(", ")) + .collect::, _>>()?; + Ok(format!("({})", field_strs.join(", "))) } (TypeInfo::Array(_, length), TypeInfo::Array(type_arg, resolved_length)) => { assert_eq!( @@ -56,76 +63,84 @@ impl TypeId { let inner_type = if ctx.abi_with_fully_specified_types { type_engine .get(type_arg.type_id()) - .abi_str(ctx, engines, false) + .abi_str(handler, ctx, engines, false)? } else { "_".to_string() }; - format!("[{}; {:?}]", inner_type, engines.help_out(length.expr())) + Ok(format!( + "[{}; {:?}]", + inner_type, + engines.help_out(length.expr()) + )) } (TypeInfo::Slice(type_arg), TypeInfo::Slice(_)) => { let inner_type = if ctx.abi_with_fully_specified_types { type_engine .get(type_arg.type_id()) - .abi_str(ctx, engines, false) + .abi_str(handler, ctx, engines, false)? } else { "_".to_string() }; - format!("[{}]", inner_type) - } - (TypeInfo::Custom { .. }, _) => { - format!("generic {}", self_abi_str) + Ok(format!("[{}]", inner_type)) } + (TypeInfo::Custom { .. }, _) => Ok(format!("generic {}", self_abi_str)), _ => type_engine .get(resolved_type_id) - .abi_str(ctx, engines, true), + .abi_str(handler, ctx, engines, true), } } } } impl TypeInfo { - pub fn abi_str(&self, ctx: &AbiStrContext, engines: &Engines, is_root: bool) -> String { + pub fn abi_str( + &self, + handler: &Handler, + ctx: &AbiStrContext, + engines: &Engines, + is_root: bool, + ) -> Result { use TypeInfo::*; let decl_engine = engines.de(); match self { - Unknown => "unknown".into(), - Never => "never".into(), - UnknownGeneric { name, .. } => name.to_string(), - Placeholder(_) => "_".to_string(), - TypeParam(param) => format!("typeparam({})", param.name()), - StringSlice => "str".into(), - StringArray(length) => format!("str[{:?}]", engines.help_out(length.expr())), - UnsignedInteger(x) => match x { + Unknown => Ok("unknown".into()), + Never => Ok("never".into()), + UnknownGeneric { name, .. } => Ok(name.to_string()), + Placeholder(_) => Ok("_".to_string()), + TypeParam(param) => Ok(format!("typeparam({})", param.name())), + StringSlice => Ok("str".into()), + StringArray(length) => Ok(format!("str[{:?}]", engines.help_out(length.expr()))), + UnsignedInteger(x) => Ok(match x { IntegerBits::Eight => "u8", IntegerBits::Sixteen => "u16", IntegerBits::ThirtyTwo => "u32", IntegerBits::SixtyFour => "u64", IntegerBits::V256 => "u256", } - .into(), - Boolean => "bool".into(), + .into()), + Boolean => Ok("bool".into()), Custom { qualified_call_path: call_path, .. - } => call_path.call_path.suffix.to_string(), + } => Ok(call_path.call_path.suffix.to_string()), Tuple(fields) => { let field_strs = fields .iter() - .map(|field| field.abi_str(ctx, engines, false)) - .collect::>(); - format!("({})", field_strs.join(", ")) + .map(|field| field.abi_str(handler, ctx, engines, false)) + .collect::, ErrorEmitted>>()?; + Ok(format!("({})", field_strs.join(", "))) } - B256 => "b256".into(), - Numeric => "u64".into(), // u64 is the default - Contract => "contract".into(), - ErrorRecovery(_) => "unknown due to error".into(), + B256 => Ok("b256".into()), + Numeric => Ok("u64".into()), // u64 is the default + Contract => Ok("contract".into()), + ErrorRecovery(_) => Ok("unknown due to error".into()), UntypedEnum(decl_id) => { let decl = engines.pe().get_enum(decl_id); - format!("untyped enum {}", decl.name) + Ok(format!("untyped enum {}", decl.name)) } UntypedStruct(decl_id) => { let decl = engines.pe().get_struct(decl_id); - format!("untyped struct {}", decl.name) + Ok(format!("untyped struct {}", decl.name)) } Enum(decl_ref) => { let decl = decl_engine.get_enum(decl_ref); @@ -134,20 +149,19 @@ impl TypeInfo { { "".into() } else { - format!( - "<{}>", - decl.generic_parameters - .iter() - .map(|p| p.abi_str(engines, ctx, false)) - .collect::>() - .join(",") - ) + let params = decl + .generic_parameters + .iter() + .map(|p| p.abi_str(handler, engines, ctx, false)) + .collect::, _>>()?; + format!("<{}>", params.join(",")) }; - format!( + let abi_call_path = get_abi_call_path(handler, &decl.call_path, &decl.attributes)?; + Ok(format!( "enum {}{}", - call_path_display(ctx, &decl.call_path), + call_path_display(ctx, &abi_call_path), type_params - ) + )) } Struct(decl_ref) => { let decl = decl_engine.get_struct(decl_ref); @@ -156,58 +170,69 @@ impl TypeInfo { { "".into() } else { - format!( - "<{}>", - decl.generic_parameters - .iter() - .map(|p| p.abi_str(engines, ctx, false)) - .collect::>() - .join(",") - ) + let params = decl + .generic_parameters + .iter() + .map(|p| p.abi_str(handler, engines, ctx, false)) + .collect::, _>>()?; + format!("<{}>", params.join(",")) }; - format!( + let abi_call_path = get_abi_call_path(handler, &decl.call_path, &decl.attributes)?; + Ok(format!( "struct {}{}", - call_path_display(ctx, &decl.call_path), + call_path_display(ctx, &abi_call_path), type_params - ) - } - ContractCaller { abi_name, .. } => { - format!("contract caller {abi_name}") - } - Array(elem_ty, length) => { - format!( - "[{}; {:?}]", - elem_ty.abi_str(ctx, engines, false), - engines.help_out(length.expr()) - ) + )) } - RawUntypedPtr => "raw untyped ptr".into(), - RawUntypedSlice => "raw untyped slice".into(), - Ptr(ty) => { - format!("__ptr {}", ty.abi_str(ctx, engines, false)) - } - Slice(ty) => { - format!("__slice {}", ty.abi_str(ctx, engines, false)) - } - Alias { ty, .. } => ty.abi_str(ctx, engines, false), + ContractCaller { abi_name, .. } => Ok(format!("contract caller {abi_name}")), + Array(elem_ty, length) => Ok(format!( + "[{}; {:?}]", + elem_ty.abi_str(handler, ctx, engines, false)?, + engines.help_out(length.expr()) + )), + RawUntypedPtr => Ok("raw untyped ptr".into()), + RawUntypedSlice => Ok("raw untyped slice".into()), + Ptr(ty) => Ok(format!( + "__ptr {}", + ty.abi_str(handler, ctx, engines, false)? + )), + Slice(ty) => Ok(format!( + "__slice {}", + ty.abi_str(handler, ctx, engines, false)? + )), + Alias { ty, .. } => Ok(ty.abi_str(handler, ctx, engines, false)?), TraitType { name, trait_type_id: _, - } => format!("trait type {}", name), + } => Ok(format!("trait type {}", name)), Ref { to_mutable_value, referenced_type, } => { - format!( + Ok(format!( "__ref {}{}", // TODO: (REFERENCES) No references in ABIs according to the RFC. Or we want to have them? if *to_mutable_value { "mut " } else { "" }, - referenced_type.abi_str(ctx, engines, false) - ) + referenced_type.abi_str(handler, ctx, engines, false)? + )) } } } } +fn get_abi_call_path( + handler: &Handler, + call_path: &CallPath, + attributes: &transform::Attributes, +) -> Result { + let mut abi_call_path = call_path.clone(); + if let Some(abi_name_attr) = attributes.abi_name() { + let name = abi_name_attr.args.first().unwrap(); + let ident = Ident::new_no_span(name.get_string(handler, abi_name_attr)?.clone()); + abi_call_path.suffix = ident; + } + Ok(abi_call_path) +} + /// `call_path_display` returns the provided `call_path` without the first prefix in case it is equal to the program name. /// If the program name is `my_program` and the `call_path` is `my_program::MyStruct` then this function returns only `MyStruct`. fn call_path_display(ctx: &AbiStrContext, call_path: &CallPath) -> String { @@ -228,10 +253,16 @@ fn call_path_display(ctx: &AbiStrContext, call_path: &CallPath) -> String { } impl GenericArgument { - pub(self) fn abi_str(&self, ctx: &AbiStrContext, engines: &Engines, is_root: bool) -> String { + pub(self) fn abi_str( + &self, + handler: &Handler, + ctx: &AbiStrContext, + engines: &Engines, + is_root: bool, + ) -> Result { engines .te() .get(self.type_id()) - .abi_str(ctx, engines, is_root) + .abi_str(handler, ctx, engines, is_root) } } diff --git a/sway-core/src/abi_generation/fuel_abi.rs b/sway-core/src/abi_generation/fuel_abi.rs index cd920053826..93f3aa6f6e6 100644 --- a/sway-core/src/abi_generation/fuel_abi.rs +++ b/sway-core/src/abi_generation/fuel_abi.rs @@ -4,8 +4,12 @@ use fuel_abi_types::abi::program::{ }; use sha2::{Digest, Sha256}; use std::collections::{BTreeMap, HashMap, HashSet}; -use sway_error::handler::{ErrorEmitted, Handler}; -use sway_types::Span; +use sway_error::{ + error::CompileError, + handler::{ErrorEmitted, Handler}, +}; +use sway_parse::is_valid_identifier_or_path; +use sway_types::{Named, Span, Spanned}; use crate::{ ast_elements::type_parameter::GenericTypeParameter, @@ -16,11 +20,27 @@ use crate::{ use super::abi_str::AbiStrContext; +#[derive(Clone, Debug)] +pub enum AbiNameDiagnosticSpan { + Attribute(Span), + Type(Span), +} + +impl AbiNameDiagnosticSpan { + pub fn span(self) -> Span { + match self { + Self::Attribute(span) => span, + Self::Type(span) => span, + } + } +} + pub struct AbiContext<'a> { pub program: &'a TyProgram, pub panic_occurrences: &'a PanicOccurrences, pub abi_with_callpaths: bool, pub type_ids_to_full_type_str: HashMap, + pub unique_names: HashMap, } impl AbiContext<'_> { @@ -34,7 +54,79 @@ impl AbiContext<'_> { } } +pub fn extract_abi_name_inner(span: &Span) -> Option { + let text = &span.src().text; + let full_attr: &str = &text[span.start()..span.end()]; + + // Find the "name" key. + let name_key_pos = full_attr.find("name")?; + let after_name = &full_attr[name_key_pos..]; + + // Find the '=' after "name". + let eq_offset_rel = after_name.find('=')?; + let after_eq = &after_name[eq_offset_rel + 1..]; + + // Find the opening quote of the literal. + let first_quote_rel = after_eq.find('"')?; + let ident_abs_start = span.start() + + name_key_pos + + eq_offset_rel + + 1 // move past '=' + + first_quote_rel + + 1; // move past the opening '"' + + // Starting at ident_abs_start, locate the closing quote. + let rest_after_ident = &text[ident_abs_start..span.end()]; + let second_quote_rel = rest_after_ident.find('"')?; + let ident_abs_end = ident_abs_start + second_quote_rel; + + Span::new( + span.src().clone(), + ident_abs_start - 1, + ident_abs_end + 1, + span.source_id().cloned(), + ) +} + impl TypeId { + fn get_abi_name_and_span_from_type_id( + engines: &Engines, + type_id: TypeId, + ) -> Result, ErrorEmitted> { + let handler = Handler::default(); + match *engines.te().get(type_id) { + TypeInfo::Enum(decl_id) => { + let enum_decl = engines.de().get_enum(&decl_id); + match enum_decl.attributes.abi_name() { + Some(abi_name_attr) => { + let name = abi_name_attr + .args + .first() + .unwrap() + .get_string(&handler, abi_name_attr)?; + Ok(Some((name.clone(), abi_name_attr.span.clone()))) + } + None => Ok(None), + } + } + TypeInfo::Struct(decl_id) => { + let struct_decl = engines.de().get_struct(&decl_id); + match struct_decl.attributes.abi_name() { + Some(abi_name_attr) => { + let name = abi_name_attr + .args + .first() + .unwrap() + .get_string(&handler, abi_name_attr)?; + Ok(Some((name.clone(), abi_name_attr.span.clone()))) + } + None => Ok(None), + } + } + _ => Ok(None), + } + } + fn get_abi_type_field_and_concrete_id( &self, handler: &Handler, @@ -43,6 +135,7 @@ impl TypeId { resolved_type_id: TypeId, ) -> Result<(String, ConcreteTypeId), ErrorEmitted> { let type_str = self.get_abi_type_str( + handler, &AbiStrContext { program_name: ctx.program.namespace.current_package_name().to_string(), abi_with_callpaths: true, @@ -51,7 +144,67 @@ impl TypeId { }, engines, resolved_type_id, + )?; + + let mut err: Option = None; + + // Right now ABI renaming is only supported for enum and struct types. + let should_check_name = matches!( + *engines.te().get(resolved_type_id), + TypeInfo::Enum(_) | TypeInfo::Struct(_) ); + + if should_check_name { + let mut has_abi_name_attribute = false; + let (name, attribute_span) = + match Self::get_abi_name_and_span_from_type_id(engines, resolved_type_id)? { + Some(res) => { + has_abi_name_attribute = true; + res + } + None => (String::new(), Span::dummy()), + }; + + let attribute_name_span = + extract_abi_name_inner(&attribute_span).unwrap_or(attribute_span.clone()); + + let type_span = match *engines.te().get(resolved_type_id) { + TypeInfo::Enum(decl_id) => engines.de().get_enum(&decl_id).name().span(), + TypeInfo::Struct(decl_id) => engines.de().get_struct(&decl_id).name().span(), + _ => unreachable!(), + }; + + let insert_span = if has_abi_name_attribute { + AbiNameDiagnosticSpan::Attribute(attribute_span.clone()) + } else { + AbiNameDiagnosticSpan::Type(type_span.clone()) + }; + + let prev_span_opt = if ctx.unique_names.contains_key(&type_str) { + ctx.unique_names.get(&type_str).cloned() + } else { + ctx.unique_names.insert(type_str.clone(), insert_span) + }; + + if has_abi_name_attribute { + if name.is_empty() || !is_valid_identifier_or_path(name.as_str()) { + err = Some(handler.emit_err(CompileError::ABIInvalidName { + span: attribute_name_span.clone(), + name, + })); + } + + if let Some(prev_span) = prev_span_opt { + let is_attribute = matches!(prev_span, AbiNameDiagnosticSpan::Attribute(_)); + err = Some(handler.emit_err(CompileError::ABIDuplicateName { + span: attribute_name_span.clone(), + other_span: prev_span.span(), + is_attribute, + })); + } + } + } + let mut hasher = Sha256::new(); hasher.update(type_str.clone()); let result = hasher.finalize(); @@ -62,18 +215,62 @@ impl TypeId { .insert(type_id.clone(), type_str.clone()) { if old_type_str != type_str { - return Err( + err = Some( handler.emit_err(sway_error::error::CompileError::ABIHashCollision { span: Span::dummy(), - hash: type_id, + hash: type_id.clone(), first_type: old_type_str, - second_type: type_str, + second_type: type_str.clone(), }), ); } } - Ok((type_str, ConcreteTypeId(type_id))) + match err { + Some(err) => Err(err), + None => Ok((type_str, ConcreteTypeId(type_id))), + } + } +} + +fn insert_unique_type(ctx: &mut AbiContext, name: String, span: Span) { + let _ = ctx + .unique_names + .insert(name, AbiNameDiagnosticSpan::Type(span)); +} + +fn process_type_name(ctx: &mut AbiContext, engines: &Engines, type_id: TypeId) { + match &*engines.te().get(type_id) { + TypeInfo::Enum(decl_id) => { + let enum_decl = engines.de().get_enum(decl_id); + insert_unique_type( + ctx, + format!("enum {}", enum_decl.name()), + enum_decl.name().span(), + ); + } + TypeInfo::Struct(decl_id) => { + let struct_decl = engines.de().get_struct(decl_id); + insert_unique_type( + ctx, + format!("struct {}", struct_decl.name()), + struct_decl.name().span(), + ); + } + TypeInfo::Alias { name: _, ty } => process_type_name(ctx, engines, ty.type_id()), + _ => {} + } +} + +fn process_type_names_from_function( + ctx: &mut AbiContext, + engines: &Engines, + function: &TyFunctionDecl, +) { + process_type_name(ctx, engines, function.return_type.type_id()); + + for param in &function.parameters { + process_type_name(ctx, engines, param.type_argument.type_id()); } } @@ -87,7 +284,26 @@ pub fn generate_program_abi( let decl_engine = engines.de(); let metadata_types: &mut Vec = &mut vec![]; let concrete_types: &mut Vec = &mut vec![]; - let mut program_abi = match &ctx.program.kind { + + match &ctx.program.kind { + TyProgramKind::Contract { abi_entries, .. } => { + abi_entries.iter().for_each(|x| { + let fn_decl = decl_engine.get_function(x); + process_type_names_from_function(ctx, engines, &fn_decl); + }); + } + TyProgramKind::Script { main_function, .. } => { + let main_function = decl_engine.get_function(main_function); + process_type_names_from_function(ctx, engines, &main_function); + } + TyProgramKind::Predicate { main_function, .. } => { + let main_function = decl_engine.get_function(main_function); + process_type_names_from_function(ctx, engines, &main_function); + } + TyProgramKind::Library { .. } => {} + }; + + let mut program_abi = handler.scope(|handler| match &ctx.program.kind { TyProgramKind::Contract { abi_entries, .. } => { let functions = abi_entries .iter() @@ -101,7 +317,7 @@ pub fn generate_program_abi( concrete_types, ) }) - .collect::, _>>()?; + .collect::>(); let logged_types = generate_logged_types(handler, ctx, engines, metadata_types, concrete_types)?; let messages_types = @@ -109,18 +325,18 @@ pub fn generate_program_abi( let configurables = generate_configurables(handler, ctx, engines, metadata_types, concrete_types)?; let error_codes = generate_error_codes(ctx.panic_occurrences); - program_abi::ProgramABI { + Ok(program_abi::ProgramABI { program_type: "contract".to_string(), spec_version, encoding_version, metadata_types: metadata_types.to_vec(), concrete_types: concrete_types.to_vec(), - functions, + functions: functions.into_iter().collect::, _>>()?, logged_types: Some(logged_types), messages_types: Some(messages_types), configurables: Some(configurables), error_codes: Some(error_codes), - } + }) } TyProgramKind::Script { main_function, .. } => { let main_function = decl_engine.get_function(main_function); @@ -138,7 +354,7 @@ pub fn generate_program_abi( let configurables = generate_configurables(handler, ctx, engines, metadata_types, concrete_types)?; let error_codes = generate_error_codes(ctx.panic_occurrences); - program_abi::ProgramABI { + Ok(program_abi::ProgramABI { program_type: "script".to_string(), spec_version, encoding_version, @@ -149,7 +365,7 @@ pub fn generate_program_abi( messages_types: Some(messages_types), configurables: Some(configurables), error_codes: Some(error_codes), - } + }) } TyProgramKind::Predicate { main_function, .. } => { let main_function = decl_engine.get_function(main_function); @@ -167,7 +383,7 @@ pub fn generate_program_abi( let configurables = generate_configurables(handler, ctx, engines, metadata_types, concrete_types)?; let error_codes = generate_error_codes(ctx.panic_occurrences); - program_abi::ProgramABI { + Ok(program_abi::ProgramABI { program_type: "predicate".to_string(), spec_version, encoding_version, @@ -178,7 +394,7 @@ pub fn generate_program_abi( messages_types: Some(messages_types), configurables: Some(configurables), error_codes: Some(error_codes), - } + }) } TyProgramKind::Library { .. } => { let logged_types = @@ -186,7 +402,7 @@ pub fn generate_program_abi( let messages_types = generate_messages_types(handler, ctx, engines, metadata_types, concrete_types)?; let error_codes = generate_error_codes(ctx.panic_occurrences); - program_abi::ProgramABI { + Ok(program_abi::ProgramABI { program_type: "library".to_string(), spec_version, encoding_version, @@ -197,9 +413,9 @@ pub fn generate_program_abi( messages_types: Some(messages_types), configurables: None, error_codes: Some(error_codes), - } + }) } - }; + })?; standardize_json_abi_types(&mut program_abi); @@ -354,7 +570,7 @@ fn update_json_type_metadata_declaration( } } -/// RUpdates the metadata type IDs used in a `program_abi::TypeConcreteDeclaration` given a HashMap from +/// Updates the metadata type IDs used in a `program_abi::TypeConcreteDeclaration` given a HashMap from /// old to new IDs fn update_json_type_concrete_declaration( type_declaration: &mut program_abi::TypeConcreteDeclaration, @@ -381,7 +597,12 @@ fn generate_concrete_type_declaration( let mut new_metadata_types_to_add = Vec::::new(); let type_metadata_decl = program_abi::TypeMetadataDeclaration { metadata_type_id: MetadataTypeId(type_id.index()), - type_field: type_id.get_abi_type_str(&ctx.to_str_context(), engines, resolved_type_id), + type_field: type_id.get_abi_type_str( + handler, + &ctx.to_str_context(), + engines, + resolved_type_id, + )?, components: type_id.get_abi_type_components( handler, ctx, @@ -409,18 +630,21 @@ fn generate_concrete_type_declaration( } else { None }; - let type_arguments = if type_metadata_decl.type_parameters.is_some() { - type_id.get_abi_type_arguments_as_concrete_type_ids( - handler, - ctx, - engines, - metadata_types, - concrete_types, - resolved_type_id, - )? - } else { - None - }; + let type_arguments = handler.scope(|handler| { + let type_arguments = if type_metadata_decl.type_parameters.is_some() { + type_id.get_abi_type_arguments_as_concrete_type_ids( + handler, + ctx, + engines, + metadata_types, + concrete_types, + resolved_type_id, + )? + } else { + None + }; + Ok(type_arguments) + })?; metadata_types.push(type_metadata_decl); metadata_types.extend(new_metadata_types_to_add); @@ -471,7 +695,12 @@ fn generate_type_metadata_declaration( )?; let type_metadata_decl = program_abi::TypeMetadataDeclaration { metadata_type_id: MetadataTypeId(type_id.index()), - type_field: type_id.get_abi_type_str(&ctx.to_str_context(), engines, resolved_type_id), + type_field: type_id.get_abi_type_str( + handler, + &ctx.to_str_context(), + engines, + resolved_type_id, + )?, components, type_parameters, }; @@ -976,165 +1205,166 @@ impl TypeId { let type_engine = engines.te(); let decl_engine = engines.de(); let resolved_params = resolved_type_id.get_type_parameters(engines); - Ok(match &*type_engine.get(*self) { - TypeInfo::Custom { - type_arguments: Some(type_arguments), - .. - } => (!type_arguments.is_empty()).then_some({ - let resolved_params = resolved_params.unwrap_or_default(); - - for (v, p) in type_arguments.iter().zip(resolved_params.iter()) { - let p = p - .as_type_parameter() - .expect("only works with type parameters"); - generate_type_metadata_declaration( - handler, - ctx, - engines, - metadata_types, - concrete_types, - v.type_id(), - p.type_id, - metadata_types_to_add, - )?; - } - - type_arguments - .iter() - .zip(resolved_params.iter()) - .map(|(arg, p)| { + handler.scope(|handler| { + Ok(match &*type_engine.get(*self) { + TypeInfo::Custom { + type_arguments: Some(type_arguments), + .. + } => (!type_arguments.is_empty()).then_some({ + let resolved_params = resolved_params.unwrap_or_default(); + for (v, p) in type_arguments.iter().zip(resolved_params.iter()) { let p = p .as_type_parameter() .expect("only works with type parameters"); - Ok(program_abi::TypeApplication { - name: "".to_string(), - type_id: program_abi::TypeId::Metadata(MetadataTypeId( - arg.initial_type_id().index(), - )), - error_message: None, - type_arguments: arg.initial_type_id().get_abi_type_arguments( - handler, - ctx, - engines, - metadata_types, - concrete_types, - p.type_id, - metadata_types_to_add, - )?, - }) - }) - .collect::, _>>()? - }), - TypeInfo::Enum(decl_ref) => { - let decl = decl_engine.get_enum(decl_ref); + let _ = generate_type_metadata_declaration( + handler, + ctx, + engines, + metadata_types, + concrete_types, + v.type_id(), + p.type_id, + metadata_types_to_add, + ); + } - let mut new_metadata_types_to_add = - Vec::::new(); - for p in decl.generic_parameters.iter() { - let p = p - .as_type_parameter() - .expect("only works with type parameters"); - generate_type_metadata_declaration( - handler, - ctx, - engines, - metadata_types, - concrete_types, - p.type_id, - p.type_id, - &mut new_metadata_types_to_add, - )?; - } + type_arguments + .iter() + .zip(resolved_params.iter()) + .map(|(arg, p)| { + let p = p + .as_type_parameter() + .expect("only works with type parameters"); + Ok(program_abi::TypeApplication { + name: "".to_string(), + type_id: program_abi::TypeId::Metadata(MetadataTypeId( + arg.initial_type_id().index(), + )), + error_message: None, + type_arguments: arg.initial_type_id().get_abi_type_arguments( + handler, + ctx, + engines, + metadata_types, + concrete_types, + p.type_id, + metadata_types_to_add, + )?, + }) + }) + .collect::, _>>()? + }), + TypeInfo::Enum(decl_ref) => { + let decl = decl_engine.get_enum(decl_ref); - let type_arguments = decl - .generic_parameters - .iter() - .map(|p| { + let mut new_metadata_types_to_add = + Vec::::new(); + for p in decl.generic_parameters.iter() { let p = p .as_type_parameter() .expect("only works with type parameters"); - Ok(program_abi::TypeApplication { - name: "".to_string(), - type_id: program_abi::TypeId::Metadata(MetadataTypeId( - p.type_id.index(), - )), - error_message: None, - type_arguments: p.type_id.get_abi_type_arguments( - handler, - ctx, - engines, - metadata_types, - concrete_types, - p.type_id, - &mut new_metadata_types_to_add, - )?, + generate_type_metadata_declaration( + handler, + ctx, + engines, + metadata_types, + concrete_types, + p.type_id, + p.type_id, + &mut new_metadata_types_to_add, + )?; + } + + let type_arguments = decl + .generic_parameters + .iter() + .map(|p| { + let p = p + .as_type_parameter() + .expect("only works with type parameters"); + Ok(program_abi::TypeApplication { + name: "".to_string(), + type_id: program_abi::TypeId::Metadata(MetadataTypeId( + p.type_id.index(), + )), + error_message: None, + type_arguments: p.type_id.get_abi_type_arguments( + handler, + ctx, + engines, + metadata_types, + concrete_types, + p.type_id, + &mut new_metadata_types_to_add, + )?, + }) }) - }) - .collect::, _>>()?; + .collect::, _>>()?; - if type_arguments.is_empty() { - None - } else { - metadata_types_to_add.extend(new_metadata_types_to_add); - Some(type_arguments) + if type_arguments.is_empty() { + None + } else { + metadata_types_to_add.extend(new_metadata_types_to_add); + Some(type_arguments) + } } - } - TypeInfo::Struct(decl_ref) => { - let decl = decl_engine.get_struct(decl_ref); + TypeInfo::Struct(decl_ref) => { + let decl = decl_engine.get_struct(decl_ref); - let mut new_metadata_types_to_add = - Vec::::new(); - for p in decl.generic_parameters.iter() { - let p = p - .as_type_parameter() - .expect("only works with type parameters"); - generate_type_metadata_declaration( - handler, - ctx, - engines, - metadata_types, - concrete_types, - p.type_id, - p.type_id, - &mut new_metadata_types_to_add, - )?; - } - - let type_arguments = decl - .generic_parameters - .iter() - .map(|p| { + let mut new_metadata_types_to_add = + Vec::::new(); + for p in decl.generic_parameters.iter() { let p = p .as_type_parameter() .expect("only works with type parameters"); - Ok(program_abi::TypeApplication { - name: "".to_string(), - type_id: program_abi::TypeId::Metadata(MetadataTypeId( - p.type_id.index(), - )), - error_message: None, - type_arguments: p.type_id.get_abi_type_arguments( - handler, - ctx, - engines, - metadata_types, - concrete_types, - p.type_id, - &mut new_metadata_types_to_add, - )?, + generate_type_metadata_declaration( + handler, + ctx, + engines, + metadata_types, + concrete_types, + p.type_id, + p.type_id, + &mut new_metadata_types_to_add, + )?; + } + + let type_arguments = decl + .generic_parameters + .iter() + .map(|p| { + let p = p + .as_type_parameter() + .expect("only works with type parameters"); + Ok(program_abi::TypeApplication { + name: "".to_string(), + type_id: program_abi::TypeId::Metadata(MetadataTypeId( + p.type_id.index(), + )), + error_message: None, + type_arguments: p.type_id.get_abi_type_arguments( + handler, + ctx, + engines, + metadata_types, + concrete_types, + p.type_id, + &mut new_metadata_types_to_add, + )?, + }) }) - }) - .collect::, _>>()?; + .collect::, _>>()?; - if type_arguments.is_empty() { - None - } else { - metadata_types_to_add.extend(new_metadata_types_to_add); - Some(type_arguments) + if type_arguments.is_empty() { + None + } else { + metadata_types_to_add.extend(new_metadata_types_to_add); + Some(type_arguments) + } } - } - _ => None, + _ => None, + }) }) } @@ -1237,27 +1467,33 @@ impl TyFunctionDecl { metadata_types: &mut Vec, concrete_types: &mut Vec, ) -> Result { - // Generate the JSON data for the function - Ok(program_abi::ABIFunction { - name: self.name.as_str().to_string(), - inputs: self - .parameters + let inputs = handler.scope(|handler| { + self.parameters .iter() - .map(|x| { - Ok(program_abi::TypeConcreteParameter { - name: x.name.to_string(), - concrete_type_id: generate_concrete_type_declaration( - handler, - ctx, - engines, - metadata_types, - concrete_types, - x.type_argument.initial_type_id(), - x.type_argument.type_id(), - )?, + .map(|param| { + generate_concrete_type_declaration( + handler, + ctx, + engines, + metadata_types, + concrete_types, + param.type_argument.initial_type_id(), + param.type_argument.type_id(), + ) + .map(|concrete_type_id| { + program_abi::TypeConcreteParameter { + name: param.name.to_string(), + concrete_type_id, + } }) }) - .collect::, _>>()?, + .collect::, ErrorEmitted>>() + })?; + + // Generate the JSON data for the function + Ok(program_abi::ABIFunction { + name: self.name.as_str().to_string(), + inputs, output: generate_concrete_type_declaration( handler, ctx, @@ -1304,10 +1540,11 @@ impl GenericTypeParameter { let type_parameter = program_abi::TypeMetadataDeclaration { metadata_type_id: type_id.clone(), type_field: self.initial_type_id.get_abi_type_str( + handler, &ctx.to_str_context(), engines, self.type_id, - ), + )?, components: self.initial_type_id.get_abi_type_components( handler, ctx, diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 96308ddf983..70928dfd14a 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -380,10 +380,11 @@ impl CollectTypesMetadata for TyExpression { TypeMetadata::get_logged_type_id(exp, ctx.experimental.new_encoding) .map_err(|err| handler.emit_err(err))?; res.push(TypeMetadata::new_logged_type( + handler, ctx.engines, logged_type_id, ctx.program_name.clone(), - )); + )?); // We still need to dive into the expression because it can have additional types to collect. // E.g., `revert some_function_that_returns_error_enum_and_internally_logs_some_types()`; diff --git a/sway-core/src/language/ty/expression/intrinsic_function.rs b/sway-core/src/language/ty/expression/intrinsic_function.rs index 810ff875e96..4279a0d9608 100644 --- a/sway-core/src/language/ty/expression/intrinsic_function.rs +++ b/sway-core/src/language/ty/expression/intrinsic_function.rs @@ -90,10 +90,11 @@ impl CollectTypesMetadata for TyIntrinsicFunctionKind { ) .map_err(|err| handler.emit_err(err))?; let logged_type = TypeMetadata::new_logged_type( + handler, ctx.engines, logged_type_id, ctx.program_name.clone(), - ); + )?; types_metadata.push(logged_type); } Intrinsic::Smo => { diff --git a/sway-core/src/transform/attribute.rs b/sway-core/src/transform/attribute.rs index fcc9895c956..80e809b24e9 100644 --- a/sway-core/src/transform/attribute.rs +++ b/sway-core/src/transform/attribute.rs @@ -321,7 +321,7 @@ pub enum ArgsExpectValues { /// /// E.g.: `#[cfg(target = "fuel", experimental_new_encoding = false)]`. Yes, - /// None of the arguments can never have values specified, or the + /// None of the arguments can have values specified, or the /// [Attribute] does not expect any arguments. /// /// E.g.: `#[storage(read, write)]`, `#[fallback]`. @@ -354,6 +354,7 @@ pub enum AttributeKind { Fallback, ErrorType, Error, + AbiName, } /// Denotes if an [ItemTraitItem] belongs to an ABI or to a trait. @@ -384,6 +385,7 @@ impl AttributeKind { FALLBACK_ATTRIBUTE_NAME => AttributeKind::Fallback, ERROR_TYPE_ATTRIBUTE_NAME => AttributeKind::ErrorType, ERROR_ATTRIBUTE_NAME => AttributeKind::Error, + ABI_NAME_ATTRIBUTE_NAME => AttributeKind::AbiName, _ => AttributeKind::Unknown, } } @@ -412,6 +414,7 @@ impl AttributeKind { Fallback => false, ErrorType => false, Error => false, + AbiName => false, } } } @@ -452,6 +455,7 @@ impl Attribute { Fallback => Multiplicity::zero(), ErrorType => Multiplicity::zero(), Error => Multiplicity::exactly(1), + AbiName => Multiplicity::exactly(1), } } @@ -507,6 +511,7 @@ impl Attribute { Fallback => None, ErrorType => None, Error => MustBeIn(vec![ERROR_M_ARG_NAME]), + AbiName => MustBeIn(vec![ABI_NAME_NAME_ARG_NAME]), } } @@ -530,6 +535,7 @@ impl Attribute { ErrorType => No, // `error(msg = "msg")`. Error => Yes, + AbiName => Yes, } } @@ -550,6 +556,7 @@ impl Attribute { Fallback => false, ErrorType => false, Error => false, + AbiName => false, } } @@ -603,6 +610,7 @@ impl Attribute { Fallback => matches!(item_kind, ItemKind::Fn(_)), ErrorType => matches!(item_kind, ItemKind::Enum(_)), Error => false, + AbiName => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)), } } @@ -628,6 +636,7 @@ impl Attribute { Fallback => false, ErrorType => false, Error => struct_or_enum_field == StructOrEnumField::EnumField, + AbiName => false, } } @@ -653,6 +662,7 @@ impl Attribute { Fallback => false, ErrorType => false, Error => false, + AbiName => false, } } @@ -675,6 +685,7 @@ impl Attribute { Fallback => false, ErrorType => false, Error => false, + AbiName => false, } } @@ -696,6 +707,7 @@ impl Attribute { Fallback => false, ErrorType => false, Error => false, + AbiName => false, } } @@ -715,6 +727,7 @@ impl Attribute { Fallback => false, ErrorType => false, Error => false, + AbiName => false, } } @@ -733,6 +746,7 @@ impl Attribute { Fallback => false, ErrorType => false, Error => false, + AbiName => false, } } @@ -784,6 +798,9 @@ impl Attribute { Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."], ErrorType => vec!["\"error_type\" attribute can only annotate enums."], Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."], + AbiName => vec![ + "\"abi_name\" attribute can only annotate structs and enums.", + ], }; if help.is_empty() && target_friendly_name.starts_with("module kind") { @@ -992,6 +1009,12 @@ impl Attributes { .map(|index| &self.attributes[index]) } + /// Returns the `#[abi_name]` [Attribute], or `None` if the + /// [Attributes] does not contain any `#[abi_name]` attributes. + pub fn abi_name(&self) -> Option<&Attribute> { + self.of_kind(AttributeKind::AbiName).last() + } + /// Returns the `#[test]` [Attribute], or `None` if the /// [Attributes] does not contain any `#[test]` attributes. pub fn test(&self) -> Option<&Attribute> { diff --git a/sway-core/src/type_system/ast_elements/type_parameter.rs b/sway-core/src/type_system/ast_elements/type_parameter.rs index cdea37dce82..79e498bf5bb 100644 --- a/sway-core/src/type_system/ast_elements/type_parameter.rs +++ b/sway-core/src/type_system/ast_elements/type_parameter.rs @@ -250,9 +250,18 @@ impl TypeParameter { } } - pub fn abi_str(&self, engines: &Engines, ctx: &AbiStrContext, is_root: bool) -> String { + pub fn abi_str( + &self, + handler: &Handler, + engines: &Engines, + ctx: &AbiStrContext, + is_root: bool, + ) -> Result { match self { - TypeParameter::Type(p) => engines.te().get(p.type_id).abi_str(ctx, engines, is_root), + TypeParameter::Type(p) => engines + .te() + .get(p.type_id) + .abi_str(handler, ctx, engines, is_root), TypeParameter::Const(_) => { todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860") } diff --git a/sway-core/src/types/collect_types_metadata.rs b/sway-core/src/types/collect_types_metadata.rs index 02de56ec7d4..046347f5be3 100644 --- a/sway-core/src/types/collect_types_metadata.rs +++ b/sway-core/src/types/collect_types_metadata.rs @@ -124,12 +124,14 @@ impl TypeMetadata { } pub(crate) fn new_logged_type( + handler: &Handler, engines: &Engines, type_id: TypeId, program_name: String, - ) -> Self { - TypeMetadata::LoggedType( + ) -> Result { + Ok(TypeMetadata::LoggedType( LogId::new(type_id.get_abi_type_str( + handler, &AbiStrContext { program_name, abi_with_callpaths: true, @@ -138,9 +140,9 @@ impl TypeMetadata { }, engines, type_id, - )), + )?), type_id, - ) + )) } } diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index 2e335cda3d7..9101e9e753f 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -1044,6 +1044,14 @@ pub enum CompileError { EncodingUnsupportedType { span: Span }, #[error("Configurables need a function named \"abi_decode_in_place\" to be in scope.")] ConfigurableMissingAbiDecodeInPlace { span: Span }, + #[error("Invalid name found for renamed ABI type.\n")] + ABIInvalidName { span: Span, name: String }, + #[error("Duplicated name found for renamed ABI type.\n")] + ABIDuplicateName { + span: Span, + other_span: Span, + is_attribute: bool, + }, #[error("Collision detected between two different types.\n Shared hash:{hash}\n First type:{first_type}\n Second type:{second_type}")] ABIHashCollision { span: Span, @@ -1304,6 +1312,8 @@ impl Spanned for CompileError { CannotBeEvaluatedToConfigurableSizeUnknown { span } => span.clone(), EncodingUnsupportedType { span } => span.clone(), ConfigurableMissingAbiDecodeInPlace { span } => span.clone(), + ABIInvalidName { span, .. } => span.clone(), + ABIDuplicateName { span, .. } => span.clone(), ABIHashCollision { span, .. } => span.clone(), InvalidRangeEndGreaterThanStart { span, .. } => span.clone(), TypeMustBeKnownAtThisPoint { span, .. } => span.clone(), @@ -3112,6 +3122,32 @@ impl ToDiagnostic for CompileError { help }, }, + ABIDuplicateName { span, other_span: other, is_attribute } => Diagnostic { + reason: Some(Reason::new(code(1), "Duplicated name found for renamed ABI type.".into())), + issue: Issue::error( + source_engine, + span.clone(), + String::new() + ), + hints: vec![ + Hint::help( + source_engine, + other.clone(), + format!("This is the existing {} with conflicting name", if *is_attribute { "attribute" } else { "type" }), + ) + ], + help: vec![], + }, + ABIInvalidName { span, name } => Diagnostic { + reason: Some(Reason::new(code(1), "Invalid name found for renamed ABI type.".into())), + issue: Issue::error( + source_engine, + span.clone(), + String::new() + ), + hints: vec![], + help: vec![format!("The name must be a valid Sway identifier{}", if name.is_empty() { " and cannot be empty" } else { "" })], + }, _ => Diagnostic { // TODO: Temporarily we use `self` here to achieve backward compatibility. // In general, `self` must not be used. All the values for the formatting diff --git a/sway-parse/src/lib.rs b/sway-parse/src/lib.rs index b6954c1078e..9ed376220af 100644 --- a/sway-parse/src/lib.rs +++ b/sway-parse/src/lib.rs @@ -24,7 +24,7 @@ pub use crate::{ keywords::RESERVED_KEYWORDS, parse::Parse, parser::Parser, - token::{lex, lex_commented, parse_int_suffix}, + token::{is_valid_identifier_or_path, lex, lex_commented, parse_int_suffix}, }; use sway_ast::{ diff --git a/sway-parse/src/token.rs b/sway-parse/src/token.rs index b67bdc16fc7..a2d9a82df7f 100644 --- a/sway-parse/src/token.rs +++ b/sway-parse/src/token.rs @@ -105,6 +105,29 @@ pub fn lex( lex_commented(handler, src, start, end, &source_id).map(|stream| stream.strip_comments()) } +pub fn is_valid_identifier_or_path(s: &str) -> bool { + // Return false if the string is empty. + if s.is_empty() { + return false; + } + + let mut chars = s.chars(); + + // The first character must be a valid starting character. + let _first = match chars.next() { + Some(c) if c.is_xid_start() || c == '_' => c, + _ => return false, + }; + + // Do not accept a lone underscore as a valid identifier. + if s == "_" { + return false; + } + + // All remaining characters must be valid identifier or path characters. + chars.all(|c| c.is_xid_continue() || c == ':') +} + pub fn lex_commented( handler: &Handler, src: Source, diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/Forc.lock new file mode 100644 index 00000000000..06f765d8ffe --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "attributes_invalid_abi_names" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-F9C5117675511A22" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/Forc.toml new file mode 100644 index 00000000000..5e429734901 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "attributes_invalid_abi_names" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } + diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/snapshot.toml b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/snapshot.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw new file mode 100644 index 00000000000..73384b4da11 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw @@ -0,0 +1,70 @@ +contract; + +mod other; +use other::OtherEnum; +use other::OtherStruct; + +#[abi_name(name = "SameName")] +struct MyStruct {} + +#[abi_name(name = "MyStruct")] +struct MyStruct0 {} + +#[abi_name(name = "SameName")] +struct MyStruct1 {} + +#[abi_name(name = "SameName")] +struct MyStruct2 {} + +#[abi_name(name = "")] +struct MyStruct3 {} + +#[abi_name(name = "this !s n0t an identif1er")] +struct MyStruct4 {} + +#[abi_name(name = "this::looks::like::a::path")] +struct MyStruct5 {} + +#[abi_name(name = "OtherStruct")] +struct MyStruct6 {} + +// OK because enums are on a different namespace +#[abi_name(name = "SameName")] +enum MyEnum { + A: () +} + +#[abi_name(name = "OtherEnum")] +enum MyEnum1 { + A: () +} + +abi MyAbi { + fn other_struct() -> OtherStruct; + fn my_struct() -> MyStruct; + fn my_struct0() -> MyStruct0; + fn my_struct1() -> MyStruct1; + fn my_struct2() -> MyStruct2; + fn my_struct3() -> MyStruct3; + fn my_struct4() -> MyStruct4; + fn my_struct5() -> MyStruct5; + fn my_struct6() -> MyStruct6; + fn other_enum() -> OtherEnum; + fn my_enum() -> MyEnum; + fn my_enum1() -> MyEnum1; +} + +impl MyAbi for Contract { + fn other_struct() -> OtherStruct { OtherStruct{} } + fn my_struct() -> MyStruct { MyStruct{} } + fn my_struct0() -> MyStruct0 { MyStruct0{} } + fn my_struct1() -> MyStruct1 { MyStruct1{} } + fn my_struct2() -> MyStruct2 { MyStruct2{} } + fn my_struct3() -> MyStruct3 { MyStruct3{} } + fn my_struct4() -> MyStruct4 { MyStruct4{} } + fn my_struct5() -> MyStruct5 { MyStruct5{} } + fn my_struct6() -> MyStruct6 { MyStruct6{} } + fn other_enum() -> OtherEnum { OtherEnum::A } + fn my_enum() -> MyEnum { MyEnum::A } + fn my_enum1() -> MyEnum1 { MyEnum1::A } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/other.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/other.sw new file mode 100644 index 00000000000..38166e057d9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/other.sw @@ -0,0 +1,9 @@ +library; + +pub struct OtherStruct { + +} + +pub enum OtherEnum { + A: () +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/stdout.snap new file mode 100644 index 00000000000..42b0a3a57ec --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/stdout.snap @@ -0,0 +1,102 @@ +--- +source: test/src/snapshot/mod.rs +--- +> forc build --path test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names +exit status: 1 +output: + Building test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names + Compiling library std (sway-lib-std) + Compiling contract attributes_invalid_abi_names (test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names) +error: Duplicated name found for renamed ABI type. + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:37:19 + | +... +37 | #[abi_name(name = "OtherEnum")] + | ^^^^^^^^^^^ + | + ::: test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/other.sw:7:10 + | +... + 7 | pub enum OtherEnum { + | --------- help: This is the existing type with conflicting name + | +____ + +error: Duplicated name found for renamed ABI type. + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:10:19 + | +... + 8 | struct MyStruct {} + | -------- help: This is the existing type with conflicting name + 9 | +10 | #[abi_name(name = "MyStruct")] + | ^^^^^^^^^^ + | +____ + +error: Duplicated name found for renamed ABI type. + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:13:19 + | +... + 7 | #[abi_name(name = "SameName")] + | ------------------------------ help: This is the existing attribute with conflicting name +... +13 | #[abi_name(name = "SameName")] + | ^^^^^^^^^^ + | +____ + +error: Duplicated name found for renamed ABI type. + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:16:19 + | +... + 7 | #[abi_name(name = "SameName")] + | ------------------------------ help: This is the existing attribute with conflicting name + 8 | struct MyStruct {} + 9 | +10 | #[abi_name(name = "MyStruct")] +11 | struct MyStruct0 {} +... +15 | +16 | #[abi_name(name = "SameName")] + | ^^^^^^^^^^ + | +____ + +error: Invalid name found for renamed ABI type. + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:19:19 + | +... +19 | #[abi_name(name = "")] + | ^^ + | + = help: The name must be a valid Sway identifier and cannot be empty +____ + +error: Invalid name found for renamed ABI type. + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:22:19 + | +... +22 | #[abi_name(name = "this !s n0t an identif1er")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: The name must be a valid Sway identifier +____ + +error: Duplicated name found for renamed ABI type. + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:28:19 + | +... +28 | #[abi_name(name = "OtherStruct")] + | ^^^^^^^^^^^^^ + | + ::: test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/other.sw:3:12 + | +... + 3 | pub struct OtherStruct { + | ----------- help: This is the existing type with conflicting name + | +____ + + Aborting due to 7 errors. +error: Failed to compile attributes_invalid_abi_names diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/src/abi_name_attr.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/src/abi_name_attr.sw new file mode 100644 index 00000000000..09d7f5d2a11 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/src/abi_name_attr.sw @@ -0,0 +1,7 @@ +library; + +#[abi_name(name = "name")] +pub struct Ok { } + +#[abi_name(nam = "name")] +pub struct NotOk { } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/src/main.sw index 8ba698bdb92..781bfaeb0fb 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/src/main.sw @@ -15,6 +15,7 @@ mod deprecated_attr; mod fallback_attr; mod error_type_attr; mod error_attr; +mod abi_name_attr; /// Outer doc comment supports multiple. /// Outer doc comment supports multiple. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/stdout.snap index 3707164666d..5610469a8ed 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/stdout.snap @@ -1,6 +1,5 @@ --- source: test/src/snapshot/mod.rs -assertion_line: 125 --- > forc build --path test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args exit status: 1 @@ -259,5 +258,16 @@ error: Attribute argument is invalid | ____ - Aborting due to 18 errors. +error: Attribute argument is invalid + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/src/abi_name_attr.sw:6:12 + | +... +6 | #[abi_name(nam = "name")] + | ^^^ "nam" is an invalid argument for attribute "abi_name". + | --- help: Did you mean "name"? + | --- help: The only valid argument is "name". + | +____ + + Aborting due to 19 errors. error: Failed to compile attributes_invalid_args diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/src/abi_name_attr.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/src/abi_name_attr.sw new file mode 100644 index 00000000000..6735ee43e2c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/src/abi_name_attr.sw @@ -0,0 +1,4 @@ +library; + +#[abi_name(name)] +pub struct NotOk { } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/src/main.sw index fad462d385c..7c04bc8fcee 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/src/main.sw @@ -16,6 +16,7 @@ mod deprecated_attr; mod fallback_attr; mod error_type_attr; mod error_attr; +mod abi_name_attr; /// Outer doc comment supports multiple. /// Outer doc comment supports multiple. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/stdout.snap index 30a53a944bd..df68b176ff3 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/stdout.snap @@ -1,6 +1,5 @@ --- source: test/src/snapshot/mod.rs -assertion_line: 125 --- > forc build --path test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values exit status: 1 @@ -130,5 +129,15 @@ error: Attribute argument must have a value | ____ - Aborting due to 10 errors. +error: Attribute argument must have a value + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_expect_values/src/abi_name_attr.sw:3:12 + | +... +3 | #[abi_name(name)] + | ^^^^ "name" argument of the attribute "abi_name" must have a value. + | ---- help: To set the value, use the `=` operator: `name = `. + | +____ + + Aborting due to 11 errors. error: Failed to compile attributes_invalid_args_expect_values diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/src/abi_name_attr.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/src/abi_name_attr.sw new file mode 100644 index 00000000000..2e454b61656 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/src/abi_name_attr.sw @@ -0,0 +1,7 @@ +library; + +#[abi_name(name = "name")] +pub struct Ok1 { } + +#[abi_name(name = "name", name = "other name")] +pub struct NotOk1 { } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/src/main.sw index a10d86f3db5..2f726f6f8db 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/src/main.sw @@ -16,6 +16,7 @@ mod deprecated_attr; mod fallback_attr; mod error_type_attr; mod error_attr; +mod abi_name_attr; /// Outer doc comment supports multiple. /// Outer doc comment supports multiple. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/stdout.snap index b321589e971..c13f8fcb9fe 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/stdout.snap @@ -1,6 +1,5 @@ --- source: test/src/snapshot/mod.rs -assertion_line: 125 --- > forc build --path test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity exit status: 1 @@ -221,5 +220,14 @@ error: Number of attribute arguments is invalid | ____ - Aborting due to 18 errors. +error: Number of attribute arguments is invalid + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_multiplicity/src/abi_name_attr.sw:6:12 + | +... +6 | #[abi_name(name = "name", name = "other name")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "abi_name" attribute must have exactly one argument, but has two. + | +____ + + Aborting due to 19 errors. error: Failed to compile attributes_invalid_args_multiplicity diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/abi_name_attr.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/abi_name_attr.sw new file mode 100644 index 00000000000..bd8c8cfd8ba --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/abi_name_attr.sw @@ -0,0 +1,4 @@ +library; + +#[abi_name(name = true)] +pub fn not_ok() {} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/main.sw index da9e3cd961b..806489a3d6d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/main.sw @@ -6,4 +6,5 @@ mod cfg_attr_experimental; mod cfg_attr_program_type; mod cfg_attr_target; mod deprecated_attr; -mod error_attr; \ No newline at end of file +mod error_attr; +mod abi_name_attr; diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/stdout.snap index edb15089e8a..11de2d61c5b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/stdout.snap @@ -45,6 +45,16 @@ error: Attribute argument value has a wrong type | ____ +error: Attribute cannot annotate item + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/abi_name_attr.sw:3:3 + | +... +3 | #[abi_name(name = true)] + | ^^^^^^^^ "abi_name" attribute cannot annotate a function declaration. + | + = help: "abi_name" attribute can only annotate structs and enums. +____ + error: Attribute argument value has a wrong type --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args_values_types/src/error_attr.sw:6:17 | @@ -65,5 +75,5 @@ error: Attribute argument value has a wrong type | ____ - Aborting due to 5 errors. + Aborting due to 6 errors. error: Failed to compile attributes_invalid_args_values_types diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/abi_name_attr.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/abi_name_attr.sw new file mode 100644 index 00000000000..e6df31baa7f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/abi_name_attr.sw @@ -0,0 +1,5 @@ +library; + +#[abi_name] +#[abi_name(name = "name")] +pub struct NotOk { } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/main.sw index 6774ed5344a..e5ffc541a4f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/main.sw @@ -12,6 +12,7 @@ mod deprecated_attr; mod fallback_attr; mod error_type_attr; mod error_attr; +mod abi_name_attr; /// Outer doc comment supports multiple. /// Outer doc comment supports multiple. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/stdout.snap index bfbdcaae274..cc044b8f380 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/stdout.snap @@ -1,6 +1,5 @@ --- source: test/src/snapshot/mod.rs -assertion_line: 125 --- > forc build --path test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity exit status: 1 @@ -113,5 +112,25 @@ error: Attribute can be applied only once | ____ - Aborting due to 8 errors. +error: Attribute can be applied only once + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/abi_name_attr.sw:4:3 + | +... +3 | #[abi_name] + | -------- info: It is already applied here. +4 | #[abi_name(name = "name")] + | ^^^^^^^^ "abi_name" attribute can be applied only once, but is applied two times. + | +____ + +error: Number of attribute arguments is invalid + --> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_multiplicity/src/abi_name_attr.sw:3:3 + | +... +3 | #[abi_name] + | ^^^^^^^^ "abi_name" attribute must have exactly one argument, but has none. + | +____ + + Aborting due to 10 errors. error: Failed to compile attributes_invalid_multiplicity diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/Forc.lock new file mode 100644 index 00000000000..075e7e09ef7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "attributes_abi_name" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-43A72CA5046445DF" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/Forc.toml new file mode 100644 index 00000000000..4e0ec3baf07 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "attributes_abi_name" + +[dependencies] +std = { path = "../../../../reduced_std_libs/sway-lib-std-core" } + diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/json_abi_oracle.json new file mode 100644 index 00000000000..998c05bc06c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/json_abi_oracle.json @@ -0,0 +1,50 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "215af2bca9e1aa8fec647dab22a0cd36c63ce5ed051a132d51323807e28c0d67", + "metadataTypeId": 1, + "type": "enum RenamedMyEnum" + }, + { + "concreteTypeId": "d31db280ac133d726851d8003bd2f06ec2d3fc76a46f1007d13914088fbd0791", + "type": "struct RenamedMyStruct" + } + ], + "configurables": [], + "encodingVersion": "1", + "errorCodes": {}, + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "my_enum", + "output": "215af2bca9e1aa8fec647dab22a0cd36c63ce5ed051a132d51323807e28c0d67" + }, + { + "attributes": null, + "inputs": [], + "name": "my_struct", + "output": "d31db280ac133d726851d8003bd2f06ec2d3fc76a46f1007d13914088fbd0791" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [ + { + "metadataTypeId": 0, + "type": "()" + }, + { + "components": [ + { + "name": "A", + "typeId": 0 + } + ], + "metadataTypeId": 1, + "type": "enum RenamedMyEnum" + } + ], + "programType": "contract", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/json_abi_oracle_new_encoding.json new file mode 100644 index 00000000000..7e10e2f782f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/json_abi_oracle_new_encoding.json @@ -0,0 +1,50 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "215af2bca9e1aa8fec647dab22a0cd36c63ce5ed051a132d51323807e28c0d67", + "metadataTypeId": 1, + "type": "enum RenamedMyEnum" + }, + { + "concreteTypeId": "d31db280ac133d726851d8003bd2f06ec2d3fc76a46f1007d13914088fbd0791", + "type": "struct RenamedMyStruct" + } + ], + "configurables": [], + "encodingVersion": "1", + "errorCodes": {}, + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "my_enum", + "output": "215af2bca9e1aa8fec647dab22a0cd36c63ce5ed051a132d51323807e28c0d67" + }, + { + "attributes": null, + "inputs": [], + "name": "my_struct", + "output": "d31db280ac133d726851d8003bd2f06ec2d3fc76a46f1007d13914088fbd0791" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [ + { + "metadataTypeId": 0, + "type": "()" + }, + { + "components": [ + { + "name": "A", + "typeId": 0 + } + ], + "metadataTypeId": 1, + "type": "enum RenamedMyEnum" + } + ], + "programType": "contract", + "specVersion": "1.1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/src/lib.sw new file mode 100644 index 00000000000..75ec89e2b0e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/src/lib.sw @@ -0,0 +1,19 @@ +contract; + +#[abi_name(name = "RenamedMyStruct")] +struct MyStruct {} + +#[abi_name(name = "RenamedMyEnum")] +enum MyEnum { + A: () +} + +abi MyAbi { + fn my_struct() -> MyStruct; + fn my_enum() -> MyEnum; +} + +impl MyAbi for Contract { + fn my_struct() -> MyStruct { MyStruct{} } + fn my_enum() -> MyEnum { MyEnum::A } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/test.toml new file mode 100644 index 00000000000..5b08fe8a4da --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/test.toml @@ -0,0 +1,3 @@ +category = "compile" +expected_warnings = 0 +validate_abi = true