mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #667 from chriseth/sol_moreFlexibleBinaryOps
Possibility for binary operators to yield types different from their operands'.
This commit is contained in:
commit
95514d1d1b
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
|
//@todo later, assignments to structs might be possible, but not to mappings
|
||||||
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
|
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
|
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
|
||||||
m_rightHandSide->expectType(*m_leftHandSide->getType());
|
|
||||||
m_type = 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
|
// 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."));
|
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionStatement::checkTypeRequirements()
|
void ExpressionStatement::checkTypeRequirements()
|
||||||
@ -225,24 +231,13 @@ void BinaryOperation::checkTypeRequirements()
|
|||||||
{
|
{
|
||||||
m_left->checkTypeRequirements();
|
m_left->checkTypeRequirements();
|
||||||
m_right->checkTypeRequirements();
|
m_right->checkTypeRequirements();
|
||||||
if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType()))
|
m_commonType = Type::binaryOperatorResult(m_operator, m_left->getType(), m_right->getType());
|
||||||
m_commonType = m_left->getType();
|
if (!m_commonType)
|
||||||
else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType()))
|
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
|
||||||
m_commonType = m_right->getType();
|
" not compatible with types " +
|
||||||
else
|
m_left->getType()->toString() + " and " +
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation: " +
|
|
||||||
m_left->getType()->toString() + " vs. " +
|
|
||||||
m_right->getType()->toString()));
|
m_right->getType()->toString()));
|
||||||
if (Token::isCompareOp(m_operator))
|
m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType;
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionCall::checkTypeRequirements()
|
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();
|
const MemberList Type::EmptyMemberList = MemberList();
|
||||||
|
|
||||||
shared_ptr<IntegerType const> IntegerType::smallestTypeForLiteral(string const& _literal)
|
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;
|
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
|
bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const
|
||||||
{
|
{
|
||||||
if (_operator == Token::DELETE)
|
if (_operator == Token::DELETE)
|
||||||
@ -192,6 +192,28 @@ u256 IntegerType::literalValue(Literal const& _literal) const
|
|||||||
return u256(value);
|
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();
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
return TypePointer();
|
||||||
|
else if (commonType->isHash() && !Token::isBitOp(_operator))
|
||||||
|
return TypePointer();
|
||||||
|
else
|
||||||
|
return commonType;
|
||||||
|
}
|
||||||
|
|
||||||
const MemberList IntegerType::AddressMemberList =
|
const MemberList IntegerType::AddressMemberList =
|
||||||
MemberList({{"balance",
|
MemberList({{"balance",
|
||||||
make_shared<IntegerType const>(256)},
|
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."));
|
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
|
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (isImplicitlyConvertibleTo(_convertTo))
|
if (isImplicitlyConvertibleTo(_convertTo))
|
||||||
|
53
Types.h
53
Types.h
@ -81,15 +81,23 @@ public:
|
|||||||
///@{
|
///@{
|
||||||
///@name Factory functions
|
///@name Factory functions
|
||||||
/// Factory functions that convert an AST @ref TypeName to a Type.
|
/// Factory functions that convert an AST @ref TypeName to a Type.
|
||||||
static std::shared_ptr<Type const> fromElementaryTypeName(Token::Value _typeToken);
|
static TypePointer fromElementaryTypeName(Token::Value _typeToken);
|
||||||
static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
|
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
|
||||||
static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName);
|
static TypePointer fromMapping(Mapping const& _typeName);
|
||||||
static std::shared_ptr<Type const> fromFunction(FunctionDefinition const& _function);
|
static TypePointer fromFunction(FunctionDefinition const& _function);
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
||||||
/// not fit any type.
|
/// 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 Category getCategory() const = 0;
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
||||||
@ -97,7 +105,6 @@ public:
|
|||||||
{
|
{
|
||||||
return isImplicitlyConvertibleTo(_convertTo);
|
return isImplicitlyConvertibleTo(_convertTo);
|
||||||
}
|
}
|
||||||
virtual bool acceptsBinaryOperator(Token::Value) const { return false; }
|
|
||||||
virtual bool acceptsUnaryOperator(Token::Value) const { return false; }
|
virtual bool acceptsUnaryOperator(Token::Value) const { return false; }
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
|
virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
|
||||||
@ -131,6 +138,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
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.
|
/// Convenience object used when returning an empty member list.
|
||||||
static const MemberList EmptyMemberList;
|
static const MemberList EmptyMemberList;
|
||||||
};
|
};
|
||||||
@ -155,7 +167,6 @@ public:
|
|||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual bool isExplicitlyConvertibleTo(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 acceptsUnaryOperator(Token::Value _operator) const override;
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
@ -173,6 +184,9 @@ public:
|
|||||||
bool isAddress() const { return m_modifier == Modifier::ADDRESS; }
|
bool isAddress() const { return m_modifier == Modifier::ADDRESS; }
|
||||||
int isSigned() const { return m_modifier == Modifier::SIGNED; }
|
int isSigned() const { return m_modifier == Modifier::SIGNED; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_bits;
|
int m_bits;
|
||||||
Modifier m_modifier;
|
Modifier m_modifier;
|
||||||
@ -217,10 +231,6 @@ public:
|
|||||||
BoolType() {}
|
BoolType() {}
|
||||||
virtual Category getCategory() const { return Category::BOOL; }
|
virtual Category getCategory() const { return Category::BOOL; }
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
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
|
virtual bool acceptsUnaryOperator(Token::Value _operator) const override
|
||||||
{
|
{
|
||||||
return _operator == Token::NOT || _operator == Token::DELETE;
|
return _operator == Token::NOT || _operator == Token::DELETE;
|
||||||
@ -231,6 +241,9 @@ public:
|
|||||||
|
|
||||||
virtual std::string toString() const override { return "bool"; }
|
virtual std::string toString() const override { return "bool"; }
|
||||||
virtual u256 literalValue(Literal const& _literal) const override;
|
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 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 bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
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 bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
|
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:
|
private:
|
||||||
TypePointer m_actualType;
|
TypePointer m_actualType;
|
||||||
};
|
};
|
||||||
@ -413,6 +438,12 @@ public:
|
|||||||
|
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override
|
||||||
|
{
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Kind m_kind;
|
Kind m_kind;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user