mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Replace casting of external functions to address by a member named "address".
This commit is contained in:
parent
d3ea86b052
commit
9f6fff2120
@ -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:
|
||||||
|
@ -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
|
||||||
===================
|
===================
|
||||||
|
@ -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())
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
@ -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:
|
||||||
|
@ -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"(
|
||||||
|
@ -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
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
@ -0,0 +1,5 @@
|
|||||||
|
contract C {
|
||||||
|
struct S { uint address; }
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (33-40): Expected identifier but got 'address'
|
Loading…
Reference in New Issue
Block a user