You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
5
+
This is a formal specification 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 apply mixins.
6
6
7
7
## Grammar
8
8
9
-
Dart enum declarations are currently restricted to:
9
+
Without the enhanced enums feature specified in this document, enum declarations are restricted to:
10
10
11
11
```dart
12
12
enum Name {
@@ -16,7 +16,7 @@ enum Name {
16
16
17
17
That is: `enum`, a single identifier for the name, and a block containing a comma separated list of identifiers.
18
18
19
-
We propose the following to also be allowed:
19
+
With the enhanced enums feature, a declaration like the following is also allowed:
where `memberDeclaration*` is almost any sequence of static and instance member declarations, or constructors,
30
-
with some necessary restrictions specified below.
29
+
where `memberDeclaration*` is almost any sequence of static and instance member declarations, or constructors, with some necessary restrictions specified below.
31
30
32
-
The `;` after the identifier list is optional if there is nothing else in the declaration (for backwards compatibility), and required if there is any member declaration after it. The identifier list may have a trailing comma in either case (like now).
31
+
The `;` after the value list is optional if there is nothing else in the declaration (for backwards compatibility), and required if there is any member declaration after it. The value list may have a trailing comma in either case (like now).
33
32
34
33
The superclass of the mixin applications is the `Enum` class (which has a concrete `index` getter and otherwise only the members of `Object`, so the only valid `super` invocations on that superclass are those valid on `Object` and `super.index`).
35
34
@@ -57,19 +56,18 @@ _We will introduce the necessary super-invocation ourselves as an implementation
57
56
58
57
It is a **compile-time error** to refer to a declared or default generative constructor of an `enum` declaration in any way, other than:
59
58
60
-
* As the target of a redirecting generative constructor of the same `enum`, or
59
+
* As the target of a redirecting generative constructor of the same `enum` declaration, or
61
60
* Implicitly in the enum value declarations of the same `enum`.
62
61
63
62
_No-one is allowed to invoke a generative constructor and create another instance of the `enum`.
64
63
That also means that a redirecting *factory* constructor cannot redirect to a generative constructor of an `enum`,
65
-
and therefore no factory constructor of an `enum` declaration can be `const`, because a `const` factory constructor
66
-
must redirect to a generative constructor._
64
+
and therefore no factory constructor of an `enum` declaration can be `const`, because a `const` factory constructor must redirect to a generative constructor._
67
65
68
66
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._
69
67
70
68
## Semantics
71
69
72
-
The `Enum` class behaves as if it was declared as:
70
+
The `Enum` class from the `dart:core` library behaves as if it was declared as:
73
71
74
72
```dart
75
73
class Enum {
@@ -79,7 +77,7 @@ class Enum {
79
77
}
80
78
```
81
79
82
-
We intend to (at least pretend to) let `enum` classes extend `Enum`, and let mixins and members access the default `index` and `toString()` through `super.`. _(In practice, we may use a different class implementing `Enum` as the superclass, but for checking the validity of `super.index`/`super.toString()`, we analyze against `Enum` itself, so it must have non-abstract implementations.)_
80
+
We intend to (at least pretend to) let `enum` classes extend `Enum`, and let mixins and members access the default `index` and `toString()` through `super.`. _(In practice, we may use a different class implementing `Enum` as the superclass, but for checking the validity of `super.index`/`super.toString()`, we analyze against `Enum` itself, so it must have concrete implementations.)_
83
81
84
82
This all makes it look as if `Enum` would be a valid superclass for the mixin applications and methods of the enhanced `enum` class.
85
83
@@ -93,7 +91,7 @@ The semantics of such an enum declaration, *E*, is defined as introducing a (sem
93
91
94
92
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._
95
93
96
-
***Superinterfaces**: The immediate superinterfaces of *C* are the interface of the superclass and the interfaces declared by *E*.
94
+
***Superinterfaces**: The immediate superinterfaces of *C* are the interface of the superclass and the interfaces declared by `implements` in *E*.
97
95
98
96
* 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`.
99
97
@@ -126,17 +124,21 @@ The semantics of such an enum declaration, *E*, is defined as introducing a (sem
126
124
127
125
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.
128
126
129
-
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.)_
127
+
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.)_
130
128
131
129
-**Static `values` list**: A static constant variable named `values` is added as by the declaration `static const List<Name> values = [id1, …, idn];`
132
130
where `id1`…`idn` are the names of the enum entries of the `enum` declaration in source/index order.
133
131
_If `Name` is generic, the `List<Name>` instantiates `Name` to its bounds._
134
132
133
+
It's a **compile-time error** if an `enum` declaration declares or inherits a concrete member named `index` which overrides the `index` getter of the `Enum` class. _Such an inherited `index` member would necessarily have been introduced by a mixin application of the `enum` declaration._
134
+
135
+
It's a **compile-time error** if an `enum` declaration declares or inherits a concrete member named `hashCode` or `==`*(an `operator ==` declaration)* which overrides the `hashCode` getter or `==` operator of the `Object` class. (The `Enum` class does not override `hashCode` or `operator==` from `Object`). _This ensures that enum values can be used as switch statement case values, which is the main advantage of using an enum over just writing a normal class._
136
+
135
137
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:
136
138
137
139
- Declaring or inheriting (from `Enum` or from a declared mixin or interface) any member with the same basename as an enum value which is not a static setter. _(The introduced static declarations would have a conflict.)_
138
-
- Declaring or mixing in a member which is not a valid override of a super-interface member declaration, including, but not limited to, the `index` and `toString` members of `Enum`.
139
-
- Declaring or inheriting an member signature with no corresponding implementation. _(For example declaring an abstract `Never get index` or `String toString([int optional])`, but not providing an implementation.)
140
+
- Declaring or mixing in a member which is not a valid override of a super-interface member declaration, including the `runtimeType`, `noSuchMethod` and `toString` members of `Object`, or any members introduced by mixin applications.
141
+
- Declaring or inheriting an member signature with no corresponding implementation. _(For example declaring an abstract `String toString([int optional])`, but not providing an implementation.)_
140
142
- Declaring a generic `enum` which does not have a valid well-bounded instantiate-to-bounds result. _(The automatically introduced `static const List<EnumName> values` requires a well-bounded instantiate-to-bounds result)_.
141
143
- Declaring a generic `enum` which does not have a regular-bounded instantiate-to-bounds result *and* that has an enum value declaration omitting the type arguments and not having arguments from which type arguments can be inferred. _(For example `enum EnumName<F extends C<F>> { foo; }` would introduce an implicit `static const foo = EnumName(0, "foo");` declaration where the constructor invocation requires a regular-bounded instantiate-to-bounds result)_.
142
144
- Using a non-constant expression as an argument of an enum value declaration.
@@ -152,20 +154,18 @@ It’s currently a compile-time error for a class to implement, extend or mix-in
152
154
153
155
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:
154
156
155
-
- 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.
156
-
- 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.)_
157
+
- It's a **compile-time error** if a *concrete* class has `Enum` as a superinterface (directly or transitively) unless it is the corresponding class of an `enum` declaration._(Abstract interfaces and mixins implementing `Enum` are allowed, but only so that they can be used by `enum` declarations, they can never be used to create an instance which implements `Enum`, but which is not an enum value.)_
158
+
- 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.)_
157
159
- 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._
158
-
- 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._
160
+
- It's a **compile-time error** if a `class`or `mixin` declaration has `Enum` as a superinterface, and that class or mixin declares or inherits a concrete instance member named `index`, `hashCode` or `==`*(an `operator ==` declaration)*. _That `index`member could override the `index` getter inherited from `Enum`, and we currently do not allow that. The `hashCode` and `operator==` declarations would prevent the enum class from having "primitive equality", and we want to ensure that enums can be used in switches._
159
161
160
-
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.
162
+
Those restrictions allow abstract classes (interfaces) which *implement*`Enum` in order to have the `int index;` getter member available, and also allow`mixin` declarations to use `Enum` as an `on` type because `mixin` declarations cannot be instantiated directly.
161
163
162
-
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._
164
+
_The restrictions still ensure that`enum` values are the only objects whose run-time type 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 `enum`declarations. It's also impossible to override the instance getters `index` and `hashCode`, or the operator `==`, and impossible to interfere with the static `values`getter, without causing a compile-time error. For example, if an enum declaration implements an interface containing the signature `Never get index;`, then because it's not possible to override `int get index;` from `Enum`, the resulting class will not implement its interface, and that is a compile-time error._
163
165
164
166
## Formatting
165
167
166
-
The recommended formatting of an `enum` declaration is to format the header (before the first `{`) just like a class declaration. Then, if the enum entries have arguments (if they are anything but single identifiers), then put each entry on a line by its own. If there is no trailing comma, put the semicolon after the last entry. If there is a trailing comma, put the semicolon on the next line, by itself. Then have an empty line before the member declarations, which are formatted just like they would be in a class declaration.
167
-
168
-
If the enum entries have no arguments, they can be listed on one line where it fits, like they are today.
168
+
The recommended formatting of an `enum` declaration is to format the header (before the first `{`) just like a `class` declaration. Then, if the enum value declarations are all single identifiers, and there is no trailing comma, try to fit the identifiers (and any following semicolon) on one line. If any of the value declarations have arguments, if the values have a trailing comma, or if the identifiers do not fit on one line, put each value declaration on a new line. If there is no trailing comma, put any semicolon after the last entry. If there is a trailing comma, put any semicolon on the next line, by itself. Then, if there are member declarations, have an empty line before the member declarations, which are formatted just like they would be in a class declaration.
169
169
170
170
## Implementation
171
171
@@ -245,13 +245,13 @@ The enum values can call the declared constructors, or the default unnamed zero-
245
245
246
246
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:
247
247
248
-
- 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.
248
+
- 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 then implemented by or mixed into an `enum` declaration, no objects can actually implement the type of that class or mixin.
249
249
- The `enum` types themselves are completely sealed. No other class can implement an `enum` type.
250
250
251
251
- 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)_.
252
252
- The `EnumName.name` extension member works on `enum` values.
253
253
254
-
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`.
254
+
If the *restrictions*are acceptable (the type is sealed; there is only a finite, statically known set of instances; the class implements `Enum`; it cannot override `index`, `hashCode` or operator `==`; and it cannot have a user-written member named `values`), there should no longer be any reason to *not* make your enum-like class a language-based `enum`.
@@ -451,3 +452,4 @@ There is a chance that people will start using `enum` declarations to declare si
451
452
1.4, 2021-10-28: Say that it's an error to refer to generative constructors, and make the `Enum` constructor public.
452
453
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.
453
454
1.6, 2022-01-27: Disallow overriding `index` or conflicting with `values`.
455
+
1.7, 2022-02-16: Disallow overriding `operator==` and `hashCode` too.
0 commit comments