Take the following code:
"Vm" and "Foo"
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.17;
contract Vm {
event ExpectRevert1();
function expectRevert() external {
emit ExpectRevert1();
}
event ExpectRevert2(bytes4 message);
function expectRevert(bytes4 message) external {
emit ExpectRevert2(message);
}
event ExpectRevert3(bytes message);
function expectRevert(bytes calldata revertData) external {
emit ExpectRevert3(revertData);
}
}
contract Foo {
Vm internal vm = new Vm();
function foo() external {
vm.expectRevert("Hello World");
}
}
Calling foo
will emit an ExpectRevert3
event, as I expected. However, if I rewrite the code like this:
Just "Foo"
contract Foo {
event ExpectRevert1();
function expectRevert() public {
emit ExpectRevert1();
}
event ExpectRevert2(bytes4 message);
function expectRevert(bytes4 message) public {
emit ExpectRevert2(message);
}
event ExpectRevert3(bytes message);
function expectRevert(bytes calldata revertData) public {
emit ExpectRevert3(revertData);
}
function foo() external {
expectRevert("Hello World");
}
}
The code does not compile anymore. I am getting this error:
TypeError: No matching declaration found after argument-dependent lookup.
--> contracts/Foo.sol:44:9:
|
44 | expectRevert("Hello World");
| ^^^^^^^^^^^^
Note: Candidate:
--> contracts/Foo.sol:27:5:
|
27 | function expectRevert() public {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: Candidate:
--> contracts/Foo.sol:33:5:
|
33 | function expectRevert(bytes4 message) public {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: Candidate:
--> contracts/Foo.sol:39:5:
|
39 | function expectRevert(bytes calldata revertData) public {
| ^ (Relevant source part starts here and spans across multiple lines).
Why does the former code compile, but the latter not? Why is Solidity able to implicitly convert (coerce?) the string literal when the call is external, but when the exact same function is called internally, it does not work?