Relax restrictions on immutable initialization

This commit is contained in:
Kamil Śliwak 2023-05-30 16:07:00 +02:00
parent 490b90d0ab
commit dad2bf6472
54 changed files with 178 additions and 396 deletions

View File

@ -2,6 +2,7 @@
Language Features:
* Allow qualified access to events from other contracts.
* Relax restrictions on initialization of immutable variables. Reads and writes may now happen at any point at construction time outside of functions and modifiers. Explicit initialization is no longer mandatory.
Compiler Features:
* Commandline Interface: Add ``--ast-compact-json`` output in assembler mode.

View File

@ -150,7 +150,7 @@ Modifiers
- ``view`` for functions: Disallows modification of state.
- ``payable`` for functions: Allows them to receive Ether together with a call.
- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
- ``immutable`` for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code.
- ``immutable`` for state variables: Allows assignment at construction time and is constant when deployed. Is stored in code.
- ``anonymous`` for events: Does not store event signature as topic.
- ``indexed`` for event parameters: Stores the parameter as topic.
- ``virtual`` for functions and modifiers: Allows the function's or modifier's

View File

@ -30,19 +30,23 @@ Not all types for constants and immutables are implemented at this time. The onl
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.4;
pragma solidity ^0.8.21;
uint constant X = 32**22 + 8;
contract C {
string constant TEXT = "abc";
bytes32 constant MY_HASH = keccak256("abc");
uint immutable decimals;
uint immutable decimals = 18;
uint immutable maxBalance;
address immutable owner = msg.sender;
constructor(uint decimals_, address ref) {
decimals = decimals_;
if (decimals_ != 0)
// Immutables are only immutable when deployed.
// At construction time they can be assigned to any number of times.
decimals = decimals_;
// Assignments to immutables can even access the environment.
maxBalance = ref.balance;
}
@ -74,10 +78,29 @@ Immutable
=========
Variables declared as ``immutable`` are a bit less restricted than those
declared as ``constant``: Immutable variables can be assigned an arbitrary
value in the constructor of the contract or at the point of their declaration.
They can be assigned only once and can, from that point on, be read even during
construction time.
declared as ``constant``: Immutable variables can be assigned a
value at construction time.
The value can be changed at any time before deployment and then it becomes permanent.
One additional restriction is that immutables can only be assigned to inside expressions for which
there is no possibility of being executed after creation.
This excludes all modifier definitions and functions other than constructors.
There are no restrictions on reading immutable variables.
The read is even allowed to happen before the variable is written to for the first time because variables in
Solidity always have a well-defined initial value.
For this reason it is also allowed to never explicitly assign a value to an immutable.
.. warning::
When accessing immutables at construction time, please keep the :ref:`initialization order
<state-variable-initialization-order>` in mind.
Even if you provide an explicit initializer, some expressions may end up being evaluated before
that initializer, especially when they are at a different level in inheritance hierarchy.
.. note::
Before Solidity 0.8.21 initialization of immutable variables was more restrictive.
Such variables had to be initialized exactly once at construction time and could not be read
before then.
The contract creation code generated by the compiler will modify the
contract's runtime code before it is returned by replacing all references
@ -86,14 +109,3 @@ you are comparing the
runtime code generated by the compiler with the one actually stored in the
blockchain. The compiler outputs where these immutables are located in the deployed bytecode
in the ``immutableReferences`` field of the :ref:`compiler JSON standard output <compiler-api>`.
.. note::
Immutables that are assigned at their declaration are only considered
initialized once the constructor of the contract is executing.
This means you cannot initialize immutables inline with a value
that depends on another immutable. You can do this, however,
inside the constructor of the contract.
This is a safeguard against different interpretations about the order
of state variable initialization and constructor execution, especially
with regards to inheritance.

View File

@ -30,6 +30,8 @@ Semantic Only Changes
This section lists the changes that are semantic-only, thus potentially
hiding new and different behavior in existing code.
.. _state-variable-initialization-order:
- The order of state variable initialization has changed in case of inheritance.
The order used to be:

View File

@ -18,8 +18,6 @@
#include <libsolidity/analysis/ImmutableValidator.h>
#include <libsolutil/CommonData.h>
#include <range/v3/view/reverse.hpp>
using namespace solidity::frontend;
@ -27,275 +25,44 @@ using namespace solidity::langutil;
void ImmutableValidator::analyze()
{
m_inCreationContext = true;
auto linearizedContracts = m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse;
for (ContractDefinition const* contract: linearizedContracts)
for (VariableDeclaration const* stateVar: contract->stateVariables())
if (stateVar->value())
stateVar->value()->accept(*this);
for (ContractDefinition const* contract: linearizedContracts)
for (std::shared_ptr<InheritanceSpecifier> const& inheritSpec: contract->baseContracts())
if (auto args = inheritSpec->arguments())
ASTNode::listAccept(*args, *this);
for (ContractDefinition const* contract: linearizedContracts)
{
for (VariableDeclaration const* stateVar: contract->stateVariables())
if (stateVar->value())
m_initializedStateVariables.emplace(stateVar);
for (FunctionDefinition const* function: contract->definedFunctions())
function->accept(*this);
if (contract->constructor())
visitCallableIfNew(*contract->constructor());
for (ModifierDefinition const* modifier: contract->functionModifiers())
modifier->accept(*this);
}
m_inCreationContext = false;
for (ContractDefinition const* contract: linearizedContracts)
{
for (auto funcDef: contract->definedFunctions())
visitCallableIfNew(*funcDef);
for (auto modDef: contract->functionModifiers())
visitCallableIfNew(*modDef);
}
checkAllVariablesInitialized(m_mostDerivedContract.location());
}
bool ImmutableValidator::visit(Assignment const& _assignment)
{
// Need to visit values first (rhs) as they might access other immutables.
_assignment.rightHandSide().accept(*this);
_assignment.leftHandSide().accept(*this);
return false;
}
bool ImmutableValidator::visit(FunctionDefinition const& _functionDefinition)
{
return analyseCallable(_functionDefinition);
return !_functionDefinition.isConstructor();
}
bool ImmutableValidator::visit(ModifierDefinition const& _modifierDefinition)
void ImmutableValidator::endVisit(MemberAccess const& _memberAccess)
{
return analyseCallable(_modifierDefinition);
}
bool ImmutableValidator::visit(MemberAccess const& _memberAccess)
{
_memberAccess.expression().accept(*this);
if (auto contractType = dynamic_cast<ContractType const*>(_memberAccess.expression().annotation().type))
if (!contractType->isSuper())
// external access, no analysis needed.
return false;
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
analyseVariableReference(*varDecl, _memberAccess);
else if (auto funcType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
if (funcType->kind() == FunctionType::Kind::Internal && funcType->hasDeclaration())
visitCallableIfNew(funcType->declaration());
return false;
}
bool ImmutableValidator::visit(IfStatement const& _ifStatement)
{
bool prevInBranch = m_inBranch;
_ifStatement.condition().accept(*this);
m_inBranch = true;
_ifStatement.trueStatement().accept(*this);
if (auto falseStatement = _ifStatement.falseStatement())
falseStatement->accept(*this);
m_inBranch = prevInBranch;
return false;
}
bool ImmutableValidator::visit(WhileStatement const& _whileStatement)
{
bool prevInLoop = m_inLoop;
m_inLoop = true;
_whileStatement.condition().accept(*this);
_whileStatement.body().accept(*this);
m_inLoop = prevInLoop;
return false;
}
bool ImmutableValidator::visit(TryStatement const& _tryStatement)
{
ScopedSaveAndRestore constructorGuard{m_inTryStatement, true};
_tryStatement.externalCall().accept(*this);
for (auto&& clause: _tryStatement.clauses())
if (clause)
clause->accept(*this);
return false;
}
void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath)
{
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifierPath.annotation().referencedDeclaration))
visitCallableIfNew(
*_identifierPath.annotation().requiredLookup == VirtualLookup::Virtual ?
callableDef->resolveVirtual(m_mostDerivedContract) :
*callableDef
);
solAssert(!dynamic_cast<VariableDeclaration const*>(_identifierPath.annotation().referencedDeclaration), "");
analyseVariableReference(_memberAccess.annotation().referencedDeclaration, _memberAccess);
}
void ImmutableValidator::endVisit(Identifier const& _identifier)
{
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_mostDerivedContract) : *callableDef);
if (auto const varDecl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
analyseVariableReference(*varDecl, _identifier);
analyseVariableReference(_identifier.annotation().referencedDeclaration, _identifier);
}
void ImmutableValidator::endVisit(Return const& _return)
void ImmutableValidator::analyseVariableReference(Declaration const* _reference, Expression const& _expression)
{
if (m_currentConstructor != nullptr)
checkAllVariablesInitialized(_return.location());
}
bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration)
{
ScopedSaveAndRestore constructorGuard{m_currentConstructor, {}};
ScopedSaveAndRestore constructorContractGuard{m_currentConstructorContract, {}};
if (FunctionDefinition const* funcDef = dynamic_cast<decltype(funcDef)>(&_callableDeclaration))
{
ASTNode::listAccept(funcDef->modifiers(), *this);
if (funcDef->isConstructor())
{
m_currentConstructorContract = funcDef->annotation().contract;
m_currentConstructor = funcDef;
}
if (funcDef->isImplemented())
funcDef->body().accept(*this);
}
else if (ModifierDefinition const* modDef = dynamic_cast<decltype(modDef)>(&_callableDeclaration))
if (modDef->isImplemented())
modDef->body().accept(*this);
return false;
}
void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression)
{
if (!_variableReference.isStateVariable() || !_variableReference.immutable())
auto const* variable = dynamic_cast<VariableDeclaration const*>(_reference);
if (!variable || !variable->isStateVariable() || !variable->immutable())
return;
// If this is not an ordinary assignment, we write and read at the same time.
bool write = _expression.annotation().willBeWrittenTo;
bool read = !_expression.annotation().willBeWrittenTo || !_expression.annotation().lValueOfOrdinaryAssignment;
if (write)
{
if (!m_currentConstructor)
m_errorReporter.typeError(
1581_error,
_expression.location(),
"Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor."
);
else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id())
m_errorReporter.typeError(
7484_error,
_expression.location(),
"Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in."
);
else if (m_inLoop)
m_errorReporter.typeError(
6672_error,
_expression.location(),
"Cannot write to immutable here: Immutable variables cannot be initialized inside a loop."
);
else if (m_inBranch)
m_errorReporter.typeError(
4599_error,
_expression.location(),
"Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement."
);
else if (m_inTryStatement)
m_errorReporter.typeError(
4130_error,
_expression.location(),
"Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement."
);
else if (m_initializedStateVariables.count(&_variableReference))
{
if (!read)
m_errorReporter.typeError(
1574_error,
_expression.location(),
"Immutable state variable already initialized."
);
else
m_errorReporter.typeError(
2718_error,
_expression.location(),
"Immutable variables cannot be modified after initialization."
);
}
else if (read)
m_errorReporter.typeError(
3969_error,
_expression.location(),
"Immutable variables must be initialized using an assignment."
);
m_initializedStateVariables.emplace(&_variableReference);
}
if (
read &&
m_inCreationContext &&
!m_initializedStateVariables.count(&_variableReference)
)
if (_expression.annotation().willBeWrittenTo)
m_errorReporter.typeError(
7733_error,
1581_error,
_expression.location(),
"Immutable variables cannot be read before they are initialized."
"Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor."
);
}
void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location)
{
for (ContractDefinition const* contract: m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse)
{
for (VariableDeclaration const* varDecl: contract->stateVariables())
if (varDecl->immutable())
if (!util::contains(m_initializedStateVariables, varDecl))
m_errorReporter.typeError(
2658_error,
_location,
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
"Construction control flow ends without initializing all immutable state variables."
);
// Don't check further than the current c'tors contract
if (contract == m_currentConstructorContract)
break;
}
}
void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration)
{
CallableDeclaration const* _callable = dynamic_cast<CallableDeclaration const*>(&_declaration);
solAssert(_callable != nullptr, "");
if (m_visitedCallables.emplace(_callable).second)
_declaration.accept(*this);
}

