diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 81f704dfdeb..ca1c33dd871 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -148,7 +148,7 @@ fn const_eval_typed_expr( TypedExpressionVariant::StructExpression { fields, .. } => { let (field_typs, field_vals): (Vec<_>, Vec<_>) = fields .iter() - .filter_map(|TypedStructExpressionField { name: _, value }| { + .filter_map(|TypedStructExpressionField { name: _, value, .. }| { const_eval_typed_expr(context, module, known_consts, value) .map(|cv| (value.return_type, cv)) }) diff --git a/sway-core/src/parse_tree/declaration/type_parameter.rs b/sway-core/src/parse_tree/declaration/type_parameter.rs index bdacbd14548..8c584a7e9c5 100644 --- a/sway-core/src/parse_tree/declaration/type_parameter.rs +++ b/sway-core/src/parse_tree/declaration/type_parameter.rs @@ -2,7 +2,10 @@ use crate::{error::*, parse_tree::*, semantic_analysis::*, type_engine::*}; use sway_types::{ident::Ident, span::Span, Spanned}; -use std::hash::{Hash, Hasher}; +use std::{ + fmt, + hash::{Hash, Hasher}, +}; #[derive(Debug, Clone, Eq)] pub struct TypeParameter { @@ -57,6 +60,12 @@ impl ReplaceSelfType for TypeParameter { } } +impl fmt::Display for TypeParameter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.name_ident, self.type_id) + } +} + impl TypeParameter { pub(crate) fn type_check( type_parameter: TypeParameter, diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/monomorphize.rs b/sway-core/src/semantic_analysis/ast_node/declaration/monomorphize.rs index d38e781c2d1..e0148605fbe 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/monomorphize.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/monomorphize.rs @@ -64,7 +64,7 @@ where fn monomorphize( self, - type_arguments: Vec, + mut type_arguments: Vec, enforce_type_arguments: EnforceTypeArguments, self_type: Option, call_site_span: Option<&Span>, @@ -107,7 +107,6 @@ where err(warnings, errors) } (false, false) => { - let mut type_arguments = type_arguments; for type_argument in type_arguments.iter_mut() { let type_id = match self_type { Some(self_type) => namespace.resolve_type_with_self( @@ -191,13 +190,15 @@ pub(crate) trait MonomorphizeHelper { fn monomorphize_inner(self, type_mapping: &TypeMapping, namespace: &mut Items) -> Self::Output; } -pub(crate) fn monomorphize_inner(decl: T, type_mapping: &TypeMapping, namespace: &mut Items) -> T +pub(crate) fn monomorphize_inner( + decl: T, + type_mapping: &TypeMapping, + _namespace: &mut Items, +) -> T where T: CopyTypes + CreateTypeId, { - let old_type_id = decl.create_type_id(); let mut new_decl = decl; new_decl.copy_types(type_mapping); - namespace.copy_methods_to_type(old_type_id, new_decl.create_type_id(), type_mapping); new_decl } diff --git a/sway-core/src/semantic_analysis/namespace/items.rs b/sway-core/src/semantic_analysis/namespace/items.rs index 995b5885c47..92f137fc7a4 100644 --- a/sway-core/src/semantic_analysis/namespace/items.rs +++ b/sway-core/src/semantic_analysis/namespace/items.rs @@ -138,33 +138,6 @@ impl Items { .get_methods_for_type(implementing_for_type_id) } - // Given a TypeInfo old_type with a set of methods available to it, make those same methods - // available to TypeInfo new_type. This is useful in situations where old_type is being - // monomorphized to new_type and and we want `get_methods_for_type()` to return the same set of - // methods for new_type as it does for old_type. - pub(crate) fn copy_methods_to_type( - &mut self, - old_type: TypeId, - new_type: TypeId, - type_mapping: &TypeMapping, - ) { - // This map grabs all (trait name, vec of methods) from self.implemented_traits - // corresponding to `old_type`. - let methods = self - .implemented_traits - .get_methods_for_type_by_trait(old_type); - - // Insert into `self.implemented_traits` the contents of the map above but with `new_type` - // as the `TypeInfo` key. - for (trait_name, mut trait_methods) in methods.into_iter() { - trait_methods - .iter_mut() - .for_each(|method| method.copy_types(type_mapping)); - self.implemented_traits - .insert(trait_name, new_type, trait_methods); - } - } - pub(crate) fn get_canonical_path(&self, symbol: &Ident) -> &[Ident] { self.use_synonyms.get(symbol).map(|v| &v[..]).unwrap_or(&[]) } diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index ee6202df506..72f9c1eb890 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -1,10 +1,8 @@ use crate::{ - type_engine::{look_up_type_id, TypeId}, + type_engine::{create_type_mapping, look_up_type_id, CopyTypes, TypeId}, CallPath, TypeInfo, TypedFunctionDeclaration, }; -use std::collections::HashMap; - type TraitName = CallPath; // This cannot be a HashMap because of how TypeInfo's are handled. @@ -46,8 +44,7 @@ impl TraitMap { ) { let mut methods_map = im::HashMap::new(); for method in methods.into_iter() { - let method_name = method.name.as_str().to_string(); - methods_map.insert(method_name, method); + methods_map.insert(method.name.as_str().to_string(), method); } self.trait_map .push_back(((trait_name, incoming_type_id), methods_map)); @@ -69,7 +66,7 @@ impl TraitMap { ) -> Vec<((CallPath, TypeId), Vec)> { let mut ret = vec![]; for ((call_path, map_type_id), methods) in self.trait_map.iter() { - if look_up_type_id(*map_type_id) == look_up_type_id(incoming_type_id) { + if look_up_type_id(incoming_type_id).is_subset_of(&look_up_type_id(*map_type_id)) { ret.push(( (call_path.clone(), *map_type_id), methods.values().cloned().collect(), @@ -88,29 +85,14 @@ impl TraitMap { if look_up_type_id(incoming_type_id) == TypeInfo::ErrorRecovery { return methods; } - for ((_, map_type_id), l_methods) in self.trait_map.iter() { - if look_up_type_id(*map_type_id) == look_up_type_id(incoming_type_id) { - methods.append(&mut l_methods.values().cloned().collect()); - } - } - methods - } - - pub(crate) fn get_methods_for_type_by_trait( - &self, - incoming_type_id: TypeId, - ) -> HashMap> { - let mut methods: HashMap> = HashMap::new(); - // small performance gain in bad case - if look_up_type_id(incoming_type_id) == TypeInfo::ErrorRecovery { - return methods; - } - for ((trait_name, map_type_id), trait_methods) in self.trait_map.iter() { - if look_up_type_id(*map_type_id) == look_up_type_id(incoming_type_id) { - methods.insert( - (*trait_name).clone(), - trait_methods.values().cloned().collect(), - ); + for ((_, map_type_id), trait_methods) in self.trait_map.iter() { + if look_up_type_id(incoming_type_id).is_subset_of(&look_up_type_id(*map_type_id)) { + let type_mapping = create_type_mapping(*map_type_id, incoming_type_id); + let mut trait_methods = trait_methods.values().cloned().collect::>(); + trait_methods + .iter_mut() + .for_each(|x| x.copy_types(&type_mapping)); + methods.append(&mut trait_methods); } } methods diff --git a/sway-core/src/type_engine/type_info.rs b/sway-core/src/type_engine/type_info.rs index 229da791d79..2f8402745b3 100644 --- a/sway-core/src/type_engine/type_info.rs +++ b/sway-core/src/type_engine/type_info.rs @@ -863,6 +863,169 @@ impl TypeInfo { ok(generics, warnings, errors) } + /// Given two `TypeInfo`'s `self` and `other`, check to see if `self` is + /// unidirectionally a subset of `other`. + /// + /// `self` is a subset of `other` if it can be generalized over `other`. + /// For example, the generic `T` is a subset of the generic `F` because + /// anything of the type `T` could also be of the type `F` (minus any + /// external context that may make this statement untrue). + /// + /// Given: + /// + /// ```ignore + /// struct Data { + /// x: T, + /// y: F, + /// } + /// ``` + /// + /// the type `Data` is a subset of any generic type. + /// + /// Given: + /// + /// ```ignore + /// struct Data { + /// x: T, + /// y: F, + /// } + /// + /// impl Data { } + /// ``` + /// + /// the type `Data` is a subset of `Data`, but _`Data` is + /// not a subset of `Data`_. + /// + /// Given: + /// + /// ```ignore + /// struct Data { + /// x: T, + /// y: F, + /// } + /// + /// impl Data { } + /// + /// fn dummy() { + /// // the type of foo is Data + /// let foo = Data { + /// x: true, + /// y: 1u64 + /// }; + /// // the type of bar is Data + /// let bar = Data { + /// x: 0u8, + /// y: 0u8 + /// }; + /// } + /// ``` + /// + /// | type: | is subset of: | is not a subset of: | + /// |-------------------|----------------------------------------------|---------------------| + /// | `Data` | `Data`, any generic type | | + /// | `Data` | any generic type | `Data` | + /// | `Data` | `Data`, any generic type | `Data` | + /// | `Data` | `Data`, `Data`, any generic type | | + /// + pub(crate) fn is_subset_of(&self, other: &TypeInfo) -> bool { + match (self, other) { + // any type is the subset of a generic + (_, Self::UnknownGeneric { .. }) => true, + (Self::Ref(l, _), Self::Ref(r, _)) => { + look_up_type_id(*l).is_subset_of(&look_up_type_id(*r)) + } + (Self::Array(l0, l1), Self::Array(r0, r1)) => { + look_up_type_id(*l0).is_subset_of(&look_up_type_id(*r0)) && l1 == r1 + } + ( + Self::Custom { + name: l_name, + type_arguments: l_type_args, + }, + Self::Custom { + name: r_name, + type_arguments: r_type_args, + }, + ) => { + let l_types = l_type_args + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + let r_types = r_type_args + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + l_name == r_name && types_are_subset_of(&l_types, &r_types) + } + ( + Self::Enum { + name: l_name, + variant_types: l_variant_types, + type_parameters: l_type_parameters, + }, + Self::Enum { + name: r_name, + variant_types: r_variant_types, + type_parameters: r_type_parameters, + }, + ) => { + let l_names = l_variant_types + .iter() + .map(|x| x.name.clone()) + .collect::>(); + let r_names = r_variant_types + .iter() + .map(|x| x.name.clone()) + .collect::>(); + let l_types = l_type_parameters + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + let r_types = r_type_parameters + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + l_name == r_name && l_names == r_names && types_are_subset_of(&l_types, &r_types) + } + ( + Self::Struct { + name: l_name, + fields: l_fields, + type_parameters: l_type_parameters, + }, + Self::Struct { + name: r_name, + fields: r_fields, + type_parameters: r_type_parameters, + }, + ) => { + let l_names = l_fields.iter().map(|x| x.name.clone()).collect::>(); + let r_names = r_fields.iter().map(|x| x.name.clone()).collect::>(); + let l_types = l_type_parameters + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + let r_types = r_type_parameters + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + l_name == r_name && l_names == r_names && types_are_subset_of(&l_types, &r_types) + } + (Self::Tuple(l_types), Self::Tuple(r_types)) => { + let l_types = l_types + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + let r_types = r_types + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + types_are_subset_of(&l_types, &r_types) + } + (a, b) => a == b, + } + } + /// Given a `TypeInfo` `self` and a list of `Ident`'s `subfields`, /// iterate through the elements of `subfields` as `subfield`, /// and recursively apply `subfield` to `self`. @@ -982,6 +1145,113 @@ impl TypeInfo { } } +/// Given two lists of `TypeInfo`'s `left` and `right`, check to see if +/// `left` is a subset of `right`. +/// +/// `left` is a subset of `right` if the following invariants are true: +/// 1. `left` and and `right` are of the same length _n_ +/// 2. For every _i_ in [0, n), `left`ᵢ is a subset of `right`ᵢ +/// 3. The elements of `left` satisfy the trait constraints of `right` +/// +/// A property that falls of out these constraints are that if `left` and +/// `right` are empty, then `left` is a subset of `right`. +/// +/// Given: +/// +/// ```ignore +/// left: [T] +/// right: [T, F] +/// ``` +/// +/// `left` is not a subset of `right` because it violates invariant #1. +/// +/// Given: +/// +/// ```ignore +/// left: [T, F] +/// right: [bool, F] +/// ``` +/// +/// `left` is not a subset of `right` because it violates invariant #2. +/// +/// Given: +/// +/// ```ignore +/// left: [T, F] +/// right: [T, T] +/// ``` +/// +/// `left` is not a subset of `right` because it violates invariant #3. +/// +/// Given: +/// +/// ```ignore +/// left: [T, T] +/// right: [T, F] +/// ``` +/// +/// `left` is a subset of `right`. +/// +/// Given: +/// +/// ```ignore +/// left: [bool, T] +/// right: [T, F] +/// ``` +/// +/// `left` is a subset of `right`. +/// +/// Given: +/// +/// ```ignore +/// left: [Data, Data] +/// right: [Data, Data] +/// ``` +/// +/// `left` is a subset of `right`. +/// +fn types_are_subset_of(left: &[TypeInfo], right: &[TypeInfo]) -> bool { + // invariant 1. `left` and and `right` are of the same length _n_ + if left.len() != right.len() { + return false; + } + + // if `left` and `right` are empty, `left` is inherently a subset of `right` + if left.is_empty() && right.is_empty() { + return true; + } + + // invariant 2. For every _i_ in [0, n), `left`ᵢ is a subset of `right`ᵢ + for (l, r) in left.iter().zip(right.iter()) { + if !l.is_subset_of(r) { + return false; + } + } + + // invariant 3. The elements of `left` satisfy the trait constraints of `right` + let mut constraints = vec![]; + for i in 0..(right.len() - 1) { + for j in (i + 1)..right.len() { + let a = right.get(i).unwrap(); + let b = right.get(j).unwrap(); + if a == b { + // if a and b are the same type + constraints.push((i, j)); + } + } + } + for (i, j) in constraints.into_iter() { + let a = left.get(i).unwrap(); + let b = left.get(j).unwrap(); + if a != b { + return false; + } + } + + // if all of the invariants are met, then `self` is a subset of `other`! + true +} + fn print_inner_types(name: String, inner_types: impl Iterator) -> String { let inner_types = inner_types.map(|x| x.to_string()).collect::>(); format!( diff --git a/sway-core/src/type_engine/type_mapping.rs b/sway-core/src/type_engine/type_mapping.rs index 8335676addc..c743cc68267 100644 --- a/sway-core/src/type_engine/type_mapping.rs +++ b/sway-core/src/type_engine/type_mapping.rs @@ -17,3 +17,111 @@ pub(crate) fn insert_type_parameters(type_parameters: &[TypeParameter]) -> TypeM }) .collect() } + +pub(crate) fn create_type_mapping(superset_type: TypeId, subset_type: TypeId) -> TypeMapping { + match (look_up_type_id(superset_type), look_up_type_id(subset_type)) { + (TypeInfo::Ref(superset_type, _), TypeInfo::Ref(subset_type, _)) => { + create_type_mapping(superset_type, subset_type) + } + (TypeInfo::Ref(superset_type, _), _) => create_type_mapping(superset_type, subset_type), + (_, TypeInfo::Ref(subset_type, _)) => create_type_mapping(superset_type, subset_type), + (TypeInfo::UnknownGeneric { .. }, _) => { + vec![(superset_type, subset_type)] + } + ( + TypeInfo::Custom { + type_arguments: type_parameters, + .. + }, + TypeInfo::Custom { type_arguments, .. }, + ) => { + let type_parameters = type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(); + let type_arguments = type_arguments.iter().map(|x| x.type_id).collect::>(); + insert_type_parameters_with_type_arguments(type_parameters, type_arguments) + } + ( + TypeInfo::Enum { + type_parameters, .. + }, + TypeInfo::Enum { + type_parameters: type_arguments, + .. + }, + ) => { + let type_parameters = type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(); + let type_arguments = type_arguments.iter().map(|x| x.type_id).collect::>(); + insert_type_parameters_with_type_arguments(type_parameters, type_arguments) + } + ( + TypeInfo::Struct { + type_parameters, .. + }, + TypeInfo::Struct { + type_parameters: type_arguments, + .. + }, + ) => { + let type_parameters = type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(); + let type_arguments = type_arguments.iter().map(|x| x.type_id).collect::>(); + insert_type_parameters_with_type_arguments(type_parameters, type_arguments) + } + (TypeInfo::Tuple(type_parameters), TypeInfo::Tuple(type_arguments)) => { + insert_type_parameters_with_type_arguments( + type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(), + type_arguments.iter().map(|x| x.type_id).collect::>(), + ) + } + (TypeInfo::Array(superset_type, _), TypeInfo::Array(subset_type, _)) => { + vec![(superset_type, subset_type)] + } + ( + TypeInfo::Storage { + fields: type_parameters, + }, + TypeInfo::Storage { + fields: type_arguments, + }, + ) => { + let type_parameters = type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(); + let type_arguments = type_arguments.iter().map(|x| x.type_id).collect::>(); + insert_type_parameters_with_type_arguments(type_parameters, type_arguments) + } + (TypeInfo::Unknown, TypeInfo::Unknown) + | (TypeInfo::Boolean, TypeInfo::Boolean) + | (TypeInfo::SelfType, TypeInfo::SelfType) + | (TypeInfo::Byte, TypeInfo::Byte) + | (TypeInfo::B256, TypeInfo::B256) + | (TypeInfo::Numeric, TypeInfo::Numeric) + | (TypeInfo::Contract, TypeInfo::Contract) + | (TypeInfo::ErrorRecovery, TypeInfo::ErrorRecovery) + | (TypeInfo::Str(_), TypeInfo::Str(_)) + | (TypeInfo::UnsignedInteger(_), TypeInfo::UnsignedInteger(_)) + | (TypeInfo::ContractCaller { .. }, TypeInfo::ContractCaller { .. }) => vec![], + _ => vec![], + } +} + +fn insert_type_parameters_with_type_arguments( + type_parameters: Vec, + type_arguments: Vec, +) -> TypeMapping { + type_parameters + .into_iter() + .zip(type_arguments.into_iter()) + .collect::>() +} diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index ee8a03a4dfc..d7ff8648e86 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -201,7 +201,7 @@ fn parse_test_toml(path: &Path) -> Result { .map_err(|e| format!("Failed to parse: {e}"))?; if !toml_content.is_table() { - Err("Malformed test description.".to_owned())?; + return Err("Malformed test description.".to_owned()); } let category = toml_content diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/.gitignore b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.lock new file mode 100644 index 00000000000..39a70d597e7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-C569FB5C26218A1B' +dependencies = [] + +[[package]] +name = 'impl_with_semantic_type_constraints' +source = 'root' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-C569FB5C26218A1B' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.toml new file mode 100644 index 00000000000..e21f59c2882 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "impl_with_semantic_type_constraints" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/src/main.sw new file mode 100644 index 00000000000..bfdc1398d1a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/src/main.sw @@ -0,0 +1,54 @@ +script; + +struct DoubleIdentity { + first: T, + second: F, +} + +impl DoubleIdentity { + fn get_first(self) -> T { + self.first + } +} + +impl DoubleIdentity { + fn get_second(self) -> F { + self.second + } +} + +impl DoubleIdentity { + fn add(self) -> u8 { + self.first + self.second + } +} + +fn main() { + let a = DoubleIdentity { + first: 0u8, + second: 1u8 + }; + let b = DoubleIdentity { + first: true, + second: false, + }; + let c = DoubleIdentity { + first: 0u64, + second: "hi" + }; + + let d = a.get_first(); + let e = a.get_second(); + let f = a.add(); + + let g = b.get_first(); + let h = b.get_second(); + // should fail + let i = b.add(); + + // should fail + let j = c.get_first(); + let k = c.get_second(); + // should fail + let l = c.add(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/test.toml new file mode 100644 index 00000000000..eb3634e032c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/test.toml @@ -0,0 +1 @@ +category = "fail" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/abi_with_tuples/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/abi_with_tuples/Forc.lock index ccbfdf27499..b94f93e5b57 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/abi_with_tuples/Forc.lock +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/abi_with_tuples/Forc.lock @@ -1,3 +1,9 @@ [[package]] -name = 'auth_testing_abi' +name = 'abi_with_tuples' +source = 'root' +dependencies = ['core'] + +[[package]] +name = 'core' +source = 'path+from-root-D1CDC58B8291BA8B' dependencies = [] diff --git a/test/src/sdk-harness/test_artifacts/methods_abi/Forc.lock b/test/src/sdk-harness/test_artifacts/methods_abi/Forc.lock new file mode 100644 index 00000000000..1fb6b0979c0 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_abi/Forc.lock @@ -0,0 +1,4 @@ +[[package]] +name = 'methods_abi' +source = 'root' +dependencies = [] diff --git a/test/src/sdk-harness/test_artifacts/methods_abi/Forc.toml b/test/src/sdk-harness/test_artifacts/methods_abi/Forc.toml new file mode 100644 index 00000000000..d4c28b1b93a --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_abi/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "methods_abi" +implicit-std = false diff --git a/test/src/sdk-harness/test_artifacts/methods_abi/src/main.sw b/test/src/sdk-harness/test_artifacts/methods_abi/src/main.sw new file mode 100644 index 00000000000..288efc7153f --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_abi/src/main.sw @@ -0,0 +1,6 @@ +library methods_abi; + +abi MethodsContract { + #[storage(read, write)] + fn test_function() -> bool; +} diff --git a/test/src/sdk-harness/test_artifacts/methods_contract/Forc.lock b/test/src/sdk-harness/test_artifacts/methods_contract/Forc.lock new file mode 100644 index 00000000000..4faa448e08d --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_contract/Forc.lock @@ -0,0 +1,22 @@ +[[package]] +name = 'core' +source = 'path+from-root-051EC93A63B1E039' +dependencies = [] + +[[package]] +name = 'methods_abi' +source = 'path+from-root-051EC93A63B1E039' +dependencies = [] + +[[package]] +name = 'methods_contract' +source = 'root' +dependencies = [ + 'methods_abi', + 'std', +] + +[[package]] +name = 'std' +source = 'path+from-root-051EC93A63B1E039' +dependencies = ['core'] diff --git a/test/src/sdk-harness/test_artifacts/methods_contract/Forc.toml b/test/src/sdk-harness/test_artifacts/methods_contract/Forc.toml new file mode 100644 index 00000000000..19f2d059625 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_contract/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "methods_contract" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } +methods_abi = { path = "../methods_abi" } diff --git a/test/src/sdk-harness/test_artifacts/methods_contract/src/main.sw b/test/src/sdk-harness/test_artifacts/methods_contract/src/main.sw new file mode 100644 index 00000000000..0ba065a12c1 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_contract/src/main.sw @@ -0,0 +1,41 @@ +contract; + +use methods_abi::MethodsContract; + +use std::result::*; +use std::identity::*; +use std::chain::auth::*; +use std::option::*; +use std::assert::*; + +fn bogus() -> Identity { + let sender = msg_sender(); + sender.unwrap() +} + +fn bogus2() -> Identity { + msg_sender().unwrap() +} + +struct MyStruct { + int_option: Option +} + +storage { + stored_struct: MyStruct, +} + +impl MethodsContract for Contract { + #[storage(read, write)] + fn test_function() -> bool { + let identity = bogus(); + let identity2 = bogus2(); + storage.stored_struct = MyStruct { + int_option: Option::Some(99u64) + }; + let stored_struct = storage.stored_struct; + let stored_option_in_struct = stored_struct.int_option; + require(stored_option_in_struct.is_some(), "Error"); + true + } +} \ No newline at end of file diff --git a/test/src/sdk-harness/test_projects/harness.rs b/test/src/sdk-harness/test_projects/harness.rs index 4469718f340..e55a53d5e1f 100644 --- a/test/src/sdk-harness/test_projects/harness.rs +++ b/test/src/sdk-harness/test_projects/harness.rs @@ -13,3 +13,4 @@ mod storage; mod storage_map; mod token_ops; mod tx_fields; +mod methods; diff --git a/test/src/sdk-harness/test_projects/methods/mod.rs b/test/src/sdk-harness/test_projects/methods/mod.rs new file mode 100644 index 00000000000..1ed7b29d007 --- /dev/null +++ b/test/src/sdk-harness/test_projects/methods/mod.rs @@ -0,0 +1,33 @@ +use fuels::prelude::*; +use fuels::signers::wallet::Wallet; +use fuels::tx::{default_parameters::MAX_GAS_PER_TX, ContractId}; +use fuels_abigen_macro::abigen; + +abigen!( + MethodsContract, + "test_artifacts/methods_contract/out/debug/methods_contract-abi.json", +); + +#[tokio::test] +async fn run_methods_test() { + let wallet = launch_provider_and_get_single_wallet().await; + let instance = get_methods_instance(wallet).await; + + let result = instance + .test_function() + .call() + .await + .unwrap(); + assert_eq!(result.value, true); +} + +async fn get_methods_instance(wallet: Wallet) -> MethodsContract { + let id = Contract::deploy( + "test_artifacts/methods_contract/out/debug/methods_contract.bin", + &wallet, + TxParameters::default(), + ) + .await + .unwrap(); + MethodsContract::new(id.to_string(), wallet) +}