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".