mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8263 from ethereum/functionSelectorPure
Mark function selectors accessed via declaration as pure.
This commit is contained in:
commit
99f88742d6
@ -2527,7 +2527,15 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
else if (TypeType const* typeType = dynamic_cast<decltype(typeType)>(exprType))
|
else if (TypeType const* typeType = dynamic_cast<decltype(typeType)>(exprType))
|
||||||
{
|
{
|
||||||
if (ContractType const* contractType = dynamic_cast<decltype(contractType)>(typeType->actualType()))
|
if (ContractType const* contractType = dynamic_cast<decltype(contractType)>(typeType->actualType()))
|
||||||
|
{
|
||||||
annotation.isLValue = annotation.referencedDeclaration->isLValue();
|
annotation.isLValue = annotation.referencedDeclaration->isLValue();
|
||||||
|
if (
|
||||||
|
auto const* functionType = dynamic_cast<FunctionType const*>(annotation.type);
|
||||||
|
functionType &&
|
||||||
|
functionType->kind() == FunctionType::Kind::Declaration
|
||||||
|
)
|
||||||
|
annotation.isPure = _memberAccess.expression().annotation().isPure;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO some members might be pure, but for example `address(0x123).balance` is not pure
|
// TODO some members might be pure, but for example `address(0x123).balance` is not pure
|
||||||
@ -2535,6 +2543,20 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
if (auto tt = dynamic_cast<TypeType const*>(exprType))
|
if (auto tt = dynamic_cast<TypeType const*>(exprType))
|
||||||
if (tt->actualType()->category() == Type::Category::Enum)
|
if (tt->actualType()->category() == Type::Category::Enum)
|
||||||
annotation.isPure = true;
|
annotation.isPure = true;
|
||||||
|
if (
|
||||||
|
auto const* functionType = dynamic_cast<FunctionType const*>(exprType);
|
||||||
|
functionType &&
|
||||||
|
functionType->hasDeclaration() &&
|
||||||
|
dynamic_cast<FunctionDefinition const*>(&functionType->declaration()) &&
|
||||||
|
memberName == "selector"
|
||||||
|
)
|
||||||
|
if (auto const* parentAccess = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
|
||||||
|
{
|
||||||
|
annotation.isPure = parentAccess->expression().annotation().isPure;
|
||||||
|
if (auto const* exprInt = dynamic_cast<Identifier const*>(&parentAccess->expression()))
|
||||||
|
if (exprInt->name() == "this" || exprInt->name() == "super")
|
||||||
|
annotation.isPure = true;
|
||||||
|
}
|
||||||
if (auto magicType = dynamic_cast<MagicType const*>(exprType))
|
if (auto magicType = dynamic_cast<MagicType const*>(exprType))
|
||||||
{
|
{
|
||||||
if (magicType->kind() == MagicType::Kind::ABI)
|
if (magicType->kind() == MagicType::Kind::ABI)
|
||||||
|
@ -1303,6 +1303,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
switch (funType->kind())
|
switch (funType->kind())
|
||||||
{
|
{
|
||||||
|
case FunctionType::Kind::Declaration:
|
||||||
|
break;
|
||||||
case FunctionType::Kind::Internal:
|
case FunctionType::Kind::Internal:
|
||||||
// We do not visit the expression here on purpose, because in the case of an
|
// We do not visit the expression here on purpose, because in the case of an
|
||||||
// internal library function call, this would push the library address forcing
|
// internal library function call, this would push the library address forcing
|
||||||
|
@ -3,9 +3,9 @@ contract B {
|
|||||||
function g() public {}
|
function g() public {}
|
||||||
}
|
}
|
||||||
contract C is B {
|
contract C is B {
|
||||||
function h() public {
|
function h() public returns (bytes4 fs, bytes4 gs) {
|
||||||
B.f.selector;
|
fs = B.f.selector;
|
||||||
B.g.selector;
|
gs = B.g.selector;
|
||||||
B.g();
|
B.g();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ contract A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contract B {
|
contract B {
|
||||||
function g() external pure {
|
function g() external pure returns(bytes4) {
|
||||||
A.f.selector;
|
return A.f.selector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ interface I {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contract B {
|
contract B {
|
||||||
function g() external pure {
|
function g() external pure returns(bytes4) {
|
||||||
I.f.selector;
|
return I.f.selector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ contract A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contract B {
|
contract B {
|
||||||
function g() external pure {
|
function g() external pure returns(bytes4) {
|
||||||
A.f.selector;
|
return A.f.selector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
interface Banana {
|
||||||
|
function transfer(address,uint256) external returns(bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Apple {
|
||||||
|
function f() public pure {
|
||||||
|
Banana.transfer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (141-156): Statement has no effect.
|
@ -0,0 +1,14 @@
|
|||||||
|
interface A {
|
||||||
|
function f() external;
|
||||||
|
}
|
||||||
|
contract B {
|
||||||
|
function g() public {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C is B {
|
||||||
|
function h() external {}
|
||||||
|
bytes4 constant s1 = A.f.selector;
|
||||||
|
bytes4 constant s2 = B.g.selector;
|
||||||
|
bytes4 constant s3 = this.h.selector;
|
||||||
|
bytes4 constant s4 = super.g.selector;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure returns (bytes4) {
|
||||||
|
function() external g;
|
||||||
|
// Make sure g.selector is not considered pure:
|
||||||
|
// If it was considered pure, this would emit a warning "Statement has no effect".
|
||||||
|
g.selector;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
contract A {
|
||||||
|
function() external public f;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
bytes4 constant s4 = A.f.selector;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (88-91): Member "f" not found or not visible after argument-dependent lookup in type(contract A).
|
@ -0,0 +1,17 @@
|
|||||||
|
contract A {
|
||||||
|
function() external public f;
|
||||||
|
}
|
||||||
|
contract B {
|
||||||
|
function() external public g;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C is B {
|
||||||
|
function() external public h;
|
||||||
|
bytes4 constant s1 = h.selector;
|
||||||
|
bytes4 constant s2 = B.g.selector;
|
||||||
|
bytes4 constant s3 = this.h.selector;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (176-186): Initial value for constant variable has to be compile-time constant.
|
||||||
|
// TypeError: (213-225): Initial value for constant variable has to be compile-time constant.
|
||||||
|
// TypeError: (252-267): Initial value for constant variable has to be compile-time constant.
|
@ -0,0 +1,9 @@
|
|||||||
|
contract B {
|
||||||
|
function() external public g;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C is B {
|
||||||
|
bytes4 constant s4 = super.g.selector;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (93-100): Member "g" not found or not visible after argument-dependent lookup in contract super C.
|
Loading…
Reference in New Issue
Block a user