[mini_types] Add an assertion to verify the runtime type of `PrimaryType`-derived objects.
The class hierarchy for `PrimaryType` has several subtypes to
represent special types in Dart:
- DynamicType (for `dynamic`)
- FutureOrType (for `FutureOr<...>`)
- InvalidType (for the invalid type)
- NeverType (for `Never`)
- NullType (for `Null`)
- VoidType (for `Void`)
When constructing a `PrimaryType` for one of these special types, we
need to make sure that the type that's being ultimately constructed is
the proper subtype, otherwise algorithms that do `is` tests on the
`Type` hierarchy will behave incorrectly.
This change adds an assertion to the `PrimaryType` constructor to
verify that the appropriate subtype of `Type` is being constructed.
Change-Id: I347cc4893da265b35e97636479700a92a8e61541
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395560
Reviewed-by: Kallen Tu <kallentu@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/_fe_analyzer_shared/test/mini_types.dart b/pkg/_fe_analyzer_shared/test/mini_types.dart
index 6190ad6..1d8ba61 100644
--- a/pkg/_fe_analyzer_shared/test/mini_types.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_types.dart
@@ -6,6 +6,9 @@
// but light weight enough to be suitable for unit testing of code in the
// `_fe_analyzer_shared` package.
+import 'dart:core' as core show Type;
+import 'dart:core' hide Type;
+
import 'package:_fe_analyzer_shared/src/type_inference/nullability_suffix.dart';
import 'package:_fe_analyzer_shared/src/types/shared_type.dart';
@@ -163,7 +166,7 @@
/// A type name that represents an ordinary interface type.
class InterfaceTypeName extends TypeNameInfo {
- InterfaceTypeName._(super.name);
+ InterfaceTypeName._(super.name) : super(expectedRuntimeType: PrimaryType);
}
/// Representation of an invalid type suitable for unit testing of code in the
@@ -261,7 +264,12 @@
PrimaryType._(this.nameInfo,
{this.args = const [], super.nullabilitySuffix = NullabilitySuffix.none})
- : super._();
+ : super._() {
+ assert(
+ runtimeType == nameInfo._expectedRuntimeType,
+ '${nameInfo.name} should use ${nameInfo._expectedRuntimeType}, but '
+ 'constructed $runtimeType instead');
+ }
PrimaryType._special(SpecialTypeName nameInfo,
{List<Type> args = const [],
@@ -427,7 +435,7 @@
/// - `Null`
/// - `void`
class SpecialTypeName extends TypeNameInfo {
- SpecialTypeName._(super.name);
+ SpecialTypeName._(super.name, {required super.expectedRuntimeType});
}
/// Representation of a type suitable for unit testing of code in the
@@ -512,7 +520,21 @@
sealed class TypeNameInfo {
final String name;
- TypeNameInfo(this.name);
+ /// The runtime type that should be used for [Type] objects that refer to
+ /// `this`.
+ ///
+ /// An assertion in the [PrimaryType] constructor verifies this.
+ ///
+ /// This ensures that the methods [Type.closureWithRespectToUnknown],
+ /// [Type.recursivelyDemote], and [Type.withNullability] (which create new
+ /// instances of [Type] based on old ones) create the appropriate subtype of
+ /// [Type]. It also ensures that when [Type] objects are directly constructed
+ /// (as they are in this file and in `mini_ast.dart`), the appropriate subtype
+ /// of [Type] is used.
+ final core.Type _expectedRuntimeType;
+
+ TypeNameInfo(this.name, {required core.Type expectedRuntimeType})
+ : _expectedRuntimeType = expectedRuntimeType;
}
/// A type name that represents a type variable.
@@ -522,7 +544,9 @@
@override
Type bound;
- TypeParameter._(super.name) : bound = Type('Object?');
+ TypeParameter._(super.name)
+ : bound = Type('Object?'),
+ super(expectedRuntimeType: TypeParameterType);
@override
String get displayName => name;
@@ -607,16 +631,19 @@
static Map<String, TypeNameInfo>? _typeNameInfoMap;
/// The [TypeNameInfo] object representing the special type `dynamic`.
- static final dynamic_ = SpecialTypeName._('dynamic');
+ static final dynamic_ =
+ SpecialTypeName._('dynamic', expectedRuntimeType: DynamicType);
/// The [TypeNameInfo] object representing the special type `error`.
- static final error_ = SpecialTypeName._('error');
+ static final error_ =
+ SpecialTypeName._('error', expectedRuntimeType: InvalidType);
/// The [TypeNameInfo] object representing the interface type `Future`.
static final future = InterfaceTypeName._('Future');
/// The [TypeNameInfo] object representing the special type `FutureOr`.
- static final futureOr = SpecialTypeName._('FutureOr');
+ static final futureOr =
+ SpecialTypeName._('FutureOr', expectedRuntimeType: FutureOrType);
/// The [TypeNameInfo] object representing the interface type `Iterable`.
static final iterable = InterfaceTypeName._('Iterable');
@@ -628,16 +655,17 @@
static final map = InterfaceTypeName._('Map');
/// The [TypeNameInfo] object representing the special type `Never`.
- static final never = SpecialTypeName._('Never');
+ static final never =
+ SpecialTypeName._('Never', expectedRuntimeType: NeverType);
/// The [TypeNameInfo] object representing the special type `Null`.
- static final null_ = SpecialTypeName._('Null');
+ static final null_ = SpecialTypeName._('Null', expectedRuntimeType: NullType);
/// The [TypeNameInfo] object representing the interface type `Stream`.
static final stream = InterfaceTypeName._('Stream');
/// The [TypeNameInfo] object representing the special type `void`.
- static final void_ = SpecialTypeName._('void');
+ static final void_ = SpecialTypeName._('void', expectedRuntimeType: VoidType);
/// Gets [_typeNameInfoMap], throwing an exception if it has not been
/// initialized.