Merge pull request #7239 from ethereum/externalFunctionToAddress

[0.6.0] Replace casting of external functions to address by a member named "address".
This commit is contained in:
chriseth 2019-08-19 16:07:26 +02:00 committed by GitHub
commit 0cd6b9e596
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 103 additions and 56 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 of external function type.
Deprecated Elements
===================

View File

@ -582,9 +582,6 @@ do not have a default.
Conversions:
A value of external function type can be explicitly converted to ``address``
resulting in the address of the contract of the function.
A function type ``A`` is implicitly convertible to a function type ``B`` if and only if
their parameter types are identical, their return types are identical,
their internal/external property is identical and the state mutability of ``A``
@ -616,8 +613,9 @@ just use ``f``, if you want to use its external form, use ``this.f``.
Members:
Public (or external) functions have the following members:
External (or public) functions have the following members:
* ``.address`` returns the address of the contract of the function.
* ``.selector`` returns the :ref:`ABI function selector <abi_function_selector>`
* ``.gas(uint)`` returns a callable function object which, when called, will send the specified amount of gas to the target function. See :ref:`External Function Calls <external-function-calls>` for more information.
* ``.value(uint)`` returns a callable function object which, when called, will send the specified amount of wei to the target function. See :ref:`External Function Calls <external-function-calls>` for more information.
@ -629,6 +627,7 @@ Example that shows how to use the members::
contract Example {
function f() public payable returns (bytes4) {
assert(this.f.address == address(this));
return this.f.selector;
}

View File

@ -1478,12 +1478,28 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
variableDeclaration->location()
);
m_errorReporter.typeError(
_functionCall.location(), ssl,
_functionCall.location(),
ssl,
"Explicit type conversion not allowed from non-payable \"address\" to \"" +
resultType->toString() +
"\", which has a payable fallback function."
);
}
else if (
auto const* functionType = dynamic_cast<FunctionType const*>(argType);
functionType &&
functionType->kind() == FunctionType::Kind::External &&
resultType->category() == Type::Category::Address
)
m_errorReporter.typeError(
_functionCall.location(),
"Explicit type conversion not allowed from \"" +
argType->toString() +
"\" to \"" +
resultType->toString() +
"\". To obtain the address of the contract of the function, " +
"you can use the .address member of the function."
);
else
m_errorReporter.typeError(
_functionCall.location(),

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

@ -718,6 +718,10 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
solUnimplementedAssert(false, "");
}
else if (member == "address")
{
solUnimplementedAssert(false, "");
}
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

@ -0,0 +1,7 @@
contract C {
function f() public view returns (address) {
return address(this.f);
}
}
// ----
// TypeError: (77-92): Explicit type conversion not allowed from "function () view external returns (address)" to "address". To obtain the address of the contract of the function, you can use the .address member of the function.

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'