From 7dc7827907087c25d89589244f7fa57f5a66e48d Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 18 Dec 2014 18:53:43 +0100 Subject: [PATCH 1/6] Possibility for binary operators to yield types different from their operands'. --- AST.cpp | 35 +++++++++++++++-------------------- Types.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++---------- Types.h | 53 ++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 99 insertions(+), 41 deletions(-) diff --git a/AST.cpp b/AST.cpp index 1fa6d8f6f..c7d617b4f 100644 --- a/AST.cpp +++ b/AST.cpp @@ -180,12 +180,18 @@ void Assignment::checkTypeRequirements() //@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) + if (m_assigmentOperator == Token::ASSIGN) + m_rightHandSide->expectType(*m_type); + else + { // compound assignment - if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) + m_rightHandSide->checkTypeRequirements(); + TypePointer resultType = Type::binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), + m_type, m_rightHandSide->getType()); + if (!resultType || *resultType != *m_type) BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); + } } void ExpressionStatement::checkTypeRequirements() @@ -225,24 +231,13 @@ void BinaryOperation::checkTypeRequirements() { m_left->checkTypeRequirements(); m_right->checkTypeRequirements(); - if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) - m_commonType = m_left->getType(); - else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) - m_commonType = m_right->getType(); - else - BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation: " + - m_left->getType()->toString() + " vs. " + + m_commonType = Type::binaryOperatorResult(m_operator, m_left->getType(), m_right->getType()); + if (!m_commonType) + BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + + " not compatible with types " + + m_left->getType()->toString() + " and " + m_right->getType()->toString())); - if (Token::isCompareOp(m_operator)) - m_type = make_shared(); - else - { - m_type = m_commonType; - if (!m_commonType->acceptsBinaryOperator(m_operator)) - BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + - " not compatible with type " + - m_commonType->toString())); - } + m_type = Token::isCompareOp(m_operator) ? make_shared() : m_commonType; } void FunctionCall::checkTypeRequirements() diff --git a/Types.cpp b/Types.cpp index 71319c3ac..664b56ff6 100644 --- a/Types.cpp +++ b/Types.cpp @@ -100,6 +100,16 @@ shared_ptr Type::forLiteral(Literal const& _literal) } } +TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) +{ + if (_b->isImplicitlyConvertibleTo(*_a)) + return _a; + else if (_a->isImplicitlyConvertibleTo(*_b)) + return _b; + else + return TypePointer(); +} + const MemberList Type::EmptyMemberList = MemberList(); shared_ptr IntegerType::smallestTypeForLiteral(string const& _literal) @@ -146,16 +156,6 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT; } -bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const -{ - if (isAddress()) - return Token::isCompareOp(_operator); - else if (isHash()) - return Token::isCompareOp(_operator) || Token::isBitOp(_operator); - else - return true; -} - bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const { if (_operator == Token::DELETE) @@ -192,6 +192,28 @@ u256 IntegerType::literalValue(Literal const& _literal) const return u256(value); } +TypePointer IntegerType::binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const +{ + if (getCategory() != _other->getCategory()) + return TypePointer(); + auto commonType = dynamic_pointer_cast(Type::commonType(_this, _other)); + + if (!commonType) + return TypePointer(); + + if (commonType->isAddress()) + { + if (!Token::isCompareOp(_operator)) + return TypePointer(); + } + else if (commonType->isHash()) + { + if (!(Token::isCompareOp(_operator) || Token::isBitOp(_operator))) + return TypePointer(); + } + return commonType; +} + const MemberList IntegerType::AddressMemberList = MemberList({{"balance", make_shared(256)}, @@ -266,6 +288,16 @@ u256 BoolType::literalValue(Literal const& _literal) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); } +TypePointer BoolType::binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const +{ + if (getCategory() != _other->getCategory()) + return TypePointer(); + if (Token::isCompareOp(_operator) || _operator == Token::AND || _operator == Token::OR) + return _this; + else + return TypePointer(); +} + bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (isImplicitlyConvertibleTo(_convertTo)) diff --git a/Types.h b/Types.h index 48539a1d7..0afa0ccd3 100644 --- a/Types.h +++ b/Types.h @@ -81,15 +81,23 @@ public: ///@{ ///@name Factory functions /// Factory functions that convert an AST @ref TypeName to a Type. - static std::shared_ptr fromElementaryTypeName(Token::Value _typeToken); - static std::shared_ptr fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); - static std::shared_ptr fromMapping(Mapping const& _typeName); - static std::shared_ptr fromFunction(FunctionDefinition const& _function); + static TypePointer fromElementaryTypeName(Token::Value _typeToken); + static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); + static TypePointer fromMapping(Mapping const& _typeName); + static TypePointer fromFunction(FunctionDefinition const& _function); /// @} /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does /// not fit any type. - static std::shared_ptr forLiteral(Literal const& _literal); + static TypePointer forLiteral(Literal const& _literal); + /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise + static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); + /// @returns the resulting type of applying the given operator or an empty pointer if this is not possible. + /// The default implementation allows comparison operators if a common type exists + static TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _a, TypePointer const& _b) + { + return _a->binaryOperatorResultImpl(_operator, _a, _b); + } virtual Category getCategory() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } @@ -97,7 +105,6 @@ public: { return isImplicitlyConvertibleTo(_convertTo); } - virtual bool acceptsBinaryOperator(Token::Value) const { return false; } virtual bool acceptsUnaryOperator(Token::Value) const { return false; } virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); } @@ -131,6 +138,11 @@ public: } protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _a, TypePointer const& _b) const + { + return Token::isCompareOp(_operator) ? commonType(_a, _b) : TypePointer(); + } + /// Convenience object used when returning an empty member list. static const MemberList EmptyMemberList; }; @@ -155,7 +167,6 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool acceptsBinaryOperator(Token::Value _operator) const override; virtual bool acceptsUnaryOperator(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; @@ -173,6 +184,9 @@ public: bool isAddress() const { return m_modifier == Modifier::ADDRESS; } int isSigned() const { return m_modifier == Modifier::SIGNED; } +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override; + private: int m_bits; Modifier m_modifier; @@ -217,10 +231,6 @@ public: BoolType() {} virtual Category getCategory() const { return Category::BOOL; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool acceptsBinaryOperator(Token::Value _operator) const override - { - return _operator == Token::AND || _operator == Token::OR; - } virtual bool acceptsUnaryOperator(Token::Value _operator) const override { return _operator == Token::NOT || _operator == Token::DELETE; @@ -231,6 +241,9 @@ public: virtual std::string toString() const override { return "bool"; } virtual u256 literalValue(Literal const& _literal) const override; + +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override; }; /** @@ -369,6 +382,12 @@ public: virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } + +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override + { + return TypePointer(); + } }; /** @@ -389,6 +408,12 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override + { + return TypePointer(); + } + private: TypePointer m_actualType; }; @@ -413,6 +438,12 @@ public: virtual std::string toString() const override; +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override + { + return TypePointer(); + } + private: Kind m_kind; From 8442974617e7d3b357144bd88c64904a12cd976f Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 28 Dec 2014 13:35:58 +0100 Subject: [PATCH 2/6] Clarified binary operator checking for integer types. --- Types.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Types.cpp b/Types.cpp index 664b56ff6..c87ce2e60 100644 --- a/Types.cpp +++ b/Types.cpp @@ -201,17 +201,17 @@ TypePointer IntegerType::binaryOperatorResultImpl(Token::Value _operator, TypePo if (!commonType) return TypePointer(); + // All integer types can be compared + if (Token::isCompareOp(_operator)) + return commonType; + + // Nothing else can be done with addresses, but hashes can receive bit operators if (commonType->isAddress()) - { - if (!Token::isCompareOp(_operator)) - return TypePointer(); - } - else if (commonType->isHash()) - { - if (!(Token::isCompareOp(_operator) || Token::isBitOp(_operator))) - return TypePointer(); - } - return commonType; + return TypePointer(); + else if (commonType->isHash() && !Token::isBitOp(_operator)) + return TypePointer(); + else + return commonType; } const MemberList IntegerType::AddressMemberList = From 52240ea88fb037f38d8458d5c4b24fb8dfaa0308 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 7 Jan 2015 12:50:23 +0100 Subject: [PATCH 3/6] Bugfix: Use parameter (not argument) type size on stack for function calls. --- ExpressionCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 6bf14f559..aa7406132 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -217,7 +217,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes()); // callee adds return parameters, but removes arguments and return label - m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1); + m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(function.getParameterTypes()) - 1); // @todo for now, the return value of a function is its first return value, so remove // all others From 9866caa625d467f5460e272eee5d69761d8d830b Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 7 Jan 2015 16:46:15 +0100 Subject: [PATCH 4/6] Fix some warnings about uninitialized members. --- Compiler.h | 2 +- CompilerStack.h | 2 +- ExpressionCompiler.h | 6 +++--- GlobalContext.h | 2 +- NameAndTypeResolver.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Compiler.h b/Compiler.h index 8471fae2a..e83d1ed3a 100644 --- a/Compiler.h +++ b/Compiler.h @@ -30,7 +30,7 @@ namespace solidity { class Compiler: private ASTConstVisitor { public: - explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {} + explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), m_returnTag(m_context.newTag()) {} void compileContract(ContractDefinition const& _contract, std::vector const& _magicGlobals, std::map const& _contracts); diff --git a/CompilerStack.h b/CompilerStack.h index 358c8fb77..e7143b7bb 100644 --- a/CompilerStack.h +++ b/CompilerStack.h @@ -113,7 +113,7 @@ private: struct Contract { - ContractDefinition const* contract; + ContractDefinition const* contract = nullptr; std::shared_ptr compiler; bytes bytecode; std::shared_ptr interfaceHandler; diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 67b16aac0..c0ee4ab48 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -146,12 +146,12 @@ private: private: CompilerContext* m_context; - LValueType m_type; + LValueType m_type = NONE; /// If m_type is STACK, this is base stack offset (@see /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. - unsigned m_baseStackOffset; + unsigned m_baseStackOffset = 0; /// Size of the value of this lvalue on the stack. - unsigned m_stackSize; + unsigned m_stackSize = 0; }; bool m_optimize; diff --git a/GlobalContext.h b/GlobalContext.h index 50a21f702..c6e35f504 100644 --- a/GlobalContext.h +++ b/GlobalContext.h @@ -56,7 +56,7 @@ public: private: std::vector> m_magicVariables; - ContractDefinition const* m_currentContract; + ContractDefinition const* m_currentContract = nullptr; std::map> mutable m_thisPointer; }; diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h index 23ac5fe77..1032a87cf 100644 --- a/NameAndTypeResolver.h +++ b/NameAndTypeResolver.h @@ -69,7 +69,7 @@ private: /// not contain code. std::map m_scopes; - DeclarationContainer* m_currentScope; + DeclarationContainer* m_currentScope = nullptr; }; /** From 238aa0ee9405f8e6d24611bca2858b980bbcba87 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 7 Jan 2015 16:58:09 +0100 Subject: [PATCH 5/6] Warnings fixes. Make Mix work with Qt 5.2 Minor other alterations. --- Types.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Types.h b/Types.h index ff8a48877..6f3ca6abf 100644 --- a/Types.h +++ b/Types.h @@ -387,6 +387,9 @@ public: protected: virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override { + (void)_operator; + (void)_this; + (void)_other; return TypePointer(); } }; From 9be58474729c918fb40e4f264d25d74d7a162607 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 7 Jan 2015 20:23:33 +0100 Subject: [PATCH 6/6] VM skips push data when looking for JUMPDEST. Warnings fixes. --- Types.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Types.h b/Types.h index 6f3ca6abf..a91a6c24e 100644 --- a/Types.h +++ b/Types.h @@ -415,6 +415,9 @@ public: protected: virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override { + (void)_operator; + (void)_this; + (void)_other; return TypePointer(); } @@ -445,6 +448,9 @@ public: protected: virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override { + (void)_operator; + (void)_this; + (void)_other; return TypePointer(); }