View File

@ -21,25 +21,15 @@
#include <libsolidity/ast/ASTVisitor.h>
#include <liblangutil/ErrorReporter.h>
#include <memory>
namespace solidity::frontend
{
/**
* Validates access and initialization of immutable variables:
* must be directly initialized in their respective c'tor or inline
* cannot be read before being initialized
* cannot be read when initializing state variables inline
* must be initialized outside loops (only one initialization)
* must be initialized outside ifs (must be initialized unconditionally)
* must be initialized exactly once (no multiple statements)
* must be initialized exactly once (no early return to skip initialization)
* must be directly initialized in a c'tor or inline
*/
class ImmutableValidator: private ASTConstVisitor
{
using CallableDeclarationSet = std::set<CallableDeclaration const*, ASTNode::CompareByID>;
public:
ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition):
m_mostDerivedContract(_contractDefinition),
@ -49,37 +39,15 @@ public:
void analyze();
private:
bool visit(Assignment const& _assignment);
bool visit(FunctionDefinition const& _functionDefinition);
bool visit(ModifierDefinition const& _modifierDefinition);
bool visit(MemberAccess const& _memberAccess);
bool visit(IfStatement const& _ifStatement);
bool visit(WhileStatement const& _whileStatement);
bool visit(TryStatement const& _tryStatement);
void endVisit(IdentifierPath const& _identifierPath);
void endVisit(MemberAccess const& _memberAccess);
void endVisit(Identifier const& _identifier);
void endVisit(Return const& _return);
bool analyseCallable(CallableDeclaration const& _callableDeclaration);
void analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression);
void checkAllVariablesInitialized(langutil::SourceLocation const& _location);
void visitCallableIfNew(Declaration const& _declaration);
void analyseVariableReference(Declaration const* _variableReference, Expression const& _expression);
ContractDefinition const& m_mostDerivedContract;
CallableDeclarationSet m_visitedCallables;
std::set<VariableDeclaration const*, ASTNode::CompareByID> m_initializedStateVariables;
langutil::ErrorReporter& m_errorReporter;
FunctionDefinition const* m_currentConstructor = nullptr;
ContractDefinition const* m_currentConstructorContract = nullptr;
bool m_inLoop = false;
bool m_inBranch = false;
bool m_inCreationContext = true;
bool m_inTryStatement = false;
};
}

