Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require 'override' and 'virtual' keyword #5424

Closed
4 of 5 tasks
chriseth opened this issue Nov 14, 2018 · 29 comments · Fixed by #7939
Closed
4 of 5 tasks

Require 'override' and 'virtual' keyword #5424

chriseth opened this issue Nov 14, 2018 · 29 comments · Fixed by #7939
Assignees
Labels
breaking change ⚠️ language design :rage4: Any changes to the language, e.g. new features

Comments

@chriseth
Copy link
Contributor

chriseth commented Nov 14, 2018

Related to #2563

Steps:

@ekpyron ekpyron changed the title Require 'override' and 'vitual' keyword Require 'override' and 'virtual' keyword Jan 16, 2019
@chriseth
Copy link
Contributor Author

chriseth commented Jan 16, 2019

draft of specification:

  • interface functions are always virtual implicitly
  • functions coming in from different bases are disallowed, unless they override the same function in a shared base
  • public state variables can override external virtual functions, but need to be marked "override", but cannot be marked virtual themselves. (can they be overridden again?)
  • overriding an "override" function is fine
  • private functions cannot be overridden at all (virtual and private cannot be combined, and private functions are "visible" for overriding detection)
  • functions without implementation still need virtual

@chriseth
Copy link
Contributor Author

How to handle combinations of overloading and overriding?

@chriseth chriseth changed the title Require 'override' and 'virtual' keyword Require 'override' and 'virtual' keyword (M/L) Jan 16, 2019
@ekpyron
Copy link
Member

ekpyron commented Jan 16, 2019

I tend to make functions that are marked override not overridable themselves unless they are again explicitly marked virtual again. Otherwise we need to think about whether we need an additional final keyword and I think just sticking to "only things explicitly marked virtual can be overriden" is easier and better than doing that... if overriding state variables are allowed to be overwritten again, that may be an issue though - but maybe that shouldn't be possible anyways (not sure about that)...

@ekpyron
Copy link
Member

ekpyron commented Jan 16, 2019

And I think it makes sense to introduce the keywords themselves in 0.6.0, but to only deprecate omitting them. So in 0.6.0 omitting the keywords would cause warnings, unless you specify e.g. pragma legacy; or pragma ignore deprecated; or sth like that and these warnings turn into errors with 0.7.0. That may be a good way to get around the adoption and real-world testing problem and if it works well we could re-use the concept for future changes.
EDIT: maybe even pragma compat 0.5;

@nventuro
Copy link
Contributor

nventuro commented Mar 8, 2019

Will this info (e.g. a function being virtual) be available in the contract artifacts? It'd interesting if we were able to e.g. have tests asserting a certain function is virtual to prevent regression errors.

@axic
Copy link
Member

axic commented Mar 8, 2019

contract artifacts

What do you exactly mean by that?

@nventuro
Copy link
Contributor

nventuro commented Mar 8, 2019

I guess the most appropriate location for such a thing would be the JSON AST (which is included in the .json file that results from compilation when using e.g. truffle).

@Marenz
Copy link
Contributor

Marenz commented Aug 12, 2019

To recap my understanding of the goals:

  • override to explicitly show that a function is overriding another function from a base
  • virtual to explicitly allow overriding in a derived function

I would argue that users want to do the virtual use case a lot and thus virtual would need to be used a lot. If that kind of usage is the default, wouldn't it be smarter to make the other way explicit, e.g. having a final keyword to mark a function explicitly as not overridable?

@Marenz Marenz self-assigned this Aug 12, 2019
@Marenz
Copy link
Contributor

Marenz commented Aug 12, 2019

How to handle combinations of overloading and overriding?

I'd say the overriding function function signature must match the signature exactly, that should include any overloads, too.
Or did I miss a scenario where this isn't enough?

@chriseth
Copy link
Contributor Author

I'd say the overriding function function signature must match the signature exactly, that should include any overloads, too.

Yes, I think this is how it is handled currently.

@chriseth
Copy link
Contributor Author

About the default: I'm actually not sure if virtual is really that common. In any case, if we do not default to non-virtual, this feature is useless.

@Marenz
Copy link
Contributor

Marenz commented Aug 12, 2019

About the default: I'm actually not sure if virtual is really that common. In any case, if we do not default to non-virtual, this feature is useless.

