diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 5c07ec80e..4bd0b2c0e 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -378,6 +378,9 @@ void Assignment::checkTypeRequirements() { m_leftHandSide->checkTypeRequirements(); m_leftHandSide->requireLValue(); + //@todo later, assignments to structs might be possible, but not to mappings + if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue()) + BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue.")); m_rightHandSide->expectType(*m_leftHandSide->getType()); m_type = m_leftHandSide->getType(); if (m_assigmentOperator != Token::ASSIGN) @@ -403,7 +406,7 @@ void Expression::expectType(Type const& _expectedType) void Expression::requireLValue() { - if (!isLvalue()) + if (!isLValue()) BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue.")); m_lvalueRequested = true; } @@ -495,7 +498,8 @@ void MemberAccess::checkTypeRequirements() m_type = type.getMemberType(*m_memberName); if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString())); - m_isLvalue = (type.getCategory() == Type::Category::STRUCT && m_type->getCategory() != Type::Category::MAPPING); + //@todo later, this will not always be STORAGE + m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE; } void IndexAccess::checkTypeRequirements() @@ -507,7 +511,7 @@ void IndexAccess::checkTypeRequirements() MappingType const& type = dynamic_cast(*m_base->getType()); m_index->expectType(*type.getKeyType()); m_type = type.getValueType(); - m_isLvalue = m_type->getCategory() != Type::Category::MAPPING; + m_lvalue = LValueType::STORAGE; } void Identifier::checkTypeRequirements() @@ -521,7 +525,7 @@ void Identifier::checkTypeRequirements() if (!variable->getType()) BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined.")); m_type = variable->getType(); - m_isLvalue = true; + m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE; return; } //@todo can we unify these with TypeName::toType()? diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 68b5c8b80..87bc3cd40 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -242,6 +242,8 @@ public: std::shared_ptr const& getType() const { return m_type; } void setType(std::shared_ptr const& _type) { m_type = _type; } + bool isLocalVariable() const { return !!dynamic_cast(getScope()); } + private: ASTPointer m_typeName; ///< can be empty ("var") @@ -526,12 +528,16 @@ private: */ class Expression: public ASTNode { +protected: + enum class LValueType { NONE, LOCAL, STORAGE }; + public: - Expression(Location const& _location): ASTNode(_location), m_isLvalue(false), m_lvalueRequested(false) {} + Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {} virtual void checkTypeRequirements() = 0; std::shared_ptr const& getType() const { return m_type; } - bool isLvalue() const { return m_isLvalue; } + bool isLValue() const { return m_lvalue != LValueType::NONE; } + bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; } /// Helper function, infer the type via @ref checkTypeRequirements and then check that it /// is implicitly convertible to @a _expectedType. If not, throw exception. @@ -546,9 +552,9 @@ public: protected: //! Inferred type of the expression, only filled after a call to checkTypeRequirements(). std::shared_ptr m_type; - //! Whether or not this expression is an lvalue, i.e. something that can be assigned to. - //! This is set during calls to @a checkTypeRequirements() - bool m_isLvalue; + //! If this expression is an lvalue (i.e. something that can be assigned to) and is stored + //! locally or in storage. This is set during calls to @a checkTypeRequirements() + LValueType m_lvalue; //! Whether the outer expression requested the address (true) or the value (false) of this expression. bool m_lvalueRequested; }; diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index a19e7450c..8804c519c 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -244,6 +244,36 @@ BOOST_AUTO_TEST_CASE(balance_invalid) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(assignment_to_mapping) +{ + char const* text = "contract test {\n" + " struct str {\n" + " mapping(uint=>uint) map;\n" + " }\n" + " str data;" + " function fun() {\n" + " var a = data.map;\n" + " data.map = a;\n" + " }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(assignment_to_struct) +{ + char const* text = "contract test {\n" + " struct str {\n" + " mapping(uint=>uint) map;\n" + " }\n" + " str data;" + " function fun() {\n" + " var a = data;\n" + " data = a;\n" + " }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() }