View File

@ -0,0 +1,15 @@
contract C {
uint8 immutable public a;
uint8 immutable public b = 0x42;
uint public c;
constructor() {
delete a;
delete b;
c = b * 2 + a;
}
}
// ----
// a() -> 0
// b() -> 0
// c() -> 0

View File

@ -0,0 +1,18 @@
contract C {
int immutable x = 1;
int immutable y = 3;
constructor() {
x--;
--x;
y++;
++y;
--y;
}
function f() public view returns (int, int) {
return (x, y);
}
}
// ----
// f() -> -1, 4

View File

@ -0,0 +1,27 @@
contract A {
uint immutable x = x + 1;
uint immutable y = x += 2;
constructor(uint) m(x += 16) m(x += 32) {
x += 64;
x += 128;
}
modifier m(uint) {
_;
}
function get() public returns (uint) {
return x;
}
}
contract B is A(A.x += 8) {
constructor(uint) {}
}
contract C is B {
constructor() B(x += 4) {}
}
// ----
// get() -> 0xff

View File

@ -0,0 +1,11 @@
contract C {
uint immutable u;
bool immutable b;
address immutable a;
function get() public returns (uint, bool, address) {
return (u, b, a);
}
}
// ----
// get() -> 0, false, 0x0

View File

@ -7,5 +7,3 @@ contract C {
x = 1;
}
}
// ----
// TypeError 2658: (86-93): Construction control flow ends without initializing all immutable state variables.

