[mini_types] Introduce `_PreType` class hierarchy. The `_PreType` class (and its subclasses) mirrors the `Type` class, except that it represents types in a more raw form, before identifiers have been resolved to their associated meanings. For example, the `_PreType` representing `int` is a `_PrePrimaryType` whose `typeName` field is the string `int`, whereas the `Type` representing `int` is a `PrimaryType` whose `nameInfo` field points to the `TypeNameInfo` object representing the class `int`. Parsing of `Type` objects is now a two-step process: the string is first converted to a `_PreType`, and then the `_PreType` is materialized into a `Type` by looking up each identifier in it in the `TypeRegistry`. This will be needed in a follow-up CL that introduces support for generic function types, to support the possiblity that a generic function type's return type refers to one of its type parameters (e.g. `List<T> Function<T>()`). The reason this is will be needed is because the meaning of the return type (`List<T>`) can't be determined until the `TypeParameter` object representing the type parameter `T` has been created. Change-Id: I9f4a73bdc0f38380518a9c39db8c788234adb806 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395686 Reviewed-by: Chloe Stefantsova <cstefantsova@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 5fbc302..e733b24 100644 --- a/pkg/_fe_analyzer_shared/test/mini_types.dart +++ b/pkg/_fe_analyzer_shared/test/mini_types.dart
@@ -1567,6 +1567,181 @@ Type withNullability(NullabilitySuffix suffix) => this; } +/// Representation of a [FunctionType] that has been parsed but hasn't had +/// meaning assigned to its identifiers yet. +class _PreFunctionType extends _PreType { + final _PreType returnType; + final List<_PreType> positionalParameterTypes; + final int requiredPositionalParameterCount; + final List<_PreNamedFunctionParameter> namedParameters; + + _PreFunctionType( + {required this.returnType, + required this.positionalParameterTypes, + required this.requiredPositionalParameterCount, + required this.namedParameters}); + + @override + Type materialize() => FunctionType( + returnType.materialize(), + [ + for (var positionalParameterType in positionalParameterTypes) + positionalParameterType.materialize() + ], + requiredPositionalParameterCount: requiredPositionalParameterCount, + namedParameters: [ + for (var namedParameter in namedParameters) + NamedFunctionParameter( + isRequired: namedParameter.isRequired, + name: namedParameter.name, + type: namedParameter.type.materialize()) + ]); +} + +/// Representation of a named function parameter in a [_PreFunctionType]. +class _PreNamedFunctionParameter { + final String name; + final _PreType type; + final bool isRequired; + + _PreNamedFunctionParameter( + {required this.name, required this.type, required this.isRequired}); +} + +/// Representation of a named component of a [_PreRecordType]. +class _PreNamedType { + final String name; + final _PreType type; + + _PreNamedType({required this.name, required this.type}); +} + +/// Representation of a [PrimaryType] or [TypeParameterType] that has been +/// parsed but hasn't had meaning assigned to its identifiers yet. +class _PrePrimaryType extends _PreType { + final String typeName; + final List<_PreType> typeArgs; + + _PrePrimaryType({required this.typeName, required this.typeArgs}); + + @override + Type materialize() { + var nameInfo = TypeRegistry.lookup(typeName); + switch (nameInfo) { + case TypeParameter(): + if (typeArgs.isNotEmpty) { + throw ParseError('Type parameter types do not accept type arguments'); + } + return TypeParameterType(nameInfo); + case InterfaceTypeName(): + return PrimaryType(nameInfo, + args: [for (var typeArg in typeArgs) typeArg.materialize()]); + case SpecialTypeName(): + if (typeName == 'dynamic') { + if (typeArgs.isNotEmpty) { + throw ParseError('`dynamic` does not accept type arguments'); + } + return DynamicType.instance; + } else if (typeName == 'error') { + if (typeArgs.isNotEmpty) { + throw ParseError('`error` does not accept type arguments'); + } + return InvalidType.instance; + } else if (typeName == 'FutureOr') { + if (typeArgs.length != 1) { + throw ParseError('`FutureOr` requires exactly one type argument'); + } + return FutureOrType(typeArgs.single.materialize()); + } else if (typeName == 'Never') { + if (typeArgs.isNotEmpty) { + throw ParseError('`Never` does not accept type arguments'); + } + return NeverType.instance; + } else if (typeName == 'Null') { + if (typeArgs.isNotEmpty) { + throw ParseError('`Null` does not accept type arguments'); + } + return NullType.instance; + } else if (typeName == 'void') { + if (typeArgs.isNotEmpty) { + throw ParseError('`void` does not accept type arguments'); + } + return VoidType.instance; + } else { + throw UnimplementedError('Unknown special type name: $typeName'); + } + } + } +} + +/// Representation of a promoted [TypeParameterType] that has been parsed but +/// hasn't had meaning assigned to its identifiers yet. +class _PrePromotedType extends _PreType { + final _PreType inner; + final _PreType promotion; + + _PrePromotedType({required this.inner, required this.promotion}); + + @override + Type materialize() { + var type = inner.materialize(); + if (type case TypeParameterType(promotion: null)) { + return TypeParameterType(type.typeParameter, + promotion: promotion.materialize()); + } else { + throw ParseError( + 'The type to the left of & must be an unpromoted type parameter'); + } + } +} + +/// Representation of a [RecordType] that has been parsed but hasn't had +/// meaning assigned to its identifiers yet. +class _PreRecordType extends _PreType { + final List<_PreType> positionalTypes; + final List<_PreNamedType> namedTypes; + + _PreRecordType({required this.positionalTypes, required this.namedTypes}); + + @override + Type materialize() => RecordType(positionalTypes: [ + for (var positionalType in positionalTypes) positionalType.materialize() + ], namedTypes: [ + for (var namedType in namedTypes) + NamedType(name: namedType.name, type: namedType.type.materialize()) + ]); +} + +/// Representation of a [Type] that has been parsed but hasn't had meaning +/// assigned to its identifiers yet. +sealed class _PreType { + /// Translates `this` into a [Type]. + /// + /// The meaning of identifiers in `this` is determined by looking them up in + /// the [TypeRegistry]. + Type materialize(); +} + +/// Representation of a [Type] with a nullability suffix that has been parsed +/// but hasn't had meaning assigned to its identifiers yet. +class _PreTypeWithNullability extends _PreType { + final _PreType inner; + final NullabilitySuffix nullabilitySuffix; + + _PreTypeWithNullability( + {required this.inner, required this.nullabilitySuffix}); + + @override + Type materialize() => inner.materialize().withNullability(nullabilitySuffix); +} + +/// Representation of an [UnknownType] that has been parsed but hasn't had +/// meaning assigned to its identifiers yet. +class _PreUnknownType extends _PreType { + @override + Type materialize() => const UnknownType(); +} + /// Shared implementation of the types `void`, `dynamic`, `null`, `Never`, and /// the invalid type. /// @@ -1628,10 +1803,10 @@ 'Error parsing type `$_typeStr` at token $_currentToken: $message'); } - List<NamedFunctionParameter> _parseNamedFunctionParameters() { + List<_PreNamedFunctionParameter> _parseNamedFunctionParameters() { assert(_currentToken == '{'); _next(); - var namedParameters = <NamedFunctionParameter>[]; + var namedParameters = <_PreNamedFunctionParameter>[]; while (true) { var isRequired = _currentToken == 'required'; if (isRequired) { @@ -1642,7 +1817,7 @@ if (_identifierRegexp.matchAsPrefix(name) == null) { _parseFailure('Expected an identifier'); } - namedParameters.add(NamedFunctionParameter( + namedParameters.add(_PreNamedFunctionParameter( name: name, type: type, isRequired: isRequired)); _next(); if (_currentToken == ',') { @@ -1659,7 +1834,8 @@ return namedParameters; } - void _parseOptionalFunctionParameters(List<Type> positionalParameterTypes) { + void _parseOptionalFunctionParameters( + List<_PreType> positionalParameterTypes) { assert(_currentToken == '['); _next(); while (true) { @@ -1677,17 +1853,17 @@ _next(); } - List<NamedType> _parseRecordTypeNamedFields() { + List<_PreNamedType> _parseRecordTypeNamedFields() { assert(_currentToken == '{'); _next(); - var namedTypes = <NamedType>[]; + var namedTypes = <_PreNamedType>[]; while (_currentToken != '}') { var type = _parseType(); var name = _currentToken; if (_identifierRegexp.matchAsPrefix(name) == null) { _parseFailure('Expected an identifier'); } - namedTypes.add(NamedType(name: name, type: type)); + namedTypes.add(_PreNamedType(name: name, type: type)); _next(); if (_currentToken == ',') { _next(); @@ -1706,8 +1882,8 @@ return namedTypes; } - Type _parseRecordTypeRest(List<Type> positionalTypes) { - List<NamedType>? namedTypes; + _PreRecordType _parseRecordTypeRest(List<_PreType> positionalTypes) { + List<_PreNamedType>? namedTypes; while (_currentToken != ')') { if (_currentToken == '{') { namedTypes = _parseRecordTypeNamedFields(); @@ -1727,34 +1903,31 @@ _parseFailure('Expected `)` or `,`'); } _next(); - return RecordType( + return _PreRecordType( positionalTypes: positionalTypes, namedTypes: namedTypes ?? const []); } - Type? _parseSuffix(Type type) { + _PreType? _parseSuffix(_PreType type) { if (_currentToken == '?') { _next(); - return type.withNullability(NullabilitySuffix.question); + return _PreTypeWithNullability( + inner: type, nullabilitySuffix: NullabilitySuffix.question); } else if (_currentToken == '*') { _next(); - return type.withNullability(NullabilitySuffix.star); + return _PreTypeWithNullability( + inner: type, nullabilitySuffix: NullabilitySuffix.star); } else if (_currentToken == '&') { - if (type case TypeParameterType(promotion: null)) { - _next(); - var promotion = _parseUnsuffixedType(); - return TypeParameterType(type.typeParameter, promotion: promotion); - } else { - _parseFailure( - 'The type to the left of & must be an unpromoted type parameter'); - } + _next(); + var promotion = _parseUnsuffixedType(); + return _PrePromotedType(inner: type, promotion: promotion); } else if (_currentToken == 'Function') { _next(); if (_currentToken != '(') { _parseFailure('Expected `(`'); } _next(); - var positionalParameterTypes = <Type>[]; - List<NamedFunctionParameter>? namedFunctionParameters; + var positionalParameterTypes = <_PreType>[]; + List<_PreNamedFunctionParameter>? namedFunctionParameters; int? requiredPositionalParameterCount; if (_currentToken != ')') { while (true) { @@ -1781,7 +1954,9 @@ } } _next(); - return FunctionType(type, positionalParameterTypes, + return _PreFunctionType( + returnType: type, + positionalParameterTypes: positionalParameterTypes, requiredPositionalParameterCount: requiredPositionalParameterCount ?? positionalParameterTypes.length, namedParameters: namedFunctionParameters ?? const []); @@ -1790,7 +1965,7 @@ } } - Type _parseType() { + _PreType _parseType() { // We currently accept the following grammar for types: // type := unsuffixedType nullability suffix* // unsuffixedType := identifier typeArgs? @@ -1825,10 +2000,10 @@ return result; } - Type _parseUnsuffixedType() { + _PreType _parseUnsuffixedType() { if (_currentToken == '_') { _next(); - return const UnknownType(); + return _PreUnknownType(); } if (_currentToken == '(') { _next(); @@ -1851,7 +2026,7 @@ _parseFailure('Expected an identifier, `_`, or `(`'); } _next(); - List<Type> typeArgs; + List<_PreType> typeArgs; if (_currentToken == '<') { _next(); typeArgs = []; @@ -1867,50 +2042,7 @@ } else { typeArgs = const []; } - var nameInfo = TypeRegistry.lookup(typeName); - switch (nameInfo) { - case TypeParameter(): - if (typeArgs.isNotEmpty) { - throw ParseError('Type parameter types do not accept type arguments'); - } - return TypeParameterType(nameInfo); - case InterfaceTypeName(): - return PrimaryType(nameInfo, args: typeArgs); - case SpecialTypeName(): - if (typeName == 'dynamic') { - if (typeArgs.isNotEmpty) { - throw ParseError('`dynamic` does not accept type arguments'); - } - return DynamicType.instance; - } else if (typeName == 'error') { - if (typeArgs.isNotEmpty) { - throw ParseError('`error` does not accept type arguments'); - } - return InvalidType.instance; - } else if (typeName == 'FutureOr') { - if (typeArgs.length != 1) { - throw ParseError('`FutureOr` requires exactly one type argument'); - } - return FutureOrType(typeArgs.single); - } else if (typeName == 'Never') { - if (typeArgs.isNotEmpty) { - throw ParseError('`Never` does not accept type arguments'); - } - return NeverType.instance; - } else if (typeName == 'Null') { - if (typeArgs.isNotEmpty) { - throw ParseError('`Null` does not accept type arguments'); - } - return NullType.instance; - } else if (typeName == 'void') { - if (typeArgs.isNotEmpty) { - throw ParseError('`void` does not accept type arguments'); - } - return VoidType.instance; - } else { - throw UnimplementedError('Unknown special type name: $typeName'); - } - } + return _PrePrimaryType(typeName: typeName, typeArgs: typeArgs); } static Type parse(String typeStr) { @@ -1920,7 +2052,7 @@ throw ParseError('Extra tokens after parsing type `$typeStr`: ' '${parser._tokens.sublist(parser._i, parser._tokens.length - 1)}'); } - return result; + return result.materialize(); } static List<String> _tokenizeTypeStr(String typeStr) {