Well, yes, my suggestion was to basically use final instead of virtual with virtual as the default. But if it is less common then my main argument doesn't apply anymore. We should gather some data.

@chriseth
Copy link
Contributor Author

Discussion resulted in relaxation of the requirements for the case of a function being inherited from multiple base classes:

A function declaration D1 is called a "base function" of a function declaration D2 if they declare the same function, D1 is in a (direct or indirect) base contract of the contract of D2 and no contract in the path through the inheritance graph contains the function.

If a function has a base function, it needs the "override" specifier and the base function needs the "virtual" specifier (which is implied in interfaces).
If a function has more than one base functions, the "override" specifier needs to list all contract types of all the base functions.

@chriseth
Copy link
Contributor Author

In addition to that: It is illegal, if a function that is not declared in a contract would have multiple base functions.

@ekpyron
Copy link
Member

ekpyron commented Aug 14, 2019

I'm still not entirely sure about this case and fine with both options, but I wanted to bring it up once more:

contract A { function f() virtual {...} }
contract B is A {}
contract C is B { function f() override {...} }

Should this be valid or not? We said ǹot having f in B indicates "do everything like the base", but does that mean "always use A's implementation of f" or "inherit the fact that A's f isvirtual"?

There's two cases: either B wants to allow overriding f or wants to disallow it, the question is what's the default.

Explicit allowing would mean you need to write
contract B is A { function f() virtual override { A.f(); } }
if overriding f in C should be possible.

Explicit disallowing would mean you need to write
contract B is A { function f() override { A.f(); } }
if overriding f in C should not be possible.

@chriseth Your specification would mean defaulting to allowing. Do we want to do it that way? I'm fine with it, just reconfirming.

@ekpyron
Copy link
Member

ekpyron commented Sep 2, 2019

I was just looking over the draft PR(s) for implementing this - and I'm realizing: requiring to redeclare functions of the base "virtual" again to allow them to be overridden would make the implementation much simpler, since we'd only need to check one level at a time :-). And by the same logic I could imagine that this would make auditing code easier, so I'm actually still leaning towards that option...

@chriseth
Copy link
Contributor Author

chriseth commented Sep 2, 2019

Can you ask the list?

@ekpyron
Copy link
Member

ekpyron commented Sep 3, 2019

One option that will get us around discussing which should be the default is to allow both, i.e. to have "virtual" and "final" inheritance, but I'm not sure, if we should seriously consider that :-).

@fasteater
Copy link

if B is A , B should inherit everything from A, which means B.f should also be virtual

@ericDeCourcy
Copy link

public state variables can override external virtual functions, but need to be marked "override", but cannot be marked virtual themselves. (can they be overridden again?)

How would this work in the case of an overloaded functions? Is this portion of the spec still being considered?

@chriseth
Copy link
Contributor Author

@ericDeCourcy currently, overloading and overriding are considered to be unrelated. We might want to restrict this, but probably not for 0.6.0.

@ekpyron
Copy link
Member

ekpyron commented Sep 12, 2019

Our current specification has one hole:

contract A {
  function f() external {...}
}
contract B {
  function f() external {...}
}
contract C is A,B {
  function f() external override(A,B) { /* wants to call A.f, but can't */ }
}

@ekpyron
Copy link
Member

ekpyron commented Sep 12, 2019

Which we could solve be allowing
function f() external override(A,B) = A.f;
Which would also be nice to have for non-external functions.

@chriseth
Copy link
Contributor Author

Sounds good! I would not allow anything like A.g for now, but we might in the future.

@Marenz
Copy link
Contributor

Marenz commented Sep 18, 2019

for reference, the current PR for override is #7320

@ekpyron
Copy link
Member

ekpyron commented Nov 26, 2019

Which we could solve be allowing
function f() external override(A,B) = A.f;
Which would also be nice to have for non-external functions.

Do we want this for 0.6.0 as well?

@chriseth
Copy link
Contributor Author

Maybe better to introduce calldata variables for public functions?

@Marenz Marenz changed the title Require 'override' and 'virtual' keyword (M/L) Require 'override' and 'virtual' keyword Nov 27, 2019
@Marenz Marenz self-assigned this Nov 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change ⚠️ language design :rage4: Any changes to the language, e.g. new features
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants