From fa148f2483fac9315dd5452a02707bbfffe8014f Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 27 Feb 2020 16:13:55 +0100 Subject: [PATCH] Parsing of immutable state variable. --- libsolidity/analysis/ReferencesResolver.cpp | 4 +++- libsolidity/analysis/TypeChecker.cpp | 9 +++++++++ libsolidity/ast/AST.cpp | 2 -- libsolidity/ast/AST.h | 1 + libsolidity/ast/Types.cpp | 2 +- libsolidity/codegen/ContractCompiler.cpp | 5 ++++- libsolidity/codegen/ExpressionCompiler.cpp | 10 ++++++---- libsolidity/codegen/ir/IRGenerator.cpp | 3 ++- .../codegen/ir/IRGeneratorForStatements.cpp | 2 ++ libsolidity/parsing/Parser.cpp | 14 ++++++++++++-- .../syntaxTests/immutable/as_function_param.sol | 5 +++++ .../libsolidity/syntaxTests/immutable/assembly.sol | 11 +++++++++++ .../syntaxTests/immutable/double_specifier.sol | 7 +++++++ test/libsolidity/syntaxTests/immutable/getter.sol | 5 +++++ .../syntaxTests/immutable/immutable_basic.sol | 3 +++ .../syntaxTests/immutable/non-value_type.sol | 5 +++++ .../syntaxTests/viewPureChecker/immutable.sol | 8 ++++++++ 17 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/syntaxTests/immutable/as_function_param.sol create mode 100644 test/libsolidity/syntaxTests/immutable/assembly.sol create mode 100644 test/libsolidity/syntaxTests/immutable/double_specifier.sol create mode 100644 test/libsolidity/syntaxTests/immutable/getter.sol create mode 100644 test/libsolidity/syntaxTests/immutable/immutable_basic.sol create mode 100644 test/libsolidity/syntaxTests/immutable/non-value_type.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/immutable.sol diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 6867f049c..8ea07f182 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -328,6 +328,8 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) if (_variable.isConstant() && !_variable.isStateVariable()) m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables."); + if (_variable.immutable() && !_variable.isStateVariable()) + m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables."); if (!_variable.typeName()) { @@ -394,7 +396,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) else if (_variable.isStateVariable()) { solAssert(varLoc == Location::Unspecified, ""); - typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage; + typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage; } else if ( dynamic_cast(_variable.scope()) || diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a9b2b5b9f..1345fbf90 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -480,6 +480,10 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) "Initial value for constant variable has to be compile-time constant." ); } + else if (_variable.immutable()) + if (!_variable.type()->isValueType()) + m_errorReporter.typeError(_variable.location(), "Immutable variables cannot have a non-value type."); + if (!_variable.isStateVariable()) { if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) @@ -641,6 +645,11 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (auto var = dynamic_cast(declaration)) { solAssert(var->type(), "Expected variable type!"); + if (var->immutable()) + { + m_errorReporter.typeError(_identifier.location, "Assembly access to immutable variables is not supported."); + return size_t(-1); + } if (var->isConstant()) { var = rootConstVariableDeclaration(*var); diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6b03c7b58..a76e6e274 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -508,8 +508,6 @@ set VariableDeclaration::allowedDataLocations() c if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter()) return set{ Location::Unspecified }; - else if (isStateVariable() && isConstant()) - return set{ Location::Memory }; else if (isExternalCallableParameter()) { set locations{ Location::CallData }; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index adb957cfe..052970bc8 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -879,6 +879,7 @@ public: bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } bool isConstant() const { return m_constantness == Constantness::Constant; } + bool immutable() const { return m_constantness == Constantness::Immutable; } ASTPointer const& overrides() const { return m_overrides; } Location referenceLocation() const { return m_location; } /// @returns a set of allowed storage locations for the variable. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 454ca8f3c..d760e0810 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1988,7 +1988,7 @@ vector> ContractType::stateVar vector variables; for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) for (VariableDeclaration const* variable: contract->stateVariables()) - if (!variable->isConstant()) + if (!(variable->isConstant() || variable->immutable())) variables.push_back(variable); TypePointers types; for (auto variable: variables) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 21886faaf..6fffbd5b5 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -525,7 +525,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr { solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); for (VariableDeclaration const* variable: _contract.stateVariables()) - if (variable->value() && !variable->isConstant()) + if (variable->value() && !variable->isConstant() && !variable->immutable()) ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable); } @@ -541,6 +541,8 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration) if (_variableDeclaration.isConstant()) ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals) .appendConstStateVariableAccessor(_variableDeclaration); + else if (_variableDeclaration.immutable()) + solUnimplementedAssert(false, ""); else ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals) .appendStateVariableAccessor(_variableDeclaration); @@ -680,6 +682,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } else if (auto variable = dynamic_cast(decl)) { + solAssert(!variable->immutable(), ""); if (variable->isConstant()) { variable = rootConstVariableDeclaration(*variable); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 0fc24d919..2b6285c0a 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -88,7 +88,7 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - solAssert(!_varDecl.isConstant(), ""); + solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), ""); CompilerContext::LocationSetter locationSetter(m_context, _varDecl); FunctionType accessorType(_varDecl); @@ -2438,10 +2438,12 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression) { - if (!_variable.isConstant()) - setLValueFromDeclaration(_variable, _expression); - else + if (_variable.isConstant()) acceptAndConvert(*_variable.value(), *_variable.annotation().type); + else if (_variable.immutable()) + solUnimplemented(""); + else + setLValueFromDeclaration(_variable, _expression); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 8da576055..9c7d9c8ac 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -157,6 +157,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) Type const* type = _varDecl.annotation().type; solAssert(!_varDecl.isConstant(), ""); + solAssert(!_varDecl.immutable(), ""); solAssert(_varDecl.isStateVariable(), ""); if (auto const* mappingType = dynamic_cast(type)) @@ -249,7 +250,7 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract) IRGeneratorForStatements generator{m_context, m_utils}; for (VariableDeclaration const* variable: contract->stateVariables()) - if (!variable->isConstant()) + if (!variable->isConstant() && !variable->immutable()) generator.initializeStateVar(*variable); out << generator.code(); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index e370acd7c..5a413a449 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -140,6 +140,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va { solAssert(m_context.isStateVariable(_varDecl), "Must be a state variable."); solAssert(!_varDecl.isConstant(), ""); + solAssert(!_varDecl.immutable(), ""); if (_varDecl.value()) { _varDecl.value()->accept(*this); @@ -1123,6 +1124,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) // If the value is visited twice, `defineExpression` is called twice on // the same expression. solUnimplementedAssert(!varDecl->isConstant(), ""); + solUnimplementedAssert(!varDecl->immutable(), ""); if (m_context.isLocalVariable(*varDecl)) setLValue(_identifier, IRLValue{ *varDecl->annotation().type, diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index edf43377f..2f23c8c22 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -730,8 +730,18 @@ ASTPointer Parser::parseVariableDeclaration( { if (_options.allowIndexed && token == Token::Indexed) isIndexed = true; - else if (token == Token::Constant) - constantness = VariableDeclaration::Constantness::Constant; + else if (token == Token::Constant || token == Token::Immutable) + { + if (constantness != VariableDeclaration::Constantness::Mutable) + parserError( + string("Constantness already set to ") + + (constantness == VariableDeclaration::Constantness::Constant ? "\"constant\"" : "\"immutable\"") + ); + else if (token == Token::Constant) + constantness = VariableDeclaration::Constantness::Constant; + else if (token == Token::Immutable) + constantness = VariableDeclaration::Constantness::Immutable; + } else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { if (location != VariableDeclaration::Location::Unspecified) diff --git a/test/libsolidity/syntaxTests/immutable/as_function_param.sol b/test/libsolidity/syntaxTests/immutable/as_function_param.sol new file mode 100644 index 000000000..3636b0aef --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/as_function_param.sol @@ -0,0 +1,5 @@ +contract C { + function f(uint immutable) public pure {} +} +// ---- +// DeclarationError: (28-42): The "immutable" keyword can only be used for state variables. diff --git a/test/libsolidity/syntaxTests/immutable/assembly.sol b/test/libsolidity/syntaxTests/immutable/assembly.sol new file mode 100644 index 000000000..7b98b67c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/assembly.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + function f() public view { + uint t; + assembly { + t := x + } + } +} +// ---- +// TypeError: (118-119): Assembly access to immutable variables is not supported. diff --git a/test/libsolidity/syntaxTests/immutable/double_specifier.sol b/test/libsolidity/syntaxTests/immutable/double_specifier.sol new file mode 100644 index 000000000..39f0f1e76 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/double_specifier.sol @@ -0,0 +1,7 @@ +contract C { + uint immutable immutable x; + uint immutable constant x; +} +// ---- +// ParserError: (32-41): Constantness already set to "immutable" +// ParserError: (64-72): Constantness already set to "immutable" diff --git a/test/libsolidity/syntaxTests/immutable/getter.sol b/test/libsolidity/syntaxTests/immutable/getter.sol new file mode 100644 index 000000000..7740f8643 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/getter.sol @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000..b4960f25b --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol @@ -0,0 +1,3 @@ +contract C { + uint immutable x; +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/immutable/non-value_type.sol b/test/libsolidity/syntaxTests/immutable/non-value_type.sol new file mode 100644 index 000000000..67398ce20 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/non-value_type.sol @@ -0,0 +1,5 @@ +contract C { + uint[] immutable x; +} +// ---- +// TypeError: (17-35): Immutable variables cannot have a non-value type. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol new file mode 100644 index 000000000..1c0e7a46f --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol @@ -0,0 +1,8 @@ +contract B { + uint immutable x; + 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".