- 
                Notifications
    You must be signed in to change notification settings 
- Fork 227
Description
(This came up organically in a project I'm working on with @sobrienpdx. I've reduced it down to a minimal repro.)
The following code is accepted by the analyzer but rejected by the front end:
abstract class A {
  String get s;
}
abstract class B implements A {}
enum E1 implements B {
  e1;
  @override
  String get s => 'E1';
}
enum E2 implements B {
  e2;
  @override
  String get s => 'E2';
}
final allValues = [...E1.values, ...E2.values];
main() {
  for (var value in allValues) {
    print(value.s);
  }
}The front end issues this error message:
Error: The getter 's' isn't defined for the class 'Object'.
 - 'Object' is from 'dart:core'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 's'.
    print(value.s);
                ^
After some digging, I figured out that the difference comes down to a subtle interaction between least upper bound and an ambiguity in the enhanced enums spec.
The spec doesn't fully pin down what the class hierarchy of an enum should look like. It says:
The specification here does not specify how the index and name of an enum is associated with the enum instances. In practice it’s possible to desugar an
enumdeclaration to aclassdeclaration, as long as the desugaring can access private names from other libraries (dart:corein particular).The existing enums are implemented as desugaring into a class extending a private
_Enumclass which holds thefinal int index;declaration and afinal String _name;declaration (used by the theEnumName.namegetter), and both fields are initialized by a constructor.In practice, the implementation of the enhanced enums will likely be something similar.
Either first declare
Enumas:abstract class Enum { Enum._(this.index, this._name); final int index; final String _name; String _$enumToString(); String toString() => _$enumToString(); }or retain the current
_Enumclass and make that the actual superclass ofenumclasses. Either works, I’ll useEnumas the superclass directly in the following.
What was implemented was:
- In the analyzer, all enums extend Enum, which extendsObject.
- In the front end, all enums extend _Enum, which in turn implementsEnum, which extendsObject.
This means that, when analyzing the code example above, the analyzer considers the class hierarchy to be:
graph BT
  B --> A --> Object
  Enum --> Object
  E1 --> B
  E1 --> Enum
  E2 --> B
  E2 --> Enum
    Whereas the front end considers the class hierarchy to be:
graph BT
  B --> A --> Object
  _Enum --> Enum --> Object
  E1 --> B
  E1 --> _Enum
  E2 --> B
  E2 --> _Enum
    The inferred type of allValues is List<LUB(E1, E2)>. LUB takes the class that's at the greatest unique depth in the hierarchy, so that means in the analyzer case the LUB is B, whereas in the front end case the LUB is Object.
I think we should probably change the analyzer to match the front end behavior, and adjust the spec to say that all enums extend a private _Enum class in dart:core. (It's not necessary to specify that _Enum implements Enum, because both the analyzer and the front end can see this by analyzing the core library).