diff --git a/Changelog.md b/Changelog.md
index f20b05aa5..6ca68ea85 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -11,6 +11,7 @@ Compiler Features:
* Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks.
* Commandline Interface: Enable output of storage layout with `--storage-layout`.
+
Bugfixes:
* Inline Assembly: Fix internal error when accessing invalid constant variables.
* Inline Assembly: Fix internal error when accessing functions.
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index ba2651e94..c7a517a0b 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -14,6 +14,8 @@ set(sources
analysis/DeclarationContainer.h
analysis/DocStringAnalyser.cpp
analysis/DocStringAnalyser.h
+ analysis/ImmutableValidator.cpp
+ analysis/ImmutableValidator.h
analysis/GlobalContext.cpp
analysis/GlobalContext.h
analysis/NameAndTypeResolver.cpp
diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp
new file mode 100644
index 000000000..e80902009
--- /dev/null
+++ b/libsolidity/analysis/ImmutableValidator.cpp
@@ -0,0 +1,214 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see .
+*/
+
+#include
+
+#include
+
+#include
+
+using namespace solidity::frontend;
+
+void ImmutableValidator::analyze()
+{
+ m_inConstructionContext = true;
+
+ auto linearizedContracts = m_currentContract.annotation().linearizedBaseContracts | boost::adaptors::reversed;
+
+ for (ContractDefinition const* contract: linearizedContracts)
+ for (VariableDeclaration const* stateVar: contract->stateVariables())
+ if (stateVar->value())
+ {
+ stateVar->value()->accept(*this);
+ solAssert(m_initializedStateVariables.emplace(stateVar).second, "");
+ }
+
+ for (ContractDefinition const* contract: linearizedContracts)
+ if (contract->constructor())
+ visitCallableIfNew(*contract->constructor());
+
+ for (ContractDefinition const* contract: linearizedContracts)
+ for (std::shared_ptr const inheritSpec: contract->baseContracts())
+ if (auto args = inheritSpec->arguments())
+ ASTNode::listAccept(*args, *this);
+
+ m_inConstructionContext = false;
+
+ for (ContractDefinition const* contract: linearizedContracts)
+ {
+ for (auto funcDef: contract->definedFunctions())
+ visitCallableIfNew(*funcDef);
+
+ for (auto modDef: contract->functionModifiers())
+ visitCallableIfNew(*modDef);
+ }
+
+ checkAllVariablesInitialized(m_currentContract.location());
+}
+
+bool ImmutableValidator::visit(FunctionDefinition const& _functionDefinition)
+{
+ return analyseCallable(_functionDefinition);
+}
+
+bool ImmutableValidator::visit(ModifierDefinition const& _modifierDefinition)
+{
+ return analyseCallable(_modifierDefinition);
+}
+
+bool ImmutableValidator::visit(MemberAccess const& _memberAccess)
+{
+ _memberAccess.expression().accept(*this);
+
+ if (auto varDecl = dynamic_cast(_memberAccess.annotation().referencedDeclaration))
+ analyseVariableReference(*varDecl, _memberAccess);
+ else if (auto funcType = dynamic_cast(_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;
+}
+
+void ImmutableValidator::endVisit(Identifier const& _identifier)
+{
+ if (auto const callableDef = dynamic_cast(_identifier.annotation().referencedDeclaration))
+ visitCallableIfNew(callableDef->resolveVirtual(m_currentContract));
+ if (auto const varDecl = dynamic_cast(_identifier.annotation().referencedDeclaration))
+ analyseVariableReference(*varDecl, _identifier);
+}
+
+void ImmutableValidator::endVisit(Return const& _return)
+{
+ if (m_currentConstructor != nullptr)
+ checkAllVariablesInitialized(_return.location());
+}
+
+bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration)
+{
+ FunctionDefinition const* prevConstructor = m_currentConstructor;
+ m_currentConstructor = nullptr;
+
+ if (FunctionDefinition const* funcDef = dynamic_cast(&_callableDeclaration))
+ {
+ ASTNode::listAccept(funcDef->modifiers(), *this);
+
+ if (funcDef->isConstructor())
+ m_currentConstructor = funcDef;
+
+ if (funcDef->isImplemented())
+ funcDef->body().accept(*this);
+ }
+ else if (ModifierDefinition const* modDef = dynamic_cast(&_callableDeclaration))
+ modDef->body().accept(*this);
+
+ m_currentConstructor = prevConstructor;
+
+ return false;
+}
+
+void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression)
+{
+ if (!_variableReference.isStateVariable() || !_variableReference.immutable())
+ return;
+
+ if (_expression.annotation().lValueRequested && _expression.annotation().lValueOfOrdinaryAssignment)
+ {
+ if (!m_currentConstructor)
+ m_errorReporter.typeError(
+ _expression.location(),
+ "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(
+ _expression.location(),
+ "Immutable variables must be initialized in the constructor of the contract they are defined in."
+ );
+ else if (m_inLoop)
+ m_errorReporter.typeError(
+ _expression.location(),
+ "Immutable variables can only be initialized once, not in a while statement."
+ );
+ else if (m_inBranch)
+ m_errorReporter.typeError(
+ _expression.location(),
+ "Immutable variables must be initialized unconditionally, not in an if statement."
+ );
+
+ if (!m_initializedStateVariables.emplace(&_variableReference).second)
+ m_errorReporter.typeError(
+ _expression.location(),
+ "Immutable state variable already initialized."
+ );
+ }
+ else if (m_inConstructionContext)
+ m_errorReporter.typeError(
+ _expression.location(),
+ "Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it."
+ );
+}
+
+void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location)
+{
+ for (ContractDefinition const* contract: m_currentContract.annotation().linearizedBaseContracts)
+ for (VariableDeclaration const* varDecl: contract->stateVariables())
+ if (varDecl->immutable())
+ if (!util::contains(m_initializedStateVariables, varDecl))
+ m_errorReporter.typeError(
+ _location,
+ solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
+ "Construction control flow ends without initializing all immutable state variables."
+ );
+}
+
+void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration)
+{
+ CallableDeclaration const* _callable = dynamic_cast(&_declaration);
+ solAssert(_callable != nullptr, "");
+
+ if (m_visitedCallables.emplace(_callable).second)
+ _declaration.accept(*this);
+}
diff --git a/libsolidity/analysis/ImmutableValidator.h b/libsolidity/analysis/ImmutableValidator.h
new file mode 100644
index 000000000..5845ba011
--- /dev/null
+++ b/libsolidity/analysis/ImmutableValidator.h
@@ -0,0 +1,78 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see .
+*/
+
+#pragma once
+
+#include
+#include
+
+#include
+
+namespace solidity::frontend
+{
+
+/**
+ * Validates access and initialization of immutable variables:
+ * must be directly initialized in their respective c'tor
+ * can not be read by any function/modifier called by the c'tor (or the c'tor itself)
+ * 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)
+*/
+class ImmutableValidator: private ASTConstVisitor
+{
+ using CallableDeclarationSet = std::set;
+
+public:
+ ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition):
+ m_currentContract(_contractDefinition),
+ m_errorReporter(_errorReporter)
+ { }
+
+ void analyze();
+
+private:
+ 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);
+ 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);
+
+ ContractDefinition const& m_currentContract;
+
+ CallableDeclarationSet m_visitedCallables;
+
+ std::set m_initializedStateVariables;
+ langutil::ErrorReporter& m_errorReporter;
+
+ FunctionDefinition const* m_currentConstructor = nullptr;
+ bool m_inLoop = false;
+ bool m_inBranch = false;
+ bool m_inConstructionContext = false;
+};
+
+}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index e6a2408f1..8e7bef338 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1337,7 +1337,10 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
bool TypeChecker::visit(Assignment const& _assignment)
{
- requireLValue(_assignment.leftHandSide());
+ requireLValue(
+ _assignment.leftHandSide(),
+ _assignment.assignmentOperator() == Token::Assign
+ );
TypePointer t = type(_assignment.leftHandSide());
_assignment.annotation().type = t;
@@ -1395,7 +1398,10 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
for (auto const& component: components)
if (component)
{
- requireLValue(*component);
+ requireLValue(
+ *component,
+ _tuple.annotation().lValueOfOrdinaryAssignment
+ );
types.push_back(type(*component));
}
else
@@ -1480,7 +1486,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
Token op = _operation.getOperator();
bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete);
if (modifying)
- requireLValue(_operation.subExpression());
+ requireLValue(_operation.subExpression(), false);
else
_operation.subExpression().accept(*this);
TypePointer const& subExprType = type(_operation.subExpression());
@@ -2988,9 +2994,10 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
return true;
}
-void TypeChecker::requireLValue(Expression const& _expression)
+void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment)
{
_expression.annotation().lValueRequested = true;
+ _expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment;
_expression.accept(*this);
if (_expression.annotation().isLValue)
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index d428a6ac9..a26ab81bd 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -158,7 +158,7 @@ private:
/// convertible to @a _expectedType.
bool expectType(Expression const& _expression, Type const& _expectedType);
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
- void requireLValue(Expression const& _expression);
+ void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
ContractDefinition const* m_scope = nullptr;
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 3d64c828f..4979d5d4e 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -62,6 +62,24 @@ class ASTConstVisitor;
class ASTNode: private boost::noncopyable
{
public:
+ struct CompareByID
+ {
+ using is_transparent = void;
+
+ bool operator()(ASTNode const* _lhs, ASTNode const* _rhs) const
+ {
+ return _lhs->id() < _rhs->id();
+ }
+ bool operator()(ASTNode const* _lhs, int64_t _rhs) const
+ {
+ return _lhs->id() < _rhs;
+ }
+ bool operator()(int64_t _lhs, ASTNode const* _rhs) const
+ {
+ return _lhs < _rhs->id();
+ }
+ };
+
using SourceLocation = langutil::SourceLocation;
explicit ASTNode(int64_t _id, SourceLocation const& _location);
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index 86636684c..a724472d0 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -208,6 +208,9 @@ struct ExpressionAnnotation: ASTAnnotation
bool isLValue = false;
/// Whether the expression is used in a context where the LValue is actually required.
bool lValueRequested = false;
+ /// Whether the expression is an lvalue that is only assigned.
+ /// Would be false for --, ++, delete, +=, -=, ....
+ bool lValueOfOrdinaryAssignment = false;
/// Types and - if given - names of arguments if the expr. is a function
/// that is called, used for overload resoultion
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 781f68d6b..fe0268fb1 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#include
#include
@@ -383,6 +384,15 @@ bool CompilerStack::analyze()
noErrors = false;
}
+ // Check that immutable variables are never read in c'tors and assigned
+ // exactly once
+ if (noErrors)
+ for (Source const* source: m_sourceOrder)
+ if (source->ast)
+ for (ASTPointer const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast(node.get()))
+ ImmutableValidator(m_errorReporter, *contract).analyze();
+
if (noErrors)
{
// Control flow graph generator and analyzer. It can check for issues such as
diff --git a/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol b/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol
new file mode 100644
index 000000000..702f03079
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol
@@ -0,0 +1,11 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ if (false)
+ return;
+
+ x = 1;
+ }
+}
+// ----
+// TypeError: (93-100): Construction control flow ends without initializing all immutable state variables.
diff --git a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol
new file mode 100644
index 000000000..b8557321b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol
@@ -0,0 +1,9 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ if (false)
+ x = 1;
+ }
+}
+// ----
+// TypeError: (93-94): Immutable variables must be initialized unconditionally, not in an if statement.
diff --git a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol
new file mode 100644
index 000000000..1423659c8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol
@@ -0,0 +1,12 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ initX();
+ }
+
+ function initX() internal {
+ x = 3;
+ }
+}
+// ----
+// TypeError: (126-127): Immutable variables can only be initialized inline or assigned directly in the constructor.
diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol
new file mode 100644
index 000000000..ad17635c4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ x = f();
+ }
+
+ function f() public pure returns (uint) { return 3 + x; }
+}
+// ----
+// TypeError: (143-144): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol
new file mode 100644
index 000000000..0b2207808
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ x = 3 + x;
+ }
+}
+// ----
+// TypeError: (78-79): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol
new file mode 100644
index 000000000..f3b724f03
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol
@@ -0,0 +1,13 @@
+contract C {
+ uint immutable x;
+ uint immutable y;
+ constructor() public {
+ (x, y) = f();
+ }
+
+ function f() internal pure returns(uint _x, uint _y) {
+ _x = 3;
+ _y = 4;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol
new file mode 100644
index 000000000..2c1c37c79
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol
@@ -0,0 +1,12 @@
+contract C {
+ uint immutable x;
+ constructor() readX(x = 3) public { }
+
+ modifier readX(uint _x) {
+ _; f(_x);
+ }
+
+ function f(uint a) internal pure {}
+}
+// ----
+// TypeError: (59-60): Immutable variables can only be initialized inline or assigned directly in the constructor.
diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol
new file mode 100644
index 000000000..b26666530
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol
@@ -0,0 +1,11 @@
+contract C {
+ uint immutable x;
+ constructor() initX public {
+ }
+
+ modifier initX() {
+ _; x = 23;
+ }
+}
+// ----
+// TypeError: (109-110): Immutable variables can only be initialized inline or assigned directly in the constructor.
diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol
new file mode 100644
index 000000000..a9a1b3a22
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol
@@ -0,0 +1,14 @@
+contract C {
+ uint immutable x;
+ constructor() readX public {
+ x = 3;
+ }
+
+ modifier readX() {
+ _; f(x);
+ }
+
+ function f(uint a) internal pure {}
+}
+// ----
+// TypeError: (126-127): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/decrement.sol b/test/libsolidity/syntaxTests/immutable/decrement.sol
new file mode 100644
index 000000000..54ac9f615
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/decrement.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint immutable x = 3;
+ constructor() public {
+ x--;
+ }
+}
+// ----
+// TypeError: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/delete.sol b/test/libsolidity/syntaxTests/immutable/delete.sol
new file mode 100644
index 000000000..def28fad2
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/delete.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint immutable x = 3;
+ constructor() public {
+ delete x;
+ }
+}
+// ----
+// TypeError: (81-82): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/function_initialization.sol b/test/libsolidity/syntaxTests/immutable/function_initialization.sol
new file mode 100644
index 000000000..c836beb27
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/function_initialization.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint immutable x = f();
+
+ function f() public pure returns (uint) { return 3; }
+}
diff --git a/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol b/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol
new file mode 100644
index 000000000..2a4365e69
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol
@@ -0,0 +1,7 @@
+contract C {
+ uint immutable x = f();
+
+ function f() public pure returns (uint) { return 3 + x; }
+}
+// ----
+// TypeError: (99-100): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol
new file mode 100644
index 000000000..04eaa621d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol
@@ -0,0 +1,14 @@
+contract B {
+ uint immutable x;
+
+ constructor(function() internal returns(uint) fp) internal {
+ x = fp();
+ }
+}
+
+contract C is B(C.f) {
+ function f() internal returns(uint) { return x = 2; }
+}
+// ----
+// TypeError: (200-201): Immutable variables can only be initialized inline or assigned directly in the constructor.
+// TypeError: (200-201): Immutable state variable already initialized.
diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol
new file mode 100644
index 000000000..2ec62e931
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol
@@ -0,0 +1,13 @@
+contract B {
+ uint immutable x;
+
+ constructor(function() internal returns(uint) fp) internal {
+ x = fp();
+ }
+}
+
+contract C is B(C.f) {
+ function f() internal returns(uint) { return x + 2; }
+}
+// ----
+// TypeError: (200-201): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/getter.sol b/test/libsolidity/syntaxTests/immutable/getter.sol
deleted file mode 100644
index 7740f8643..000000000
--- a/test/libsolidity/syntaxTests/immutable/getter.sol
+++ /dev/null
@@ -1,5 +0,0 @@
-contract C {
- uint immutable public x;
-}
-// ----
-// UnimplementedFeatureError: NONE
diff --git a/test/libsolidity/syntaxTests/immutable/immutable_basic.sol b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol
index b4960f25b..3d8e04636 100644
--- a/test/libsolidity/syntaxTests/immutable/immutable_basic.sol
+++ b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol
@@ -1,3 +1,3 @@
contract C {
- uint immutable x;
-}
\ No newline at end of file
+ uint immutable x = 0;
+}
diff --git a/test/libsolidity/syntaxTests/immutable/increment.sol b/test/libsolidity/syntaxTests/immutable/increment.sol
new file mode 100644
index 000000000..0068bf87d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/increment.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint immutable x = 3;
+ constructor() public {
+ x++;
+ }
+}
+// ----
+// TypeError: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol b/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol
new file mode 100644
index 000000000..5f659bff6
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint immutable x = 0;
+ uint y = f();
+
+ function f() internal returns(uint) { return x; }
+}
+// ----
+// TypeError: (107-108): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol
new file mode 100644
index 000000000..ee794fd37
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol
@@ -0,0 +1,15 @@
+contract B {
+ uint immutable x;
+
+ constructor() public {
+ x = 3;
+ }
+}
+
+contract C is B {
+ uint immutable y;
+ constructor() public {
+ y = 3;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol
new file mode 100644
index 000000000..0d98f8b40
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol
@@ -0,0 +1,14 @@
+contract B {
+ uint immutable x;
+
+ constructor(uint _x) public {
+ x = _x;
+ }
+}
+
+contract C is B {
+ uint immutable y;
+ constructor() B(y = 3) public { }
+}
+// ----
+// TypeError: (155-156): Immutable variables can only be initialized inline or assigned directly in the constructor.
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol
new file mode 100644
index 000000000..16ed52f36
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol
@@ -0,0 +1,13 @@
+contract B {
+ uint immutable x;
+
+ constructor(uint _x) public {
+ x = _x;
+ }
+}
+
+contract C is B(C.y = 3) {
+ uint immutable y;
+}
+// ----
+// TypeError: (111-114): Immutable variables can only be initialized inline or assigned directly in the constructor.
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol
new file mode 100644
index 000000000..ed352746f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol
@@ -0,0 +1,16 @@
+contract B {
+ uint immutable x;
+
+ constructor(uint _x) public {
+ x = _x;
+ }
+}
+
+contract C is B(C.y) {
+ uint immutable y;
+ constructor() public {
+ y = 3;
+ }
+}
+// ----
+// TypeError: (111-114): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol
new file mode 100644
index 000000000..d56fb487a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol
@@ -0,0 +1,19 @@
+contract B {
+ uint immutable x;
+
+ constructor() public {
+ x = xInit();
+ }
+
+ function xInit() internal virtual returns(uint) {
+ return 3;
+ }
+}
+
+contract C is B {
+ function xInit() internal override returns(uint) {
+ return x;
+ }
+}
+// ----
+// TypeError: (260-261): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol
new file mode 100644
index 000000000..5dda6bfb0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol
@@ -0,0 +1,19 @@
+contract B {
+ uint immutable x = 3;
+
+ function readX() internal virtual returns(uint) {
+ return x;
+ }
+}
+
+contract C is B {
+ constructor() public {
+ B.readX;
+ }
+
+ function readX() internal override returns(uint) {
+ return 3;
+ }
+}
+// ----
+// TypeError: (109-110): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol
new file mode 100644
index 000000000..278efe55f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol
@@ -0,0 +1,19 @@
+contract B {
+ uint immutable x = 3;
+
+ function readX() internal view virtual returns(uint) {
+ return x;
+ }
+}
+
+contract C is B {
+ constructor() public {
+ super.readX();
+ }
+
+ function readX() internal view override returns(uint) {
+ return 1;
+ }
+}
+// ----
+// TypeError: (114-115): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol
new file mode 100644
index 000000000..f1e60dbd8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol
@@ -0,0 +1,21 @@
+contract B {
+ uint immutable x;
+
+ constructor() readX public {
+ x = 3;
+ }
+
+ modifier readX() virtual {
+ _; f(3);
+ }
+
+ function f(uint a) internal pure {}
+}
+
+contract C is B {
+ modifier readX() override {
+ _; f(x);
+ }
+}
+// ----
+// TypeError: (252-253): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol
new file mode 100644
index 000000000..24a5239ed
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol
@@ -0,0 +1,12 @@
+contract B {
+ uint immutable x = 4;
+}
+
+contract C is B {
+ constructor() public {
+ x = 3;
+ }
+}
+// ----
+// TypeError: (95-96): Immutable variables must be initialized in the constructor of the contract they are defined in.
+// TypeError: (95-96): Immutable state variable already initialized.
diff --git a/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol b/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol
new file mode 100644
index 000000000..a6db6a665
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol
@@ -0,0 +1,8 @@
+contract C {
+ constructor() public {
+ return;
+ }
+
+ uint immutable x = 3;
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol
new file mode 100644
index 000000000..2827ab389
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol
@@ -0,0 +1,9 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ while (true)
+ x = 1;
+ }
+}
+// ----
+// TypeError: (95-96): Immutable variables can only be initialized once, not in a while statement.
diff --git a/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol
new file mode 100644
index 000000000..c71335b83
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol
@@ -0,0 +1,29 @@
+contract A {
+ function f() internal virtual returns(uint) { return 3; }
+}
+
+contract B {
+ uint immutable x;
+
+ constructor() public {
+ x = xInit();
+ }
+
+ function xInit() internal virtual returns(uint) {
+ return f();
+ }
+
+ function f() internal virtual returns(uint) { return 3; }
+}
+
+contract C is A, B {
+ function xInit() internal override returns(uint) {
+ return B.xInit();
+ }
+
+ function f() internal override(A, B) returns(uint) {
+ return x;
+ }
+}
+// ----
+// TypeError: (496-497): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol
new file mode 100644
index 000000000..eaabbb71a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol
@@ -0,0 +1,29 @@
+contract A {
+ function f() internal virtual returns(uint) { return 3; }
+}
+
+contract B {
+ uint immutable x;
+
+ constructor() public {
+ x = xInit();
+ }
+
+ function xInit() internal virtual returns(uint) {
+ return f();
+ }
+
+ function f() internal virtual returns(uint) { return 3; }
+}
+
+contract C is A, B {
+ function xInit() internal override returns(uint) {
+ return super.xInit();
+ }
+
+ function f() internal override(A, B) returns(uint) {
+ return x;
+ }
+}
+// ----
+// TypeError: (500-501): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol b/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol
new file mode 100644
index 000000000..8ff940fef
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol
@@ -0,0 +1,9 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ x = 1;
+ x = 4;
+ }
+}
+// ----
+// TypeError: (85-86): Immutable state variable already initialized.
diff --git a/test/libsolidity/syntaxTests/immutable/private_state_var.sol b/test/libsolidity/syntaxTests/immutable/private_state_var.sol
new file mode 100644
index 000000000..2cfc22a85
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/private_state_var.sol
@@ -0,0 +1,20 @@
+contract B {
+ uint immutable private x = f();
+
+ constructor() public {
+ }
+
+ function f() internal view virtual returns(uint) { return 1; }
+ function readX() internal view returns(uint) { return x; }
+}
+
+contract C is B {
+ uint immutable y;
+ constructor() public {
+ y = 3;
+ }
+ function f() internal view override returns(uint) { return readX(); }
+
+}
+// ----
+// TypeError: (209-210): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol
new file mode 100644
index 000000000..b64f236a9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint immutable x = 0;
+ uint y = 0;
+
+ function f() internal {
+ y = x + 1;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol
new file mode 100644
index 000000000..4de778ba3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol
@@ -0,0 +1,12 @@
+contract C {
+ uint immutable x = 0;
+ uint y = 0;
+
+ function f() readX internal {
+ }
+
+ modifier readX() {
+ _;
+ y = x + 1;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol b/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol
new file mode 100644
index 000000000..d35c68d8a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol
@@ -0,0 +1,6 @@
+contract C {
+ uint immutable x = 0;
+ uint y = x;
+}
+// ----
+// TypeError: (52-53): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it.
diff --git a/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol b/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol
new file mode 100644
index 000000000..0fd5461d4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ return;
+
+ x = 1;
+ }
+}
+// ----
+// TypeError: (70-77): Construction control flow ends without initializing all immutable state variables.
diff --git a/test/libsolidity/syntaxTests/immutable/selector.sol b/test/libsolidity/syntaxTests/immutable/selector.sol
new file mode 100644
index 000000000..4b880c8e8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/selector.sol
@@ -0,0 +1,12 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ x = 3;
+ this.readX.selector;
+ }
+
+ function readX() external view returns(uint) { return x; }
+}
+// ----
+// Warning: (85-104): Statement has no effect.
+// Warning: (85-89): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed.
diff --git a/test/libsolidity/syntaxTests/immutable/selector_function_name.sol b/test/libsolidity/syntaxTests/immutable/selector_function_name.sol
new file mode 100644
index 000000000..c84f72408
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/selector_function_name.sol
@@ -0,0 +1,13 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ x = 3;
+ C.selector.selector;
+ C.selector;
+ }
+
+ function selector() external view returns(uint) { return x; }
+}
+// ----
+// Warning: (85-104): Statement has no effect.
+// Warning: (114-124): Statement has no effect.
diff --git a/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol b/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol
new file mode 100644
index 000000000..a8b91a639
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol
@@ -0,0 +1,16 @@
+contract C {
+ uint immutable x;
+ constructor() public {
+ x = 3;
+ readX().selector;
+ }
+
+ function f() external view returns(uint) {
+ return x;
+ }
+
+ function readX() public view returns(function() external view returns(uint) _f) {
+ _f = this.f;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/immutable/uninitialized.sol b/test/libsolidity/syntaxTests/immutable/uninitialized.sol
new file mode 100644
index 000000000..18c60ea9d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/uninitialized.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint immutable x;
+}
+// ----
+// TypeError: (0-36): Construction control flow ends without initializing all immutable state variables.
diff --git a/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol b/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol
new file mode 100644
index 000000000..69aa5448f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol
@@ -0,0 +1,21 @@
+contract B {
+ uint immutable private x;
+
+ constructor() public {
+ }
+
+ function f() internal view virtual returns(uint) { return 1; }
+ function readX() internal view returns(uint) { return x; }
+}
+
+contract C is B {
+ uint immutable y;
+ constructor() public {
+ y = 3;
+ }
+ function f() internal view override returns(uint) { return readX(); }
+
+}
+// ----
+// TypeError: (0-209): Construction control flow ends without initializing all immutable state variables.
+// TypeError: (211-375): Construction control flow ends without initializing all immutable state variables.
diff --git a/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol
new file mode 100644
index 000000000..2b5614650
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint immutable x = 1;
+
+ function readX() internal view returns(uint) {
+ return x + 3;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol
new file mode 100644
index 000000000..e844de080
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint immutable x = 0;
+
+ function f() internal {
+ x = 1;
+ }
+}
+// ----
+// TypeError: (76-77): Immutable variables can only be initialized inline or assigned directly in the constructor.
+// TypeError: (76-77): Immutable state variable already initialized.
diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol
new file mode 100644
index 000000000..592633379
--- /dev/null
+++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol
@@ -0,0 +1,12 @@
+contract C {
+ uint immutable x = 0;
+
+ function f() readX internal { }
+
+ modifier readX() {
+ _; x = 1;
+ }
+}
+// ----
+// TypeError: (111-112): Immutable variables can only be initialized inline or assigned directly in the constructor.
+// TypeError: (111-112): Immutable state variable already initialized.
diff --git a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol
index 1c0e7a46f..9028c7ccc 100644
--- a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol
+++ b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol
@@ -1,8 +1,8 @@
contract B {
- uint immutable x;
+ uint immutable x = 1;
function f() public pure returns (uint) {
return x;
}
}
// ----
-// TypeError: (96-97): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
+// TypeError: (100-101): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".