argotorg / argotorg/solidity

Wrong evaluation order of arguments to parent constructors

Open
#10,480 18 comments 0 reactions 0 assignees View on GitHub
breaking change :warning: language design :rage4: low impact medium effort
Dominant language
C++
Stars
25.7k
Forks
6.2k
Avg merge
1d 11h
Merged PRs (30d)
21

Description

## Description

The arguments to parent constructors are evaluated before all parent constructors.

This is a problem when those arguments are (non-pure) function calls, because the function is evaluated before its defining contract has been initialized.

## Steps to Reproduce

```solidity
pragma solidity 0.7.5;

contract Foo {
event FooConstructor();
event FooGetValue();

uint value;

constructor (uint _value) {
emit FooConstructor();
value = _value;
}

function getValue() public returns (uint) {
emit FooGetValue();
return value;
}
}

contract Bar {
event BarConstructor(uint value);

constructor (uint value) {
emit BarConstructor(value);
}
}

contract Child is Foo, Bar {
constructor () Foo(5) Bar(Foo.getValue()) {
}
}
```

Deploying `Child` currently emits the following sequence of events:

1. `FooGetValue()`
2. `FooConstructor()`
3. BarConstructor(value: 0)

Note: The same thing happens if you add a constraint on linearization such as `Bar is Foo`.

### Expected Behavior

Deploying `Child` should emit the following:

1. `FooConstructor()`
2. `FooGetValue()`
3. BarConstructor(value: 5)

That is, `Foo` is first initialized by calling its constructor, only then `Foo.getValue()` is evaluated, and its value can then be used as argument to `Bar`'s constructor.

### Specification

In general, I would expect the following to happen during contract construction:

Given the linearization of a contract's inheritance `C_0`, ..., `C_n` (from base to most derived), and the argument expressions for each constructor `e_i1`, ..., `e_im` (`m` different for every `C_i`)

- For each `C_i` = `C_0`, ..., `C_n` in order
- Evaluate all of `e_i1`, ..., `e_im` (I believe this order is not specified, as for function calls in general)
- Evaluate the constructor `C_i(v_i1, ..., v_im)` (where `v_ik` are the results of the previous step)

Every non-pure contract function call in the expression `e_ik` must be of a function defined in one of `C_0`, ..., `C_(i-1)`, that is only contracts that have been initialized at the point it is evaluated. (Note: this check can be relaxed somewhat but I decided to keep the simpler version.)

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the Solidity 0.7.5 Child/Foo/Bar example and trace constructor argument evaluation against base-constructor initialization and inheritance linearization. The work is done when the emitted events follow the expected FooConstructor, FooGetValue, and BarConstructor(value: 5) order, with the stated restrictions on non-pure calls enforced.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, solidity
Domain
blockchain, compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.