diff --git a/Changelog.md b/Changelog.md index 6eb4c5f5b..a8db61f6e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,8 @@ ### 0.8.15 (unreleased) Language Features: +* General: Add `E.selector` for a non-anonymous event `E` to access the 32-byte selector topic. +* General: Errors and Events allow qualified access from other contracts. Compiler Features: diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index 27df4dde1..2595527d1 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -73,6 +73,16 @@ four indexed arguments rather than three. In particular, it is possible to "fake" the signature of another event using an anonymous event. +Members of Events +================= + +- ``event.selector``: For non-anonymous events, this is a ``bytes32`` value + containing the ``keccak256`` hash of the event signature, as used in the default topic. + + +Example +======= + .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4eba73c5d..7c6101bdd 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1216,7 +1216,7 @@ public: FunctionTypePointer functionType(bool /*_internal*/) const override; bool isVisibleInDerivedContracts() const override { return true; } - bool isVisibleViaContractTypeAccess() const override { return false; /* TODO */ } + bool isVisibleViaContractTypeAccess() const override { return true; } EventDefinitionAnnotation& annotation() const override; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index cedac68f3..48e4afa51 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3335,6 +3335,12 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const } case Kind::Error: return {{"selector", TypeProvider::fixedBytes(4)}}; + case Kind::Event: + { + if (!(dynamic_cast(declaration()).isAnonymous())) + return {{"selector", TypeProvider::fixedBytes(32)}}; + return MemberList::MemberMap(); + } default: return MemberList::MemberMap(); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 36646ea21..95087aa02 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1602,9 +1602,14 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { if (functionType->hasDeclaration()) { - m_context << functionType->externalIdentifier(); - /// need to store it as bytes4 - utils().leftShiftNumberOnStack(224); + if (functionType->kind() == FunctionType::Kind::Event) + m_context << u256(h256::Arith(util::keccak256(functionType->externalSignature()))); + else + { + m_context << functionType->externalIdentifier(); + /// need to store it as bytes4 + utils().leftShiftNumberOnStack(224); + } return false; } else if (auto const* expr = dynamic_cast(&_memberAccess.expression())) @@ -1775,9 +1780,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) if (member == "selector") { auto const& functionType = dynamic_cast(*_memberAccess.expression().annotation().type); + // all events should have already been caught by this stage + solAssert(!(functionType.kind() == FunctionType::Kind::Event)); + if (functionType.kind() == FunctionType::Kind::External) CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 2); m_context << Instruction::SWAP1 << Instruction::POP; + /// need to store it as bytes4 utils().leftShiftNumberOnStack(224); } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 9e48c811c..5eaeed743 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1785,7 +1785,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) functionType.declaration().isPartOfExternalInterface(), "" ); - define(IRVariable{_memberAccess}) << formatNumber(functionType.externalIdentifier() << 224) << "\n"; + define(IRVariable{_memberAccess}) << formatNumber( + util::selectorFromSignature(functionType.externalSignature()) + ) << "\n"; + } + else if (functionType.kind() == FunctionType::Kind::Event) + { + solAssert(functionType.hasDeclaration()); + solAssert(functionType.kind() == FunctionType::Kind::Event); + solAssert( + !(dynamic_cast(functionType.declaration()).isAnonymous()) + ); + define(IRVariable{_memberAccess}) << formatNumber( + u256(h256::Arith(util::keccak256(functionType.externalSignature()))) + ) << "\n"; } else solAssert(false, "Invalid use of .selector: " + functionType.toString(false)); diff --git a/test/libsolidity/semanticTests/error/error_selector.sol b/test/libsolidity/semanticTests/error/error_selector.sol new file mode 100644 index 000000000..e7e40fd6e --- /dev/null +++ b/test/libsolidity/semanticTests/error/error_selector.sol @@ -0,0 +1,48 @@ +library L { + error E(); +} +library S { + error E(uint); +} +library T { + error E(); +} + +error E(); + +interface I { + error E(); + function f() external pure; +} + +contract D { + error F(); +} + +contract C is D { + function test1() public pure returns (bytes4, bytes4, bytes4, bytes4) { + assert(L.E.selector == T.E.selector); + assert(L.E.selector != S.E.selector); + assert(E.selector == L.E.selector); + assert(I.E.selector == L.E.selector); + return (L.E.selector, S.E.selector, E.selector, I.E.selector); + } + + bytes4 s1 = L.E.selector; + bytes4 s2 = S.E.selector; + bytes4 s3 = T.E.selector; + bytes4 s4 = I.E.selector; + function test2() external returns (bytes4, bytes4, bytes4, bytes4) { + return (s1, s2, s3, s4); + } + + function test3() external returns (bytes4) { + return (F.selector); + } +} +// ==== +// compileViaYul: also +// ---- +// test1() -> 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x2ff06700000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000 +// test2() -> 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x2ff06700000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000 +// test3() -> 0x28811f5900000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/events/event_selector.sol b/test/libsolidity/semanticTests/events/event_selector.sol new file mode 100644 index 000000000..b6d940093 --- /dev/null +++ b/test/libsolidity/semanticTests/events/event_selector.sol @@ -0,0 +1,47 @@ +library L { + event E(); +} +library S { + event E(uint); +} +library T { + event E(); +} +interface I { + event E(); +} + +contract D { + event F(); +} + +contract C is D { + function test1() external pure returns (bytes32, bytes32, bytes32) { + assert(L.E.selector == T.E.selector); + assert(I.E.selector == L.E.selector); + + assert(L.E.selector != S.E.selector); + assert(T.E.selector != S.E.selector); + assert(I.E.selector != S.E.selector); + + return (L.E.selector, S.E.selector, I.E.selector); + } + + bytes32 s1 = L.E.selector; + bytes32 s2 = S.E.selector; + bytes32 s3 = T.E.selector; + bytes32 s4 = I.E.selector; + function test2() external returns (bytes32, bytes32, bytes32, bytes32) { + return (s1, s2, s3, s4); + } + + function test3() external returns (bytes32) { + return (F.selector); + } +} +// ==== +// compileViaYul: also +// ---- +// test1() -> 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x2ff0672f372fbe844b353429d4510ea5e43683af134c54f75f789ff57bc0c0, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028 +// test2() -> 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x2ff0672f372fbe844b353429d4510ea5e43683af134c54f75f789ff57bc0c0, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028 +// test3() -> 0x28811f5935c16a099486acb976b3a6b4942950a1425a74e9eb3e9b7f7135e12a diff --git a/test/libsolidity/semanticTests/events/simple.sol b/test/libsolidity/semanticTests/events/simple.sol new file mode 100644 index 000000000..7190de413 --- /dev/null +++ b/test/libsolidity/semanticTests/events/simple.sol @@ -0,0 +1,17 @@ +contract C { + event E(); +} + +contract Test { + event E(uint256, uint256); + function f() public { + emit C.E(); + emit E(1,2); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> +// ~ emit E() +// ~ emit E(uint256,uint256): 0x01, 0x02 diff --git a/test/libsolidity/syntaxTests/errors/error_selector_syntax.sol b/test/libsolidity/syntaxTests/errors/error_selector_syntax.sol new file mode 100644 index 000000000..ebd598cb2 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/error_selector_syntax.sol @@ -0,0 +1,48 @@ +library L { + error E(); +} +library S { + error E(uint); +} +library T { + error E(); +} + +error E(); + +interface I { + error E(); + function f() external pure; +} + +contract D { + error F(); +} + +contract C is D { + function test1() public pure returns (bytes4, bytes4, bytes4, bytes4) { + assert(L.E.selector == T.E.selector); + assert(L.E.selector != S.E.selector); + assert(E.selector == L.E.selector); + assert(I.E.selector == L.E.selector); + return (L.E.selector, S.E.selector, E.selector, I.E.selector); + } + + bytes4 s1 = L.E.selector; + bytes4 s2 = S.E.selector; + bytes4 s3 = T.E.selector; + bytes4 s4 = I.E.selector; + + function test2() view external returns (bytes4, bytes4, bytes4, bytes4) { + return (s1, s2, s3, s4); + } + + function test3() pure external returns (bytes4) { + return (F.selector); + } +} +// ---- +// Warning 2519: (16-26): This declaration shadows an existing declaration. +// Warning 2519: (45-59): This declaration shadows an existing declaration. +// Warning 2519: (78-88): This declaration shadows an existing declaration. +// Warning 2519: (122-132): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/events/event_selector_library_called_inside_function.sol b/test/libsolidity/syntaxTests/events/event_selector_library_called_inside_function.sol new file mode 100644 index 000000000..49741a5ca --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_selector_library_called_inside_function.sol @@ -0,0 +1,12 @@ +library Y { + event E() anonymous; +} + +contract D { + function test1() external pure returns (bytes32) { + return Y.E.selector; + } + +} +// ---- +// TypeError 9582: (123-135): Member "selector" not found or not visible after argument-dependent lookup in function (). diff --git a/test/libsolidity/syntaxTests/events/event_selector_library_declared_outside_but_called_inside_function.sol b/test/libsolidity/syntaxTests/events/event_selector_library_declared_outside_but_called_inside_function.sol new file mode 100644 index 000000000..72d224ba2 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_selector_library_declared_outside_but_called_inside_function.sol @@ -0,0 +1,13 @@ +library Y { + event E() anonymous; +} + +contract C { + bytes32 s5 = Y.E.selector; + + function test2() view external returns (bytes32) { + return s5; + } +} +// ---- +// TypeError 9582: (70-82): Member "selector" not found or not visible after argument-dependent lookup in function (). diff --git a/test/libsolidity/syntaxTests/events/event_selector_syntax.sol b/test/libsolidity/syntaxTests/events/event_selector_syntax.sol new file mode 100644 index 000000000..14d2c94d8 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_selector_syntax.sol @@ -0,0 +1,43 @@ +library L { + event E(); +} +library S { + event E(uint); +} +library T { + event E(); +} +interface I { + event E(); +} + +contract D { + event F(); +} + +contract C is D { + function test1() external pure returns (bytes32, bytes32, bytes32) { + assert(L.E.selector == T.E.selector); + assert(I.E.selector == L.E.selector); + + assert(L.E.selector != S.E.selector); + assert(T.E.selector != S.E.selector); + assert(I.E.selector != S.E.selector); + + return (L.E.selector, S.E.selector, I.E.selector); + } + + bytes32 s1 = L.E.selector; + bytes32 s2 = S.E.selector; + bytes32 s3 = T.E.selector; + bytes32 s4 = I.E.selector; + + function test2() view external returns (bytes32, bytes32, bytes32, bytes32) { + return (s1, s2, s3, s4); + } + + function test3() pure external returns (bytes32) { + return (F.selector); + } +} +// ----