Ambiguous Evaluation Order¶
Statements that leave the sequence of variable evaluation open produce inconsistent outcomes. The Solidity documentation states:
The evaluation order of expressions is not specified (more formally, the order in which the children of one node in the expression tree are evaluated is not specified, but they are of course evaluated before the node itself). It is only guaranteed that statements are executed in order and short-circuiting for boolean expressions is done.
Expressions in Solidity are therefore not evaluated in a fully deterministic way. The output is deterministic for a particular compiler version, but nothing guarantees it across versions. The ambiguity becomes a problem when multiple functions independently affect shared stateful objects and are invoked within the same statement, because the final outcome of the statement varies with the sequence in which those functions are evaluated. Mikhail Vladimirov gives an example on the Ethereum StackExchange:
1 2 3 4 5 | |
Instructions like addmod and mulmod, as well as events, diverge from the standard pattern of evaluation order, so code incorporating them yields outcomes that depend on the compiler.
The EthTrust Security Levels Specification underlines the risk with a second case. Where functions g and h manipulate any variable that the outcome of function f relies on, an invocation like f(g(x), h(y)) returns no consistent result.
The remedy is to store intermediate results in temporary variables, which fixes the evaluation order regardless of compiler variations or complex functional interactions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
The deterministicResult function sets the evaluation order explicitly by storing the result of firstTransform in the temporary variable firstResult and passing that into secondTransform. The sequence of execution is then predictable regardless of the side effects inside each transformation.