Merge pull request #4887 from ethereum/addressSplit

Split IntegerType into IntegerType and AddressType.
This commit is contained in:
chriseth 2018-09-05 21:40:49 +02:00 committed by GitHub
commit 08a7a51f07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 174 additions and 102 deletions

View File

@ -86,6 +86,7 @@ Compiler Features:
* C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods. * 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: Nicer error message when trying to reference overloaded identifiers in inline assembly.
* Type Checker: Show named argument in case of error. * 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. * Tests: Determine transaction status during IPC calls.
* Code Generator: Allocate and free local variables according to their scope. * Code Generator: Allocate and free local variables according to their scope.
* Removed ``pragma experimental "v0.5.0";``. * Removed ``pragma experimental "v0.5.0";``.

View File

@ -2103,7 +2103,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"after argument-dependent lookup in " + exprType->toString() + "after argument-dependent lookup in " + exprType->toString() +
(memberName == "value" ? " - did you forget the \"payable\" modifier?" : "."); (memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".");
if (exprType->category() == Type::Category::Contract) 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) if (addressMember.name == memberName)
{ {
Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression()); Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
@ -2353,7 +2353,7 @@ void TypeChecker::endVisit(Literal const& _literal)
if (_literal.looksLikeAddress()) if (_literal.looksLikeAddress())
{ {
// Assign type here if it even looks like an address. This prevents double errors for invalid addresses // Assign type here if it even looks like an address. This prevents double errors for invalid addresses
_literal.annotation().type = make_shared<IntegerType>(160, IntegerType::Modifier::Address); _literal.annotation().type = make_shared<AddressType>();
string msg; string msg;
if (_literal.value().length() != 42) // "0x" + 40 hex digits if (_literal.value().length() != 42) // "0x" + 40 hex digits

View File

@ -323,7 +323,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
ASTString const& member = _memberAccess.memberName(); ASTString const& member = _memberAccess.memberName();
switch (_memberAccess.expression().annotation().type->category()) switch (_memberAccess.expression().annotation().type->category())
{ {
case Type::Category::Integer: case Type::Category::Address:
if (member == "balance") if (member == "balance")
mutability = StateMutability::View; mutability = StateMutability::View;
break; break;

View File

@ -299,7 +299,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
case Token::Byte: case Token::Byte:
return make_shared<FixedBytesType>(1); return make_shared<FixedBytesType>(1);
case Token::Address: case Token::Address:
return make_shared<IntegerType>(160, IntegerType::Modifier::Address); return make_shared<AddressType>();
case Token::Bool: case Token::Bool:
return make_shared<BoolType>(); return make_shared<BoolType>();
case Token::Bytes: case Token::Bytes:
@ -439,6 +439,59 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
return members; 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<FixedBytesType const&>(_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<TupleType>() : 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<IntegerType>(256)},
{"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
{"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
{"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
};
}
namespace namespace
{ {
@ -448,7 +501,7 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT
if (_operator == Token::SHR) if (_operator == Token::SHR)
return false; return false;
else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType)) else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType))
return !otherInt->isAddress(); return true;
else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType)) else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType))
return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned();
else else
@ -460,8 +513,6 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT
IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier): IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier):
m_bits(_bits), m_modifier(_modifier) m_bits(_bits), m_modifier(_modifier)
{ {
if (isAddress())
solAssert(m_bits == 160, "");
solAssert( solAssert(
m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0,
"Invalid bit number for integer type: " + dev::toString(m_bits) "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 string IntegerType::richIdentifier() const
{ {
if (isAddress()) return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
return "t_address";
else
return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
} }
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@ -483,8 +531,6 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.m_bits < m_bits) if (convertTo.m_bits < m_bits)
return false; return false;
if (isAddress())
return convertTo.isAddress();
else if (isSigned()) else if (isSigned())
return convertTo.isSigned(); return convertTo.isSigned();
else else
@ -493,11 +539,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
else if (_convertTo.category() == Category::FixedPoint) else if (_convertTo.category() == Category::FixedPoint)
{ {
FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo); FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo);
return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue();
if (isAddress())
return false;
else
return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue();
} }
else else
return false; return false;
@ -506,6 +548,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return _convertTo.category() == category() || return _convertTo.category() == category() ||
_convertTo.category() == Category::Address ||
_convertTo.category() == Category::Contract || _convertTo.category() == Category::Contract ||
_convertTo.category() == Category::Enum || _convertTo.category() == Category::Enum ||
(_convertTo.category() == Category::FixedBytes && numBits() == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8) || (_convertTo.category() == Category::FixedBytes && numBits() == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8) ||
@ -517,10 +560,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
// "delete" is ok for all integer types // "delete" is ok for all integer types
if (_operator == Token::Delete) if (_operator == Token::Delete)
return make_shared<TupleType>(); return make_shared<TupleType>();
// no further unary operators for addresses // we allow +, -, ++ and --
else if (isAddress())
return TypePointer();
// for non-address integers, we allow +, -, ++ and --
else if (_operator == Token::Add || _operator == Token::Sub || else if (_operator == Token::Add || _operator == Token::Sub ||
_operator == Token::Inc || _operator == Token::Dec || _operator == Token::Inc || _operator == Token::Dec ||
_operator == Token::BitNot) _operator == Token::BitNot)
@ -539,20 +579,10 @@ bool IntegerType::operator==(Type const& _other) const
string IntegerType::toString(bool) const string IntegerType::toString(bool) const
{ {
if (isAddress())
return "address";
string prefix = isSigned() ? "int" : "uint"; string prefix = isSigned() ? "int" : "uint";
return prefix + dev::toString(m_bits); 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 bigint IntegerType::minValue() const
{ {
if (isSigned()) if (isSigned())
@ -580,8 +610,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
if (Token::isShiftOp(_operator)) if (Token::isShiftOp(_operator))
{ {
// Shifts are not symmetric with respect to the type // Shifts are not symmetric with respect to the type
if (isAddress())
return TypePointer();
if (isValidShiftAndAmountType(_operator, *_other)) if (isValidShiftAndAmountType(_operator, *_other))
return shared_from_this(); return shared_from_this();
else else
@ -599,9 +627,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
return TypePointer(); return TypePointer();
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType)) if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
{ {
// Nothing else can be done with addresses
if (intType->isAddress())
return TypePointer();
// Signed EXP is not allowed // Signed EXP is not allowed
if (Token::Exp == _operator && intType->isSigned()) if (Token::Exp == _operator && intType->isSigned())
return TypePointer(); return TypePointer();
@ -612,22 +637,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
return commonType; return commonType;
} }
MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) const
{
if (isAddress())
return {
{"balance", make_shared<IntegerType>(256)},
{"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
{"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
{"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
};
else
return MemberList::MemberMap();
}
FixedPointType::FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, FixedPointType::Modifier _modifier): FixedPointType::FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, FixedPointType::Modifier _modifier):
m_totalBits(_totalBits), m_fractionalDigits(_fractionalDigits), m_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 bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return _convertTo.category() == category() || return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
(_convertTo.category() == Category::Integer && !dynamic_cast<IntegerType const&>(_convertTo).isAddress());
} }
TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const
@ -912,8 +920,6 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (isFractional()) if (isFractional())
return false; return false;
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo); IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
if (targetType.isAddress())
return false;
if (m_value == rational(0)) if (m_value == rational(0))
return true; return true;
unsigned forSignBit = (targetType.isSigned() ? 1 : 0); unsigned forSignBit = (targetType.isSigned() ? 1 : 0);
@ -1368,6 +1374,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) || return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) ||
(_convertTo.category() == Category::Address && numBytes() == 20) ||
_convertTo.category() == Category::FixedPoint || _convertTo.category() == Category::FixedPoint ||
_convertTo.category() == category(); _convertTo.category() == category();
} }
@ -1469,9 +1476,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return return isImplicitlyConvertibleTo(_convertTo) || _convertTo.category() == Category::Address;
isImplicitlyConvertibleTo(_convertTo) ||
_convertTo == IntegerType(160, IntegerType::Modifier::Address);
} }
bool ContractType::isPayable() const bool ContractType::isPayable() const
@ -2584,12 +2589,8 @@ bool FunctionType::operator==(Type const& _other) const
bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
if (m_kind == Kind::External && _convertTo.category() == Category::Integer) if (m_kind == Kind::External && _convertTo.category() == Category::Address)
{
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.isAddress())
return true; return true;
}
return _convertTo.category() == category(); return _convertTo.category() == category();
} }
@ -3274,7 +3275,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
{ {
case Kind::Block: case Kind::Block:
return MemberList::MemberMap({ return MemberList::MemberMap({
{"coinbase", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, {"coinbase", make_shared<AddressType>()},
{"timestamp", make_shared<IntegerType>(256)}, {"timestamp", make_shared<IntegerType>(256)},
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)},
{"difficulty", make_shared<IntegerType>(256)}, {"difficulty", make_shared<IntegerType>(256)},
@ -3283,7 +3284,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
}); });
case Kind::Message: case Kind::Message:
return MemberList::MemberMap({ return MemberList::MemberMap({
{"sender", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, {"sender", make_shared<AddressType>()},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(DataLocation::CallData)}, {"data", make_shared<ArrayType>(DataLocation::CallData)},
@ -3291,7 +3292,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
}); });
case Kind::Transaction: case Kind::Transaction:
return MemberList::MemberMap({ return MemberList::MemberMap({
{"origin", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, {"origin", make_shared<AddressType>()},
{"gasprice", make_shared<IntegerType>(256)} {"gasprice", make_shared<IntegerType>(256)}
}); });
case Kind::ABI: case Kind::ABI:

View File

@ -141,7 +141,7 @@ public:
virtual ~Type() = default; virtual ~Type() = default;
enum class Category enum class Category
{ {
Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array,
FixedBytes, Contract, Struct, Function, Enum, Tuple, FixedBytes, Contract, Struct, Function, Enum, Tuple,
Mapping, TypeType, Modifier, Magic, Module, Mapping, TypeType, Modifier, Magic, Module,
InaccessibleDynamic 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 class IntegerType: public Type
{ {
public: public:
enum class Modifier enum class Modifier
{ {
Unsigned, Signed, Address Unsigned, Signed
}; };
virtual Category category() const override { return Category::Integer; } virtual Category category() const override { return Category::Integer; }
@ -339,17 +370,12 @@ public:
virtual unsigned storageBytes() const override { return m_bits / 8; } virtual unsigned storageBytes() const override { return m_bits / 8; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
virtual std::string toString(bool _short) 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 encodingType() const override { return shared_from_this(); }
virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
unsigned numBits() const { return m_bits; } unsigned numBits() const { return m_bits; }
bool isAddress() const { return m_modifier == Modifier::Address; }
bool isSigned() const { return m_modifier == Modifier::Signed; } bool isSigned() const { return m_modifier == Modifier::Signed; }
bigint minValue() const; bigint minValue() const;
@ -729,7 +755,7 @@ public:
{ {
if (isSuper()) if (isSuper())
return TypePointer{}; return TypePointer{};
return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address); return std::make_shared<AddressType>();
} }
virtual TypePointer interfaceType(bool _inLibrary) const override virtual TypePointer interfaceType(bool _inLibrary) const override
{ {

View File

@ -197,6 +197,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
templ("functionName", functionName); templ("functionName", functionName);
switch (_type.category()) switch (_type.category())
{ {
case Type::Category::Address:
templ("body", "cleaned := " + cleanupFunction(IntegerType(160)) + "(value)");
break;
case Type::Category::Integer: case Type::Category::Integer:
{ {
IntegerType const& type = dynamic_cast<IntegerType const&>(_type); IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
@ -239,7 +242,7 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
break; break;
} }
case Type::Category::Contract: case Type::Category::Contract:
templ("body", "cleaned := " + cleanupFunction(IntegerType(160, IntegerType::Modifier::Address)) + "(value)"); templ("body", "cleaned := " + cleanupFunction(AddressType()) + "(value)");
break; break;
case Type::Category::Enum: case Type::Category::Enum:
{ {
@ -284,6 +287,12 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
auto fromCategory = _from.category(); auto fromCategory = _from.category();
switch (fromCategory) switch (fromCategory)
{ {
case Type::Category::Address:
body =
Whiskers("converted := <convert>(value)")
("convert", conversionFunction(IntegerType(160), _to))
.render();
break;
case Type::Category::Integer: case Type::Category::Integer:
case Type::Category::RationalNumber: case Type::Category::RationalNumber:
case Type::Category::Contract: case Type::Category::Contract:
@ -314,16 +323,19 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
.render(); .render();
} }
else if (toCategory == Type::Category::FixedPoint) else if (toCategory == Type::Category::FixedPoint)
{
solUnimplemented("Not yet implemented - FixedPointType."); solUnimplemented("Not yet implemented - FixedPointType.");
} else if (toCategory == Type::Category::Address)
body =
Whiskers("converted := <convert>(value)")
("convert", conversionFunction(_from, IntegerType(160)))
.render();
else else
{ {
solAssert( solAssert(
toCategory == Type::Category::Integer || toCategory == Type::Category::Integer ||
toCategory == Type::Category::Contract, toCategory == Type::Category::Contract,
""); "");
IntegerType const addressType(160, IntegerType::Modifier::Address); IntegerType const addressType(160);
IntegerType const& to = IntegerType const& to =
toCategory == Type::Category::Integer ? toCategory == Type::Category::Integer ?
dynamic_cast<IntegerType const&>(_to) : dynamic_cast<IntegerType const&>(_to) :
@ -375,6 +387,11 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
("shift", shiftRightFunction(256 - from.numBytes() * 8)) ("shift", shiftRightFunction(256 - from.numBytes() * 8))
("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to)) ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
.render(); .render();
else if (toCategory == Type::Category::Address)
body =
Whiskers("converted := <convert>(value)")
("convert", conversionFunction(_from, IntegerType(160)))
.render();
else else
{ {
// clear for conversion to longer bytes // clear for conversion to longer bytes
@ -410,7 +427,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
solAssert(false, ""); solAssert(false, "");
} }
solAssert(!body.empty(), ""); solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName());
templ("body", body); templ("body", body);
return templ.render(); return templ.render();
}); });

View File

@ -657,6 +657,11 @@ void CompilerUtils::convertType(
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8) if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded); convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
} }
else if (targetTypeCategory == Type::Category::Address)
{
solAssert(typeOnStack.numBytes() * 8 == 160, "");
rightShiftNumberOnStack(256 - 160);
}
else else
{ {
// clear for conversion to longer bytes // clear for conversion to longer bytes
@ -690,23 +695,33 @@ void CompilerUtils::convertType(
break; break;
case Type::Category::FixedPoint: case Type::Category::FixedPoint:
solUnimplemented("Not yet implemented - FixedPointType."); solUnimplemented("Not yet implemented - FixedPointType.");
case Type::Category::Address:
case Type::Category::Integer: case Type::Category::Integer:
case Type::Category::Contract: case Type::Category::Contract:
case Type::Category::RationalNumber: case Type::Category::RationalNumber:
if (targetTypeCategory == Type::Category::FixedBytes) if (targetTypeCategory == Type::Category::FixedBytes)
{ {
solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::RationalNumber, solAssert(
"Invalid conversion to FixedBytesType requested."); 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 // conversion from bytes to string. no need to clean the high bit
// only to shift left because of opposite alignment // only to shift left because of opposite alignment
FixedBytesType const& targetBytesType = dynamic_cast<FixedBytesType const&>(_targetType); FixedBytesType const& targetBytesType = dynamic_cast<FixedBytesType const&>(_targetType);
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack)) if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
{
if (targetBytesType.numBytes() * 8 > typeOnStack->numBits()) if (targetBytesType.numBytes() * 8 > typeOnStack->numBits())
cleanHigherOrderBits(*typeOnStack); cleanHigherOrderBits(*typeOnStack);
}
else if (stackTypeCategory == Type::Category::Address)
solAssert(targetBytesType.numBytes() * 8 == 160, "");
leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8); leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8);
} }
else if (targetTypeCategory == Type::Category::Enum) else if (targetTypeCategory == Type::Category::Enum)
{ {
solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested.");
solAssert(_typeOnStack.mobileType(), ""); solAssert(_typeOnStack.mobileType(), "");
// just clean // just clean
convertType(_typeOnStack, *_typeOnStack.mobileType(), true); convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
@ -733,8 +748,8 @@ void CompilerUtils::convertType(
} }
else else
{ {
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract || targetTypeCategory == Type::Category::Address, "");
IntegerType addressType(160, IntegerType::Modifier::Address); IntegerType addressType(160);
IntegerType const& targetType = targetTypeCategory == Type::Category::Integer IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
? dynamic_cast<IntegerType const&>(_targetType) : addressType; ? dynamic_cast<IntegerType const&>(_targetType) : addressType;
if (stackTypeCategory == Type::Category::RationalNumber) if (stackTypeCategory == Type::Category::RationalNumber)
@ -996,10 +1011,8 @@ void CompilerUtils::convertType(
m_context << Instruction::ISZERO << Instruction::ISZERO; m_context << Instruction::ISZERO << Instruction::ISZERO;
break; break;
default: default:
if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Integer) if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address)
{ {
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_targetType);
solAssert(targetType.isAddress(), "Function type can only be converted to address.");
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack); FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
solAssert(typeOnStack.kind() == FunctionType::Kind::External, "Only external function type can be converted."); solAssert(typeOnStack.kind() == FunctionType::Kind::External, "Only external function type can be converted.");

View File

@ -1259,7 +1259,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
identifier = FunctionType(*function).externalIdentifier(); identifier = FunctionType(*function).externalIdentifier();
else else
solAssert(false, "Contract member is neither variable nor function."); 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; m_context << identifier;
} }
else else
@ -1267,12 +1267,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
break; break;
} }
case Type::Category::Integer: case Type::Category::Integer:
{
solAssert(false, "Invalid member access to integer");
break;
}
case Type::Category::Address:
{ {
if (member == "balance") if (member == "balance")
{ {
utils().convertType( utils().convertType(
*_memberAccess.expression().annotation().type, *_memberAccess.expression().annotation().type,
IntegerType(160, IntegerType::Modifier::Address), AddressType(),
true true
); );
m_context << Instruction::BALANCE; m_context << Instruction::BALANCE;
@ -1280,11 +1285,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member)) else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member))
utils().convertType( utils().convertType(
*_memberAccess.expression().annotation().type, *_memberAccess.expression().annotation().type,
IntegerType(160, IntegerType::Modifier::Address), AddressType(),
true true
); );
else else
solAssert(false, "Invalid member access to integer"); solAssert(false, "Invalid member access to address");
break; break;
} }
case Type::Category::Function: case Type::Category::Function:
@ -1578,7 +1583,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
{ {
case Type::Category::RationalNumber: case Type::Category::RationalNumber:
case Type::Category::Bool: case Type::Category::Bool:
case Type::Category::Integer: case Type::Category::Address:
m_context << type->literalValue(&_literal); m_context << type->literalValue(&_literal);
break; break;
case Type::Category::StringLiteral: case Type::Category::StringLiteral:

View File

@ -394,7 +394,7 @@ void SMTChecker::endVisit(Identifier const& _identifier)
void SMTChecker::endVisit(Literal const& _literal) void SMTChecker::endVisit(Literal const& _literal)
{ {
Type const& type = *_literal.annotation().type; 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<RationalNumberType const*>(&type)) if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&type))
solAssert(!rational->isFractional(), ""); solAssert(!rational->isFractional(), "");
@ -540,6 +540,8 @@ void SMTChecker::assignment(VariableDeclaration const& _variable, smt::Expressio
TypePointer type = _variable.type(); TypePointer type = _variable.type();
if (auto const* intType = dynamic_cast<IntegerType const*>(type.get())) if (auto const* intType = dynamic_cast<IntegerType const*>(type.get()))
checkUnderOverflow(_value, *intType, _location); checkUnderOverflow(_value, *intType, _location);
else if (dynamic_cast<AddressType const*>(type.get()))
checkUnderOverflow(_value, IntegerType(160), _location);
m_interface->addAssertion(newValue(_variable) == _value); 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))); m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e)));
break; break;
} }
case Type::Category::Address:
case Type::Category::Integer: case Type::Category::Integer:
m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e)));
break; break;

