Consider the following code:
pragma solidity ^0.4.21;
contract Base {
uint public x;
function doSomething() external {
x++;
}
function doSomethingElse() public {
x++;
}
}
contract Extender is Base {
function doSomething() external {
super.doSomething(); // <---- COMPILER ERROR HERE
}
function doSomethingElse() public {
super.doSomethingElse();
}
}
The error can be seen by pasting the code in remix, which is:
Type Error: Member "doSomething" not found or not visible after argument-dependent lookup in contract super Extender
Which actually makes sense, because super triggers an internal call, and external functions cannot be called internally.
The question is, beyond the reason for the error, is this behavior desired?
I'm not sure I understand. As you say, external functions cannot be called internally. If you change it to public, it can be called internally. If the behaviour is not desired, change the function to public.
Or which behaviour are you talking about? The fact that external functions can be overridden?
Which actually makes sense, because super triggers an internal call, and external functions cannot be called internally.
I think the expectation here would be that super.doSomething() would work if the visibility in the parent is internal or public, but is not working since it is marked as external.
In fact the following works as expected:
pragma solidity ^0.4.21;
contract Base {
uint public x;
function doSomething() {
x++;
}
function doSomethingElse() public {
x++;
}
}
contract Extender is Base {
function doSomething() {
super.doSomething();
}
function doSomethingElse() public {
super.doSomethingElse();
}
}
Closing since the issue seems solved. Please feel free to reopen if you want to restart the discussion.
I'd like to reopen this issue. It seems to impair composability for smart contracts quite a bit!
Many developers are writing contracts with external methods that correctly aren't called internally, but they're completely unaware that ancestor methods can't call super.methodName() as a result. There's nothing in the docs or compiler about why.
For example, I can't write a non-fungible token contract that inherits from a base contract and calls if (super.isApprovedForAll(...)) { .... to check if a user approved access. The compiler shows the error: "TypeError: Member "isApprovedForAll" not found or not visible after argument-dependent lookup in contract super ERC1155Tradable".
What's the security benefit of preventing external methods from being called by child instances? I understand the case where a state-modifying method like setApprovalForAll(...) external would be confusing if it could be called by an inheriting contract, but I can't think of a scenario where it would matter.
@alexanderatallah sorry for the late reply. The reason is just that the EVM does not allow such calls. You cannot call an overwritten function externally because it means that the entry point is replaced and you cannot call it internally because it is not public.
Most helpful comment
I'd like to reopen this issue. It seems to impair composability for smart contracts quite a bit!
Many developers are writing contracts with
externalmethods that correctly aren't called internally, but they're completely unaware that ancestor methods can't callsuper.methodName()as a result. There's nothing in the docs or compiler about why.For example, I can't write a non-fungible token contract that inherits from a base contract and calls
if (super.isApprovedForAll(...)) { ....to check if a user approved access. The compiler shows the error:"TypeError: Member "isApprovedForAll" not found or not visible after argument-dependent lookup in contract super ERC1155Tradable".What's the security benefit of preventing
externalmethods from being called by child instances? I understand the case where a state-modifying method likesetApprovalForAll(...) externalwould be confusing if it could be called by an inheriting contract, but I can't think of a scenario where it would matter.