mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8475 from ethereum/immutable-functioncallgraph
implement ImmutableValidator class
This commit is contained in:
commit
8d28089abc
@ -12,6 +12,7 @@ Compiler Features:
|
|||||||
* Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks.
|
* 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`.
|
* Commandline Interface: Enable output of storage layout with `--storage-layout`.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* Inline Assembly: Fix internal error when accessing invalid constant variables.
|
* Inline Assembly: Fix internal error when accessing invalid constant variables.
|
||||||
* Inline Assembly: Fix internal error when accessing functions.
|
* Inline Assembly: Fix internal error when accessing functions.
|
||||||
|
@ -14,6 +14,8 @@ set(sources
|
|||||||
analysis/DeclarationContainer.h
|
analysis/DeclarationContainer.h
|
||||||
analysis/DocStringAnalyser.cpp
|
analysis/DocStringAnalyser.cpp
|
||||||
analysis/DocStringAnalyser.h
|
analysis/DocStringAnalyser.h
|
||||||
|
analysis/ImmutableValidator.cpp
|
||||||
|
analysis/ImmutableValidator.h
|
||||||
analysis/GlobalContext.cpp
|
analysis/GlobalContext.cpp
|
||||||
analysis/GlobalContext.h
|
analysis/GlobalContext.h
|
||||||
analysis/NameAndTypeResolver.cpp
|
analysis/NameAndTypeResolver.cpp
|
||||||
|
214
libsolidity/analysis/ImmutableValidator.cpp
Normal file
214
libsolidity/analysis/ImmutableValidator.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/analysis/ImmutableValidator.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
|
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<InheritanceSpecifier> 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<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImmutableValidator::endVisit(Identifier const& _identifier)
|
||||||
|
{
|
||||||
|
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||||
|
visitCallableIfNew(callableDef->resolveVirtual(m_currentContract));
|
||||||
|
if (auto const varDecl = dynamic_cast<VariableDeclaration const*>(_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<decltype(funcDef)>(&_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<decltype(modDef)>(&_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<CallableDeclaration const*>(&_declaration);
|
||||||
|
solAssert(_callable != nullptr, "");
|
||||||
|
|
||||||
|
if (m_visitedCallables.emplace(_callable).second)
|
||||||
|
_declaration.accept(*this);
|
||||||
|
}
|
78
libsolidity/analysis/ImmutableValidator.h
Normal file
78
libsolidity/analysis/ImmutableValidator.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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
|
||||||
|
* 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<CallableDeclaration const*, ASTNode::CompareByID>;
|
||||||
|
|
||||||
|
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<VariableDeclaration const*, ASTNode::CompareByID> m_initializedStateVariables;
|
||||||
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
|
|
||||||
|
FunctionDefinition const* m_currentConstructor = nullptr;
|
||||||
|
bool m_inLoop = false;
|
||||||
|
bool m_inBranch = false;
|
||||||
|
bool m_inConstructionContext = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -1337,7 +1337,10 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
|
|||||||
|
|
||||||
bool TypeChecker::visit(Assignment const& _assignment)
|
bool TypeChecker::visit(Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
requireLValue(_assignment.leftHandSide());
|
requireLValue(
|
||||||
|
_assignment.leftHandSide(),
|
||||||
|
_assignment.assignmentOperator() == Token::Assign
|
||||||
|
);
|
||||||
TypePointer t = type(_assignment.leftHandSide());
|
TypePointer t = type(_assignment.leftHandSide());
|
||||||
_assignment.annotation().type = t;
|
_assignment.annotation().type = t;
|
||||||
|
|
||||||
@ -1395,7 +1398,10 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
|||||||
for (auto const& component: components)
|
for (auto const& component: components)
|
||||||
if (component)
|
if (component)
|
||||||
{
|
{
|
||||||
requireLValue(*component);
|
requireLValue(
|
||||||
|
*component,
|
||||||
|
_tuple.annotation().lValueOfOrdinaryAssignment
|
||||||
|
);
|
||||||
types.push_back(type(*component));
|
types.push_back(type(*component));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1480,7 +1486,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
|
|||||||
Token op = _operation.getOperator();
|
Token op = _operation.getOperator();
|
||||||
bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete);
|
bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete);
|
||||||
if (modifying)
|
if (modifying)
|
||||||
requireLValue(_operation.subExpression());
|
requireLValue(_operation.subExpression(), false);
|
||||||
else
|
else
|
||||||
_operation.subExpression().accept(*this);
|
_operation.subExpression().accept(*this);
|
||||||
TypePointer const& subExprType = type(_operation.subExpression());
|
TypePointer const& subExprType = type(_operation.subExpression());
|
||||||
@ -2988,9 +2994,10 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::requireLValue(Expression const& _expression)
|
void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment)
|
||||||
{
|
{
|
||||||
_expression.annotation().lValueRequested = true;
|
_expression.annotation().lValueRequested = true;
|
||||||
|
_expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment;
|
||||||
_expression.accept(*this);
|
_expression.accept(*this);
|
||||||
|
|
||||||
if (_expression.annotation().isLValue)
|
if (_expression.annotation().isLValue)
|
||||||
|
@ -158,7 +158,7 @@ private:
|
|||||||
/// convertible to @a _expectedType.
|
/// convertible to @a _expectedType.
|
||||||
bool expectType(Expression const& _expression, Type const& _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.
|
/// 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;
|
ContractDefinition const* m_scope = nullptr;
|
||||||
|
|
||||||
|
@ -62,6 +62,24 @@ class ASTConstVisitor;
|
|||||||
class ASTNode: private boost::noncopyable
|
class ASTNode: private boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
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;
|
using SourceLocation = langutil::SourceLocation;
|
||||||
|
|
||||||
explicit ASTNode(int64_t _id, SourceLocation const& _location);
|
explicit ASTNode(int64_t _id, SourceLocation const& _location);
|
||||||
@ -1039,7 +1057,7 @@ public:
|
|||||||
ContractDefinition const*
|
ContractDefinition const*
|
||||||
) const override
|
) const override
|
||||||
{
|
{
|
||||||
solAssert(false, "Tried to resolve virtual event.");
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -208,6 +208,9 @@ struct ExpressionAnnotation: ASTAnnotation
|
|||||||
bool isLValue = false;
|
bool isLValue = false;
|
||||||
/// Whether the expression is used in a context where the LValue is actually required.
|
/// Whether the expression is used in a context where the LValue is actually required.
|
||||||
bool lValueRequested = false;
|
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
|
/// Types and - if given - names of arguments if the expr. is a function
|
||||||
/// that is called, used for overload resoultion
|
/// that is called, used for overload resoultion
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include <libsolidity/analysis/SyntaxChecker.h>
|
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
#include <libsolidity/analysis/ViewPureChecker.h>
|
#include <libsolidity/analysis/ViewPureChecker.h>
|
||||||
|
#include <libsolidity/analysis/ImmutableValidator.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/ast/TypeProvider.h>
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
@ -383,6 +384,15 @@ bool CompilerStack::analyze()
|
|||||||
noErrors = false;
|
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<ASTNode> const& node: source->ast->nodes())
|
||||||
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
|
ImmutableValidator(m_errorReporter, *contract).analyze();
|
||||||
|
|
||||||
if (noErrors)
|
if (noErrors)
|
||||||
{
|
{
|
||||||
// Control flow graph generator and analyzer. It can check for issues such as
|
// Control flow graph generator and analyzer. It can check for issues such as
|
||||||
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -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.
|
@ -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.
|
@ -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.
|
8
test/libsolidity/syntaxTests/immutable/decrement.sol
Normal file
8
test/libsolidity/syntaxTests/immutable/decrement.sol
Normal file
@ -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.
|
8
test/libsolidity/syntaxTests/immutable/delete.sol
Normal file
8
test/libsolidity/syntaxTests/immutable/delete.sol
Normal file
@ -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.
|
@ -0,0 +1,5 @@
|
|||||||
|
contract C {
|
||||||
|
uint immutable x = f();
|
||||||
|
|
||||||
|
function f() public pure returns (uint) { return 3; }
|
||||||
|
}
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -1,5 +0,0 @@
|
|||||||
contract C {
|
|
||||||
uint immutable public x;
|
|
||||||
}
|
|
||||||
// ----
|
|
||||||
// UnimplementedFeatureError: NONE
|
|
@ -1,3 +1,3 @@
|
|||||||
contract C {
|
contract C {
|
||||||
uint immutable x;
|
uint immutable x = 0;
|
||||||
}
|
}
|
||||||
|
8
test/libsolidity/syntaxTests/immutable/increment.sol
Normal file
8
test/libsolidity/syntaxTests/immutable/increment.sol
Normal file
@ -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.
|
@ -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.
|
15
test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol
Normal file
15
test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
contract B {
|
||||||
|
uint immutable x;
|
||||||
|
|
||||||
|
constructor() public {
|
||||||
|
x = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C is B {
|
||||||
|
uint immutable y;
|
||||||
|
constructor() public {
|
||||||
|
y = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
constructor() public {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint immutable x = 3;
|
||||||
|
}
|
||||||
|
// ----
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -0,0 +1,9 @@
|
|||||||
|
contract C {
|
||||||
|
uint immutable x;
|
||||||
|
constructor() public {
|
||||||
|
x = 1;
|
||||||
|
x = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (85-86): Immutable state variable already initialized.
|
20
test/libsolidity/syntaxTests/immutable/private_state_var.sol
Normal file
20
test/libsolidity/syntaxTests/immutable/private_state_var.sol
Normal file
@ -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.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
uint immutable x = 0;
|
||||||
|
uint y = 0;
|
||||||
|
|
||||||
|
function f() internal {
|
||||||
|
y = x + 1;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
contract C {
|
||||||
|
uint immutable x = 0;
|
||||||
|
uint y = 0;
|
||||||
|
|
||||||
|
function f() readX internal {
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier readX() {
|
||||||
|
_;
|
||||||
|
y = x + 1;
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
@ -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.
|
12
test/libsolidity/syntaxTests/immutable/selector.sol
Normal file
12
test/libsolidity/syntaxTests/immutable/selector.sol
Normal file
@ -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.
|
@ -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.
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
5
test/libsolidity/syntaxTests/immutable/uninitialized.sol
Normal file
5
test/libsolidity/syntaxTests/immutable/uninitialized.sol
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
contract C {
|
||||||
|
uint immutable x;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (0-36): Construction control flow ends without initializing all immutable state variables.
|
@ -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.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
uint immutable x = 1;
|
||||||
|
|
||||||
|
function readX() internal view returns(uint) {
|
||||||
|
return x + 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -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.
|
@ -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.
|
@ -1,8 +1,8 @@
|
|||||||
contract B {
|
contract B {
|
||||||
uint immutable x;
|
uint immutable x = 1;
|
||||||
function f() public pure returns (uint) {
|
function f() public pure returns (uint) {
|
||||||
return x;
|
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".
|
||||||
|
Loading…
Reference in New Issue
Block a user