dart-lang / dart-lang/language

Non-virtual and re-declared virtual members.

Open
#3,141 4 comments 0 reactions 0 assignees View on GitHub
request
Dominant language
TeX
Stars
2.9k
Forks
239
Avg merge
2d 18h
Merged PRs (30d)
14

Description

This is an idea for a Dart adaption of the C# virtual-method system, without the overloading. It's inspired by trying to make `override` a language feature.

The idea is to allow instance methods to be declared `new`, `override` and/or `final`.

A `new` member will introduce a completely new virtual member, unrelated to any superclass member of the same name. It will *shadow* a superclass member with the same base-name rather than override it.

An `override` marked member *must* override one or more superclass/super-interface members with the same name.

A `final` member *cannot* be overridden in a subclass. A declaration of the same name will effectively be `new` (or will only refer to other inherited members with the same name).

Example:
```dart
class C {
new
int foo() => 42;
}
class B extends C {
override
int foo()=> 37;
}
class C extends B {
final override
int foo() => 87;
}
class D extends C {
new
String foo() => "Not a C.foo";
}
class E extends B {
new
String foo() => "Not a B.foo";
}
cl;ass Bad extends C {
override
int foo() => 0; // Cannot override final member, no non-final member to override.
}
```
There is no *requirement* to use `new`, `override` or `final`. If you don't, a declaration works like `override` if any superinterface has a non-final member with the same name, and as `new` if not, which is how things are today. (But you can obviously introduce a lint which forces you to always use a modifier).

The new functionality is the ability to make a member *final*, which means it cannot be overridden.
If I do `if (o is C) { print(o.foo()); }`, the complier can do *static dispatch* to `C.foo`, because the `foo` called on an object with static type `C` is always `C.foo`.

This does cause some complications, if you want to implement both `B` and `E` on the same object, because those two types both have an `foo` member, and *it's not the same member*. Effectively they correspond to different entries in the V-table, the name clash is just an accident.

So, we might need to allow:
```dart
class F implements B, E {
override
int B::foo() => 42;
override
String E::foo() => "F.foo!";
}
```
That's slightly annoying syntactically, possibly confusing to users, and it's not clear what you get if you do `(o as F).foo()`.
Here I'd say *neither*, doing `(o as F).foo()` would be an error, because `F` doesn't have a single canonical `foo` itself. It inherits two, on equal footing, but doesn't say which one it should expose itself, so it's just conflicted.

To make one of them canonical for `F` you should write:
```dart
class F implements B, E {
override
int B::foo() => 42;
override
String foo() => "F.foo!"; // No `E::`
}
```
to 1) make the latter `foo` override all non-final super-interface methods named `foo` that do not have a specific separate implementation, and 2) make it the `foo` of the class `F`.

All this complication, what does it even buy?

Two things:
* The ability to make member `final` and therefore non-virtual in subtypes (a `new final` method would be non-virtual from the start), and
* Maybe avoid some combinations of interfaces from being impossible.

I'm not sure it's worth it, but I want to put the idea out there. There might be very useful use-cases that I haven't thought of.

Contributor guide

Open the contributing guide

Research direction

No implementation files or tests are named. Start by reviewing the Dart language specification and the current rules for inherited members, interfaces, overriding, and dispatch. Done would require a resolved design for new, override, and final members, including conflicting interfaces and the proposed B::foo syntax.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.