mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Possibility for binary operators to yield types different from their operands'.
This commit is contained in:
		
							parent
							
								
									59835e9df1
								
							
						
					
					
						commit
						7dc7827907
					
				
							
								
								
									
										35
									
								
								AST.cpp
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								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<BoolType>(); | ||||
| 	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<BoolType>() : m_commonType; | ||||
| } | ||||
| 
 | ||||
| void FunctionCall::checkTypeRequirements() | ||||
|  | ||||
							
								
								
									
										52
									
								
								Types.cpp
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								Types.cpp
									
									
									
									
									
								
							| @ -100,6 +100,16 @@ shared_ptr<Type const> 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 const> 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<IntegerType const>(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<IntegerType const>(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)) | ||||
|  | ||||
							
								
								
									
										53
									
								
								Types.h
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								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<Type const> fromElementaryTypeName(Token::Value _typeToken); | ||||
| 	static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); | ||||
| 	static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName); | ||||
| 	static std::shared_ptr<Type const> 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<Type const> 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; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user