Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

immutable state variables are not considered pure in inheritance #9554

Closed
elenadimitrova opened this issue Jul 31, 2020 · 17 comments · Fixed by #10240
Closed

immutable state variables are not considered pure in inheritance #9554

elenadimitrova opened this issue Jul 31, 2020 · 17 comments · Fixed by #10240
Projects

Comments

@elenadimitrova
Copy link
Collaborator

With Solidity 0.7.0 a pure getter implemented via an immutable state variable throws TypeError: Overriding public state variable changes state mutability from "pure" to "view”.

interface ISample {
    function WETH() external pure returns (address);
}

contract Sample is ISample {
    address public immutable override WETH;
}

This is probably coming from the "Disallow public state variables overwriting pure functions" breaking change however immutables should be excluded from this as they can be considered pure. I tested with constant instead of immutable and this works okay when exposed through pure.

@chriseth
Copy link
Contributor

While I said in the chat that constant and immutable should have the same mutability, maybe we should think a bit harder about this - at least the value of an immutable variable can change once across its lifetime is not a compile-time constant.

@chriseth
Copy link
Contributor

Although I of course agree that this is very annoying. If we conclude that they should be view in general, maybe we can at least make them pure if their initial value is a constant.

@chriseth chriseth added this to New issues in Solidity via automation Aug 26, 2020
@chriseth chriseth moved this from New issues to Design Backlog in Solidity Aug 26, 2020
@deluca-mike
Copy link

deluca-mike commented Nov 2, 2020

There seems to be a larger issue here. The compiler should not throw an error if a pure function references an immutable variable, since The contract creation code generated by the compiler will modify the contract’s runtime code before it is returned by replacing all references to immutables by the values assigned to the them.

So either immutable variables result in inline constants (literals), or they don't.

@bshastry
Copy link
Contributor

bshastry commented Nov 3, 2020

May be related. Immutables could be used as lambda functions function aliases. Currently, since immutables are view, one cannot use them as aliases for pure free functions. The example below does not work if view becomes pure although the underlying functions squared, cubed, and proxy are pure.

function squared(uint x) view returns (uint) { return x ** 2; }
function cubed(uint x) view returns (uint) { return x ** 3; }
function proxy(function (uint) view returns (uint) func, uint input) view returns (uint) { return func(input); }
contract C {
        function (function (uint) view returns (uint), uint) view returns (uint) immutable proxyImmutable = proxy;
        function foo() public view returns (uint) {
                return proxyImmutable(squared, 3) + proxyImmutable(cubed, 3);
        }
}
// ====
// compileViaYul: also
// ----
// foo() -> 0x24

I'm not sure if there is a real use-case here though i.e., using immutables as higher-order functions.

@deluca-mike
Copy link

deluca-mike commented Nov 5, 2020