View File

@ -50,7 +50,7 @@ bool SSAVariable::isSupportedType(Type::Category _category)
bool SSAVariable::isInteger(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) bool SSAVariable::isBool(Type::Category _category)

View File

@ -29,7 +29,11 @@ SymbolicIntVariable::SymbolicIntVariable(
): ):
SymbolicVariable(_decl, _interface) 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 smt::Expression SymbolicIntVariable::valueAtSequence(int _seq) const
@ -44,9 +48,11 @@ void SymbolicIntVariable::setZeroValue(int _seq)
void SymbolicIntVariable::setUnknownValue(int _seq) void SymbolicIntVariable::setUnknownValue(int _seq)
{ {
auto const& intType = dynamic_cast<IntegerType const&>(*m_declaration.type()); auto intType = dynamic_pointer_cast<IntegerType const>(m_declaration.type());
m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType)); if (!intType)
m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); intType = make_shared<IntegerType>(160);
m_interface.addAssertion(valueAtSequence(_seq) >= minValue(*intType));
m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(*intType));
} }
smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t)

View File

@ -53,7 +53,7 @@ contract VestedToken is StandardToken, LimitedTransferToken {
uint256 count = grants[_to].push( uint256 count = grants[_to].push(
TokenGrant( 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, _value,
_cliff, _cliff,
_vesting, _vesting,
@ -84,7 +84,7 @@ contract VestedToken is StandardToken, LimitedTransferToken {
revert(); revert();
} }
address receiver = grant.burnsOnRevoke ? 0xdead : msg.sender; address receiver = grant.burnsOnRevoke ? 0x000000000000000000000000000000000000dEaD : msg.sender;
uint256 nonVested = nonVestedTokens(grant, uint64(now)); uint256 nonVested = nonVestedTokens(grant, uint64(now));