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) ### 0.6.0 (unreleased)
Breaking changes: Breaking changes:
* General: Disallow explicit conversions from external function types to ``address`` and add a member called ``address`` to them as replacement.
Language Features: 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>`_. `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 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. 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 Deprecated Elements
=================== ===================

View File

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

View File

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

View File

@ -1338,6 +1338,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
/// need to store it as bytes4 /// need to store it as bytes4
utils().leftShiftNumberOnStack(224); 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 else
solAssert( solAssert(
!!_memberAccess.expression().annotation().type->memberType(member), !!_memberAccess.expression().annotation().type->memberType(member),

View File

@ -1552,7 +1552,13 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
{ {
m_scanner->next(); m_scanner->next();
nodeFactory.markEndPosition(); 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; break;
} }
case Token::LParen: 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))); 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) BOOST_AUTO_TEST_CASE(copy_internal_function_array_to_storage)
{ {
char const* sourceCode = R"( 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 { contract C {
function f() public view returns (address) { function f() public view returns (address) {
return address(this.f); return this.f.address;
} }
} }

View File

@ -1,7 +1,7 @@
contract C { contract C {
function f() public view returns (address payable) { 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'