Replace casting of external functions to address by a member named "address".

This commit is contained in:
Daniel Kirchner 2019-08-13 16:43:25 +02:00 committed by chriseth
parent d3ea86b052
commit 9f6fff2120
12 changed files with 72 additions and 51 deletions

View File

@ -1,6 +1,7 @@
### 0.6.0 (unreleased)
Breaking changes:
* General: Disallow explicit conversions from external function types to ``address`` and add a member called ``address`` to them as replacement.
Language Features:

View File

@ -9,6 +9,14 @@ For the full list check
`the release changelog <https://github.com/ethereum/solidity/releases/tag/v0.6.0>`_.
Syntactic Only Changes
======================
This section lists purely syntactic changes that do not affect the behavior of existing code.
* Conversions from external function types to ``address`` are now disallowed. Instead external
function types have a member called ``address``, similar to the existing ``selector`` member.
Semantic Only Changes
=====================
@ -27,6 +35,7 @@ How to update your code
This section gives detailed instructions on how to update prior code for every breaking change.
* Change ``address(f)`` to ``f.address`` for ``f`` being a variable of external function type.
Deprecated Elements
===================

View File

@ -2726,8 +2726,6 @@ bool FunctionType::operator==(Type const& _other) const
BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (m_kind == Kind::External && _convertTo == *TypeProvider::address())
return true;
return _convertTo.category() == category();
}
@ -2922,7 +2920,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
{
MemberList::MemberMap members;
if (m_kind == Kind::External)
{
members.emplace_back("selector", TypeProvider::fixedBytes(4));
members.emplace_back("address", TypeProvider::address());
}
if (m_kind != Kind::BareDelegateCall)
{
if (isPayable())

View File

@ -1112,37 +1112,28 @@ void CompilerUtils::convertType(
m_context << Instruction::ISZERO << Instruction::ISZERO;
break;
default:
if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address)
// we used to allow conversions from function to address
solAssert(!(stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address), "");
if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function)
{
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
solAssert(typeOnStack.kind() == FunctionType::Kind::External, "Only external function type can be converted.");
// stack: <address> <function_id>
m_context << Instruction::POP;
FunctionType const& targetType = dynamic_cast<FunctionType const&>(_targetType);
solAssert(
typeOnStack.isImplicitlyConvertibleTo(targetType) &&
typeOnStack.sizeOnStack() == targetType.sizeOnStack() &&
(typeOnStack.kind() == FunctionType::Kind::Internal || typeOnStack.kind() == FunctionType::Kind::External) &&
typeOnStack.kind() == targetType.kind(),
"Invalid function type conversion requested."
);
}
else
{
if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function)
{
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
FunctionType const& targetType = dynamic_cast<FunctionType const&>(_targetType);
solAssert(
typeOnStack.isImplicitlyConvertibleTo(targetType) &&
typeOnStack.sizeOnStack() == targetType.sizeOnStack() &&
(typeOnStack.kind() == FunctionType::Kind::Internal || typeOnStack.kind() == FunctionType::Kind::External) &&
typeOnStack.kind() == targetType.kind(),
"Invalid function type conversion requested."
);
}
else
// All other types should not be convertible to non-equal types.
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
// All other types should not be convertible to non-equal types.
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32)
m_context
<< ((u256(1) << (8 * _targetType.storageBytes())) - 1)
<< Instruction::AND;
}
if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32)
m_context
<< ((u256(1) << (8 * _targetType.storageBytes())) - 1)
<< Instruction::AND;
break;
}

View File

@ -1338,6 +1338,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
/// need to store it as bytes4
utils().leftShiftNumberOnStack(224);
}
else if (member == "address")
{
auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type);
solAssert(functionType.kind() == FunctionType::Kind::External, "");
// stack: <address> <function_id>
m_context << Instruction::POP;
}
else
solAssert(
!!_memberAccess.expression().annotation().type->memberType(member),

View File

@ -1552,7 +1552,13 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
{
m_scanner->next();
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
if (m_scanner->currentToken() == Token::Address)
{
expression = nodeFactory.createNode<MemberAccess>(expression, make_shared<ASTString>("address"));
m_scanner->next();
}
else
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
break;
}
case Token::LParen:

View File

@ -11201,25 +11201,6 @@ BOOST_AUTO_TEST_CASE(function_array_cross_calls)
ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(5), u256(6), u256(7)));
}
BOOST_AUTO_TEST_CASE(external_function_to_address)
{
char const* sourceCode = R"(
contract C {
function f() public returns (bool) {
return address(this.f) == address(this);
}
function g(function() external cb) public returns (address) {
return address(cb);
}
}
)";
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(callContractFunction("f()"), encodeArgs(true));
ABI_CHECK(callContractFunction("g(function)", fromHex("00000000000000000000000000000000000004226121ff00000000000000000")), encodeArgs(u160(0x42)));
}
BOOST_AUTO_TEST_CASE(copy_internal_function_array_to_storage)
{
char const* sourceCode = R"(

View File

@ -0,0 +1,11 @@
contract C {
function f() public returns (bool) {
return this.f.address == address(this);
}
function g(function() external cb) public returns (address) {
return cb.address;
}
}
// ----
// f() -> true
// g(function): hex"00000000000000000000000000000000000004226121ff00000000000000000" -> 0x42

View File

@ -1,5 +1,5 @@
contract C {
function f() public view returns (address) {
return address(this.f);
return this.f.address;
}
}

View File

@ -1,7 +1,7 @@
contract C {
function f() public view returns (address payable) {
return address(this.f);
return this.f.address;
}
}
// ----
// TypeError: (85-100): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable.
// TypeError: (85-99): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable.

View File

@ -0,0 +1,9 @@
contract C {
struct S { uint a; }
function f() public pure returns(address) {
S memory s = S(42);
return s.address;
}
}
// ----
// TypeError: (129-138): Member "address" not found or not visible after argument-dependent lookup in struct C.S memory.

View File

@ -0,0 +1,5 @@
contract C {
struct S { uint address; }
}
// ----
// ParserError: (33-40): Expected identifier but got 'address'