Skip to content

Commit 0da6894

Browse files
committed
Implement Generic Function Instantiation via wrapping node
This is a re-land for https://dart-review.googlesource.com/c/sdk/+/217880. The primary difference is the call-sites of insertGenericFunctionInstantiation. There are more, and many more test cases. This is a non-breaking change (when analyzing code at language version 2.14 and earlier). If constructor-tearoffs is enabled (language version 2.15), then FunctionReference nodes are inserted, to represent generic function instantiation. Constant evaluation can then use the `typeArgumentTypes` to instantiate arbitrary function-typed expressions. If constructor-tearoffs is not enabled, generic function instantiation continues to take place at a SimpleIdentifier, PrefixedIdentifier, or PropertyAccess only, with constant evaluation using SimpleIdentifier.tearOffTypeArgumentTypes. Bug: #46020 Change-Id: Icf876cb6866bc1030e0cefaaafe221757d5b5639 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/218221 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
1 parent 3d0f937 commit 0da6894

14 files changed

+734
-137
lines changed

pkg/analyzer/lib/src/dart/constant/evaluation.dart

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -682,27 +682,37 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
682682
if (functionResult == null) {
683683
return functionResult;
684684
}
685+
686+
// Report an error if any of the _inferred_ type argument types refer to a
687+
// type parameter. If, however, `node.typeArguments` is not `null`, then
688+
// any type parameters contained therein are reported as non-constant in
689+
// [ConstantVerifier].
690+
if (node.typeArguments == null &&
691+
(node.typeArgumentTypes?.any(hasTypeParameterReference) ?? false)) {
692+
_error(node, null);
693+
}
694+
685695
var typeArgumentList = node.typeArguments;
686696
if (typeArgumentList == null) {
687-
return functionResult;
688-
} else {
689-
var typeArguments = <DartType>[];
690-
for (var typeArgument in typeArgumentList.arguments) {
691-
var object = typeArgument.accept(this);
692-
if (object == null) {
693-
return null;
694-
}
695-
var typeArgumentType = object.toTypeValue();
696-
if (typeArgumentType == null) {
697-
return null;
698-
}
699-
// TODO(srawlins): Test type alias types (`typedef i = int`) used as
700-
// type arguments. Possibly change implementation based on
701-
// canonicalization rules.
702-
typeArguments.add(typeArgumentType);
697+
return _instantiateFunctionType(node, functionResult);
698+
}
699+
700+
var typeArguments = <DartType>[];
701+
for (var typeArgument in typeArgumentList.arguments) {
702+
var object = typeArgument.accept(this);
703+
if (object == null) {
704+
return null;
703705
}
704-
return _dartObjectComputer.typeInstantiate(functionResult, typeArguments);
706+
var typeArgumentType = object.toTypeValue();
707+
if (typeArgumentType == null) {
708+
return null;
709+
}
710+
// TODO(srawlins): Test type alias types (`typedef i = int`) used as
711+
// type arguments. Possibly change implementation based on
712+
// canonicalization rules.
713+
typeArguments.add(typeArgumentType);
705714
}
715+
return _dartObjectComputer.typeInstantiate(functionResult, typeArguments);
706716
}
707717

