blob: a08f61a06f32178c5b3efbbf899b8eadfcc807c1 [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.
import 'key.dart';
import 'shared.dart';
import 'space.dart';
import 'static_type.dart';
import 'witness.dart';
part 'types/bool.dart';
part 'types/enum.dart';
part 'types/future_or.dart';
part 'types/list.dart';
part 'types/map.dart';
part 'types/record.dart';
part 'types/sealed.dart';
/// [StaticType] based on a non-nullable [Type].
///
/// All [StaticType] implementation in this library are based on [Type] through
/// this class. Additionally, the `static_type.dart` library has fixed
/// [StaticType] implementations for `Object`, `Null`, `Never` and nullable
/// types.
class TypeBasedStaticType<Type extends Object> extends NonNullableStaticType {
final TypeOperations<Type> _typeOperations;
final FieldLookup<Type> _fieldLookup;
final Type _type;
TypeBasedStaticType(this._typeOperations, this._fieldLookup, this._type);
@override
Map<Key, StaticType> get fields => _fieldLookup.getFieldTypes(_type);
@override
StaticType? getAdditionalField(Key key) =>
_fieldLookup.getAdditionalFieldType(_type, key);
/// Returns a [Restriction] value for static types the determines subtypes of
/// the [_type]. For instance individual elements of an enum.
Restriction get restriction => const Unrestricted();
@override
bool isSubtypeOfInternal(StaticType other) {
return other is TypeBasedStaticType<Type> &&
_typeOperations.isSubtypeOf(_type, other._type) &&
restriction.isSubtypeOf(_typeOperations, other.restriction);
}
@override
bool get isSealed => false;
@override
String get name => _typeOperations.typeToString(_type);
@override
int get hashCode => Object.hash(_type, restriction);
@override
bool operator ==(other) {
if (identical(this, other)) return true;
return other is TypeBasedStaticType<Type> &&
_type == other._type &&
restriction == other.restriction;
}
Type get typeForTesting => _type;
@override
void witnessToText(StringBuffer buffer, FieldWitness witness,
Map<Key, FieldWitness> witnessFields) {
if (!_typeOperations.hasSimpleName(_type)) {
buffer.write(name);
buffer.write(' _');
// If we have restrictions on the record type we create an and pattern.
String additionalStart = ' && Object(';
String additionalEnd = '';
String comma = '';
for (MapEntry<Key, FieldWitness> entry in witnessFields.entries) {
Key key = entry.key;
if (key is! ListKey) {
buffer.write(additionalStart);
additionalStart = '';
additionalEnd = ')';
buffer.write(comma);
comma = ', ';
buffer.write(key.name);
buffer.write(': ');
FieldWitness field = entry.value;
field.witnessToText(buffer);
}
}
buffer.write(additionalEnd);
} else {
super.witnessToText(buffer, witness, witnessFields);
}
}
}
/// [StaticType] for an object restricted by its [restriction].
abstract class RestrictedStaticType<Type extends Object,
Identity extends Restriction> extends TypeBasedStaticType<Type> {
@override
final Identity restriction;
@override
final String name;
RestrictedStaticType(super.typeOperations, super.fieldLookup, super.type,
this.restriction, this.name);
}
/// [StaticType] for an object restricted to a single value.
class ValueStaticType<Type extends Object, T extends Object>
extends RestrictedStaticType<Type, IdentityRestriction<T>> {
ValueStaticType(super.typeOperations, super.fieldLookup, super.type,
super.restriction, super.name);
@override
void witnessToText(StringBuffer buffer, FieldWitness witness,
Map<Key, FieldWitness> witnessFields) {
buffer.write(name);
// If we have restrictions on the value we create an and pattern.
String additionalStart = ' && Object(';
String additionalEnd = '';
String comma = '';
for (MapEntry<Key, FieldWitness> entry in witnessFields.entries) {
Key key = entry.key;
if (key is! RecordKey) {
buffer.write(additionalStart);
additionalStart = '';
additionalEnd = ')';
buffer.write(comma);
comma = ', ';
buffer.write(key.name);
buffer.write(': ');
FieldWitness field = entry.value;
field.witnessToText(buffer);
}
}
buffer.write(additionalEnd);
}
}
/// Interface for a restriction within a subtype relation.
///
/// This is used for instance to model enum values within an enum type and
/// map patterns within a map type.
abstract class Restriction<Type extends Object> {
/// Returns `true` if this [Restriction] covers the whole type.
bool get isUnrestricted;
/// Returns `true` if this restriction is a subtype of [other].
bool isSubtypeOf(TypeOperations<Type> typeOperations, Restriction other);
}
/// The unrestricted [Restriction] that covers all values of a type.
class Unrestricted implements Restriction<Object> {
const Unrestricted();
@override
bool get isUnrestricted => true;
@override
bool isSubtypeOf(TypeOperations<Object> typeOperations, Restriction other) =>
other.isUnrestricted;
}
/// [Restriction] based a unique [identity] value.
class IdentityRestriction<Identity extends Object>
implements Restriction<Object> {
final Identity identity;
const IdentityRestriction(this.identity);
@override
bool get isUnrestricted => false;
@override
bool isSubtypeOf(TypeOperations<Object> typeOperations, Restriction other) =>
other.isUnrestricted ||
other is IdentityRestriction<Identity> && identity == other.identity;
}