Instrumentation for `immutable`
Closed this issue · 6 comments
If state variable annotation present on a contract, scribble
generates an auxiliary function with the original constructor body and calls it in the the constructor. That newly generated function is preceded/succeeded by scribble
generated code, according to the annotations. However, if an immutable
is set in the constructor, the newly generated function tries to set the immutable
inside its body which is not allowed by solidity. When the contract is being compiled after arming, the following error can be observed:
[⠒] Compiling...
[⠑] Compiling 1 files with 0.8.20
[⠘] Solc 0.8.20 finished in 9.39ms
Error:
Compiler run failed:
Error (1581): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.
--> src/Example.sol:21:9:
|
21 | x = 5;
| ^
Example contract:
/// #if_succeeds x == 5;
contract Example {
uint private immutable x;
constructor() {
x = 5;
}
}
After arming:
/// #if_succeeds x == 5;
contract Example {
uint private immutable x;
constructor() {
_original_Example_constructor();
unchecked {
if (!(x == 5)) {
emit __ScribbleUtilsLib__14.AssertionFailed("000389:0066:000 0: ");
assert(false);
}
}
}
function _original_Example_constructor() private {
x = 5;
}
}
library __ScribbleUtilsLib__14 {
event AssertionFailed(string message);
event AssertionFailedData(int eventId, bytes encodingData);
function assertionFailed(string memory arg_0) internal {
emit AssertionFailed(arg_0);
}
function assertionFailedData(int arg_0, bytes memory arg_1) internal {
emit AssertionFailedData(arg_0, arg_1);
}
function isInContract() internal returns (bool res) {
assembly {
res := sload(0x5f0b92cf9616afdee4f4136f66393f1343b027f01be893fa569eb2e2b667a40c)
}
}
function setInContract(bool v) internal {
assembly {
sstore(0x5f0b92cf9616afdee4f4136f66393f1343b027f01be893fa569eb2e2b667a40c, v)
}
}
}
Scribble version used: 0.6.23
Hello there. I took some time to look into it and figure out what is happening here. Here are few clues:
- For each of the contracts, Scribble finds annotations, that are attached in their docstring. Particularly interesting is following:
Lines 370 to 377 in ccc74a7
- There is an interesting comment here, that is stating that we should avoid instrumenting functions for a set circumstances:
Lines 395 to 411 in ccc74a7
contract
s. - Some lines below, we have following section:
Lines 436 to 441 in ccc74a7
instrumentFunction
, that is causing function to beinterpose
d.
I guess allowedFuncProp
have an entry, that forces constructor
to be processed by instrumentFunction
. From the inline comments I'm not sure if it is intended behavior.
I'm currently consider options to introduce a fix.
Ofc, that seems to be a bug, as instrumented code can not be compiled.
I also think, that issue can be related to https://docs.scribble.codes/language/annotations/lang-function-specifications#contract-level-if_succeeds-annotations - if_succeeds
somehow causing constructor
to be instrumented, however inline docs state that it should not happen. Im still investigating and gathering more clues to consider a proper fix.
Easy (and probably not the best) solution would be to process all of the state variables and make them mutable. However, I believe there should be a better solution.
While we are finishing discussion and review for the fix, anyone who hits the issue could use tweaked Scribble from branch rewrite-immutable-state-vars
of PR #239.
Steps to manually install Scribble from the repository or specific branch are described in README. Just checkout specific branch before installing.
thanks for prompt resolution!
@kasperpawlowski Hey there. New Scribble release is out. It should no longer have an issue when installed from NPM. Give it a try. Thanks for the feedback.