From 87804b6419a5894601441efe511015adda5fb119 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 3 Sep 2018 17:45:58 +0200 Subject: Split IntegerType into IntegerType and AddressType. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 4 +- libsolidity/analysis/ViewPureChecker.cpp | 2 +- libsolidity/ast/Types.cpp | 131 +++++++++++---------- libsolidity/ast/Types.h | 44 +++++-- libsolidity/codegen/ABIFunctions.cpp | 27 ++++- libsolidity/codegen/CompilerUtils.cpp | 27 +++-- libsolidity/codegen/ExpressionCompiler.cpp | 15 ++- libsolidity/formal/SMTChecker.cpp | 5 +- libsolidity/formal/SSAVariable.cpp | 2 +- libsolidity/formal/SymbolicIntVariable.cpp | 14 ++- .../zeppelin/token/VestedToken.sol | 4 +- 12 files changed, 174 insertions(+), 102 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8760b818..14a362be 100644 --- a/Changelog.md +++ b/Changelog.md @@ -86,6 +86,7 @@ Compiler Features: * C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods. * Type Checker: Nicer error message when trying to reference overloaded identifiers in inline assembly. * Type Checker: Show named argument in case of error. + * Type System: IntegerType is split into IntegerType and AddressType internally. * Tests: Determine transaction status during IPC calls. * Code Generator: Allocate and free local variables according to their scope. * Removed ``pragma experimental "v0.5.0";``. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a2b72896..a3f4459e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2103,7 +2103,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) "after argument-dependent lookup in " + exprType->toString() + (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "."); if (exprType->category() == Type::Category::Contract) - for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr)) + for (auto const& addressMember: AddressType().nativeMembers(nullptr)) if (addressMember.name == memberName) { Identifier const* var = dynamic_cast(&_memberAccess.expression()); @@ -2353,7 +2353,7 @@ void TypeChecker::endVisit(Literal const& _literal) if (_literal.looksLikeAddress()) { // Assign type here if it even looks like an address. This prevents double errors for invalid addresses - _literal.annotation().type = make_shared(160, IntegerType::Modifier::Address); + _literal.annotation().type = make_shared(); string msg; if (_literal.value().length() != 42) // "0x" + 40 hex digits diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index be6b7ef7..113a3177 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -323,7 +323,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) ASTString const& member = _memberAccess.memberName(); switch (_memberAccess.expression().annotation().type->category()) { - case Type::Category::Integer: + case Type::Category::Address: if (member == "balance") mutability = StateMutability::View; break; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a6867dcb..a302203b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -299,7 +299,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::Byte: return make_shared(1); case Token::Address: - return make_shared(160, IntegerType::Modifier::Address); + return make_shared(); case Token::Bool: return make_shared(); case Token::Bytes: @@ -439,6 +439,59 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition return members; } +string AddressType::richIdentifier() const +{ + return "t_address"; +} + +bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return isImplicitlyConvertibleTo(_convertTo) || + _convertTo.category() == Category::Contract || + _convertTo.category() == Category::Integer || + (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast(_convertTo).numBytes() * 8); +} + +string AddressType::toString(bool) const +{ + return "address"; +} + +u256 AddressType::literalValue(Literal const* _literal) const +{ + solAssert(_literal, ""); + solAssert(_literal->value().substr(0, 2) == "0x", ""); + return u256(_literal->value()); +} + +TypePointer AddressType::unaryOperatorResult(Token::Value _operator) const +{ + return _operator == Token::Delete ? make_shared() : TypePointer(); +} + + +TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + // Addresses can only be compared. + if (!Token::isCompareOp(_operator)) + return TypePointer(); + + return Type::commonType(shared_from_this(), _other); +} + +MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const +{ + return { + {"balance", make_shared(256)}, + {"call", make_shared(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)}, + {"callcode", make_shared(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)}, + {"delegatecall", make_shared(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false)}, + {"send", make_shared(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, + {"staticcall", make_shared(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)}, + {"transfer", make_shared(strings{"uint"}, strings(), FunctionType::Kind::Transfer)} + }; +} + namespace { @@ -448,7 +501,7 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT if (_operator == Token::SHR) return false; else if (IntegerType const* otherInt = dynamic_cast(&_shiftAmountType)) - return !otherInt->isAddress(); + return true; else if (RationalNumberType const* otherRat = dynamic_cast(&_shiftAmountType)) return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); else @@ -460,8 +513,6 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) { - if (isAddress()) - solAssert(m_bits == 160, ""); solAssert( m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, "Invalid bit number for integer type: " + dev::toString(m_bits) @@ -470,10 +521,7 @@ IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier): string IntegerType::richIdentifier() const { - if (isAddress()) - return "t_address"; - else - return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits()); + return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits()); } bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -483,8 +531,6 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const IntegerType const& convertTo = dynamic_cast(_convertTo); if (convertTo.m_bits < m_bits) return false; - if (isAddress()) - return convertTo.isAddress(); else if (isSigned()) return convertTo.isSigned(); else @@ -493,11 +539,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const else if (_convertTo.category() == Category::FixedPoint) { FixedPointType const& convertTo = dynamic_cast(_convertTo); - - if (isAddress()) - return false; - else - return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue(); + return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue(); } else return false; @@ -506,6 +548,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.category() == category() || + _convertTo.category() == Category::Address || _convertTo.category() == Category::Contract || _convertTo.category() == Category::Enum || (_convertTo.category() == Category::FixedBytes && numBits() == dynamic_cast(_convertTo).numBytes() * 8) || @@ -517,10 +560,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // "delete" is ok for all integer types if (_operator == Token::Delete) return make_shared(); - // no further unary operators for addresses - else if (isAddress()) - return TypePointer(); - // for non-address integers, we allow +, -, ++ and -- + // we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || _operator == Token::Dec || _operator == Token::BitNot) @@ -539,20 +579,10 @@ bool IntegerType::operator==(Type const& _other) const string IntegerType::toString(bool) const { - if (isAddress()) - return "address"; string prefix = isSigned() ? "int" : "uint"; return prefix + dev::toString(m_bits); } -u256 IntegerType::literalValue(Literal const* _literal) const -{ - solAssert(m_modifier == Modifier::Address, ""); - solAssert(_literal, ""); - solAssert(_literal->value().substr(0, 2) == "0x", ""); - return u256(_literal->value()); -} - bigint IntegerType::minValue() const { if (isSigned()) @@ -580,8 +610,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe if (Token::isShiftOp(_operator)) { // Shifts are not symmetric with respect to the type - if (isAddress()) - return TypePointer(); if (isValidShiftAndAmountType(_operator, *_other)) return shared_from_this(); else @@ -599,9 +627,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe return TypePointer(); if (auto intType = dynamic_pointer_cast(commonType)) { - // Nothing else can be done with addresses - if (intType->isAddress()) - return TypePointer(); // Signed EXP is not allowed if (Token::Exp == _operator && intType->isSigned()) return TypePointer(); @@ -612,22 +637,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe return commonType; } -MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) const -{ - if (isAddress()) - return { - {"balance", make_shared(256)}, - {"call", make_shared(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)}, - {"callcode", make_shared(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)}, - {"delegatecall", make_shared(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false)}, - {"send", make_shared(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, - {"staticcall", make_shared(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)}, - {"transfer", make_shared(strings{"uint"}, strings(), FunctionType::Kind::Transfer)} - }; - else - return MemberList::MemberMap(); -} - FixedPointType::FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, FixedPointType::Modifier _modifier): m_totalBits(_totalBits), m_fractionalDigits(_fractionalDigits), m_modifier(_modifier) { @@ -658,8 +667,7 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.category() == category() || - (_convertTo.category() == Category::Integer && !dynamic_cast(_convertTo).isAddress()); + return _convertTo.category() == category() || _convertTo.category() == Category::Integer; } TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const @@ -912,8 +920,6 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const if (isFractional()) return false; IntegerType const& targetType = dynamic_cast(_convertTo); - if (targetType.isAddress()) - return false; if (m_value == rational(0)) return true; unsigned forSignBit = (targetType.isSigned() ? 1 : 0); @@ -1368,6 +1374,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast(_convertTo).numBits()) || + (_convertTo.category() == Category::Address && numBytes() == 20) || _convertTo.category() == Category::FixedPoint || _convertTo.category() == category(); } @@ -1469,9 +1476,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return - isImplicitlyConvertibleTo(_convertTo) || - _convertTo == IntegerType(160, IntegerType::Modifier::Address); + return isImplicitlyConvertibleTo(_convertTo) || _convertTo.category() == Category::Address; } bool ContractType::isPayable() const @@ -2584,12 +2589,8 @@ bool FunctionType::operator==(Type const& _other) const bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (m_kind == Kind::External && _convertTo.category() == Category::Integer) - { - IntegerType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.isAddress()) + if (m_kind == Kind::External && _convertTo.category() == Category::Address) return true; - } return _convertTo.category() == category(); } @@ -3274,7 +3275,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { case Kind::Block: return MemberList::MemberMap({ - {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, + {"coinbase", make_shared()}, {"timestamp", make_shared(256)}, {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"difficulty", make_shared(256)}, @@ -3283,7 +3284,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Message: return MemberList::MemberMap({ - {"sender", make_shared(160, IntegerType::Modifier::Address)}, + {"sender", make_shared()}, {"gas", make_shared(256)}, {"value", make_shared(256)}, {"data", make_shared(DataLocation::CallData)}, @@ -3291,7 +3292,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Transaction: return MemberList::MemberMap({ - {"origin", make_shared(160, IntegerType::Modifier::Address)}, + {"origin", make_shared()}, {"gasprice", make_shared(256)} }); case Kind::ABI: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index b860bf6a..7ee66838 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -141,7 +141,7 @@ public: virtual ~Type() = default; enum class Category { - Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, + Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, FixedBytes, Contract, Struct, Function, Enum, Tuple, Mapping, TypeType, Modifier, Magic, Module, InaccessibleDynamic @@ -314,14 +314,45 @@ protected: }; /** - * Any kind of integer type (signed, unsigned, address). + * Type for addresses. + */ +class AddressType: public Type +{ +public: + virtual Category category() const override { return Category::Address; } + + explicit AddressType() + { + } + + virtual std::string richIdentifier() const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; } + virtual unsigned storageBytes() const override { return 160 / 8; } + virtual bool isValueType() const override { return true; } + + virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + + virtual std::string toString(bool _short) const override; + + virtual u256 literalValue(Literal const* _literal) const override; + + virtual TypePointer encodingType() const override { return shared_from_this(); } + virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } +}; + +/** + * Any kind of integer type (signed, unsigned). */ class IntegerType: public Type { public: enum class Modifier { - Unsigned, Signed, Address + Unsigned, Signed }; virtual Category category() const override { return Category::Integer; } @@ -339,17 +370,12 @@ public: virtual unsigned storageBytes() const override { return m_bits / 8; } virtual bool isValueType() const override { return true; } - virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; - virtual std::string toString(bool _short) const override; - virtual u256 literalValue(Literal const* _literal) const override; - virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } unsigned numBits() const { return m_bits; } - bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } bigint minValue() const; @@ -729,7 +755,7 @@ public: { if (isSuper()) return TypePointer{}; - return std::make_shared(160, IntegerType::Modifier::Address); + return std::make_shared(); } virtual TypePointer interfaceType(bool _inLibrary) const override { diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index dda77958..5e5fe84a 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -197,6 +197,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) templ("functionName", functionName); switch (_type.category()) { + case Type::Category::Address: + templ("body", "cleaned := " + cleanupFunction(IntegerType(160)) + "(value)"); + break; case Type::Category::Integer: { IntegerType const& type = dynamic_cast(_type); @@ -239,7 +242,7 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) break; } case Type::Category::Contract: - templ("body", "cleaned := " + cleanupFunction(IntegerType(160, IntegerType::Modifier::Address)) + "(value)"); + templ("body", "cleaned := " + cleanupFunction(AddressType()) + "(value)"); break; case Type::Category::Enum: { @@ -284,6 +287,12 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) auto fromCategory = _from.category(); switch (fromCategory) { + case Type::Category::Address: + body = + Whiskers("converted := (value)") + ("convert", conversionFunction(IntegerType(160), _to)) + .render(); + break; case Type::Category::Integer: case Type::Category::RationalNumber: case Type::Category::Contract: @@ -314,16 +323,19 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) .render(); } else if (toCategory == Type::Category::FixedPoint) - { solUnimplemented("Not yet implemented - FixedPointType."); - } + else if (toCategory == Type::Category::Address) + body = + Whiskers("converted := (value)") + ("convert", conversionFunction(_from, IntegerType(160))) + .render(); else { solAssert( toCategory == Type::Category::Integer || toCategory == Type::Category::Contract, ""); - IntegerType const addressType(160, IntegerType::Modifier::Address); + IntegerType const addressType(160); IntegerType const& to = toCategory == Type::Category::Integer ? dynamic_cast(_to) : @@ -375,6 +387,11 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) ("shift", shiftRightFunction(256 - from.numBytes() * 8)) ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to)) .render(); + else if (toCategory == Type::Category::Address) + body = + Whiskers("converted := (value)") + ("convert", conversionFunction(_from, IntegerType(160))) + .render(); else { // clear for conversion to longer bytes @@ -410,7 +427,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) solAssert(false, ""); } - solAssert(!body.empty(), ""); + solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName()); templ("body", body); return templ.render(); }); diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index b30851fb..e6ad6d9c 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -657,6 +657,11 @@ void CompilerUtils::convertType( if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8) convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded); } + else if (targetTypeCategory == Type::Category::Address) + { + solAssert(typeOnStack.numBytes() * 8 == 160, ""); + rightShiftNumberOnStack(256 - 160); + } else { // clear for conversion to longer bytes @@ -690,23 +695,33 @@ void CompilerUtils::convertType( break; case Type::Category::FixedPoint: solUnimplemented("Not yet implemented - FixedPointType."); + case Type::Category::Address: case Type::Category::Integer: case Type::Category::Contract: case Type::Category::RationalNumber: if (targetTypeCategory == Type::Category::FixedBytes) { - solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::RationalNumber, - "Invalid conversion to FixedBytesType requested."); + solAssert( + stackTypeCategory == Type::Category::Address || + stackTypeCategory == Type::Category::Integer || + stackTypeCategory == Type::Category::RationalNumber, + "Invalid conversion to FixedBytesType requested." + ); // conversion from bytes to string. no need to clean the high bit // only to shift left because of opposite alignment FixedBytesType const& targetBytesType = dynamic_cast(_targetType); if (auto typeOnStack = dynamic_cast(&_typeOnStack)) + { if (targetBytesType.numBytes() * 8 > typeOnStack->numBits()) cleanHigherOrderBits(*typeOnStack); + } + else if (stackTypeCategory == Type::Category::Address) + solAssert(targetBytesType.numBytes() * 8 == 160, ""); leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8); } else if (targetTypeCategory == Type::Category::Enum) { + solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested."); solAssert(_typeOnStack.mobileType(), ""); // just clean convertType(_typeOnStack, *_typeOnStack.mobileType(), true); @@ -733,8 +748,8 @@ void CompilerUtils::convertType( } else { - solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); - IntegerType addressType(160, IntegerType::Modifier::Address); + solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract || targetTypeCategory == Type::Category::Address, ""); + IntegerType addressType(160); IntegerType const& targetType = targetTypeCategory == Type::Category::Integer ? dynamic_cast(_targetType) : addressType; if (stackTypeCategory == Type::Category::RationalNumber) @@ -996,10 +1011,8 @@ void CompilerUtils::convertType( m_context << Instruction::ISZERO << Instruction::ISZERO; break; default: - if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Integer) + if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address) { - IntegerType const& targetType = dynamic_cast(_targetType); - solAssert(targetType.isAddress(), "Function type can only be converted to address."); FunctionType const& typeOnStack = dynamic_cast(_typeOnStack); solAssert(typeOnStack.kind() == FunctionType::Kind::External, "Only external function type can be converted."); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 4cc4ba53..4150bc11 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1259,7 +1259,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) identifier = FunctionType(*function).externalIdentifier(); else solAssert(false, "Contract member is neither variable nor function."); - utils().convertType(type, IntegerType(160, IntegerType::Modifier::Address), true); + utils().convertType(type, AddressType(), true); m_context << identifier; } else @@ -1267,12 +1267,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) break; } case Type::Category::Integer: + { + solAssert(false, "Invalid member access to integer"); + break; + } + case Type::Category::Address: { if (member == "balance") { utils().convertType( *_memberAccess.expression().annotation().type, - IntegerType(160, IntegerType::Modifier::Address), + AddressType(), true ); m_context << Instruction::BALANCE; @@ -1280,11 +1285,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if ((set{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, - IntegerType(160, IntegerType::Modifier::Address), + AddressType(), true ); else - solAssert(false, "Invalid member access to integer"); + solAssert(false, "Invalid member access to address"); break; } case Type::Category::Function: @@ -1578,7 +1583,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { case Type::Category::RationalNumber: case Type::Category::Bool: - case Type::Category::Integer: + case Type::Category::Address: m_context << type->literalValue(&_literal); break; case Type::Category::StringLiteral: diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 88c1e56a..49c90405 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -394,7 +394,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) void SMTChecker::endVisit(Literal const& _literal) { Type const& type = *_literal.annotation().type; - if (type.category() == Type::Category::Integer || type.category() == Type::Category::RationalNumber) + if (type.category() == Type::Category::Integer || type.category() == Type::Category::Address || type.category() == Type::Category::RationalNumber) { if (RationalNumberType const* rational = dynamic_cast(&type)) solAssert(!rational->isFractional(), ""); @@ -540,6 +540,8 @@ void SMTChecker::assignment(VariableDeclaration const& _variable, smt::Expressio TypePointer type = _variable.type(); if (auto const* intType = dynamic_cast(type.get())) checkUnderOverflow(_value, *intType, _location); + else if (dynamic_cast(type.get())) + checkUnderOverflow(_value, IntegerType(160), _location); m_interface->addAssertion(newValue(_variable) == _value); } @@ -862,6 +864,7 @@ void SMTChecker::createExpr(Expression const& _e) m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; } + case Type::Category::Address: case Type::Category::Integer: m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index f3213e03..4fc2dd45 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -50,7 +50,7 @@ bool SSAVariable::isSupportedType(Type::Category _category) bool SSAVariable::isInteger(Type::Category _category) { - return _category == Type::Category::Integer; + return _category == Type::Category::Integer || _category == Type::Category::Address; } bool SSAVariable::isBool(Type::Category _category) diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp index 5e71fdcc..4f65b1fd 100644 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -29,7 +29,11 @@ SymbolicIntVariable::SymbolicIntVariable( ): SymbolicVariable(_decl, _interface) { - solAssert(m_declaration.type()->category() == Type::Category::Integer, ""); + solAssert( + m_declaration.type()->category() == Type::Category::Integer || + m_declaration.type()->category() == Type::Category::Address, + "" + ); } smt::Expression SymbolicIntVariable::valueAtSequence(int _seq) const @@ -44,9 +48,11 @@ void SymbolicIntVariable::setZeroValue(int _seq) void SymbolicIntVariable::setUnknownValue(int _seq) { - auto const& intType = dynamic_cast(*m_declaration.type()); - m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType)); - m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); + auto intType = dynamic_pointer_cast(m_declaration.type()); + if (!intType) + intType = make_shared(160); + m_interface.addAssertion(valueAtSequence(_seq) >= minValue(*intType)); + m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(*intType)); } smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) diff --git a/test/compilationTests/zeppelin/token/VestedToken.sol b/test/compilationTests/zeppelin/token/VestedToken.sol index 7edc7d16..2cd607bc 100644 --- a/test/compilationTests/zeppelin/token/VestedToken.sol +++ b/test/compilationTests/zeppelin/token/VestedToken.sol @@ -53,7 +53,7 @@ contract VestedToken is StandardToken, LimitedTransferToken { uint256 count = grants[_to].push( TokenGrant( - _revokable ? msg.sender : 0, // avoid storing an extra 20 bytes when it is non-revokable + _revokable ? msg.sender : 0x0000000000000000000000000000000000000000, // avoid storing an extra 20 bytes when it is non-revokable _value, _cliff, _vesting, @@ -84,7 +84,7 @@ contract VestedToken is StandardToken, LimitedTransferToken { revert(); } - address receiver = grant.burnsOnRevoke ? 0xdead : msg.sender; + address receiver = grant.burnsOnRevoke ? 0x000000000000000000000000000000000000dEaD : msg.sender; uint256 nonVested = nonVestedTokens(grant, uint64(now)); -- cgit v1.2.3