tact icon indicating copy to clipboard operation
tact copied to clipboard

`self` in traits and contracts

Open anton-trunov opened this issue 6 months ago • 5 comments

Problem statement

Take a look at the following snippet.

 import "@stdlib/deploy";

trait Trait {
    id: Int;

    get fun contractData(): Trait {     // typechecker thinks it's ok here
        return self;
    }
}

contract Contract with Deployable, Trait {
    id: Int as uint32 = 0;
}

Its compilation fail with the following error message:

💼 Compiling project test-trait ...
Tact compilation failed
Error: test-trait.tact:5:9: Type mismatch: "Contract" is not assignable to "Trait"
Line 5, col 9:
  4 |     get fun contractData(): Trait {
> 5 |         return self;
              ^~~~~~~~~~~~
  6 |     }

Inside the Trait trait the self variable has type Trait, but then contractData gets re-typechecked as a contract getter inside the Contract contract where self has type Contract.

Possible solutions

First of all, let me note that disallowing re-typechecking is not a solution, since during code generation we need the full type information.

Forbid using self as a standalone variable

In other words, passing self around, returning it from functions, including getters (see also a related issue: #579) would be prohibited. Only expressions of the form self.Var would be allowed in this case.

This restriction's scope could be limited to traits only, so a whole-contract state getter featured in #579 is still possible.

Introduce Self type variable

So self: Self in any context (including mutating methods). This would allow to generalize the type of the problematic getter as follows:

trait Trait {
    id: Int;

    get fun contractData(): Self {  // `Self` means `Trait` here 
        return self;
    }
}

But when the getter gets inserted in the contract scope, the contractData's result type will be Contract.

This solution would support to implement a trait that allows easier debugging by providing the whole contract state:

trait ContractStateGetter {
    get fun contractState(): Self { return self }
}

which could be included automatically when the debug config option is set to true.

anton-trunov avatar Aug 02 '24 14:08 anton-trunov