708718
@override
@@ -1000,7 +1010,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
10001010
DartObjectImpl? visitSimpleIdentifier(SimpleIdentifier node) {
10011011
var value = _lexicalEnvironment?[node.name];
10021012
if (value != null) {
1003-
return _instantiateFunctionType(node, value);
1013+
return _instantiateFunctionTypeForSimpleIdentifier(node, value);
10041014
}
10051015

10061016
return _getConstantValue(node, node);
@@ -1212,6 +1222,8 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
12121222
var variableElement =
12131223
element is PropertyAccessorElement ? element.variable : element;
12141224

1225+
// TODO(srawlins): Remove this check when [FunctionReference]s are inserted
1226+
// for generic function instantiation for pre-constructor-references code.
12151227
if (node is SimpleIdentifier &&
12161228
(node.tearOffTypeArgumentTypes?.any(hasTypeParameterReference) ??
12171229
false)) {
@@ -1230,7 +1242,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
12301242
if (value == null) {
12311243
return value;
12321244
}
1233-
return _instantiateFunctionType(identifier, value);
1245+
return _instantiateFunctionTypeForSimpleIdentifier(identifier, value);
12341246
}
12351247
} else if (variableElement is ConstructorElement) {
12361248
return DartObjectImpl(
@@ -1246,7 +1258,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
12461258
function.type,
12471259
FunctionState(function),
12481260
);
1249-
return _instantiateFunctionType(identifier, rawType);
1261+
return _instantiateFunctionTypeForSimpleIdentifier(identifier, rawType);
12501262
}
12511263
} else if (variableElement is ClassElement) {
12521264
var type = variableElement.instantiate(
@@ -1305,12 +1317,41 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
13051317
return null;
13061318
}
13071319

1320+
/// If the type of [value] is a generic [FunctionType], and [node] has type
1321+
/// argument types, returns [value] type-instantiated with those [node]'s
1322+
/// type argument types, otherwise returns [value].
1323+
DartObjectImpl? _instantiateFunctionType(
1324+
FunctionReference node, DartObjectImpl value) {
1325+
var functionElement = value.toFunctionValue();
1326+
if (functionElement is! ExecutableElement) {
1327+
return value;
1328+
}
1329+
var valueType = functionElement.type;
1330+
if (valueType.typeFormals.isNotEmpty) {
1331+
var typeArgumentTypes = node.typeArgumentTypes;
1332+
if (typeArgumentTypes != null && typeArgumentTypes.isNotEmpty) {
1333+
var instantiatedType =
1334+
functionElement.type.instantiate(typeArgumentTypes);
1335+
var substitution = _substitution;
1336+
if (substitution != null) {
1337+
instantiatedType =
1338+
substitution.substituteType(instantiatedType) as FunctionType;
1339+
}
1340+
return value.typeInstantiate(
1341+
typeSystem, instantiatedType, typeArgumentTypes);
1342+
}
1343+
}
1344+
return value;
1345+
}
1346+
13081347
/// If the type of [value] is a generic [FunctionType], and [node] is a
13091348
/// [SimpleIdentifier] with tear-off type argument types, returns [value]
13101349
/// type-instantiated with those [node]'s tear-off type argument types,
13111350
/// otherwise returns [value].
1312-
DartObjectImpl? _instantiateFunctionType(
1351+
DartObjectImpl? _instantiateFunctionTypeForSimpleIdentifier(
13131352
SimpleIdentifier node, DartObjectImpl value) {
1353+
// TODO(srawlins): When all code uses [FunctionReference]s generated via
1354+
// generic function instantiation, remove this method and all call sites.
13141355
var functionElement = value.toFunctionValue();
13151356
if (functionElement is! ExecutableElement) {
13161357
return value;

pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,21 @@ class FunctionReferenceResolver {
242242
if (node.function is ConstructorReference) {
243243
node.staticType = DynamicTypeImpl.instance;
244244
} else {
245-
var typeArguments = _checkTypeArguments(
246-
// `node.typeArguments`, coming from the parser, is never null.
247-
node.typeArguments!, name, rawType.typeFormals,
248-
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
249-
);
245+
var typeArguments = node.typeArguments;
246+
if (typeArguments == null) {
247+
node.staticType = rawType;
248+
} else {
249+
var typeArgumentTypes = _checkTypeArguments(
250+
typeArguments,
251+
name,
252+
rawType.typeFormals,
253+
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
254+
);
250255

251-
var invokeType = rawType.instantiate(typeArguments);
252-
node.typeArgumentTypes = typeArguments;
253-
node.staticType = invokeType;
256+
var invokeType = rawType.instantiate(typeArgumentTypes);
257+
node.typeArgumentTypes = typeArgumentTypes;
258+
node.staticType = invokeType;
259+
}
254260
}
255261
} else {
256262
if (_resolver.isConstructorTearoffsEnabled) {

pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,15 @@ class PrefixedIdentifierResolver {
8383
type = result.functionTypeCallType!;
8484
}
8585

86-
type = _inferenceHelper.inferTearOff(node, identifier, type);
87-
86+
if (!_resolver.isConstructorTearoffsEnabled) {
87+
// Only perform a generic function instantiation on a [PrefixedIdentifier]
88+
// in pre-constructor-tearoffs code. In constructor-tearoffs-enabled code,
89+
// generic function instantiation is performed at assignability check
90+
// sites.
91+
// TODO(srawlins): Switch all resolution to use the latter method, in a
92+
// breaking change release.
93+
type = _inferenceHelper.inferTearOff(node, identifier, type);
94+
}
8895
_recordStaticType(identifier, type);
8996
_recordStaticType(node, type);
9097
}

pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import 'package:analyzer/src/dart/element/element.dart';
1212
import 'package:analyzer/src/dart/element/type.dart';
1313
import 'package:analyzer/src/dart/element/type_provider.dart';
1414
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
15-
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
1615
import 'package:analyzer/src/dart/resolver/property_element_resolver.dart';
1716
import 'package:analyzer/src/error/codes.dart';
1817
import 'package:analyzer/src/generated/resolver.dart';
@@ -25,8 +24,6 @@ class SimpleIdentifierResolver {
2524

2625
ErrorReporter get _errorReporter => _resolver.errorReporter;
2726

28-
InvocationInferenceHelper get _inferenceHelper => _resolver.inferenceHelper;
29-
3027
TypeProviderImpl get _typeProvider => _resolver.typeProvider;
3128

3229
void resolve(SimpleIdentifierImpl node) {
@@ -259,7 +256,17 @@ class SimpleIdentifierResolver {
259256
} else {
260257
staticType = DynamicTypeImpl.instance;
261258
}
262-
staticType = _inferenceHelper.inferTearOff(node, node, staticType);
259+
260+
if (!_resolver.isConstructorTearoffsEnabled) {
261+
// Only perform a generic function instantiation on a [PrefixedIdentifier]
262+
// in pre-constructor-tearoffs code. In constructor-tearoffs-enabled code,
263+
// generic function instantiation is performed at assignability check
264+
// sites.
265+
// TODO(srawlins): Switch all resolution to use the latter method, in a
266+
// breaking change release.
267+
staticType =
268+
_resolver.inferenceHelper.inferTearOff(node, node, staticType);
269+
}
263270
_recordStaticType(node, staticType);
264271
}
265272

pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,14 @@ class VariableDeclarationResolver {
6565
_resolver.flowAnalysis.flow?.lateInitializer_end();
6666
}
6767

68+
initializer = _resolver.insertImplicitCallReference(initializer);
69+
6870
// Initializers of top-level variables and fields are already included
6971
// into elements during linking.
7072
if (element is ConstLocalVariableElementImpl) {
7173
element.constantInitializer = initializer;
7274
}
7375

74-
var callReference = _resolver.insertImplicitCallReference(initializer);
75-
if (callReference != initializer) {
76-
initializer = callReference;
77-
}
78-
7976
_resolver.checkForInvalidAssignment(node.name, initializer,
8077
whyNotPromoted: whyNotPromoted);
8178
}

0 commit comments

Comments
 (0)