Wrong evaluation order of arguments to parent constructors
- 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
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