@bshastry But the documentation I quoted gives the impression that immutable acts as an inline replacement, sort of like a preprocessor. (i.e. replacing all bytecode instances of my_immutable_var with whatever value is provided at deploy-time. Which is why the documentation further alludes to the fact that the deployed bytecode is not expected to match the compiled bytecode before deploying.

This is important if you are comparing the runtime code generated by the compiler with the one actually stored in the blockchain.

Solidity automation moved this from Design Backlog to Done Nov 10, 2020
@elenadimitrova
Copy link
Collaborator Author

elenadimitrova commented Dec 29, 2020

Has this been implemented for immutable variables with literal number values only? I am still getting an error compiling with 0.7.6 testing with an address type value.

@chriseth
Copy link
Contributor

I'm sorry, but I fear that addresses do not work, only numbers that are not addresses.

@CodeForcer
Copy link

Why is this issue closed? This still occurs in Solidity 0.8.4

@chriseth
Copy link
Contributor

chriseth commented Jun 7, 2021

@CodeForcer we concluded that not all immutables can be pure, because their value can depend on constructor input. Can you give a specific code example?

@deluca-mike
Copy link

deluca-mike commented Nov 3, 2021

@chriseth What does pure mean? If it means "does not read from storage" then immutables set in a constructor result in pure getters, by definition.

For example, in

contract Token  {
    uint256 public immutable totalSupply;

    constructor(uint256 totalSupply_) {
        totalSupply = totalSupply_;
    }
}

the function totalSupply() external returns (uint256); is absolutely pure. It will not read from storage.

Can you give an example where reading an immutable variable reads storage? Or are you suggesting that the above definition of pure is incorrect?

@chriseth
Copy link
Contributor

chriseth commented Nov 3, 2021

pure implies "can be computed without knowing only the source code". It is not equivalent to this statement because it is a syntactic and not a semantic property.
An example why immutables cannot be pure:

contract C {
  uint immutable x = block.number;
}

@deluca-mike
Copy link

deluca-mike commented Nov 3, 2021

@chriseth This is the first I hear of that definition.

"Functions can be declared pure in which case they promise not to read from or modify the state." from https://docs.soliditylang.org/en/v0.8.9/contracts.html?highlight=pure#pure-functions

And this has been defined this way for quite a long time.

I am not sure why what is in the pre-deployed source code matters. Solidity interfaces, as used by off-chain clients and, more importantly, by other contracts, just care that pure will not read from storage.

Whatever the bytecode looks like before it appears on-chain, at an address, seems completely irrelevant.

Since the contract only exists post-constructor, the state mutability is implied to be defined for such a deployed contract.

@chriseth
Copy link
Contributor

chriseth commented Nov 4, 2021

It seems this was only part of the initial issue description and not added to the documentation. After all, this only became relevant after we introduced immutable: #992

@chriseth
Copy link
Contributor

chriseth commented Nov 8, 2021

Added information to the documentation: #12256

@deluca-mike
Copy link

Immutables end up as in-line litterals. The on-chain byte code and cost for the following foo functions are identical:

contract ImmutableTest1 {
    function foo() external pure returns (uint256) { return uint256(4); }
}

contract ImmutableTest2 {
    uint256 internal constant _foo = uint256(4);
    function foo() external pure returns (uint256) { return _foo; }
}

contract ImmutableTest3 {
    uint256 internal immutable _foo;
    constructor() { _foo = uint256(4); }
    function foo() external view returns (uint256) { return _foo; }
}

contract ImmutableTest4 {
    uint256 internal immutable _foo;
    constructor(uint256 foo_) { _foo = foo_; }
    function foo() external view returns (uint256) { return _foo; }
}

There 4 foo functions are as pure as anything else that we have defined as pure in solidity.

In #992, you state:

"the keyword pure is introduced for functions, they are view functions with the additional restriction that their value only depends on the function arguments. This means they cannot use SSTORE, SLOAD, cannot send or receive ether, cannot use msg or block and can only call other pure functions."

Thus, by that definition, the above 4 contracts all have pure foo functions.

It hurts no one to allow pure functions to contain immutables, and only results in objective technical accuracy, rather than subjective semantics.

"pure implies "can be computed without knowing only the source code"."

Can you point me to where this is defined anywhere in Solidity/EVM literature or among Solidity developers? This is honestly the first time I ever hear of this.

Pure implies that the output is dependant without any state, other than the functions themselves.

@chriseth
Copy link
Contributor

Pure implies that the output is dependant without any state, other than the functions themselves.

Exactly that is what it is. In the example the value of the immutable depends on the blockchain state. A copy of the contract deployed at a different time will have a different value. The idea of a pure function is that if you compile it again and put it in a different contract, it will return the same value, which it does not if it accesses the immutable in the example above.

@wighawag
Copy link

Feel like there should be something in-between view and pure

it would be useful to have a another modifier that ensure these function only access bytecode and nothing else.

the view modifier is too general as even when you intend to use immutables as constants in your code, you do not get error when reading storage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Solidity
  
Done
Development

Successfully merging a pull request may close this issue.

6 participants