View File

@ -5,5 +5,3 @@ contract C {
x = 1;
}
}
// ----
// TypeError 4599: (86-87): Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement.

View File

@ -6,5 +6,3 @@ contract C {
function f() public view returns (uint) { return 3 + x; }
}
// ----
// TypeError 7733: (136-137): Immutable variables cannot be read before they are initialized.

View File

@ -4,5 +4,3 @@ contract C {
x = 3 + x;
}
}
// ----
// TypeError 7733: (71-72): Immutable variables cannot be read before they are initialized.

View File

@ -9,4 +9,3 @@ contract C {
function f(uint a) internal pure {}
}
// ----
// TypeError 1581: (59-60): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -10,5 +10,3 @@ contract C {
function f(uint a) internal pure {}
}
// ----
// TypeError 7733: (119-120): Immutable variables cannot be read before they are initialized.

View File

@ -4,5 +4,3 @@ contract C {
x--;
}
}
// ----
// TypeError 3969: (63-64): Immutable variables must be initialized using an assignment.

View File

@ -1,8 +0,0 @@
contract C {
uint immutable x;
constructor() {
delete x;
}
}
// ----
// TypeError 3969: (70-71): Immutable variables must be initialized using an assignment.

View File

@ -1,8 +0,0 @@
contract C {
uint immutable x = 3;
constructor() {
delete x;
}
}
// ----
// TypeError 2718: (74-75): Immutable variables cannot be modified after initialization.

