aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/ast
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/ast')
-rw-r--r--libsolidity/ast/Types.cpp154
-rw-r--r--libsolidity/ast/Types.h22
2 files changed, 121 insertions, 55 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 21631693..68c201a8 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -355,13 +355,7 @@ TypePointer Type::forLiteral(Literal const& _literal)
case Token::FalseLiteral:
return make_shared<BoolType>();
case Token::Number:
- {
- tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal);
- if (get<0>(validLiteral) == true)
- return make_shared<RationalNumberType>(get<1>(validLiteral));
- else
- return TypePointer();
- }
+ return RationalNumberType::forLiteral(_literal);
case Token::StringLiteral:
return make_shared<StringLiteralType>(_literal);
default:
@@ -400,17 +394,17 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _p
encodingType = encodingType->interfaceType(_inLibraryCall);
if (encodingType)
encodingType = encodingType->encodingType();
- if (auto structType = dynamic_cast<StructType const*>(encodingType.get()))
- {
- // Structs are fine in the following circumstances:
- // - ABIv2 without packed encoding or,
- // - storage struct for a library
- if (!(
- (_encoderV2 && !_packed) ||
- (structType->location() == DataLocation::Storage && _inLibraryCall)
- ))
+ // Structs are fine in the following circumstances:
+ // - ABIv2 without packed encoding or,
+ // - storage struct for a library
+ if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage))
+ return encodingType;
+ TypePointer baseType = encodingType;
+ while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType.get()))
+ baseType = arrayType->baseType();
+ if (dynamic_cast<StructType const*>(baseType.get()))
+ if (!_encoderV2 || _packed)
return TypePointer();
- }
return encodingType;
}
@@ -779,6 +773,30 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
}
}
+TypePointer RationalNumberType::forLiteral(Literal const& _literal)
+{
+ solAssert(_literal.token() == Token::Number, "");
+ tuple<bool, rational> validLiteral = isValidLiteral(_literal);
+ if (get<0>(validLiteral))
+ {
+ TypePointer compatibleBytesType;
+ if (_literal.isHexNumber())
+ {
+ size_t digitCount = count_if(
+ _literal.value().begin() + 2, // skip "0x"
+ _literal.value().end(),
+ [](unsigned char _c) -> bool { return isxdigit(_c); }
+ );
+ // require even number of digits
+ if (!(digitCount & 1))
+ compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2);
+ }
+
+ return make_shared<RationalNumberType>(get<1>(validLiteral), compatibleBytesType);
+ }
+ return TypePointer();
+}
+
tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal)
{
rational value;
@@ -918,14 +936,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
case Category::FixedBytes:
- {
- FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
- if (isFractional())
- return false;
- if (integerType())
- return fixedBytes.numBytes() * 8 >= integerType()->numBits();
- return false;
- }
+ return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
default:
return false;
}
@@ -933,11 +944,15 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- TypePointer mobType = mobileType();
- return
- (mobType && mobType->isExplicitlyConvertibleTo(_convertTo)) ||
- (!isFractional() && _convertTo.category() == Category::FixedBytes)
- ;
+ if (isImplicitlyConvertibleTo(_convertTo))
+ return true;
+ else if (_convertTo.category() != Category::FixedBytes)
+ {
+ TypePointer mobType = mobileType();
+ return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo));
+ }
+ else
+ return false;
}
TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const
@@ -2555,28 +2570,10 @@ bool FunctionType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
-
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
- if (
- m_kind != other.m_kind ||
- m_stateMutability != other.stateMutability() ||
- m_parameterTypes.size() != other.m_parameterTypes.size() ||
- m_returnParameterTypes.size() != other.m_returnParameterTypes.size()
- )
- return false;
-
- auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
- if (
- !equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) ||
- !equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare)
- )
- return false;
- //@todo this is ugly, but cannot be prevented right now
- if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
- return false;
- if (bound() != other.bound())
+ if (!equalExcludingStateMutability(other))
return false;
- if (bound() && *selfType() != *other.selfType())
+ if (m_stateMutability != other.stateMutability())
return false;
return true;
}
@@ -2592,6 +2589,31 @@ bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return _convertTo.category() == category();
}
+bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (_convertTo.category() != category())
+ return false;
+
+ FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
+
+ if (!equalExcludingStateMutability(convertTo))
+ return false;
+
+ // non-payable should not be convertible to payable
+ if (m_stateMutability != StateMutability::Payable && convertTo.stateMutability() == StateMutability::Payable)
+ return false;
+
+ // payable should be convertible to non-payable, because you are free to pay 0 ether
+ if (m_stateMutability == StateMutability::Payable && convertTo.stateMutability() == StateMutability::NonPayable)
+ return true;
+
+ // e.g. pure should be convertible to view, but not the other way around.
+ if (m_stateMutability > convertTo.stateMutability())
+ return false;
+
+ return true;
+}
+
TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::Value::Delete)
@@ -2849,6 +2871,38 @@ bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const
);
}
+bool FunctionType::hasEqualReturnTypes(FunctionType const& _other) const
+{
+ if (m_returnParameterTypes.size() != _other.m_returnParameterTypes.size())
+ return false;
+ return equal(
+ m_returnParameterTypes.cbegin(),
+ m_returnParameterTypes.cend(),
+ _other.m_returnParameterTypes.cbegin(),
+ [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }
+ );
+}
+
+bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) const
+{
+ if (m_kind != _other.m_kind)
+ return false;
+
+ if (!hasEqualParameterTypes(_other) || !hasEqualReturnTypes(_other))
+ return false;
+
+ //@todo this is ugly, but cannot be prevented right now
+ if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet)
+ return false;
+
+ if (bound() != _other.bound())
+ return false;
+
+ solAssert(!bound() || *selfType() == *_other.selfType(), "");
+
+ return true;
+}
+
bool FunctionType::isBareCall() const
{
switch (m_kind)
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 787c9360..34f862c3 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -423,12 +423,12 @@ public:
virtual Category category() const override { return Category::RationalNumber; }
- /// @returns true if the literal is a valid integer.
- static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
+ static TypePointer forLiteral(Literal const& _literal);
- explicit RationalNumberType(rational const& _value):
- m_value(_value)
+ explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
+ m_value(_value), m_compatibleBytesType(_compatibleBytesType)
{}
+
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
@@ -463,6 +463,13 @@ public:
private:
rational m_value;
+ /// Bytes type to which the rational can be explicitly converted.
+ /// Empty for all rationals that are not directly parsed from hex literals.
+ TypePointer m_compatibleBytesType;
+
+ /// @returns true if the literal is a valid integer.
+ static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
+
/// @returns true if the literal is a valid rational number.
static std::tuple<bool, rational> parseRational(std::string const& _value);
@@ -1006,6 +1013,7 @@ public:
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
@@ -1035,8 +1043,12 @@ public:
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
/// expression the function is called on.
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
- /// @returns true if the types of parameters are equal (doesn't check return parameter types)
+ /// @returns true if the types of parameters are equal (does not check return parameter types)
bool hasEqualParameterTypes(FunctionType const& _other) const;
+ /// @returns true iff the return types are equal (does not check parameter types)
+ bool hasEqualReturnTypes(FunctionType const& _other) const;
+ /// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
+ bool equalExcludingStateMutability(FunctionType const& _other) const;
/// @returns true if the ABI is used for this call (only meaningful for external calls)
bool isBareCall() const;