-
Notifications
You must be signed in to change notification settings - Fork 215
Say that you cannot override index
or conflict with values
.
#2080
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
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
8b6dac5
Say that you cannot override `index` or conflict with `values`.
lrhn d4d146a
Update feature-specification.md
lrhn 30f130b
Rephrase the `values` restriction.
lrhn 29490a2
Make sentence a comment.
lrhn 3740e58
Update accepted/future-releases/enhanced-enums/feature-specification.md
lrhn 0bc530e
Tweak sentence.
lrhn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
# Dart Enhanced Enum Classes | ||
|
||
Author: [email protected]<br>Version: 1.5<br>Tracking issue [#158](https://github.com/dart-lang/language/issues/158) | ||
Author: [email protected]<br>Version: 1.6<br>Tracking issue [#158](https://github.com/dart-lang/language/issues/158) | ||
|
||
This is a formal proposal for a language feature which allows `enum` declarations to declare classes with fields, methods and const constructors initializing those fields. Further, `enum` declarations can implement interfaces and, as an optional feature, apply mixins. | ||
|
||
|
@@ -56,6 +56,7 @@ It is a **compile-time error** if the initializer list of a non-redirecting gene | |
_We will introduce the necessary super-invocation ourselves as an implementation detail. From the user’s perspective, they extend `Enum` which has no public constructors. We could allow `super()`, which would then be a constructor of `Enum`, but it's simpler to just disallow super invocations entirely._ | ||
|
||
It is a **compile-time error** to refer to a declared or default generative constructor of an `enum` declaration in any way, other than: | ||
|
||
* As the target of a redirecting generative constructor of the same `enum`, or | ||
* Implicitly in the enum value declarations of the same `enum`. | ||
|
||
|
@@ -64,6 +65,8 @@ That also means that a redirecting *factory* constructor cannot redirect to a ge | |
and therefore no factory constructor of an `enum` declaration can be `const`, because a `const` factory constructor | ||
must redirect to a generative constructor._ | ||
|
||
It's a **compile-time error** if the enum declaration contains a static or instance member declaration with the name `values`, or if the superclass or any superinterface of the enum declaration has an interface member named `values`. _A `values` static constant member will be provided for the class, this restriction ensures that there is no conflict with that declaration._ | ||
|
||
## Semantics | ||
|
||
The `Enum` class behaves as if it was declared as: | ||
|
@@ -91,7 +94,7 @@ The semantics of such an enum declaration, *E*, is defined as introducing a (sem | |
It’s a **compile-time error** if such a mixin application introduces any instance variables. _We need to be able to call an implementation specific superclass `const` constructor of `Enum`, and a mixin application of a mixin with a field does not make its forwarding constructor `const`. Currently that’s the only restriction, but if we add further restrictions on mixin applications having `const` forwarding constructors, those should also apply here._ | ||
|
||
* **Superinterfaces**: The immediate superinterfaces of *C* are the interface of the superclass and the interfaces declared by *E*. | ||
|
||
* If `E` is declared as `enum Name with Mixin1, Mixin2 implements Type1, Type2 { … }` then the immediate superinterfaces of *C* are the interfaces of `Name with Mixin1, Mixin2`, `Type1` and `Type2`. | ||
|
||
- **Declared members**: For each member declaration of the `enum` declaration *E*, the same member is added to the class *C*. This includes constructors (which must be `const` generative or non-`const` factory constructors.) | ||
|
@@ -102,34 +105,32 @@ The semantics of such an enum declaration, *E*, is defined as introducing a (sem | |
```dart | ||
const Name(); | ||
``` | ||
_(This differs from the default constructor of a normal `class` declaration by being constant.)_ | ||
|
||
_(This differs from the default constructor of a normal `class` declaration by being constant, and by being added even if a factory constructor is present. If no generative constructor is declared, and the unnamed constructor is taken by a factory constructor, there is no way for the enum declaration to compile successfully, since the declaration must contain at least one enum value, and that enum value must refer to a generative constructor.)_ | ||
eernstg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- **Enum values**: For each `<enumEntry>` with name `id` and index *i* in the comma-separated list of enum entries, a constant value is created, and a static constant variable named `id` is created in *C* with that value. All the constant values are associated, in some implementation dependent way, with | ||
- their name `id` as a string `"id"`, | ||
|
||
- their name `id` as a string `"id"`, | ||
- their index *i* as an `int`, and | ||
- their `enum` class’s name as a string, `"Name"`, | ||
all of which is accessible to the `toString` and `index` member of `Enum`, and to the `EnumName.name` extension getter. The values are computed as follows. | ||
- `id` ↦ `Name()` (no arguments, equivalent to empty argument list) | ||
- `id(args)` ↦ `Name(args)` | ||
- `id<types>(args)` ↦ `Name<types>(args)` | ||
- `id.named(args)` ↦ `Name._$named(args)` | ||
- `id<types>.named(args)` ↦ `Name<types>._$named(args)` | ||
|
||
all of which are accessible to the `toString` and `index` member of `Enum`, and to the `EnumName.name` extension getter. The values are computed as by the following constant constructor invocations. | ||
|
||
- `id` ↦ `const Name()` (no arguments, equivalent to empty argument list) | ||
- `id(args)` ↦ `const Name(args)` | ||
- `id<types>(args)` ↦ `const Name<types>(args)` | ||
- `id.named(args)` ↦ `const Name.named(args)` | ||
- `id<types>.named(args)` ↦ `const Name<types>.named(args)` | ||
|
||
where `args` are considered as occurring in a `const` context, and it’s a **compile-time error** if they are then not compile-time constants. | ||
|
||
Type inference is applied to the resulting constructor invocations, with no context type, where necessary, so omitted type arguments to a generic `enum` class are filled in by type inference, using the type of arguments, if any, and then the type of the constant variable is the static type of the constant object creation expression. | ||
|
||
The objects created here are *not canonicalized* like other constant object creations. _(In practice, the index value is considered part of the object, so no two objects will have the same state.)_ | ||
|
||
- **Static `values` list**: If the class does not declare or inherit a member with base-name `values`, a static constant variable named `values` is added as by the declaration `static const List<Name> values = [id1, …, idn];` | ||
where `id1`…`idn` are the names of the enum entries of the `enum` declaration in source/index order. | ||
If `Name` is generic, the `List<Name>` instantiates `Name` to its bounds. | ||
|
||
The resulting constructor invocations are subject to type inference, using the empty context type. *This implies that inferred type arguments to the constructor invocation itself may depend on the types of the argument expressions of `args`.* The type of the constant variable is the static type of the resulting constant object creation expression. | ||
|
||
The objects created here are *not canonicalized* like other constant object creations. _(In practice, the index value is considered part of the object, so no two objects will have the same state.)_ | ||
|
||
- **Static `values` list**: A static constant variable named `values` is added as by the declaration `static const List<Name> values = [id1, …, idn];` | ||
where `id1`…`idn` are the names of the enum entries of the `enum` declaration in source/index order. | ||
_If `Name` is generic, the `List<Name>` instantiates `Name` to its bounds._ | ||
|
||
If the resulting class would have any naming conflicts, or other compile-time errors, the `enum` declaration is invalid and a compile-time error occurs. Such errors include, but are not limited to: | ||
|
||
|
@@ -151,13 +152,14 @@ It’s currently a compile-time error for a class to implement, extend or mix-in | |
|
||
Because we want to allow interfaces and mixins that are intended to be applied to `enum` declarations, and therefore to assume `Enum` to be a superclass, we loosen that restriction to: | ||
|
||
- It’s a compile-time error if a *non-abstract* class has `Enum` as a superinterface (directly or transitively) unless it is the corresponding class of an `enum` declaration. | ||
|
||
- It's a compile-time error if a *non-abstract* class has `Enum` as a superinterface (directly or transitively) unless it is the corresponding class of an `enum` declaration. | ||
- It is a compile-time error if a class implements, extends or mixes-in the class or interface introduced by an `enum` declaration. _(An enum class can’t be used as a mixin since it is not a `mixin` declaration and the class has a superclass other than `Object`, but we include “mixes-in” for completeness.)_ | ||
- It's a **compile-time error** if a `class` or `mixin` declaration has `Enum` as a superinterface and the interface of the declarations contains an instance member with the name `values`, whether declared or inherited. _If any concrete class implements this interface, it will be an `enum` declaration class, and then the `values` member would conflict with the static `values` constant getter that is automatically added to `enum` declaration classes. Such an instance `values` declaration is either useless or wrong, so we disallow it entirely._ | ||
- It's a compile-time error if a `class`, `mixin` or `enum` declaration has `Enum` as a superinterface, and it declares a non-abstract instance member named `index`. _That member would override the `index` getter inherited from `Enum`, and we currently do not allow that._ | ||
lrhn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Those restrictions allows abstract classes (interfaces) which implements `Enum` in order to have the `int index;` getter member available, and it allows `mixin` declarations to use `Enum` as an `on` type because `mixin` declarations cannot be instantiated directly. | ||
|
||
This restriction still ensure `enum` values are the only object instances which implements `Enum`, while making it valid to declare `abstract class MyInterface implements Enum` and `mixin MyMixin on Enum` for interfaces and mixins intended to be used in declaring `enum` classes. | ||
The restrictions still ensure `enum` values are the only object instances which implements `Enum`, while making it valid to declare `abstract class MyInterface implements Enum` and `mixin MyMixin on Enum` for interfaces and mixins intended to be used in declaring `enum` classes. It's also impossible to override or prevent the instance `index` and static `values` members without causing a compile-time error. _Say implementing an interface with `Never get index;` as a member, then because it's not possible to override `int get index;` from `Enum`, the resulting class does not implement its interface and is a compile-time error._ | ||
|
||
## Formatting | ||
|
||
|
@@ -231,6 +233,8 @@ class LogPriority extends Enum with LogriorityMixin implements Comparable<LogPri | |
} | ||
``` | ||
|
||
where the `_$` represents a fresh name. | ||
|
||
In practice, we may choose to have a subclass of `Enum` as the actual superclass of the `enum` class, rather than use `Enum` directly. We’ll have to make sure that it makes no difference wrt. which declarations are valid (at least outside of `dart:core`, and for `enum`s declared inside `dart:core` it’s our own responsibility to not conflict with names used by the enum implementation.) | ||
|
||
## Summary | ||
|
@@ -241,13 +245,13 @@ The enum values can call the declared constructors, or the default unnamed zero- | |
|
||
Enum instances are objects like any other object, and with this change they can implement interfaces and inherit members from mixins. The main difference between an `enum` declaration and a hand-written “equivalent class” using the enum pattern is that: | ||
|
||
- The `enum` types implement `Enum`. The `Enum` type is otherwise sealed against instantiation, so no other objects than enum entries can implement it. | ||
- The `enum` types implement `Enum`. The `Enum` type is otherwise sealed against instantiation, so no other *objects* than enum entries can implement it. Abstract classes and mixins can implement or extend `Enum`, but unless they are implemented or mixed into an `enum` declaration, no objects can implement that type. | ||
- The `enum` types themselves are completely sealed. No other class can implement an `enum` type. | ||
|
||
- Because of that, `enum` types support exhaustiveness checking in `switch` cases in the language _(meaning that flow-control can see that an exhaustive switch over enum values cannot pass through without executing at least one `case`, which can then affect variable promotion)_. | ||
- The `EnumName.name` extension member works on `enum` values. | ||
|
||
If the *restrictions* (the type is sealed, there is only a finite, enumerable number of instances, and the class implements `Enum`, so it must have an `int index` getter), are acceptable, there should no longer be any reason to *not* make your enum class a language-based `enum`. | ||
If the *restrictions* (the type is sealed, there is only a finite, enumerable number of instances, and the class implements `Enum`, so it must have an `int index` getter and no `values` member), are acceptable, there should no longer be any reason to *not* make your enum-like class a language-based `enum`. | ||
eernstg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Examples: | ||
|
||
|
@@ -446,4 +450,5 @@ There is a chance that people will start using `enum` declarations to declare si | |
1.2, 2021-10-25: Tweak some wordings and ambiguities. | ||
1.3, 2021-10-27: Add examples of potential errors in the corresponding class declaration. | ||
1.4, 2021-10-28: Say that it's an error to refer to generative constructors, and make the `Enum` constructor public. | ||
1.5, 2021-12-07: Say that `index` and `toString` are inherited from the superclass, `values` is omitted if it would conflict. Rephrase specification in terms of defining a semantic class, not a syntactic one. | ||
1.5, 2021-12-07: Say that `index` and `toString` are inherited from the superclass, `values` is omitted if it would conflict. Rephrase specification in terms of defining a semantic class, not a syntactic one. | ||
1.6, 2022-01-27: Disallow overriding `index` or conflicting with `values`. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.