diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 7235b57aa..e414e27c4 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1529,6 +1529,8 @@ bool TypeChecker::visit(Identifier const& _identifier) !!annotation.referencedDeclaration, "Referenced declaration is null after overload resolution." ); + auto variableDeclaration = dynamic_cast(annotation.referencedDeclaration); + annotation.isConstant = variableDeclaration != nullptr && variableDeclaration->isConstant(); annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.type = annotation.referencedDeclaration->type(); if (!annotation.type) @@ -1612,7 +1614,10 @@ void TypeChecker::requireLValue(Expression const& _expression) { _expression.annotation().lValueRequested = true; _expression.accept(*this); - if (!_expression.annotation().isLValue) + + if (_expression.annotation().isConstant) + typeError(_expression.location(), "Cannot assign to a constant variable."); + else if (!_expression.annotation().isLValue) typeError(_expression.location(), "Expression has to be an lvalue."); } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 768e56db5..9c4c3ae8b 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -154,6 +154,8 @@ struct ExpressionAnnotation: ASTAnnotation { /// Inferred type of the expression. TypePointer type; + /// Whether the expression is a constant variable + bool isConstant = false; /// Whether it is an LValue (i.e. something that can be assigned to). bool isLValue = false; /// Whether the expression is used in a context where the LValue is actually required. diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a4e601f74..576421fdf 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4865,6 +4865,17 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_modifier_following_non_payable_p CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(assignment_to_constant) +{ + char const* text = R"( + contract c { + uint constant a = 1; + function f() { a = 2; } + } + )"; + CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); +} + BOOST_AUTO_TEST_SUITE_END() }