Allow type inference for Contracts and (some) Structs

Discussing with @paulrberg and @leo on this twitter thread:

Currently, declaration of variables of certain types is rather verbose, especially when it comes to complex types that typically need a constructor (i.e. you have the type there already).

Examples:

contract MyContract {}

function foo() {
    // constructor already tells us the type of the contract
    MyContract _contract = new MyContract();
}

contract HasStruct {
    // again, we already know B is type MyStruct storage
    MyStruct B = MyStruct({
        bar: "baz"
    });
}

I’d like to suggest a better alternative would be type inference for certain types, namely Contracts and Structs declared in contract storage. The compiler should be able to infer the type of the Contract or Struct from the constructor (or struct constructor).

This only would make sense in any situation where a variable is declared at the same time as being initialised.

Examples:

function proposedFoo() {
    // _contract is inferred to be of type MyContract
    _contract = new MyContract();
}

contract ProposedHasStruct {
    // B is inferred to be of type MyStruct storage
    B = MyStruct({bar: "baz"});
}

contract ProposedContract {
    // inferred type MyContract (storage)
    _contract = new MyContract();
}

The above examples reduce verbosity. Although it might seem trivial in a small example like above, extra verbosity is a pain especially in larger codebases and when refactoring. Problem gets worse when tests are also written in solidity so this would be a nice win for developer experience.

Some examples that would NOT work:

  • Structs declared inside function scopes - memory or storage must be explicitly defined so we definitely still need the type.
  • Variables declared but then later initialised

Leo raises a good point that contract inheritance introduces ambiguity into the contract type. My suggestion would therefore be to default to the type of the constructor, then allow for explicit type declarations for parent classes:

contract MyChildContract is MyContract {}

function inferred() {
    // infer child as type MyChildContract in memory
    child = new MyChildContract();
}

function explicit() {
    // allow declaring of parent classes as an override
    MyContract child = new MyChildContract(); 
}

function prohibited() {
    // not allowed - must wrap the address in this scenario
    UnknownContract child = new MyChildContract();
}

Would welcome thoughts, comments criticisms

1 Like

First, this is ambiguous:

_contract = new MyContract();

Currently this is valid code if _contract has already been declared because = can be used in in expressions.

Also, I think that it reduces readability. Now you cannot be sure whether it’s a declaration of a new variable or an assignment without knowing the context.

I think it’s less error-prone when declarations have a dedicated syntax. For the compiler to distinguish them, we’d need for example a keyword that introduces a declaration:

var _contract = new MyContract();

which, incidentally, used to be a feature of the compiler but was removed long ago. Do we want var back? :slight_smile:

Second, I really liked the idea of inline structs, which would already allow initializing them in a DRY way:

MyStruct memory B = {bar: "baz"};

Do we really want to have yet another way to do this? Solidity has a lot of influences from C++ but having a million ways to initialize stuff fortunately wasn’t one of them so far :stuck_out_tongue:

1 Like

Hey @cameel,

Point taken on the initialization keyword. I agree without something like var you’re losing context at initialisation, and at this point you haven’t gained a ton of savings on verbosity. Although, when it comes to refactoring, it’s currently a bit of a pain to rewrite all types. Can’t see bringing back var being a popular option though.

Inline structs as Paul suggested would be a great alternative for structs, no reason to have both initialisation options.

Guess we can park this!

Well, we could call it let since that’s popular nowadays and does not have that bad association :slight_smile: And it would not be as lax as var used to be since the type would still be in the expression. I’m not sure I like it but still, we’re just brainstorming the idea here so happy to hear other opinions :slight_smile:

1 Like