View File

@ -3,5 +3,3 @@ contract C {
function f() public view returns (uint) { return 3 + x; }
}
// ----
// TypeError 7733: (99-100): Immutable variables cannot be read before they are initialized.

View File

@ -11,4 +11,3 @@ contract C is B(C.f) {
}
// ----
// TypeError 1581: (200-201): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.
// TypeError 1574: (109-110): Immutable state variable already initialized.

View File

@ -10,4 +10,3 @@ contract C is B(C.f) {
function f() internal view returns(uint) { return x + 2; }
}
// ----
// TypeError 7733: (205-206): Immutable variables cannot be read before they are initialized.

View File

@ -4,5 +4,3 @@ contract C {
x++;
}
}
// ----
// TypeError 3969: (63-64): Immutable variables must be initialized using an assignment.

View File

@ -1,11 +0,0 @@
contract C {
uint immutable x;
uint immutable y;
constructor() {
++x;
--y;
}
}
// ----
// TypeError 3969: (77-78): Immutable variables must be initialized using an assignment.
// TypeError 3969: (86-87): Immutable variables must be initialized using an assignment.

View File

@ -5,4 +5,3 @@ contract C {
function f() internal pure returns(uint) { return x; }
}
// ----
// TypeError 7733: (112-113): Immutable variables cannot be read before they are initialized.

View File

@ -10,5 +10,3 @@ contract C is B {
uint immutable y;
constructor() B(y = 3) { }
}
// ----
// TypeError 1581: (148-149): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -9,5 +9,3 @@ contract B {
contract C is B(C.y = 3) {
uint immutable y;
}
// ----
// TypeError 1581: (104-107): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -12,5 +12,3 @@ contract C is B(C.y) {
y = 3;
}
}
// ----
// TypeError 7733: (104-107): Immutable variables cannot be read before they are initialized.

View File

@ -16,4 +16,3 @@ contract C is B {
}
}
// ----
// TypeError 7733: (263-264): Immutable variables cannot be read before they are initialized.

View File

@ -17,5 +17,3 @@ contract C is B {
_; f(x);
}
}
// ----
// TypeError 7733: (245-246): Immutable variables cannot be read before they are initialized.

View File

@ -7,5 +7,3 @@ contract C is B {
x = 3;
}
}
// ----
// TypeError 7484: (88-89): Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in.

View File

@ -4,4 +4,3 @@ contract D {
constructor() m(t = 2) {}
}
// ----
// TypeError 1581: (83-84): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -3,5 +3,3 @@ contract D is C {
uint immutable t;
constructor() C(t=2) {}
}
// ----
// TypeError 1581: (92-93): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -0,0 +1,7 @@
contract D {
uint immutable t;
modifier m(uint) { _; }
function f() public m(t = 2) {}
}
// ----
// TypeError 1581: (89-90): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -0,0 +1,11 @@
contract B {
uint immutable x;
}
contract C is B {
function f() public {
B.x = 42;
}
}
// ----
// TypeError 1581: (90-93): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -0,0 +1,13 @@
contract B {
uint immutable x;
function g() public {}
}
contract C is B {
function f() public {
super.x = 42;
}
}
// ----
// TypeError 9582: (118-125): Member "x" not found or not visible after argument-dependent lookup in type(contract super C).

