@@ -130,6 +130,24 @@ class FunctionType extends Type
130130 nullabilitySuffix: nullabilitySuffix);
131131 }
132132
133+ @override
134+ FunctionType ? substitute (Map <TypeParameter , Type > substitution) {
135+ var newReturnType = returnType.substitute (substitution);
136+ var newPositionalParameters = positionalParameters.substitute (substitution);
137+ var newNamedParameters = namedParameters.substitute (substitution);
138+ if (newReturnType == null &&
139+ newPositionalParameters == null &&
140+ newNamedParameters == null ) {
141+ return null ;
142+ } else {
143+ return FunctionType (newReturnType ?? returnType,
144+ newPositionalParameters ?? positionalParameters,
145+ requiredPositionalParameterCount: requiredPositionalParameterCount,
146+ namedParameters: newNamedParameters ?? namedParameters,
147+ nullabilitySuffix: nullabilitySuffix);
148+ }
149+ }
150+
133151 @override
134152 Type withNullability (NullabilitySuffix suffix) =>
135153 FunctionType (returnType, positionalParameters,
@@ -179,6 +197,13 @@ class FutureOrType extends PrimaryType {
179197 return FutureOrType (newArg, nullabilitySuffix: nullabilitySuffix);
180198 }
181199
200+ @override
201+ Type ? substitute (Map <TypeParameter , Type > substitution) {
202+ var newArg = typeArgument.substitute (substitution);
203+ if (newArg == null ) return null ;
204+ return FutureOrType (newArg, nullabilitySuffix: nullabilitySuffix);
205+ }
206+
182207 @override
183208 Type withNullability (NullabilitySuffix suffix) =>
184209 FutureOrType (typeArgument, nullabilitySuffix: suffix);
@@ -204,7 +229,9 @@ class InvalidType extends _SpecialSimpleType
204229
205230/// A named parameter of a function type.
206231class NamedFunctionParameter
207- implements SharedNamedFunctionParameterStructure <Type > {
232+ implements
233+ SharedNamedFunctionParameterStructure <Type >,
234+ _Substitutable <NamedFunctionParameter > {
208235 @override
209236 final String name;
210237
@@ -227,11 +254,20 @@ class NamedFunctionParameter
227254 type == other.type &&
228255 isRequired == other.isRequired;
229256
257+ @override
258+ NamedFunctionParameter ? substitute (Map <TypeParameter , Type > substitution) {
259+ var newType = type.substitute (substitution);
260+ if (newType == null ) return null ;
261+ return NamedFunctionParameter (
262+ isRequired: isRequired, name: name, type: newType);
263+ }
264+
230265 @override
231266 String toString () => [if (isRequired) 'required' , type, name].join (' ' );
232267}
233268
234- class NamedType implements SharedNamedTypeStructure <Type > {
269+ class NamedType
270+ implements SharedNamedTypeStructure <Type >, _Substitutable <NamedType > {
235271 @override
236272 final String name;
237273
@@ -246,6 +282,13 @@ class NamedType implements SharedNamedTypeStructure<Type> {
246282 @override
247283 bool operator == (Object other) =>
248284 other is NamedType && name == other.name && type == other.type;
285+
286+ @override
287+ NamedType ? substitute (Map <TypeParameter , Type > substitution) {
288+ var newType = type.substitute (substitution);
289+ if (newType == null ) return null ;
290+ return NamedType (name: name, type: newType);
291+ }
249292}
250293
251294/// Representation of the type `Never` suitable for unit testing of code in the
@@ -348,6 +391,14 @@ class PrimaryType extends Type {
348391 args: newArgs, nullabilitySuffix: nullabilitySuffix);
349392 }
350393
394+ @override
395+ Type ? substitute (Map <TypeParameter , Type > substitution) {
396+ var newArgs = args.substitute (substitution);
397+ if (newArgs == null ) return null ;
398+ return PrimaryType ._(nameInfo,
399+ args: newArgs, nullabilitySuffix: nullabilitySuffix);
400+ }
401+
351402 @override
352403 Type withNullability (NullabilitySuffix suffix) =>
353404 PrimaryType ._(nameInfo, args: args, nullabilitySuffix: suffix);
@@ -439,6 +490,17 @@ class RecordType extends Type implements SharedRecordTypeStructure<Type> {
439490 );
440491 }
441492
493+ @override
494+ Type ? substitute (Map <TypeParameter , Type > substitution) {
495+ var newPositionalTypes = positionalTypes.substitute (substitution);
496+ var newNamedTypes = namedTypes.substitute (substitution);
497+ if (newPositionalTypes == null && newNamedTypes == null ) return null ;
498+ return RecordType (
499+ positionalTypes: newPositionalTypes ?? positionalTypes,
500+ namedTypes: newNamedTypes ?? namedTypes,
501+ nullabilitySuffix: nullabilitySuffix);
502+ }
503+
442504 @override
443505 Type withNullability (NullabilitySuffix suffix) => RecordType (
444506 positionalTypes: positionalTypes,
@@ -502,7 +564,7 @@ class SpecialTypeName extends TypeNameInfo {
502564
503565/// Representation of a type suitable for unit testing of code in the
504566/// `_fe_analyzer_shared` package.
505- abstract class Type implements SharedTypeStructure <Type > {
567+ abstract class Type implements SharedTypeStructure <Type >, _Substitutable < Type > {
506568 @override
507569 final NullabilitySuffix nullabilitySuffix;
508570
@@ -582,11 +644,11 @@ sealed class TypeNameInfo {
582644 /// An assertion in the [PrimaryType] constructor verifies this.
583645 ///
584646 /// This ensures that the methods [Type.closureWithRespectToUnknown] ,
585- /// [Type.recursivelyDemote] , and [Type.withNullability] (which create new
586- /// instances of [Type] based on old ones) create the appropriate subtype of
587- /// [Type] . It also ensures that when [Type] objects are directly constructed
588- /// (as they are in this file and in `mini_ast.dart` ), the appropriate subtype
589- /// of [Type] is used.
647+ /// [Type.recursivelyDemote] , [Type.substitute] , and [Type.withNullability]
648+ /// (which create new instances of [Type] based on old ones) create the
649+ /// appropriate subtype of [Type] . It also ensures that when [Type] objects
650+ /// are directly constructed (as they are in this file and in
651+ /// `mini_ast.dart` ), the appropriate subtype of [Type] is used.
590652 final core.Type _expectedRuntimeType;
591653
592654 TypeNameInfo (this .name, {required core.Type expectedRuntimeType})
@@ -674,6 +736,10 @@ class TypeParameterType extends Type {
674736 }
675737 }
676738
739+ @override
740+ Type ? substitute (Map <TypeParameter , Type > substitution) =>
741+ substitution[typeParameter];
742+
677743 @override
678744 Type withNullability (NullabilitySuffix suffix) =>
679745 TypeParameterType (typeParameter,
@@ -1411,6 +1477,9 @@ class UnknownType extends Type implements SharedUnknownTypeStructure<Type> {
14111477 @override
14121478 Type ? recursivelyDemote ({required bool covariant }) => null ;
14131479
1480+ @override
1481+ Type ? substitute (Map <TypeParameter , Type > substitution) => null ;
1482+
14141483 @override
14151484 Type withNullability (NullabilitySuffix suffix) =>
14161485 UnknownType (nullabilitySuffix: suffix);
@@ -1448,6 +1517,22 @@ abstract class _SpecialSimpleType extends PrimaryType {
14481517
14491518 @override
14501519 Type ? recursivelyDemote ({required bool covariant }) => null ;
1520+
1521+ @override
1522+ Type ? substitute (Map <TypeParameter , Type > substitution) => null ;
1523+ }
1524+
1525+ /// Interface for [Type] and the data structures that comprise it, allowing
1526+ /// type substitutions to be performed.
1527+ abstract class _Substitutable <T extends _Substitutable <T >> {
1528+ /// If `this` contains any references to a [TypeParameter] matching one of the
1529+ /// keys in [substitution] , returns a clone of `this` with those references
1530+ /// replaced by the corresponding value. Otherwise returns `null` .
1531+ ///
1532+ /// For example, if `t` is a reference to the [TypeParameter] object
1533+ /// representing `T` , then `Type('Map<T, U>` ).substitute({t: Type('int')})`
1534+ /// returns a [Type] object representing `Map<int, U>` .
1535+ T ? substitute (Map <TypeParameter , Type > substitution);
14511536}
14521537
14531538class _TypeParser {
@@ -1878,3 +1963,25 @@ extension on List<Type> {
18781963 return newList;
18791964 }
18801965}
1966+
1967+ extension < T extends _Substitutable <T >> on List <T > {
1968+ /// Helper method for performing substitutions on the constituent parts of a
1969+ /// [Type] that are stored in lists.
1970+ ///
1971+ /// Calls [_Substitutable.substitute] on each element of the list; if all
1972+ /// those calls returned `null` (meaning no substitutions were done), returns
1973+ /// `null` . Otherwise returns a new [List] in which each element requiring
1974+ /// substitutions is replaced with the substitution result.
1975+ List <T >? substitute (Map <TypeParameter , Type > substitution) {
1976+ List <T >? result;
1977+ for (int i = 0 ; i < length; i++ ) {
1978+ var oldListElement = this [i];
1979+ var newType = oldListElement.substitute (substitution);
1980+ if (newType != null && result == null ) {
1981+ result = sublist (0 , i);
1982+ }
1983+ result? .add (newType ?? oldListElement);
1984+ }
1985+ return result;
1986+ }
1987+ }
0 commit comments