blob: 548aafdb45cf668d94db01ef80fe53b1be518ac6 [file] [log] [blame]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of '../types.dart';
/// Interface implemented by analyzer/CFE to support [StaticType]s for enums.
abstract class EnumOperations<Type extends Object, EnumClass extends Object,
EnumElement extends Object, EnumElementValue extends Object> {
/// Returns the enum class declaration for the [type] or `null` if
/// [type] is not an enum type.
EnumClass? getEnumClass(Type type);
/// Returns the enum elements defined by [enumClass].
Iterable<EnumElement> getEnumElements(EnumClass enumClass);
/// Returns the value defined by the [enumElement]. The encoding is specific
/// the implementation of this interface but must ensure constant value
/// identity.
EnumElementValue getEnumElementValue(EnumElement enumElement);
/// Returns the declared name of the [enumElement].
String getEnumElementName(EnumElement enumElement);
/// Returns the static type of the [enumElement].
Type getEnumElementType(EnumElement enumElement);
}
/// [EnumInfo] stores information to compute the static type for and the type
/// of and enum class and its enum elements.
class EnumInfo<Type extends Object, EnumClass extends Object,
EnumElement extends Object, EnumElementValue extends Object> {
final TypeOperations<Type> _typeOperations;
final FieldLookup<Type> _fieldLookup;
final EnumOperations<Type, EnumClass, EnumElement, EnumElementValue>
_enumOperations;
final EnumClass _enumClass;
Map<EnumElementValue, EnumElementStaticType<Type, EnumElement>>?
_enumElements;
EnumInfo(this._typeOperations, this._fieldLookup, this._enumOperations,
this._enumClass);
/// Returns a map of the enum elements and their corresponding [StaticType]s
/// declared by [_enumClass].
Map<EnumElementValue, EnumElementStaticType<Type, EnumElement>>
get enumElements => _enumElements ??= _createEnumElements();
/// Returns the [StaticType] corresponding to [enumElementValue].
EnumElementStaticType<Type, EnumElement> getEnumElement(
EnumElementValue enumElementValue) {
return enumElements[enumElementValue]!;
}
Map<EnumElementValue, EnumElementStaticType<Type, EnumElement>>
_createEnumElements() {
Map<EnumElementValue, EnumElementStaticType<Type, EnumElement>> elements =
{};
for (EnumElement element in _enumOperations.getEnumElements(_enumClass)) {
EnumElementValue value = _enumOperations.getEnumElementValue(element);
elements[value] = new EnumElementStaticType<Type, EnumElement>(
_typeOperations,
_fieldLookup,
_enumOperations.getEnumElementType(element),
new IdentityRestriction<EnumElement>(element),
_enumOperations.getEnumElementName(element));
}
return elements;
}
}
/// [StaticType] for an instantiation of an enum that support access to the
/// enum values that populate its type through the [subtypes] property.
class EnumStaticType<Type extends Object, EnumElement extends Object>
extends TypeBasedStaticType<Type> {
final EnumInfo<Type, Object, EnumElement, Object> _enumInfo;
List<StaticType>? _enumElements;
EnumStaticType(
super.typeOperations, super.fieldLookup, super.type, this._enumInfo);
@override
bool get isSealed => true;
@override
Iterable<StaticType> getSubtypes(Set<Key> keysOfInterest) => enumElements;
List<StaticType> get enumElements => _enumElements ??= _createEnumElements();
List<StaticType> _createEnumElements() {
List<StaticType> elements = [];
for (EnumElementStaticType<Type, EnumElement> enumElement
in _enumInfo.enumElements.values) {
// For generic enums, the individual enum elements might not be subtypes
// of the concrete enum type. For instance
//
// enum E<T> {
// a<int>(),
// b<String>(),
// c<bool>(),
// }
//
// method<T extends num>(E<T> e) {
// switch (e) { ... }
// }
//
// Here the enum elements `E.b` and `E.c` cannot be actual values of `e`
// because of the bound `num` on `T`.
//
// We detect this by checking whether the enum element type is a subtype
// of the overapproximation of [_type], in this case whether the element
// types are subtypes of `E<num>`.
//
// Since all type arguments on enum values are fixed, we don't have to
// avoid the trivial subtype instantiation `E<Never>`.
if (_typeOperations.isSubtypeOf(
enumElement._type, _typeOperations.overapproximate(_type))) {
// Since the type of the enum element might not itself be a subtype of
// [_type], for instance in the example above the type of `Enum.a`,
// `Enum<int>`, is not a subtype of `Enum<T>`, we wrap the static type
// to establish the subtype relation between the [StaticType] for the
// enum element and this [StaticType].
elements.add(new WrappedStaticType(enumElement, this));
}
}
return elements;
}
}
/// [StaticType] for a single enum element.
///
/// In the [StaticType] model, individual enum elements are represented as
/// unique subtypes of the enum type, modelled using [EnumStaticType].
class EnumElementStaticType<Type extends Object, EnumElement extends Object>
extends ValueStaticType<Type, EnumElement> {
EnumElementStaticType(super.typeOperations, super.fieldLookup, super.type,
super.restriction, super.name);
}