Skip to content

Commit f0e4392

Browse files
authored
Merge pull request #12921 from ethereum/add_event_and_error_selector_fields_on_the_line_of_function_selector_field
Adding event and error selector fields
2 parents 2bbc9a1 + d4c06d2 commit f0e4392

13 files changed

+273
-5
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
### 0.8.15 (unreleased)
22

33
Language Features:
4+
* General: Add `E.selector` for a non-anonymous event `E` to access the 32-byte selector topic.
5+
* General: Errors and Events allow qualified access from other contracts.
46

57

68
Compiler Features:

docs/contracts/events.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ four indexed arguments rather than three.
7373
In particular, it is possible to "fake" the signature of another event
7474
using an anonymous event.
7575

76+
Members of Events
77+
=================
78+
79+
- ``event.selector``: For non-anonymous events, this is a ``bytes32`` value
80+
containing the ``keccak256`` hash of the event signature, as used in the default topic.
81+
82+
83+
Example
84+
=======
85+
7686
.. code-block:: solidity
7787
7888
// SPDX-License-Identifier: GPL-3.0

libsolidity/ast/AST.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,7 @@ class EventDefinition: public CallableDeclaration, public StructurallyDocumented
12161216
FunctionTypePointer functionType(bool /*_internal*/) const override;
12171217

12181218
bool isVisibleInDerivedContracts() const override { return true; }
1219-
bool isVisibleViaContractTypeAccess() const override { return false; /* TODO */ }
1219+
bool isVisibleViaContractTypeAccess() const override { return true; }
12201220

12211221
EventDefinitionAnnotation& annotation() const override;
12221222

libsolidity/ast/Types.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3335,6 +3335,12 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
33353335
}
33363336
case Kind::Error:
33373337
return {{"selector", TypeProvider::fixedBytes(4)}};
3338+
case Kind::Event:
3339+
{
3340+
if (!(dynamic_cast<EventDefinition const&>(declaration()).isAnonymous()))
3341+
return {{"selector", TypeProvider::fixedBytes(32)}};
3342+
return MemberList::MemberMap();
3343+
}
33383344
default:
33393345
return MemberList::MemberMap();
33403346
}

libsolidity/codegen/ExpressionCompiler.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,9 +1602,14 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
16021602
{
16031603
if (functionType->hasDeclaration())
16041604
{
1605-
m_context << functionType->externalIdentifier();
1606-
/// need to store it as bytes4
1607-
utils().leftShiftNumberOnStack(224);
1605+
if (functionType->kind() == FunctionType::Kind::Event)
1606+
m_context << u256(h256::Arith(util::keccak256(functionType->externalSignature())));
1607+
else
1608+
{
1609+
m_context << functionType->externalIdentifier();
1610+
/// need to store it as bytes4
1611+
utils().leftShiftNumberOnStack(224);
1612+
}
16081613
return false;
16091614
}
16101615
else if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
@@ -1775,9 +1780,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
17751780
if (member == "selector")
17761781
{
17771782
auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type);
1783+
// all events should have already been caught by this stage
1784+
solAssert(!(functionType.kind() == FunctionType::Kind::Event));
1785+
17781786
if (functionType.kind() == FunctionType::Kind::External)
17791787
CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 2);
17801788
m_context << Instruction::SWAP1 << Instruction::POP;
1789+
17811790
/// need to store it as bytes4
17821791
utils().leftShiftNumberOnStack(224);
17831792
}

