Parsing of immutable state variable.

This commit is contained in:
chriseth 2020-02-27 16:13:55 +01:00
parent f8f18f2e55
commit fa148f2483
17 changed files with 84 additions and 12 deletions

View File

@ -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<StructDefinition const*>(_variable.scope()) ||

View File

@ -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<VariableDeclaration const*>(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);

View File

@ -508,8 +508,6 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
return set<Location>{ Location::Unspecified };
else if (isStateVariable() && isConstant())
return set<Location>{ Location::Memory };
else if (isExternalCallableParameter())
{
set<Location> locations{ Location::CallData };

View File

@ -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<OverrideSpecifier> const& overrides() const { return m_overrides; }
Location referenceLocation() const { return m_location; }
/// @returns a set of allowed storage locations for the variable.

View File

@ -1988,7 +1988,7 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
vector<VariableDeclaration const*> 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)

View File

@ -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<VariableDeclaration const*>(decl))
{
solAssert(!variable->immutable(), "");
if (variable->isConstant())
{
variable = rootConstVariableDeclaration(*variable);

View File

@ -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)

View File

@ -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<MappingType const*>(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();

View File

@ -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,

View File

@ -730,8 +730,18 @@ ASTPointer<VariableDeclaration> 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)

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
contract C {
uint immutable public x;
}
// ----
// UnimplementedFeatureError: NONE

View File

@ -0,0 +1,3 @@
contract C {
uint immutable x;
}

View File

@ -0,0 +1,5 @@
contract C {
uint[] immutable x;
}
// ----
// TypeError: (17-35): Immutable variables cannot have a non-value type.

View File

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