diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 464902971..e41425c6e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2527,7 +2527,15 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) else if (TypeType const* typeType = dynamic_cast(exprType)) { if (ContractType const* contractType = dynamic_cast(typeType->actualType())) + { annotation.isLValue = annotation.referencedDeclaration->isLValue(); + if ( + auto const* functionType = dynamic_cast(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 @@ -2535,6 +2543,20 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (auto tt = dynamic_cast(exprType)) if (tt->actualType()->category() == Type::Category::Enum) annotation.isPure = true; + if ( + auto const* functionType = dynamic_cast(exprType); + functionType && + functionType->hasDeclaration() && + dynamic_cast(&functionType->declaration()) && + memberName == "selector" + ) + if (auto const* parentAccess = dynamic_cast(&_memberAccess.expression())) + { + annotation.isPure = parentAccess->expression().annotation().isPure; + if (auto const* exprInt = dynamic_cast(&parentAccess->expression())) + if (exprInt->name() == "this" || exprInt->name() == "super") + annotation.isPure = true; + } if (auto magicType = dynamic_cast(exprType)) { if (magicType->kind() == MagicType::Kind::ABI) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c8ded9078..0fc24d919 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1303,6 +1303,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { switch (funType->kind()) { + case FunctionType::Kind::Declaration: + break; case FunctionType::Kind::Internal: // 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 diff --git a/test/libsolidity/syntaxTests/types/contractTypeType/members/base_contract.sol b/test/libsolidity/syntaxTests/types/contractTypeType/members/base_contract.sol index 154a57d18..609d21b7b 100644 --- a/test/libsolidity/syntaxTests/types/contractTypeType/members/base_contract.sol +++ b/test/libsolidity/syntaxTests/types/contractTypeType/members/base_contract.sol @@ -3,9 +3,9 @@ contract B { function g() public {} } contract C is B { - function h() public { - B.f.selector; - B.g.selector; + function h() public returns (bytes4 fs, bytes4 gs) { + fs = B.f.selector; + gs = B.g.selector; B.g(); } } diff --git a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_contract_name.sol b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_contract_name.sol index ec53a0f60..63320917e 100644 --- a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_contract_name.sol +++ b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_contract_name.sol @@ -3,7 +3,7 @@ contract A { } contract B { - function g() external pure { - A.f.selector; + function g() external pure returns(bytes4) { + return A.f.selector; } } diff --git a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_interface_name.sol b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_interface_name.sol index e733b6701..b721a5a69 100644 --- a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_interface_name.sol +++ b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_interface_name.sol @@ -3,7 +3,7 @@ interface I { } contract B { - function g() external pure { - I.f.selector; + function g() external pure returns(bytes4) { + return I.f.selector; } } diff --git a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_via_contract_name_public.sol b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_via_contract_name_public.sol index 28d1ff6d1..533f30279 100644 --- a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_via_contract_name_public.sol +++ b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_via_contract_name_public.sol @@ -3,7 +3,7 @@ contract A { } contract B { - function g() external pure { - A.f.selector; + function g() external pure returns(bytes4) { + return A.f.selector; } } diff --git a/test/libsolidity/syntaxTests/types/function_types/function_definition_expression.sol b/test/libsolidity/syntaxTests/types/function_types/function_definition_expression.sol new file mode 100644 index 000000000..8afe07f57 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/function_definition_expression.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/function_selector_pure.sol b/test/libsolidity/syntaxTests/types/function_types/selector/function_selector_pure.sol new file mode 100644 index 000000000..cb7ad8c56 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/function_selector_pure.sol @@ -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; +} diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/local_variable_selector_not_pure.sol b/test/libsolidity/syntaxTests/types/function_types/selector/local_variable_selector_not_pure.sol new file mode 100644 index 000000000..481683af3 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/local_variable_selector_not_pure.sol @@ -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; + } +} diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_contract_name.sol b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_contract_name.sol new file mode 100644 index 000000000..79d74893b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_contract_name.sol @@ -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). diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_not_pure.sol b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_not_pure.sol new file mode 100644 index 000000000..2bd5a2a31 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_not_pure.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_super.sol b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_super.sol new file mode 100644 index 000000000..c6d658d1d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_super.sol @@ -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.