View File

@ -2,5 +2,3 @@ contract C { constructor(uint) {} }
contract D is C(D.t = 2) {
uint immutable t;
}
// ----
// TypeError 1581: (52-55): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -5,5 +5,3 @@ contract C {
x = 1;
}
}
// ----
// TypeError 6672: (88-89): Cannot write to immutable here: Immutable variables cannot be initialized inside a loop.

View File

@ -26,4 +26,3 @@ contract C is A, B {
}
}
// ----
// TypeError 7733: (499-500): Immutable variables cannot be read before they are initialized.

View File

@ -26,4 +26,3 @@ contract C is A, B {
}
}
// ----
// TypeError 7733: (503-504): Immutable variables cannot be read before they are initialized.

View File

@ -5,5 +5,3 @@ contract C {
x = 4;
}
}
// ----
// TypeError 1574: (78-79): Immutable state variable already initialized.

View File

@ -16,5 +16,3 @@ contract C is B {
function f() internal view override returns(uint) { return readX(); }
}
// ----
// TypeError 7733: (202-203): Immutable variables cannot be read before they are initialized.

View File

@ -8,5 +8,3 @@ contract B is A {
uint immutable x = 2;
function f() override pure internal returns (uint) { return x; }
}
// ----
// TypeError 7733: (223-224): Immutable variables cannot be read before they are initialized.

View File

@ -3,5 +3,3 @@ contract C {
uint x = f();
function f() internal pure returns (uint) { return t; }
}
// ----
// TypeError 7733: (106-107): Immutable variables cannot be read before they are initialized.

View File

@ -13,4 +13,3 @@ contract C {
}
}
// ----
// TypeError 7733: (141-142): Immutable variables cannot be read before they are initialized.

View File

@ -2,5 +2,3 @@ contract C {
uint immutable x = 0;
uint y = x;
}
// ----
// TypeError 7733: (52-53): Immutable variables cannot be read before they are initialized.

View File

@ -7,4 +7,4 @@ contract C {
}
}
// ----
// TypeError 2658: (63-70): Construction control flow ends without initializing all immutable state variables.
// Warning 5740: (80-85): Unreachable code.

View File

@ -0,0 +1,13 @@
contract C {
uint immutable public x = 42;
function g() external view returns (uint) {}
function f() public view returns (uint) {
return this.x();
}
function h() public view returns (function () external view returns (uint)) {
return this.x;
}
}

View File

@ -0,0 +1,11 @@
contract C {
uint immutable public x = 42;
function g() external view returns (uint) {}
function f() public view {
this.x = this.g;
}
}
// ----
// TypeError 4247: (137-143): Expression has to be an lvalue.

View File

@ -33,9 +33,3 @@ contract B
}
// ====
// EVMVersion: >=byzantium
// ----
// TypeError 4130: (108-116): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement.
// TypeError 4130: (144-152): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement.
// TypeError 4130: (216-224): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement.
// TypeError 4130: (297-305): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement.
// TypeError 4130: (357-365): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement.

View File

@ -1,5 +1,3 @@
contract C {
uint immutable x;
}
// ----
// TypeError 2658: (0-36): Construction control flow ends without initializing all immutable state variables.

View File

@ -16,6 +16,3 @@ contract C is B {
function f() internal view override returns(uint) { return readX(); }
}
// ----
// TypeError 2658: (0-202): Construction control flow ends without initializing all immutable state variables.
// TypeError 2658: (204-361): Construction control flow ends without initializing all immutable state variables.

View File

@ -3,6 +3,3 @@ contract C {
uint immutable x = z = y = 3;
uint immutable y = 5;
}
// ----
// TypeError 1581: (66-67): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.
// TypeError 1581: (62-63): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

View File

@ -1,5 +1,3 @@
contract C {
int immutable x = x = 5;
}
// ----
// TypeError 1581: (35-36): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.