diff --git a/sway-core/src/error.rs b/sway-core/src/error.rs index d1bf6a55d10..c7b14a862fd 100644 --- a/sway-core/src/error.rs +++ b/sway-core/src/error.rs @@ -514,6 +514,11 @@ pub enum CompileError { variable_name: Ident, span: Span, }, + #[error( + "Cannot call associated function \"{fn_name}\" as method call. Use associated function \ + syntax instead." + )] + AssociatedFunctionCalledAsMethod { fn_name: Ident, span: Span }, #[error( "Generic type \"{name}\" is not in scope. Perhaps you meant to specify type parameters in \ the function signature? For example: \n`fn \ @@ -1048,6 +1053,7 @@ impl Spanned for CompileError { ReassignmentToNonVariable { span, .. } => span.clone(), AssignmentToNonMutable { name } => name.span(), MethodRequiresMutableSelf { span, .. } => span.clone(), + AssociatedFunctionCalledAsMethod { span, .. } => span.clone(), TypeParameterNotInTypeScope { span, .. } => span.clone(), MultipleImmediates(span) => span.clone(), MismatchedTypeInTrait { span, .. } => span.clone(), diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/function/function_parameter.rs b/sway-core/src/semantic_analysis/ast_node/declaration/function/function_parameter.rs index 7d917d0552e..ccc3eb9325e 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/function/function_parameter.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/function/function_parameter.rs @@ -36,6 +36,10 @@ impl CopyTypes for TypedFunctionParameter { } impl TypedFunctionParameter { + pub fn is_self(&self) -> bool { + self.name.as_str() == "self" + } + pub(crate) fn type_check( mut ctx: TypeCheckContext, parameter: FunctionParameter, diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 1a03e347b60..27f9a354920 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -158,6 +158,25 @@ pub(crate) fn type_check_method_application( } }; + // If this function is being called with method call syntax, a.b(c), + // then make sure the first parameter is self, else issue an error. + if !method.is_contract_call { + if let MethodName::FromModule { ref method_name } = method_name { + let is_first_param_self = method + .parameters + .get(0) + .map(|f| f.is_self()) + .unwrap_or_default(); + if !is_first_param_self { + errors.push(CompileError::AssociatedFunctionCalledAsMethod { + fn_name: method_name.clone(), + span, + }); + return err(warnings, errors); + } + } + } + // type check all of the arguments against the parameters in the method declaration for (arg, param) in args_buf.iter().zip(method.parameters.iter()) { // if the return type cannot be cast into the annotation type then it is a type error @@ -249,6 +268,14 @@ pub(crate) fn type_check_method_application( None }; + if method.parameters.is_empty() && !method.is_contract_call { + errors.push(CompileError::AssociatedFunctionCalledAsMethod { + fn_name: method_name, + span, + }); + return err(warnings, errors); + } + let exp = check!( instantiate_function_application_simple( CallPath { diff --git a/sway-lib-std/src/address.sw b/sway-lib-std/src/address.sw index 0eedbb3b90f..a822a8badd2 100644 --- a/sway-lib-std/src/address.sw +++ b/sway-lib-std/src/address.sw @@ -17,10 +17,7 @@ impl core::ops::Eq for Address { pub trait From { fn from(b: b256) -> Self; -} { - fn into(addr: Address) -> b256 { - addr.value - } + fn into(self) -> b256; } /// Functions for casting between the b256 and Address types. @@ -30,4 +27,8 @@ impl From for Address { value: bits, } } + + fn into(self) -> b256 { + self.value + } } diff --git a/sway-lib-std/src/contract_id.sw b/sway-lib-std/src/contract_id.sw index 5eedc46edc7..22c226e2502 100644 --- a/sway-lib-std/src/contract_id.sw +++ b/sway-lib-std/src/contract_id.sw @@ -18,10 +18,7 @@ impl core::ops::Eq for ContractId { // TODO: make this a generic trait. tracked here: https://github.com/FuelLabs/sway-lib-std/issues/58 pub trait From { fn from(b: b256) -> Self; -} { - fn into(id: ContractId) -> b256 { - id.value - } + fn into(self) -> b256; } /// Functions for casting between the b256 and ContractId types. @@ -31,4 +28,8 @@ impl From for ContractId { value: bits, } } + + fn into(self) -> b256 { + self.value + } } diff --git a/sway-lib-std/src/u128.sw b/sway-lib-std/src/u128.sw index 82a7ce2dfaf..3f257743992 100644 --- a/sway-lib-std/src/u128.sw +++ b/sway-lib-std/src/u128.sw @@ -19,10 +19,7 @@ pub enum U128Error { pub trait From { /// Function for creating U128 from its u64 components. pub fn from(upper: u64, lower: u64) -> Self; -} { - fn into(val: U128) -> (u64, u64) { - (val.upper, val.lower) - } + fn into(self) -> (u64, u64); } impl From for U128 { @@ -31,6 +28,10 @@ impl From for U128 { upper, lower, } } + + fn into(self) -> (u64, u64) { + (self.upper, self.lower) + } } impl core::ops::Eq for U128 { diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 5d9547c3deb..3cd9a9598e2 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -19,11 +19,7 @@ pub enum U256Error { pub trait From { /// Function for creating a U256 from its u64 components. pub fn from(a: u64, b: u64, c: u64, d: u64) -> Self; -} { - /// Function for extracting 4 u64s from a U256. - fn into(val: U256) -> (u64, u64, u64, u64) { - (val.a, val.b, val.c, val.d) - } + fn into(self) -> (u64, u64, u64, u64); } impl From for U256 { @@ -32,6 +28,11 @@ impl From for U256 { a, b, c, d, } } + + /// Function for extracting 4 u64s from a U256. + fn into(self) -> (u64, u64, u64, u64) { + (self.a, self.b, self.c, self.d) + } } impl core::ops::Eq for U256 { diff --git a/sway-lib-std/src/vm/evm/evm_address.sw b/sway-lib-std/src/vm/evm/evm_address.sw index b93fe3f3c9b..9861d86e9bc 100644 --- a/sway-lib-std/src/vm/evm/evm_address.sw +++ b/sway-lib-std/src/vm/evm/evm_address.sw @@ -18,10 +18,7 @@ impl core::ops::Eq for EvmAddress { pub trait From { fn from(b: b256) -> Self; -} { - fn into(addr: EvmAddress) -> b256 { - addr.value - } + fn into(self) -> b256; } /// Functions for casting between the b256 and Address types. @@ -36,4 +33,8 @@ impl From for EvmAddress { value: bits, } } + + fn into(self) -> b256 { + self.value + } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/Forc.lock new file mode 100644 index 00000000000..ebfd522afec --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/Forc.lock @@ -0,0 +1,9 @@ +[[package]] +name = 'associated_fn_as_method_call' +source = 'root' +dependencies = ['core'] + +[[package]] +name = 'core' +source = 'path+from-root-9B28D193F31217CF' +dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/Forc.toml new file mode 100644 index 00000000000..2861484b8d0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "associated_fn_as_method_call" +entry = "main.sw" + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/json_abi_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/json_abi_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/src/main.sw new file mode 100644 index 00000000000..f522f7925f8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/src/main.sw @@ -0,0 +1,13 @@ +script; + +struct Bar {} + +impl Bar { + fn associated() {} +} + +fn main() -> u64 { + let bar = Bar {}; + bar.associated(); + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/test.toml new file mode 100644 index 00000000000..60be2a8ff67 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_as_method_call/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: bar.associated() +# nextln: $()Cannot call associated function "associated" as method call. Use associated function syntax instead. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/Forc.lock new file mode 100644 index 00000000000..a60cf5c41ef --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/Forc.lock @@ -0,0 +1,9 @@ +[[package]] +name = 'associated_fn_params_as_method_call' +source = 'root' +dependencies = ['core'] + +[[package]] +name = 'core' +source = 'path+from-root-6CC50AAEABFFA120' +dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/Forc.toml new file mode 100644 index 00000000000..f5a36fe0695 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "associated_fn_params_as_method_call" +entry = "main.sw" + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/json_abi_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/json_abi_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/src/main.sw new file mode 100644 index 00000000000..f522f7925f8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/src/main.sw @@ -0,0 +1,13 @@ +script; + +struct Bar {} + +impl Bar { + fn associated() {} +} + +fn main() -> u64 { + let bar = Bar {}; + bar.associated(); + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/test.toml new file mode 100644 index 00000000000..60be2a8ff67 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/associated_fn_params_as_method_call/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: bar.associated() +# nextln: $()Cannot call associated function "associated" as method call. Use associated function syntax instead.