Hello,
I’d like to ask for more feedback on one of the issues from the bug tracker: Do not require override for functions from interfaces #8281. We’d like to make a decision there and either move it to implementation backlog or close it but the current discussion is inconclusive so far and all the options have their proponents.
Let me summarize the available options first:
- Do not change anything. Keep requiring
override
no matter whether the function being overridden is only declared in an interface or defined in a contract. - Do not require
override
when overriding a function that is only a declaration.-
variant A: Do not require it even if declarations from multiple base contracts/interfaces are being overridden (i.e. the
override(Base1, Base2)
syntax). - variant B: Still require it when overriding declarations from multiple base contracts.
-
variant A: Do not require it even if declarations from multiple base contracts/interfaces are being overridden (i.e. the
- Introduce a new keyword (e.g.
implements
) to be used when only a declaration is being overridden.-
variant A: If multiple inheritance is used and some of the base contracts have a definition and some have a declaration, require both
override
andimplements
. -
variant B: Do not require
implements
ifoverride
is already present.
-
variant A: If multiple inheritance is used and some of the base contracts have a definition and some have a declaration, require both
Please say which option you would prefer and why.
Two additional questions also require clarification. I think that the answer should be “yes” in both cases for consistency but I want to make sure everyone agrees:
- Should the same rules apply to functions declared in interfaces and functions without a body in abstract contracts?
- Should the same rules apply to virtual modifiers without a body?
Below is my attempt at summarizing arguments from the issue with some extra additions from me. All organized based on use cases of the override
keyword.
Use case 1: Ensuring that the overridden function exists
- Without any keyword the compiler cannot detect mistakes. Removing it removes the check to ensure that the function actually exists in the interface and that it has the same name, parameters and return values.
- In many cases this check is redundant because if there is a mismatch, the compiler will notice that not all functions from the interface have been implemented.
- There are cases where it’s not redundant. For example when a new implementation is being added in an abstract contract.
-
override
allows detecting mismatches if the interface gets modified. The compiler can point out all the functions in derived contracts that need to be updated too.- This, again, can be redundant in many cases.
- A related situation is when you switch from one base class implementing an interface to another that’s supposed to be a drop-in replacement. With a keyword the compiler can notice that the new base class has an extra declaration that matches one of the functions in the derived class (see @ekpyron’s example).
- Removing the keyword also removes this check for situations when a declaration in one interface overrides a declaration in another.
- Note that when an interface inherits from multiple other interfaces providing the same declaration, it must override it so it’s not a useless in practice as it may seem at first.
Use case 2: Clarifying that the inherited behavior might be affected
- Behavior can only change if an implementation already exists. Declarations in interfaces do not define behavior so having them also covered by
override
reduces the usefulness of the keyword in this use case.- This is especially important for auditing.
- Overriding functions from interfaces is very common and requiring
override
desensitizes the reader to it. It no longer stands out as something worth paying attention to. - Adding the keyword to nearly all functions is also tedious when writing code.
Use case 3: Clarifying which function is being overridden
When multiple virtual functions with the same signature are defined independently in multiple base contracts, the compiler requires specifying the names of these contracts to make sure the programmer is aware that they are all being overridden. I think this is not a big concern when the functions being overridden are only declarations.
Use case 4: Ensuring that an interface is completely implemented
The compiler can already detect that not all declaration from an interface or an abstract contract are implemented. Removing the keyword for overrides of declarations does not change that.