Skip to content

Commit

Permalink
Check for calling of associated functions as method calls.
Browse files Browse the repository at this point in the history
This adds a new error and checking for calls of associated functions
using method call syntax.

This means for code like the following:

```
struct Bar {}

impl Bar {
  fn associated() {}
}

fn main() -> u64 {
  let bar = Bar {};
  bar.associated();
  0
}
```

we now emit an error:

```
error
  --> main.sw:11:3
   |
 9 |
10 |   let bar = Bar {};
11 |   bar.associated();
|   ^^^^^^^^^^^^^^^^ Cannot call associated function "associated" as
method call. Use associated function syntax instead.
12 |   0
13 | }
   |
```

This brings us closer to Rust behaviour, which also checks for this and
only allows functions taking a self to be called with method call
syntax.

It adds a few more tests for this and also updates some standard library
trait methods to take self so they work with the new check.
  • Loading branch information
tritao committed Jul 4, 2022
1 parent 40deb20 commit 3ca7651
Show file tree
Hide file tree
Showing 18 changed files with 111 additions and 21 deletions.
6 changes: 6 additions & 0 deletions sway-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,11 @@ pub enum CompileError {
variable_name: Ident,
span: Span,
},
#[error(
"Cannot call associated function \"{fn_name}\" as a method. 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 \
Expand Down Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,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);
}
}
}

// Validate mutability of self. Check that the variable that the method is called on is mutable
// _if_ the method requires mutable self.
if let (
Expand Down
9 changes: 5 additions & 4 deletions sway-lib-std/src/address.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -30,4 +27,8 @@ impl From for Address {
value: bits,
}
}

fn into(self) -> b256 {
self.value
}
}
9 changes: 5 additions & 4 deletions sway-lib-std/src/contract_id.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -31,4 +28,8 @@ impl From for ContractId {
value: bits,
}
}

fn into(self) -> b256 {
self.value
}
}
9 changes: 5 additions & 4 deletions sway-lib-std/src/u128.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
11 changes: 6 additions & 5 deletions sway-lib-std/src/u256.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
9 changes: 5 additions & 4 deletions sway-lib-std/src/vm/evm/evm_address.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -36,4 +33,8 @@ impl From for EvmAddress {
value: bits,
}
}

fn into(self) -> b256 {
self.value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'associated_fn_as_method_call'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
license = "Apache-2.0"
name = "associated_fn_as_method_call"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
script;

struct Bar {}

impl Bar {
fn associated() {}
}

fn main() -> u64 {
let bar = Bar {};
bar.associated();
0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "fail"

# check: bar.associated()
# nextln: $()Cannot call associated function "associated" as a method. Use associated function syntax instead.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'associated_fn_params_as_method_call'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
license = "Apache-2.0"
name = "associated_fn_params_as_method_call"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
script;

struct Bar {}

impl Bar {
fn associated() {}
}

fn main() -> u64 {
let bar = Bar {};
bar.associated();
0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "fail"

# check: bar.associated()
# nextln: $()Cannot call associated function "associated" as a method. Use associated function syntax instead.

0 comments on commit 3ca7651

Please sign in to comment.