Skip to content

Commit

Permalink
Fixes method application self mutability check. (FuelLabs#4538)
Browse files Browse the repository at this point in the history
## Description
We now check method application self parameters mutability when the used
arguments is a `StructFieldAccess`. When a `StructFieldAccess` is used
to call a method application with a mutable self we now make sure that
the `StructFieldAccess` prefix variable is also mutable.

Closes FuelLabs#4408

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
esdrubal authored May 4, 2023
1 parent bbe746d commit 9f59f53
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -180,43 +180,69 @@ pub(crate) fn type_check_method_application(

// Validate mutability of self. Check that the variable that the method is called on is mutable
// _if_ the method requires mutable self.
fn mutability_check(
ctx: &TypeCheckContext,
method_name_binding: &TypeBinding<MethodName>,
span: &Span,
exp: &ty::TyExpressionVariant,
) -> CompileResult<()> {
let mut warnings = vec![];
let mut errors = vec![];

match exp {
ty::TyExpressionVariant::VariableExpression { name, .. } => {
let unknown_decl = check!(
ctx.namespace.resolve_symbol(name).cloned(),
return err(warnings, errors),
warnings,
errors
);

let is_decl_mutable = match unknown_decl {
ty::TyDecl::ConstantDecl { .. } => false,
_ => {
let variable_decl = check!(
unknown_decl.expect_variable().cloned(),
return err(warnings, errors),
warnings,
errors
);
variable_decl.mutability.is_mutable()
}
};

if !is_decl_mutable {
errors.push(CompileError::MethodRequiresMutableSelf {
method_name: method_name_binding.inner.easy_name(),
variable_name: name.clone(),
span: span.clone(),
});
return err(warnings, errors);
}

ok((), warnings, errors)
}
ty::TyExpressionVariant::StructFieldAccess { prefix, .. } => {
mutability_check(ctx, method_name_binding, span, &prefix.expression)
}
_ => ok((), warnings, errors),
}
}

if let (
Some(ty::TyExpression {
expression: ty::TyExpressionVariant::VariableExpression { name, .. },
..
expression: exp, ..
}),
Some(ty::TyFunctionParameter { is_mutable, .. }),
) = (args_buf.get(0), method.parameters.get(0))
{
if *is_mutable {
let unknown_decl = check!(
ctx.namespace.resolve_symbol(name).cloned(),
check!(
mutability_check(&ctx, &method_name_binding, &span, exp),
return err(warnings, errors),
warnings,
errors
);

let is_decl_mutable = match unknown_decl {
ty::TyDecl::ConstantDecl { .. } => false,
_ => {
let variable_decl = check!(
unknown_decl.expect_variable().cloned(),
return err(warnings, errors),
warnings,
errors
);
variable_decl.mutability.is_mutable()
}
};

if !is_decl_mutable {
errors.push(CompileError::MethodRequiresMutableSelf {
method_name: method_name_binding.inner.easy_name(),
variable_name: name.clone(),
span,
});
return err(warnings, errors);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,34 @@ struct A {
a: u64,
}

struct B {
a: A,
}

struct C {
b: B,
}

impl A {
fn f(ref mut self) {
self.a = 42;
}
}

impl B {
fn f(self) {
// Expecting error: Cannot call method "f" on variable "self" because "self" is not declared as mutable.
self.a.f();
}
}

impl C {
fn f(self) {
// Expecting error: Cannot call method "f" on variable "self" because "self" is not declared as mutable.
self.b.a.f();
}
}

fn main() -> bool {
let a = A {
a: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
category = "fail"

# check: $()Cannot call method "f" on variable "a" because "a" is not declared as mutable.
# check: $()self.a.f();
# nextln: $()Cannot call method "f" on variable "self" because "self" is not declared as mutable.

# check: $()self.b.a.f();
# nextln: $()Cannot call method "f" on variable "self" because "self" is not declared as mutable.

# check: $()a.f();
# nextln: $()Cannot call method "f" on variable "a" because "a" is not declared as mutable.

0 comments on commit 9f59f53

Please sign in to comment.