[analyzer][cfe] Share constraint generation for non-generic function types
This change combines function-handling logic from the analyzer's
`TypeConstraintGatherer._functionType0` and the CFE's
`TypeConstraintGatherer._isNullabilityAwareSubtypeMatch` methods into
`TypeConstraintGenerator.performSubtypeConstraintGenerationForFunctionTypes`,
which is in `_fe_analyzer_shared`.
The CFE and the analyzer have some pretty significant differences in
how they represent function types:
- In the analyzer, all function parameters are in a single
`parameters` list; each element of this list (of type
`ParameterElement`) can be queried to find out if it is named or
unnamed, and if it is required or optional. A convention enforced
partially by the `FunctionType` constructor is that the `parameters`
list stores reqired unnamed parameters first, then either optional
unnamed parameters or named parameters; named parameters are sorted
by name. The analyzer provides additional getters
`namedParameterTypes`, `normalParameterNames`,
`normalParameterTypes`, `optionalParameterNames`, and
`optionalParameterTypes`, which provide other views of this
information (for example, `namedParameterTypes` contains just the
named parameters, as a map from name to `ParameterElement`).
- In the CFE, unnamed and named parameters are in two separate lists
(`positionalParameters`, of type `List<DartType>`, and
`namedParameters`, of type `List<NamedType>`); in
`positionalParameters`, required parameters come before optional
ones. A single integer (`requiredParameterCount`) indicates how many
elements of `positionalParameters` are required, and by convention,
`namedParameters` is sorted by name.
In order to share logic between these representations, I had to come
up with a common API that these two representations could be easily
adapted to. The analyzer's representation proved to be easier to
adapt, so I based the common API mostly on the CFE's representation,
but with some name changes for clarity. The shared API is:
- `positionalParameterTypes` gets a list of positional parameter types
- `requiredPositionalParameterCount` tells how many entries in
`positionalParameterTypes` are required.
- `returnType` gets the function type's return type.
- `sortedNamedParameters` gets a list of information about named
parameters. The list elements are sorted by name, and each element
of this list is of type `FunctionParameterStructure` (a common
interface implemented both by the analyzer's `ParameterElement` and
the CFE's `NamedType`).
- `typeFormals` gets a list of the function type's formal type
parameters.
To minimize the performance impact of adapting the analyzer to this
API, the analyzer computes `positionalParameterTypes`,
`requiredPositionalParameterCount`, and `sortedNamedParameters` at the
time a `FunctionType` is constructed. Hopefully this should not be too
much of a performance hit, since doing so does not take too much more
effort than checking that the named parameters are sorted (which the
`FunctionType` constructor was already doing).
This is based on previous work by Chloe Stefantsova in
https://dart-review.googlesource.com/c/sdk/+/386480.
Change-Id: Iefe18d72771146399d81747ceab9c929516b0523
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/386322
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart
index a6835d4..f7a47b8 100644
--- a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart
@@ -182,9 +182,6 @@
/// non-negative n, and some types T1, ..., Tn.
bool isExtensionType(SharedTypeView<TypeStructure> type);
- /// Returns `true` if [type] is `F`, `F?`, or `F*` for some function type `F`.
- bool isFunctionType(SharedTypeView<TypeStructure> type);
-
/// Returns `true` if [type] is `A<T1, ..., Tn>`, `A<T1, ..., Tn>?`, or
/// `A<T1, ..., Tn>*` for some class, mixin, or enum A, some non-negative n,
/// and some types T1, ..., Tn. The method returns `false` if [type] is an
@@ -868,6 +865,8 @@
/// Abstract interface of a type constraint generator.
abstract class TypeConstraintGenerator<
TypeStructure extends SharedTypeStructure<TypeStructure>,
+ FunctionParameterStructure extends SharedNamedFunctionParameterStructure<
+ TypeStructure>,
Variable extends Object,
TypeParameterStructure extends SharedTypeParameterStructure<TypeStructure>,
TypeDeclarationType extends Object,
@@ -900,6 +899,156 @@
TypeDeclarationType type, TypeDeclaration typeDeclaration);
/// Matches [p] against [q] as a subtype against supertype and returns true if
+ /// [p] and [q] are both function types, and [p] is a subtype of [q] under
+ /// some constraints imposed on type parameters occurring in [q]; false if
+ /// both [p] and [q] are function types, but [p] can't possibly be a subtype
+ /// of [q] under any constraints; and null otherwise.
+ ///
+ /// An invariant of the type inference is that only [p] or [q] may be a
+ /// schema (in other words, may contain the unknown type `_`); the other must
+ /// be simply a type. If [leftSchema] is `true`, [p] may contain `_`; if it is
+ /// `false`, [q] may contain `_`.
+ ///
+ /// As the generator computes the constraints making the relation possible, it
+ /// changes its internal state. The current state of the generator can be
+ /// obtained by [currentState], and the generator can be restored to a state
+ /// via [restoreState]. All of the shared constraint generation methods are
+ /// supposed to restore the generator to the prior state in case of a
+ /// mismatch, taking that responsibility away from the caller.
+ bool? performSubtypeConstraintGenerationForFunctionTypes(
+ TypeStructure p, TypeStructure q,
+ {required bool leftSchema, required AstNode? astNodeForTesting}) {
+ if (p is SharedFunctionTypeStructure<TypeStructure, TypeParameterStructure,
+ FunctionParameterStructure> &&
+ q is SharedFunctionTypeStructure<TypeStructure, TypeParameterStructure,
+ FunctionParameterStructure>) {
+ // A function type (M0,..., Mn, [M{n+1}, ..., Mm]) -> R0 is a subtype
+ // match for a function type (N0,..., Nk, [N{k+1}, ..., Nr]) -> R1 with
+ // respect to L under constraints C0 + ... + Cr + C
+ //
+ // If R0 is a subtype match for a type R1 with respect to L under
+ // constraints C. If n <= k and r <= m. And for i in 0...r, Ni is a
+ // subtype match for Mi with respect to L under constraints Ci.
+
+ if (p.typeFormals.isEmpty &&
+ q.typeFormals.isEmpty &&
+ p.sortedNamedParameters.isEmpty &&
+ q.sortedNamedParameters.isEmpty &&
+ p.requiredPositionalParameterCount <=
+ q.requiredPositionalParameterCount &&
+ p.positionalParameterTypes.length >=
+ q.positionalParameterTypes.length) {
+ final TypeConstraintGeneratorState state = currentState;
+
+ if (!performSubtypeConstraintGenerationInternal(
+ p.returnType, q.returnType,
+ leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+ restoreState(state);
+ return null;
+ }
+ for (int i = 0; i < q.positionalParameterTypes.length; ++i) {
+ if (!performSubtypeConstraintGenerationInternal(
+ q.positionalParameterTypes[i], p.positionalParameterTypes[i],
+ leftSchema: !leftSchema, astNodeForTesting: astNodeForTesting)) {
+ restoreState(state);
+ return null;
+ }
+ }
+ return true;
+ } else if (p.typeFormals.isEmpty &&
+ q.typeFormals.isEmpty &&
+ p.positionalParameterTypes.length ==
+ p.requiredPositionalParameterCount &&
+ q.positionalParameterTypes.length ==
+ q.requiredPositionalParameterCount &&
+ p.requiredPositionalParameterCount ==
+ q.requiredPositionalParameterCount &&
+ p.sortedNamedParameters.isNotEmpty &&
+ q.sortedNamedParameters.length <= p.sortedNamedParameters.length) {
+ // Function types with named parameters are treated analogously to the
+ // positional parameter case above.
+
+ final TypeConstraintGeneratorState state = currentState;
+
+ if (!performSubtypeConstraintGenerationInternal(
+ p.returnType, q.returnType,
+ leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+ restoreState(state);
+ return null;
+ }
+ for (int i = 0; i < p.positionalParameterTypes.length; ++i) {
+ if (!performSubtypeConstraintGenerationInternal(
+ q.positionalParameterTypes[i], p.positionalParameterTypes[i],
+ leftSchema: !leftSchema, astNodeForTesting: astNodeForTesting)) {
+ restoreState(state);
+ return null;
+ }
+ }
+ // Consume parameter names from p and q in order. Since the named
+ // parameters in p and q are already sorted by name, we can do this by
+ // iterating through both lists in tandem.
+ int i = 0;
+ int j = 0;
+ while (true) {
+ // Determine whether the next parameter should be consumed from p,
+ // q, or both (because the next set of names matches). If the next
+ // parameter should be consumed from p, comparisonResult will be set
+ // to a value < 0. If the next parameter should be consumed from q,
+ // comparisonResult will be set to a value > 0. If the next
+ // parameter should be consumed from both, comparisonResult will be
+ // set to 0.
+ int comparisonResult;
+ if (i >= p.sortedNamedParameters.length) {
+ if (j >= q.sortedNamedParameters.length) {
+ // No parameters left.
+ return true;
+ } else {
+ // No more parameters in p, so the next parameter must come from
+ // q.
+ comparisonResult = 1;
+ }
+ } else if (j >= q.sortedNamedParameters.length) {
+ // No more parameters in q, so the next parameter must come from
+ // p.
+ comparisonResult = -1;
+ } else {
+ comparisonResult = p.sortedNamedParameters[i].name
+ .compareTo(q.sortedNamedParameters[j].name);
+ }
+ if (comparisonResult > 0) {
+ // Extra parameter in q that q that doesn't exist in p. No match.
+ restoreState(state);
+ return null;
+ } else if (comparisonResult < 0) {
+ // Extra parameter in p that doesn't exist in q. Ok if not
+ // required.
+ if (p.sortedNamedParameters[i].isRequired) {
+ restoreState(state);
+ return null;
+ } else {
+ i++;
+ }
+ } else {
+ // The next parameter in p and q matches, so match their types.
+ if (!performSubtypeConstraintGenerationInternal(
+ q.sortedNamedParameters[j].type,
+ p.sortedNamedParameters[i].type,
+ leftSchema: !leftSchema,
+ astNodeForTesting: astNodeForTesting)) {
+ restoreState(state);
+ return null;
+ }
+ i++;
+ j++;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /// Matches [p] against [q] as a subtype against supertype and returns true if
/// [p] and [q] are both FutureOr, with or without nullability suffixes as
/// defined by [enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr],
/// and [p] is a subtype of [q] under some constraints imposed on type
@@ -968,7 +1117,9 @@
/// Matches [p] against [q] as a subtype against supertype and returns true if
/// [p] and [q] are both type declaration types as defined by the enum
/// [TypeDeclarationKind], and [p] is a subtype of [q] under some constraints
- /// imposed on type parameters occurring in [q], and false otherwise.
+ /// imposed on type parameters occurring in [q]; false if both [p] and [q] are
+ /// type declaration types, but [p] can't possibly be a subtype of [q] under
+ /// any constraints; and null otherwise.
///
/// An invariant of the type inference is that only [p] or [q] may be a
/// schema (in other words, may contain the unknown type `_`); the other must
@@ -1163,6 +1314,10 @@
mixin TypeConstraintGeneratorMixin<
TypeStructure extends SharedTypeStructure<TypeStructure>,
+ // Work around https://github.com/dart-lang/dart_style/issues/1568
+ // ignore: lines_longer_than_80_chars
+ FunctionParameterStructure extends SharedNamedFunctionParameterStructure<
+ TypeStructure>,
Variable extends Object,
// Work around https://github.com/dart-lang/dart_style/issues/1568
// ignore: lines_longer_than_80_chars
@@ -1170,8 +1325,14 @@
TypeDeclarationType extends Object,
TypeDeclaration extends Object,
AstNode extends Object>
- on TypeConstraintGenerator<TypeStructure, Variable, TypeParameterStructure,
- TypeDeclarationType, TypeDeclaration, AstNode> {
+ on TypeConstraintGenerator<
+ TypeStructure,
+ FunctionParameterStructure,
+ Variable,
+ TypeParameterStructure,
+ TypeDeclarationType,
+ TypeDeclaration,
+ AstNode> {
@override
bool performSubtypeConstraintGenerationLeftSchema(
SharedTypeSchemaView<TypeStructure> p, SharedTypeView<TypeStructure> q,
diff --git a/pkg/_fe_analyzer_shared/lib/src/types/shared_type.dart b/pkg/_fe_analyzer_shared/lib/src/types/shared_type.dart
index 6311538..1332c38 100644
--- a/pkg/_fe_analyzer_shared/lib/src/types/shared_type.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/types/shared_type.dart
@@ -11,6 +11,31 @@
implements SharedTypeStructure<TypeStructure> {}
/// Common interface for data structures used by the implementations to
+/// represent function types.
+abstract interface class SharedFunctionTypeStructure<
+ TypeStructure extends SharedTypeStructure<TypeStructure>,
+ TypeParameterStructure extends SharedTypeParameterStructure<TypeStructure>,
+ FunctionParameterStructure extends SharedNamedFunctionParameterStructure<
+ TypeStructure>> implements SharedTypeStructure<TypeStructure> {
+ /// All the positional parameter types, starting with the required ones, and
+ /// followed by the optional ones.
+ List<TypeStructure> get positionalParameterTypes;
+
+ /// The number of elements of [positionalParameterTypes] that are required
+ /// parameters.
+ int get requiredPositionalParameterCount;
+
+ /// The return type.
+ TypeStructure get returnType;
+
+ /// All the named parameters, sorted by name.
+ List<FunctionParameterStructure> get sortedNamedParameters;
+
+ /// The type parameters of the function type.
+ List<TypeParameterStructure> get typeFormals;
+}
+
+/// Common interface for data structures used by the implementations to
/// represent a type resulting from a compile-time error.
///
/// The implementations may choose to suppress further errors that arise from
@@ -20,6 +45,20 @@
implements SharedTypeStructure<TypeStructure> {}
/// Common interface for data structures used by the implementations to
+/// represent a named parameter of a function type.
+abstract interface class SharedNamedFunctionParameterStructure<
+ TypeStructure extends SharedTypeStructure<TypeStructure>> {
+ /// Whether this named parameter is required.
+ bool get isRequired;
+
+ /// The name of the parameter.
+ String get name;
+
+ /// The type of the parameter.
+ TypeStructure get type;
+}
+
+/// Common interface for data structures used by the implementations to
/// represent a name/type pair.
abstract interface class SharedNamedTypeStructure<
TypeStructure extends SharedTypeStructure<TypeStructure>> {
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index fbcd39e..1f7f998 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -2958,11 +2958,6 @@
}
@override
- bool isFunctionType(SharedTypeView<Type> type) {
- return type.unwrapTypeView() is FunctionType;
- }
-
- @override
bool isInterfaceType(SharedTypeView<Type> type) {
Type unwrappedType = type.unwrapTypeView();
return unwrappedType is PrimaryType && unwrappedType.isInterfaceType;
diff --git a/pkg/_fe_analyzer_shared/test/mini_types.dart b/pkg/_fe_analyzer_shared/test/mini_types.dart
index 49b4701..39d43e6 100644
--- a/pkg/_fe_analyzer_shared/test/mini_types.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_types.dart
@@ -30,15 +30,16 @@
/// `_fe_analyzer_shared` package.
///
/// Type parameters are not (yet) supported.
-class FunctionType extends Type {
- /// The return type.
+class FunctionType extends Type
+ implements
+ SharedFunctionTypeStructure<Type, Never, NamedFunctionParameter> {
+ @override
final Type returnType;
/// A list of the types of positional parameters.
final List<Type> positionalParameters;
- /// The number of elements of [positionalParameters] that are required
- /// parameters.
+ @override
final int requiredPositionalParameterCount;
/// A list of the named parameters, sorted by name.
@@ -58,6 +59,15 @@
}
@override
+ List<Type> get positionalParameterTypes => positionalParameters;
+
+ @override
+ List<NamedFunctionParameter> get sortedNamedParameters => namedParameters;
+
+ @override
+ List<Never> get typeFormals => const [];
+
+ @override
Type? closureWithRespectToUnknown({required bool covariant}) {
Type? newReturnType =
returnType.closureWithRespectToUnknown(covariant: covariant);
@@ -163,7 +173,9 @@
}
/// A named parameter of a function type.
-class NamedFunctionParameter extends NamedType {
+class NamedFunctionParameter extends NamedType
+ implements SharedNamedFunctionParameterStructure<Type> {
+ @override
final bool isRequired;
NamedFunctionParameter(
diff --git a/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart b/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart
index ab4224d..bf69d57 100644
--- a/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart
+++ b/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart
@@ -12,6 +12,214 @@
import '../mini_types.dart';
main() {
+ group('performSubtypeConstraintGenerationForFunctionTypes:', () {
+ test('Matching functions with no parameters', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function()'), Type('void Function()'),
+ leftSchema: false, astNodeForTesting: Node.placeholder()))
+ .equals(true);
+ check(tcg._constraints).isEmpty();
+ });
+
+ group('Matching functions with positional parameters:', () {
+ test('None optional', () {
+ var tcg = _TypeConstraintGatherer({'T', 'U'});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function(int, String)'), Type('void Function(T, U)'),
+ leftSchema: false, astNodeForTesting: Node.placeholder()))
+ .equals(true);
+ check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+ });
+
+ test('Some optional on LHS', () {
+ var tcg = _TypeConstraintGatherer({'T', 'U'});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function(int, [String])'),
+ Type('void Function(T, U)'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(true);
+ check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+ });
+ });
+
+ group('Non-matching functions with positional parameters:', () {
+ test('Non-matching due to return types', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('int Function(int)'), Type('String Function(int)'),
+ leftSchema: false, astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+
+ test('Non-matching due to parameter types', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function(int)'), Type('void Function(String)'),
+ leftSchema: false, astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+
+ test('Non-matching due to optional parameters on RHS', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function()'), Type('void Function([int])'),
+ leftSchema: false, astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+
+ test('Non-matching due to more parameters being required on LHS', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function(int)'), Type('void Function([int])'),
+ leftSchema: false, astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+ });
+
+ group('Matching functions with named parameters:', () {
+ test('None optional', () {
+ var tcg = _TypeConstraintGatherer({'T', 'U'});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function({required int x, required String y})'),
+ Type('void Function({required T x, required U y})'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(true);
+ check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+ });
+
+ test('Some optional on LHS', () {
+ var tcg = _TypeConstraintGatherer({'T', 'U'});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function({required int x, String y})'),
+ Type('void Function({required T x, required U y})'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(true);
+ check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+ });
+
+ test('Optional named parameter on LHS', () {
+ var tcg = _TypeConstraintGatherer({'T'});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function(int, {String x})'),
+ Type('void Function(T)'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(true);
+ check(tcg._constraints).unorderedEquals(['T <: int']);
+ });
+
+ test('Extra optional named parameter on LHS', () {
+ var tcg = _TypeConstraintGatherer({'T'});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function({String x, int y})'),
+ Type('void Function({T y})'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(true);
+ check(tcg._constraints).unorderedEquals(['T <: int']);
+ });
+ });
+
+ group('Non-matching functions with named parameters:', () {
+ test('Non-matching due to return types', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('int Function({int x})'), Type('String Function({int x})'),
+ leftSchema: false, astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+
+ test('Non-matching due to named parameter types', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function({int x})'),
+ Type('void Function({String x})'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+
+ test('Non-matching due to required named parameter on LHS', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function({required int x})'),
+ Type('void Function()'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+
+ test('Non-matching due to optional named parameter on RHS', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function()'), Type('void Function({int x})'),
+ leftSchema: false, astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+
+ test('Non-matching due to named parameter on RHS, with decoys on LHS',
+ () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function({int x, int y})'),
+ Type('void Function({int z})'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+ });
+
+ test('Matching functions with named and positional parameters', () {
+ var tcg = _TypeConstraintGatherer({'T', 'U'});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function(int, {String y})'),
+ Type('void Function(T, {U y})'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(true);
+ check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+ });
+
+ group('Non-matching functions with named and positional parameters:', () {
+ test(
+ 'Non-matching due to LHS not accepting optional positional parameter',
+ () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function(int, {String x})'),
+ Type('void Function(int, [String])'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+
+ test('Non-matching due to positional parameter length mismatch', () {
+ var tcg = _TypeConstraintGatherer({});
+ check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+ Type('void Function(int, {String x})'),
+ Type('void Function(int, String)'),
+ leftSchema: false,
+ astNodeForTesting: Node.placeholder()))
+ .equals(null);
+ check(tcg._constraints).isEmpty();
+ });
+ });
+ });
+
group('performSubtypeConstraintGenerationForFutureOr:', () {
test('FutureOr matches FutureOr with constraints based on arguments', () {
// `FutureOr<T> <# FutureOr<int>` reduces to `T <# int`
@@ -308,11 +516,11 @@
});
}
-class _TypeConstraintGatherer extends TypeConstraintGenerator<Type, Var,
- TypeParameter, Type, String, Node>
+class _TypeConstraintGatherer extends TypeConstraintGenerator<Type,
+ NamedFunctionParameter, Var, TypeParameter, Type, String, Node>
with
- TypeConstraintGeneratorMixin<Type, Var, TypeParameter, Type, String,
- Node> {
+ TypeConstraintGeneratorMixin<Type, NamedFunctionParameter, Var,
+ TypeParameter, Type, String, Node> {
final _typeVariablesBeingConstrained = <TypeParameter>{};
@override
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 655aab3..058d507 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -2198,7 +2198,10 @@
///
/// Clients may not extend, implement or mix-in this class.
abstract class ParameterElement
- implements PromotableElement, ConstantEvaluationTarget {
+ implements
+ PromotableElement,
+ ConstantEvaluationTarget,
+ SharedNamedFunctionParameterStructure<DartType> {
@override
ParameterElement get declaration;
@@ -2255,6 +2258,7 @@
/// change the meaning of this getter. The parameter `{@required int x}`
/// will return `false` and the parameter `{@required required int x}`
/// will return `true`.
+ @override
bool get isRequired;
/// Whether the parameter is both a required and named parameter.
diff --git a/pkg/analyzer/lib/dart/element/type.dart b/pkg/analyzer/lib/dart/element/type.dart
index 845b75b..372a52a 100644
--- a/pkg/analyzer/lib/dart/element/type.dart
+++ b/pkg/analyzer/lib/dart/element/type.dart
@@ -248,7 +248,11 @@
/// T<sub>xk</sub> xk}) → T</i>.
///
/// Clients may not extend, implement or mix-in this class.
-abstract class FunctionType implements DartType {
+abstract class FunctionType
+ implements
+ DartType,
+ SharedFunctionTypeStructure<DartType, TypeParameterElement,
+ ParameterElement> {
@override
Null get element;
@@ -298,6 +302,7 @@
List<ParameterElement> get parameters;
/// The type of object returned by this type of function.
+ @override
DartType get returnType;
/// The formal type parameters of this generic function; for example,
@@ -307,6 +312,7 @@
// These are distinct from the `typeParameters` list, which contains type
// parameters from surrounding contexts, and thus are free type variables
// from the perspective of this function type.
+ @override
List<TypeParameterElement> get typeFormals;
/// The type parameters.
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 8f78341..8d7da60 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -106,13 +106,80 @@
@override
final NullabilitySuffix nullabilitySuffix;
- FunctionTypeImpl({
- required this.typeFormals,
+ @override
+ final List<DartType> positionalParameterTypes;
+
+ @override
+ final int requiredPositionalParameterCount;
+
+ @override
+ final List<ParameterElement> sortedNamedParameters;
+
+ factory FunctionTypeImpl({
+ required List<TypeParameterElement> typeFormals,
required List<ParameterElement> parameters,
+ required DartType returnType,
+ required NullabilitySuffix nullabilitySuffix,
+ InstantiatedTypeAliasElement? alias,
+ }) {
+ int? firstNamedParameterIndex;
+ var requiredPositionalParameterCount = 0;
+ var positionalParameterTypes = <DartType>[];
+ List<ParameterElement> sortedNamedParameters;
+
+ // Check if already sorted.
+ var namedParametersAlreadySorted = true;
+ var lastNamedParameterName = '';
+ for (var i = 0; i < parameters.length; ++i) {
+ var parameter = parameters[i];
+ if (parameter.isNamed) {
+ firstNamedParameterIndex ??= i;
+ var name = parameter.name;
+ if (lastNamedParameterName.compareTo(name) > 0) {
+ namedParametersAlreadySorted = false;
+ break;
+ }
+ lastNamedParameterName = name;
+ } else {
+ positionalParameterTypes.add(parameter.type);
+ if (parameter.isRequiredPositional) {
+ requiredPositionalParameterCount++;
+ }
+ }
+ }
+ sortedNamedParameters = firstNamedParameterIndex == null
+ ? const []
+ : parameters.sublist(firstNamedParameterIndex, parameters.length);
+ if (!namedParametersAlreadySorted) {
+ // Sort named parameters.
+ sortedNamedParameters.sort((a, b) => a.name.compareTo(b.name));
+
+ // Combine into a new list, with sorted named parameters.
+ parameters = parameters.toList();
+ parameters.replaceRange(
+ firstNamedParameterIndex!, parameters.length, sortedNamedParameters);
+ }
+ return FunctionTypeImpl._(
+ typeFormals: typeFormals,
+ parameters: parameters,
+ returnType: returnType,
+ nullabilitySuffix: nullabilitySuffix,
+ positionalParameterTypes: positionalParameterTypes,
+ requiredPositionalParameterCount: requiredPositionalParameterCount,
+ sortedNamedParameters: sortedNamedParameters,
+ alias: alias);
+ }
+
+ FunctionTypeImpl._({
+ required this.typeFormals,
+ required this.parameters,
required this.returnType,
required this.nullabilitySuffix,
+ required this.positionalParameterTypes,
+ required this.requiredPositionalParameterCount,
+ required this.sortedNamedParameters,
super.alias,
- }) : parameters = _sortNamedParameters(parameters);
+ });
@override
Null get element => null;
@@ -305,11 +372,14 @@
@override
TypeImpl withNullability(NullabilitySuffix nullabilitySuffix) {
if (this.nullabilitySuffix == nullabilitySuffix) return this;
- return FunctionTypeImpl(
+ return FunctionTypeImpl._(
typeFormals: typeFormals,
parameters: parameters,
returnType: returnType,
nullabilitySuffix: nullabilitySuffix,
+ positionalParameterTypes: positionalParameterTypes,
+ requiredPositionalParameterCount: requiredPositionalParameterCount,
+ sortedNamedParameters: sortedNamedParameters,
alias: alias,
);
}
@@ -415,44 +485,6 @@
}
return true;
}
-
- /// If named parameters are already sorted in [parameters], return it.
- /// Otherwise, return a new list, in which named parameters are sorted.
- static List<ParameterElement> _sortNamedParameters(
- List<ParameterElement> parameters,
- ) {
- int? firstNamedParameterIndex;
-
- // Check if already sorted.
- var namedParametersAlreadySorted = true;
- var lastNamedParameterName = '';
- for (var i = 0; i < parameters.length; ++i) {
- var parameter = parameters[i];
- if (parameter.isNamed) {
- firstNamedParameterIndex ??= i;
- var name = parameter.name;
- if (lastNamedParameterName.compareTo(name) > 0) {
- namedParametersAlreadySorted = false;
- break;
- }
- lastNamedParameterName = name;
- }
- }
- if (namedParametersAlreadySorted) {
- return parameters;
- }
-
- // Sort named parameters.
- var namedParameters =
- parameters.sublist(firstNamedParameterIndex!, parameters.length);
- namedParameters.sort((a, b) => a.name.compareTo(b.name));
-
- // Combine into a new list, with sorted named parameters.
- var newParameters = parameters.toList();
- newParameters.replaceRange(
- firstNamedParameterIndex, parameters.length, namedParameters);
- return newParameters;
- }
}
class InstantiatedTypeAliasElementImpl implements InstantiatedTypeAliasElement {
diff --git a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
index 8dce2fb..b12474a 100644
--- a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
@@ -24,14 +24,21 @@
/// to make one type schema a subtype of another.
class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
DartType,
+ ParameterElement,
PromotableElement,
TypeParameterElement,
InterfaceType,
InterfaceElement,
AstNode>
with
- shared.TypeConstraintGeneratorMixin<DartType, PromotableElement,
- TypeParameterElement, InterfaceType, InterfaceElement, AstNode> {
+ shared.TypeConstraintGeneratorMixin<
+ DartType,
+ ParameterElement,
+ PromotableElement,
+ TypeParameterElement,
+ InterfaceType,
+ InterfaceElement,
+ AstNode> {
final TypeSystemImpl _typeSystem;
final Set<TypeParameterElement> _typeParameters = Set.identity();
final List<
@@ -307,15 +314,13 @@
// If `Q` is `Function` then the match holds under no constraints:
// If `P` is a function type.
if (_typeSystemOperations.isDartCoreFunction(SharedTypeView(Q))) {
- if (_typeSystemOperations.isFunctionType(SharedTypeView(P))) {
+ if (P is SharedFunctionTypeStructure) {
return true;
}
}
- if (_typeSystemOperations.isFunctionType(SharedTypeView(P)) &&
- _typeSystemOperations.isFunctionType(SharedTypeView(Q))) {
- return _functionType(P as FunctionType, Q as FunctionType, leftSchema,
- nodeForTesting: nodeForTesting);
+ if (P is FunctionType && Q is FunctionType) {
+ return _functionType(P, Q, leftSchema, nodeForTesting: nodeForTesting);
}
// A type `P` is a subtype match for `Record` with respect to `L` under no
@@ -451,102 +456,12 @@
/// respect to `L` under constraints `C0 + ... + Cr + C`.
bool _functionType0(FunctionType f, FunctionType g, bool leftSchema,
{required AstNode? nodeForTesting}) {
- var rewind = _constraints.length;
-
- // If `R0` is a subtype match for a type `R1` with respect to `L` under
- // constraints `C`.
- if (!trySubtypeMatch(f.returnType, g.returnType, leftSchema,
- nodeForTesting: nodeForTesting)) {
- _constraints.length = rewind;
- return false;
+ bool? result = performSubtypeConstraintGenerationForFunctionTypes(f, g,
+ leftSchema: leftSchema, astNodeForTesting: nodeForTesting);
+ if (result != null) {
+ return result;
}
-
- var fParameters = f.parameters;
- var gParameters = g.parameters;
-
- // And for `i` in `0...r`, `Ni` is a subtype match for `Mi` with respect
- // to `L` under constraints `Ci`.
- var fIndex = 0;
- var gIndex = 0;
- while (fIndex < fParameters.length && gIndex < gParameters.length) {
- var fParameter = fParameters[fIndex];
- var gParameter = gParameters[gIndex];
- if (fParameter.isRequiredPositional) {
- if (gParameter.isRequiredPositional) {
- if (trySubtypeMatch(gParameter.type, fParameter.type, leftSchema,
- nodeForTesting: nodeForTesting)) {
- fIndex++;
- gIndex++;
- } else {
- _constraints.length = rewind;
- return false;
- }
- } else {
- _constraints.length = rewind;
- return false;
- }
- } else if (fParameter.isOptionalPositional) {
- if (gParameter.isPositional) {
- if (trySubtypeMatch(gParameter.type, fParameter.type, leftSchema,
- nodeForTesting: nodeForTesting)) {
- fIndex++;
- gIndex++;
- } else {
- _constraints.length = rewind;
- return false;
- }
- } else {
- _constraints.length = rewind;
- return false;
- }
- } else if (fParameter.isNamed) {
- if (gParameter.isNamed) {
- var compareNames = fParameter.name.compareTo(gParameter.name);
- if (compareNames == 0) {
- if (trySubtypeMatch(gParameter.type, fParameter.type, leftSchema,
- nodeForTesting: nodeForTesting)) {
- fIndex++;
- gIndex++;
- } else {
- _constraints.length = rewind;
- return false;
- }
- } else if (compareNames < 0) {
- if (fParameter.isRequiredNamed) {
- _constraints.length = rewind;
- return false;
- } else {
- fIndex++;
- }
- } else {
- assert(compareNames > 0);
- // The subtype must accept all parameters of the supertype.
- _constraints.length = rewind;
- return false;
- }
- } else {
- break;
- }
- }
- }
-
- // The supertype must provide all required parameters to the subtype.
- while (fIndex < fParameters.length) {
- var fParameter = fParameters[fIndex++];
- if (fParameter.isRequired) {
- _constraints.length = rewind;
- return false;
- }
- }
-
- // The subtype must accept all parameters of the supertype.
- assert(fIndex == fParameters.length);
- if (gIndex < gParameters.length) {
- _constraints.length = rewind;
- return false;
- }
-
- return true;
+ return false;
}
/// If `P` is `(M0, ..., Mk)` and `Q` is `(N0, ..., Nk)`, then the match
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 35ad1b9..15f28d2 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -530,11 +530,6 @@
}
@override
- bool isFunctionType(SharedTypeView<DartType> type) {
- return type.unwrapTypeView() is FunctionType;
- }
-
- @override
bool isInterfaceType(SharedTypeView<DartType> type) {
DartType unwrappedType = type.unwrapTypeView();
return unwrappedType is InterfaceType &&
diff --git a/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart b/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart
index 270cee8..2cc1e045 100644
--- a/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart
+++ b/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart
@@ -25,6 +25,7 @@
/// based on an attempt to make one type schema a subtype of another.
class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
DartType,
+ NamedType,
VariableDeclaration,
StructuralParameter,
TypeDeclarationType,
@@ -33,6 +34,7 @@
with
shared.TypeConstraintGeneratorMixin<
DartType,
+ NamedType,
VariableDeclaration,
StructuralParameter,
TypeDeclarationType,
@@ -632,11 +634,12 @@
_protoConstraints.length = baseConstraintCount;
}
- bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
- p, q,
- leftSchema: constrainSupertype, astNodeForTesting: treeNodeForTesting);
- if (result != null) {
- return result;
+ bool? constraintGenerationResult =
+ performSubtypeConstraintGenerationForTypeDeclarationTypes(p, q,
+ leftSchema: constrainSupertype,
+ astNodeForTesting: treeNodeForTesting);
+ if (constraintGenerationResult != null) {
+ return constraintGenerationResult;
}
// If Q is Function then the match holds under no constraints:
@@ -644,90 +647,16 @@
// If P is a function type.
if (typeOperations.isDartCoreFunction(new SharedTypeView(q)) &&
// Coverage-ignore(suite): Not run.
- typeOperations.isFunctionType(new SharedTypeView(p))) {
+ p is FunctionType) {
return true;
}
- // A function type (M0,..., Mn, [M{n+1}, ..., Mm]) -> R0 is a subtype match
- // for a function type (N0,..., Nk, [N{k+1}, ..., Nr]) -> R1 with respect to
- // L under constraints C0 + ... + Cr + C
- //
- // If R0 is a subtype match for a type R1 with respect to L under
- // constraints C. If n <= k and r <= m. And for i in 0...r, Ni is a
- // subtype match for Mi with respect to L under constraints Ci.
- if (typeOperations.isFunctionType(new SharedTypeView(p)) &&
- typeOperations.isFunctionType(new SharedTypeView(q)) &&
- (p as FunctionType).typeParameters.isEmpty &&
- (q as FunctionType).typeParameters.isEmpty &&
- p.namedParameters.isEmpty &&
- q.namedParameters.isEmpty &&
- p.requiredParameterCount <= q.requiredParameterCount &&
- p.positionalParameters.length >= q.positionalParameters.length) {
- final int baseConstraintCount = _protoConstraints.length;
-
- if (_isNullabilityAwareSubtypeMatch(p.returnType, q.returnType,
- constrainSupertype: constrainSupertype,
- treeNodeForTesting: treeNodeForTesting)) {
- bool isMatch = true;
- for (int i = 0; isMatch && i < q.positionalParameters.length; ++i) {
- isMatch = isMatch &&
- _isNullabilityAwareSubtypeMatch(
- q.positionalParameters[i], p.positionalParameters[i],
- constrainSupertype: !constrainSupertype,
- treeNodeForTesting: treeNodeForTesting);
- }
- if (isMatch) return true;
- }
- // Coverage-ignore-block(suite): Not run.
- _protoConstraints.length = baseConstraintCount;
- }
-
- // Function types with named parameters are treated analogously to the
- // positional parameter case above.
- if (typeOperations.isFunctionType(new SharedTypeView(p)) &&
- typeOperations.isFunctionType(new SharedTypeView(q)) &&
- (p as FunctionType).typeParameters.isEmpty &&
- (q as FunctionType).typeParameters.isEmpty &&
- p.positionalParameters.length == p.requiredParameterCount &&
- q.positionalParameters.length == q.requiredParameterCount &&
- p.requiredParameterCount == q.requiredParameterCount &&
- (p.namedParameters.isNotEmpty ||
- // Coverage-ignore(suite): Not run.
- q.namedParameters.isNotEmpty)) {
- final int baseConstraintCount = _protoConstraints.length;
-
- if (_isNullabilityAwareSubtypeMatch(p.returnType, q.returnType,
- constrainSupertype: constrainSupertype,
- treeNodeForTesting: treeNodeForTesting)) {
- bool isMatch = true;
- for (int i = 0;
- isMatch && i < p.positionalParameters.length;
- // Coverage-ignore(suite): Not run.
- ++i) {
- // Coverage-ignore-block(suite): Not run.
- isMatch = isMatch &&
- _isNullabilityAwareSubtypeMatch(
- q.positionalParameters[i], p.positionalParameters[i],
- constrainSupertype: !constrainSupertype,
- treeNodeForTesting: treeNodeForTesting);
- }
- Map<String, DartType> pNamedTypes = {};
- for (int i = 0; isMatch && i < p.namedParameters.length; ++i) {
- pNamedTypes[p.namedParameters[i].name] = p.namedParameters[i].type;
- }
- for (int i = 0; isMatch && i < q.namedParameters.length; ++i) {
- isMatch =
- isMatch && pNamedTypes.containsKey(q.namedParameters[i].name);
- isMatch = isMatch &&
- _isNullabilityAwareSubtypeMatch(q.namedParameters[i].type,
- pNamedTypes[q.namedParameters[i].name]!,
- constrainSupertype: !constrainSupertype,
- treeNodeForTesting: treeNodeForTesting);
- }
- if (isMatch) return true;
- }
- // Coverage-ignore-block(suite): Not run.
- _protoConstraints.length = baseConstraintCount;
+ constraintGenerationResult =
+ performSubtypeConstraintGenerationForFunctionTypes(p, q,
+ leftSchema: constrainSupertype,
+ astNodeForTesting: treeNodeForTesting);
+ if (constraintGenerationResult != null) {
+ return constraintGenerationResult;
}
// A generic function type <T0 extends B00, ..., Tn extends B0n>F0 is a
@@ -747,10 +676,10 @@
// with respect to L under constraints C0. And C1 is C02 + ... + Cn2 + C0.
// And C2 is C1 with each constraint replaced with its closure with respect
// to [Z0, ..., Zn].
- if (typeOperations.isFunctionType(new SharedTypeView(p)) &&
- typeOperations.isFunctionType(new SharedTypeView(q)) &&
- (p as FunctionType).typeParameters.isNotEmpty &&
- (q as FunctionType).typeParameters.isNotEmpty &&
+ if (p is FunctionType &&
+ q is FunctionType &&
+ p.typeParameters.isNotEmpty &&
+ q.typeParameters.isNotEmpty &&
p.typeParameters.length == q.typeParameters.length) {
final int baseConstraintCount = _protoConstraints.length;
@@ -1052,12 +981,12 @@
subtype as InterfaceType, supertype as InterfaceType,
treeNodeForTesting: treeNodeForTesting);
}
- if (typeOperations.isFunctionType(new SharedTypeView(subtype))) {
+ if (subtype is FunctionType) {
if (typeOperations.isInterfaceType(new SharedTypeView(supertype))) {
return supertype == _environment.coreTypes.functionLegacyRawType ||
supertype == _environment.coreTypes.objectLegacyRawType;
} else if (supertype is FunctionType) {
- return _isFunctionSubtypeMatch(subtype as FunctionType, supertype,
+ return _isFunctionSubtypeMatch(subtype, supertype,
treeNodeForTesting: treeNodeForTesting);
}
}
diff --git a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
index 3180c14..31dcd22 100644
--- a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
+++ b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
@@ -722,11 +722,6 @@
}
@override
- bool isFunctionType(SharedTypeView<DartType> type) {
- return type.unwrapTypeView() is FunctionType;
- }
-
- @override
DartType? matchFutureOrInternal(DartType type) {
if (type is! FutureOrType) {
return null;
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 81a960c..955b82e 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -866,6 +866,7 @@
injecting
inlines
insertion
+insists
inspect
inspecting
inspection
@@ -1801,6 +1802,7 @@
taking
talk
talks
+tandem
target's
tarjan
tarjan's
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index b3cb405..3588929 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -74,6 +74,8 @@
import 'package:_fe_analyzer_shared/src/types/shared_type.dart'
show
SharedDynamicTypeStructure,
+ SharedNamedFunctionParameterStructure,
+ SharedFunctionTypeStructure,
SharedInvalidTypeStructure,
SharedNamedTypeStructure,
SharedRecordTypeStructure,
@@ -11605,7 +11607,9 @@
}
/// A possibly generic function type.
-class FunctionType extends DartType {
+class FunctionType extends DartType
+ implements
+ SharedFunctionTypeStructure<DartType, StructuralParameter, NamedType> {
final List<StructuralParameter> typeParameters;
final int requiredParameterCount;
final List<DartType> positionalParameters;
@@ -11614,6 +11618,7 @@
@override
final Nullability declaredNullability;
+ @override
final DartType returnType;
@override
@@ -11643,6 +11648,18 @@
};
@override
+ List<DartType> get positionalParameterTypes => positionalParameters;
+
+ @override
+ int get requiredPositionalParameterCount => requiredParameterCount;
+
+ @override
+ List<NamedType> get sortedNamedParameters => namedParameters;
+
+ @override
+ List<StructuralParameter> get typeFormals => typeParameters;
+
+ @override
R accept<R>(DartTypeVisitor<R> v) => v.visitFunctionType(this);
@override
@@ -12176,7 +12193,10 @@
/// A named parameter in [FunctionType].
class NamedType extends Node
- implements Comparable<NamedType>, SharedNamedTypeStructure<DartType> {
+ implements
+ Comparable<NamedType>,
+ SharedNamedTypeStructure<DartType>,
+ SharedNamedFunctionParameterStructure<DartType> {
// Flag used for serialization if [isRequired].
static const int FlagRequiredNamedType = 1 << 0;
@@ -12184,6 +12204,7 @@
final String name;
@override
final DartType type;
+ @override
final bool isRequired;
const NamedType(this.name, this.type, {this.isRequired = false});