libsolidity/codegen/ir/IRGeneratorForStatements.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1785,7 +1785,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
17851785
functionType.declaration().isPartOfExternalInterface(),
17861786
""
17871787
);
1788-
define(IRVariable{_memberAccess}) << formatNumber(functionType.externalIdentifier() << 224) << "\n";
1788+
define(IRVariable{_memberAccess}) << formatNumber(
1789+
util::selectorFromSignature(functionType.externalSignature())
1790+
) << "\n";
1791+
}
1792+
else if (functionType.kind() == FunctionType::Kind::Event)
1793+
{
1794+
solAssert(functionType.hasDeclaration());
1795+
solAssert(functionType.kind() == FunctionType::Kind::Event);
1796+
solAssert(
1797+
!(dynamic_cast<EventDefinition const&>(functionType.declaration()).isAnonymous())
1798+
);
1799+
define(IRVariable{_memberAccess}) << formatNumber(
1800+
u256(h256::Arith(util::keccak256(functionType.externalSignature())))
1801+
) << "\n";
17891802
}
17901803
else
17911804
solAssert(false, "Invalid use of .selector: " + functionType.toString(false));
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
library L {
2+
error E();
3+
}
4+
library S {
5+
error E(uint);
6+
}
7+
library T {
8+
error E();
9+
}
10+
11+
error E();
12+
13+
interface I {
14+
error E();
15+
function f() external pure;
16+
}
17+
18+
contract D {
19+
error F();
20+
}
21+
22+
contract C is D {
23+
function test1() public pure returns (bytes4, bytes4, bytes4, bytes4) {
24+
assert(L.E.selector == T.E.selector);
25+
assert(L.E.selector != S.E.selector);
26+
assert(E.selector == L.E.selector);
27+
assert(I.E.selector == L.E.selector);
28+
return (L.E.selector, S.E.selector, E.selector, I.E.selector);
29+
}
30+
31+
bytes4 s1 = L.E.selector;
32+
bytes4 s2 = S.E.selector;
33+
bytes4 s3 = T.E.selector;
34+
bytes4 s4 = I.E.selector;
35+
function test2() external returns (bytes4, bytes4, bytes4, bytes4) {
36+
return (s1, s2, s3, s4);
37+
}
38+
39+
function test3() external returns (bytes4) {
40+
return (F.selector);
41+
}
42+
}
43+
// ====
44+
// compileViaYul: also
45+
// ----
46+
// test1() -> 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x2ff06700000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000
47+
// test2() -> 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x2ff06700000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000
48+
// test3() -> 0x28811f5900000000000000000000000000000000000000000000000000000000
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
library L {
2+
event E();
3+
}
4+
library S {
5+
event E(uint);
6+
}
7+
library T {
8+
event E();
9+
}
10+
interface I {
11+
event E();
12+
}
13+
14+
contract D {
15+
event F();
16+
}
17+
18+
contract C is D {
19+
function test1() external pure returns (bytes32, bytes32, bytes32) {
20+
assert(L.E.selector == T.E.selector);
21+
assert(I.E.selector == L.E.selector);
22+
23+
assert(L.E.selector != S.E.selector);
24+
assert(T.E.selector != S.E.selector);
25+
assert(I.E.selector != S.E.selector);
26+
27+
return (L.E.selector, S.E.selector, I.E.selector);
28+
}
29+
30+
bytes32 s1 = L.E.selector;
31+
bytes32 s2 = S.E.selector;
32+
bytes32 s3 = T.E.selector;
33+
bytes32 s4 = I.E.selector;
34+
function test2() external returns (bytes32, bytes32, bytes32, bytes32) {
35+
return (s1, s2, s3, s4);
36+
}
37+
38+
function test3() external returns (bytes32) {
39+
return (F.selector);
40+
}
41+
}
42+
// ====
43+
// compileViaYul: also
44+
// ----
45+
// test1() -> 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x2ff0672f372fbe844b353429d4510ea5e43683af134c54f75f789ff57bc0c0, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028
46+
// test2() -> 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x2ff0672f372fbe844b353429d4510ea5e43683af134c54f75f789ff57bc0c0, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028
47+
// test3() -> 0x28811f5935c16a099486acb976b3a6b4942950a1425a74e9eb3e9b7f7135e12a
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
contract C {
2+
event E();
3+
}
4+
5+
contract Test {
6+
event E(uint256, uint256);
7+
function f() public {
8+
emit C.E();
9+
emit E(1,2);
10+
}
11+
}
12+
// ====
13+
// compileViaYul: also
14+
// ----
15+
// f() ->
16+
// ~ emit E()
17+
// ~ emit E(uint256,uint256): 0x01, 0x02
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
library L {
2+
error E();
3+
}
4+
library S {
5+
error E(uint);
6+
}
7+
library T {
8+
error E();
9+
}
10+
11+
error E();
12+
13+
interface I {
14+
error E();
15+
function f() external pure;
16+
}
17+
18+
contract D {
19+
error F();
20+
}
21+
22+
contract C is D {
23+
function test1() public pure returns (bytes4, bytes4, bytes4, bytes4) {
24+
assert(L.E.selector == T.E.selector);
25+
assert(L.E.selector != S.E.selector);
26+
assert(E.selector == L.E.selector);
27+
assert(I.E.selector == L.E.selector);
28+
return (L.E.selector, S.E.selector, E.selector, I.E.selector);
29+
}
30+
31+
bytes4 s1 = L.E.selector;
32+
bytes4 s2 = S.E.selector;
33+
bytes4 s3 = T.E.selector;
34+
bytes4 s4 = I.E.selector;
35+
36+
function test2() view external returns (bytes4, bytes4, bytes4, bytes4) {
37+
return (s1, s2, s3, s4);
38+
}
39+
40+
function test3() pure external returns (bytes4) {
41+
return (F.selector);
42+
}
43+
}
44+
// ----
45+
// Warning 2519: (16-26): This declaration shadows an existing declaration.
46+
// Warning 2519: (45-59): This declaration shadows an existing declaration.
47+
// Warning 2519: (78-88): This declaration shadows an existing declaration.
48+
// Warning 2519: (122-132): This declaration shadows an existing declaration.

0 commit comments

Comments
 (0)