Version 2.15.0-79.0.dev
Merge commit '29ba68fcf2ec39cae25ae02e1f56006eb5a40e72' into 'dev'
diff --git a/DEPS b/DEPS
index dc0dbdd..503c2f8 100644
--- a/DEPS
+++ b/DEPS
@@ -44,8 +44,8 @@
# co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
# hashes. It requires access to the dart-build-access group, which EngProd
# has.
- "co19_rev": "a3af05fb4ee10b065bf0f5bfe4e9546bc077d5e8",
- "co19_2_rev": "7637bcb24abf21b5e2f8dd24d3fe619f777391f5",
+ "co19_rev": "a4f77a02a3676b1c2e7390c860cb0486c8515d3f",
+ "co19_2_rev": "3e1ea1af9ef293d7f6a8f3332b5c71c7072a30e0",
# The internal benchmarks to use. See go/dart-benchmarks-internal
"benchmarks_internal_rev": "076df10d9b77af337f2d8029725787155eb1cd52",
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index c4bf424..af1fa6f 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -1043,7 +1043,8 @@
} else if (operatorType == TokenType.TILDE_SLASH) {
return _dartObjectComputer.integerDivide(node, leftResult, rightResult);
} else {
- // TODO(brianwilkerson) Figure out which error to report.
+ // TODO(https://github.com/dart-lang/sdk/issues/47061): Use a specific
+ // error code.
_error(node, null);
return null;
}
@@ -1108,7 +1109,8 @@
DartObjectImpl? visitInstanceCreationExpression(
InstanceCreationExpression node) {
if (!node.isConst) {
- // TODO(brianwilkerson) Figure out which error to report.
+ // TODO(https://github.com/dart-lang/sdk/issues/47061): Use a specific
+ // error code.
_error(node, null);
return null;
}
@@ -1209,7 +1211,8 @@
}
}
}
- // TODO(brianwilkerson) Figure out which error to report.
+ // TODO(https://github.com/dart-lang/sdk/issues/47061): Use a specific
+ // error code.
_error(node, null);
return null;
}
@@ -1220,7 +1223,8 @@
@override
DartObjectImpl? visitNode(AstNode node) {
- // TODO(brianwilkerson) Figure out which error to report.
+ // TODO(https://github.com/dart-lang/sdk/issues/47061): Use a specific
+ // error code.
_error(node, null);
return null;
}
@@ -1274,7 +1278,8 @@
} else if (node.operator.type == TokenType.MINUS) {
return _dartObjectComputer.negated(node, operand);
} else {
- // TODO(brianwilkerson) Figure out which error to report.
+ // TODO(https://github.com/dart-lang/sdk/issues/47061): Use a specific
+ // error code.
_error(node, null);
return null;
}
@@ -1655,7 +1660,8 @@
// Constants may not refer to type parameters.
}
- // TODO(brianwilkerson) Figure out which error to report.
+ // TODO(https://github.com/dart-lang/sdk/issues/47061): Use a specific
+ // error code.
_error(node, null);
return null;
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
index e2a2b97..75ac605 100644
--- a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
@@ -53,7 +53,7 @@
if (enclosingElement is TypeAliasElement) {
enclosingElement = enclosingElement.aliasedType.element;
}
- // TODO(srawlins): Handle `enclosingElement` being a functio typedef:
+ // TODO(srawlins): Handle `enclosingElement` being a function typedef:
// typedef F<T> = void Function(); var a = F<int>.extensionOnType;`.
// This is illegal.
if (enclosingElement is ClassElement) {
@@ -123,16 +123,7 @@
constructorName.staticElement = constructorElement.declaration;
constructorName.name?.staticElement = constructorElement.declaration;
node.staticType = inferred;
- // TODO(srawlins): Always set the TypeName's type to `null`, here, and
- // in the "else" case below, at the very end of [_inferArgumentTypes].
- // This requires refactoring how type arguments are checked against
- // bounds, as this is currently always done with the [TypeName], in
- // type_argument_verifier.dart.
- if (inferred.typeFormals.isNotEmpty) {
- constructorName.type.type = null;
- } else {
- constructorName.type.type = inferredReturnType;
- }
+ constructorName.type.type = null;
}
} else {
var constructorElement = constructorName.staticElement;
@@ -141,6 +132,7 @@
} else {
node.staticType = constructorElement.type;
}
+ constructorName.type.type = null;
}
}
}
diff --git a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
index 1bdea4c..71ab7b9 100644
--- a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
+++ b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
@@ -32,8 +32,67 @@
TypeSystemImpl get _typeSystem =>
_libraryElement.typeSystem as TypeSystemImpl;
+ void checkConstructorReference(ConstructorReference node) {
+ var classElement = node.constructorName.type.name.staticElement;
+ List<TypeParameterElement> typeParameters;
+ if (classElement is TypeAliasElement) {
+ typeParameters = classElement.typeParameters;
+ } else if (classElement is ClassElement) {
+ typeParameters = classElement.typeParameters;
+ } else {
+ return;
+ }
+
+ if (typeParameters.isEmpty) {
+ return;
+ }
+ var typeArgumentList = node.constructorName.type.typeArguments;
+ if (typeArgumentList == null) {
+ return;
+ }
+ var constructorType = node.staticType;
+ if (constructorType is DynamicType) {
+ // An erroneous constructor reference.
+ return;
+ }
+ if (constructorType is! FunctionType) {
+ return;
+ }
+ var typeArguments = [
+ for (var type in typeArgumentList.arguments) type.type!,
+ ];
+ if (typeArguments.length != typeParameters.length) {
+ // Wrong number of type arguments to be reported elsewhere.
+ return;
+ }
+ var typeArgumentListLength = typeArgumentList.arguments.length;
+ var substitution = Substitution.fromPairs(typeParameters, typeArguments);
+ for (var i = 0; i < typeArguments.length; i++) {
+ var typeParameter = typeParameters[i];
+ var typeArgument = typeArguments[i];
+
+ var bound = typeParameter.bound;
+ if (bound == null) {
+ continue;
+ }
+
+ bound = _libraryElement.toLegacyTypeIfOptOut(bound);
+ bound = substitution.substituteType(bound);
+
+ if (!_typeSystem.isSubtypeOf(typeArgument, bound)) {
+ var errorNode =
+ i < typeArgumentListLength ? typeArgumentList.arguments[i] : node;
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
+ errorNode,
+ [typeArgument, typeParameter.name, bound],
+ );
+ }
+ }
+ }
+
void checkFunctionExpressionInvocation(FunctionExpressionInvocation node) {
- _checkTypeArguments(
+ _checkInvocationTypeArguments(
node.typeArguments?.arguments,
node.function.staticType,
node.staticInvokeType,
@@ -42,7 +101,7 @@
}
void checkFunctionReference(FunctionReference node) {
- _checkTypeArguments(
+ _checkInvocationTypeArguments(
node.typeArguments?.arguments,
node.function.staticType,
node.staticType,
@@ -80,7 +139,7 @@
}
void checkMethodInvocation(MethodInvocation node) {
- _checkTypeArguments(
+ _checkInvocationTypeArguments(
node.typeArguments?.arguments,
node.function.staticType,
node.staticInvokeType,
@@ -313,40 +372,9 @@
}
}
- /// Checks to ensure that the given list of type [arguments] does not have a
- /// type parameter as a type argument. The [errorCode] is either
- /// [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST] or
- /// [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP].
- void _checkTypeArgumentConst(
- NodeList<TypeAnnotation> arguments, ErrorCode errorCode) {
- for (TypeAnnotation type in arguments) {
- if (type is TypeName && type.type is TypeParameterType) {
- _errorReporter.reportErrorForNode(errorCode, type, [type.name]);
- }
- }
- }
-
- /// Verify that the given list of [typeArguments] contains exactly the
- /// [expectedCount] of elements, reporting an error with the [errorCode]
- /// if not.
- void _checkTypeArgumentCount(
- TypeArgumentList typeArguments,
- int expectedCount,
- ErrorCode errorCode,
- ) {
- int actualCount = typeArguments.arguments.length;
- if (actualCount != expectedCount) {
- _errorReporter.reportErrorForNode(
- errorCode,
- typeArguments,
- [actualCount],
- );
- }
- }
-
/// Verify that each type argument in [typeArgumentList] is within its bounds,
/// as defined by [genericType].
- void _checkTypeArguments(
+ void _checkInvocationTypeArguments(
List<TypeAnnotation>? typeArgumentList,
DartType? genericType,
DartType? instantiatedType,
@@ -407,6 +435,37 @@
}
}
+ /// Checks to ensure that the given list of type [arguments] does not have a
+ /// type parameter as a type argument. The [errorCode] is either
+ /// [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST] or
+ /// [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP].
+ void _checkTypeArgumentConst(
+ NodeList<TypeAnnotation> arguments, ErrorCode errorCode) {
+ for (TypeAnnotation type in arguments) {
+ if (type is TypeName && type.type is TypeParameterType) {
+ _errorReporter.reportErrorForNode(errorCode, type, [type.name]);
+ }
+ }
+ }
+
+ /// Verify that the given list of [typeArguments] contains exactly the
+ /// [expectedCount] of elements, reporting an error with the [errorCode]
+ /// if not.
+ void _checkTypeArgumentCount(
+ TypeArgumentList typeArguments,
+ int expectedCount,
+ ErrorCode errorCode,
+ ) {
+ int actualCount = typeArguments.arguments.length;
+ if (actualCount != expectedCount) {
+ _errorReporter.reportErrorForNode(
+ errorCode,
+ typeArguments,
+ [actualCount],
+ );
+ }
+ }
+
/// Given a [node] without type arguments that refers to [element], issues
/// an error if [type] is a generic type, and the type arguments were not
/// supplied from inference or a non-dynamic default instantiation.
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index a702c1c..31e2553 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -558,6 +558,11 @@
}
@override
+ void visitConstructorReference(ConstructorReference node) {
+ _typeArgumentsVerifier.checkConstructorReference(node);
+ }
+
+ @override
void visitContinueStatement(ContinueStatement node) {
var labelNode = node.label;
if (labelNode != null) {
diff --git a/pkg/analyzer/test/generated/constant_test.dart b/pkg/analyzer/test/generated/constant_test.dart
index f97d66a..4920947 100644
--- a/pkg/analyzer/test/generated/constant_test.dart
+++ b/pkg/analyzer/test/generated/constant_test.dart
@@ -126,6 +126,50 @@
assertType(value.type, 'C');
}
+ test_constructorReference_generic_named() async {
+ var result = await _getExpressionValue("C<int>.foo", context: '''
+class C<T> {
+ C.foo();
+}
+''');
+ expect(result.isValid, isTrue);
+ DartObject value = result.value!;
+ assertType(value.type, 'C<int> Function()');
+ }
+
+ test_constructorReference_generic_unnamed() async {
+ var result = await _getExpressionValue("C<int>.new", context: '''
+class C<T> {
+ C.new();
+}
+''');
+ expect(result.isValid, isTrue);
+ DartObject value = result.value!;
+ assertType(value.type, 'C<int> Function()');
+ }
+
+ test_constructorReference_nonGeneric_named() async {
+ var result = await _getExpressionValue("C.foo", context: '''
+class C {
+ const C.foo();
+}
+''');
+ expect(result.isValid, isTrue);
+ DartObject value = result.value!;
+ assertType(value.type, 'C Function()');
+ }
+
+ test_constructorReference_nonGeneric_unnamed() async {
+ var result = await _getExpressionValue("C.new", context: '''
+class C {
+ const C();
+}
+''');
+ expect(result.isValid, isTrue);
+ DartObject value = result.value!;
+ assertType(value.type, 'C Function()');
+ }
+
test_divide_double_double() async {
await _assertValueDouble(3.2 / 2.3, "3.2 / 2.3");
}
diff --git a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
index 00b6b27..3262f0b 100644
--- a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
@@ -23,7 +23,7 @@
test_alias_generic_const() async {
await assertNoErrorsInCode('''
class A<T, U> {
- A.foo();
+ const A.foo();
}
typedef TA<T, U> = A<U, T>;
@@ -37,7 +37,27 @@
substitution: {'T': 'String', 'U': 'int'}),
classElement,
'A<String, int> Function()',
- expectedTypeNameType: 'A<String, int>',
+ expectedTypeNameElement: findElement.typeAlias('TA'),
+ );
+ }
+
+ test_alias_generic_const_differingNumberOfTypeParamters() async {
+ await assertNoErrorsInCode('''
+class A<T, U> {
+ A.foo() {}
+}
+typedef TA<T> = A<T, String>;
+
+const x = TA<int>.foo;
+''');
+
+ var classElement = findElement.class_('A');
+ assertConstructorReference(
+ findNode.constructorReference('TA<int>.foo;'),
+ elementMatcher(classElement.getNamedConstructor('foo')!,
+ substitution: {'T': 'int', 'U': 'String'}),
+ classElement,
+ 'A<int, String> Function()',
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -61,7 +81,6 @@
substitution: {'T': 'String', 'U': 'int'}),
classElement,
'A<String, int> Function()',
- expectedTypeNameType: 'A<String, int>',
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -85,7 +104,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -109,7 +127,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -135,7 +152,6 @@
substitution: {'T': 'String'}),
classElement,
'A<String> Function()',
- expectedTypeNameType: 'A<String>',
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -156,7 +172,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
);
}
@@ -178,7 +193,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
);
}
@@ -241,7 +255,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
);
}
@@ -265,7 +278,6 @@
null,
classElement,
'dynamic',
- expectedTypeNameType: 'A<int>',
);
}
@@ -287,7 +299,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
);
}
@@ -309,7 +320,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
);
}
@@ -331,7 +341,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
);
}
@@ -355,7 +364,6 @@
substitution: {'T': 'String'}),
classElement,
'A<String> Function()',
- expectedTypeNameType: 'A<String>',
);
}
@@ -381,7 +389,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
expectedPrefix: findElement.import('package:test/a.dart').prefix,
expectedTypeNameElement:
findElement.importFind('package:test/a.dart').typeAlias('TA'),
@@ -409,7 +416,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
expectedPrefix: findElement.import('package:test/a.dart').prefix,
);
}
@@ -438,7 +444,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
expectedPrefix: findElement.import('package:test/a.dart').prefix,
);
}
@@ -464,7 +469,6 @@
substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
expectedPrefix: findElement.import('package:test/a.dart').prefix,
);
}
@@ -491,7 +495,6 @@
classElement.unnamedConstructor,
classElement,
'A Function()',
- expectedTypeNameType: 'A',
);
}
@@ -518,7 +521,6 @@
classElement.unnamedConstructor,
classElement,
'A Function()',
- expectedTypeNameType: 'A',
);
}
@@ -547,7 +549,6 @@
classElement.unnamedConstructor,
classElement,
'A Function()',
- expectedTypeNameType: 'A',
);
}
@@ -571,7 +572,6 @@
constructorElement,
classElement,
'A<Never> Function()',
- expectedTypeNameType: 'A<Never>',
);
}
@@ -593,7 +593,6 @@
constructorElement,
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
);
}
@@ -615,7 +614,6 @@
constructorElement,
classElement,
'A<T> Function<T>()',
- expectedTypeNameType: null,
);
}
@@ -637,7 +635,6 @@
constructorElement,
classElement,
'A<T> Function<T extends num>()',
- expectedTypeNameType: null,
);
}
@@ -656,7 +653,6 @@
classElement.unnamedConstructor,
classElement,
'A Function()',
- expectedTypeNameType: 'A',
);
}
@@ -677,7 +673,6 @@
classElement.getNamedConstructor('foo')!,
classElement,
'A Function()',
- expectedTypeNameType: 'A',
);
}
@@ -698,7 +693,6 @@
classElement.unnamedConstructor,
classElement,
'A Function()',
- expectedTypeNameType: 'A',
);
}
@@ -719,7 +713,6 @@
constructorElement,
classElement,
'A<T> Function<T>()',
- expectedTypeNameType: null,
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -743,7 +736,6 @@
constructorElement,
findElement.class_('A'),
'A<String, U> Function<U>()',
- expectedTypeNameType: null,
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -765,7 +757,6 @@
elementMatcher(constructorElement, substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -789,7 +780,6 @@
elementMatcher(constructorElement, substitution: {'T': 'int'}),
classElement,
'A<int> Function()',
- expectedTypeNameType: 'A<int>',
expectedTypeNameElement: findElement.typeAlias('TA'),
);
}
@@ -817,7 +807,6 @@
null,
classElement,
'dynamic',
- expectedTypeNameType: 'A<int>',
);
}
@@ -840,7 +829,6 @@
classElement.getNamedConstructor('foo')!,
classElement,
'A Function()',
- expectedTypeNameType: 'A',
);
}
}
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 9e3ea8b..9a340186 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -166,7 +166,6 @@
Object? expectedConstructorElement,
ClassElement expectedClassElement,
String expectedType, {
- required String? expectedTypeNameType,
PrefixElement? expectedPrefix,
Element? expectedTypeNameElement,
}) {
@@ -184,7 +183,7 @@
var typeName = node.constructorName.type;
expectedTypeNameElement ??= expectedClassElement;
- assertTypeName(typeName, expectedTypeNameElement, expectedTypeNameType,
+ assertTypeName(typeName, expectedTypeNameElement, null,
expectedPrefix: expectedPrefix);
}
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 9ea82b2..5bd8d77 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -6332,7 +6332,7 @@
staticElement: self::@class::A
staticType: null
token: A @35
- type: A
+ type: null
staticType: A Function()
accessors
synthetic static get v @-1
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 65efa17..f900fc0 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -662,7 +662,7 @@
parent_isolate_ = nullptr;
if (isolate == nullptr) {
- FailedSpawn(error);
+ FailedSpawn(error, false);
free(error);
return;
}
@@ -701,6 +701,7 @@
}
if (!success) {
+ state_ = nullptr;
Dart_ShutdownIsolate();
return;
}
@@ -811,11 +812,25 @@
return true;
}
- void FailedSpawn(const char* error) {
+ void FailedSpawn(const char* error, bool has_current_isolate = true) {
ReportError(error != nullptr
? error
: "Unknown error occured during Isolate spawning.");
- state_ = nullptr;
+ // Destruction of [IsolateSpawnState] may cause destruction of [Message]
+ // which make need to delete persistent handles (which requires a current
+ // isolate group).
+ if (has_current_isolate) {
+ ASSERT(IsolateGroup::Current() == state_->isolate_group());
+ state_ = nullptr;
+ } else {
+ ASSERT(IsolateGroup::Current() == nullptr);
+ const bool kBypassSafepoint = false;
+ const bool result = Thread::EnterIsolateGroupAsHelper(
+ state_->isolate_group(), Thread::kUnknownTask, kBypassSafepoint);
+ ASSERT(result);
+ state_ = nullptr;
+ Thread::ExitIsolateGroupAsHelper(kBypassSafepoint);
+ }
}
void ReportError(const char* error) {
diff --git a/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart b/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
index 5626076..ac5e9d7 100644
--- a/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
@@ -112,8 +112,11 @@
expect(arguments, isA<Map>());
expect(arguments['isolateGroupId'], isA<String>());
if (!const ['GC', 'Compiler', 'CompilerVerbose'].contains(event['cat']) &&
- !const ['FinishTopLevelClassLoading', 'FinishClassLoading']
- .contains(event['name'])) {
+ !const [
+ 'FinishTopLevelClassLoading',
+ 'FinishClassLoading',
+ 'ProcessPendingClasses'
+ ].contains(event['name'])) {
expect(arguments['isolateId'], isA<String>());
}
}
diff --git a/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart
index 0f8e1de..38d088e 100644
--- a/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart
@@ -112,8 +112,11 @@
expect(arguments, isA<Map>());
expect(arguments['isolateGroupId'], isA<String>());
if (!const ['GC', 'Compiler', 'CompilerVerbose'].contains(event['cat']) &&
- !const ['FinishTopLevelClassLoading', 'FinishClassLoading']
- .contains(event['name'])) {
+ !const [
+ 'FinishTopLevelClassLoading',
+ 'FinishClassLoading',
+ 'ProcessPendingClasses'
+ ].contains(event['name'])) {
expect(arguments['isolateId'], isA<String>());
}
}
diff --git a/runtime/tests/vm/dart/isolates/closures_without_captured_variables_test.dart b/runtime/tests/vm/dart/isolates/closures_without_captured_variables_test.dart
new file mode 100644
index 0000000..d7970cc
--- /dev/null
+++ b/runtime/tests/vm/dart/isolates/closures_without_captured_variables_test.dart
@@ -0,0 +1,95 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// VMOptions=--enable-isolate-groups
+
+// The tests in this file will only succeed when isolate groups are enabled
+// (hence the VMOptions above).
+
+import 'dart:async';
+import 'dart:isolate';
+
+import 'package:expect/expect.dart';
+
+import 'fast_object_copy_test.dart' show ClassWithNativeFields;
+
+main() async {
+ final rp = ReceivePort();
+
+ testNormalEnclosingFunction(rp);
+ testNormalNestedEnclosingFunction(rp);
+ testNormalNestedEnclosingFunction2(rp);
+
+ final si = StreamIterator(rp);
+ for (int i = 0; i < 3; ++i) {
+ Expect.isTrue(await si.moveNext());
+ Expect.equals(42, (si.current)());
+ }
+ si.cancel(); // closes the port
+}
+
+testNormalEnclosingFunction(ReceivePort rp) {
+ final invalidObject = ClassWithNativeFields();
+ final normalObject = Object();
+
+ captureInvalidObject() => invalidObject;
+ captureNormalObject() => normalObject;
+ captureNothing() => 42;
+
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureInvalidObject));
+
+ // TODO(http://dartbug.com/36983): Avoid capturing more than needed.
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureNormalObject));
+
+ // Should not throw, since the [captureNothing] closure should not have a
+ // parent context and therefore not transitively refer [rp].
+ rp.sendPort.send(captureNothing);
+}
+
+testNormalNestedEnclosingFunction(ReceivePort rp) {
+ final invalidObject = ClassWithNativeFields();
+ final normalObject = Object();
+ nested() {
+ captureInvalidObject() => invalidObject;
+ captureNormalObject() => normalObject;
+ captureNothing() => 42;
+
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureInvalidObject));
+
+ // TODO(http://dartbug.com/36983): Avoid capturing more than needed.
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureNormalObject));
+
+ // Should not throw, since the [captureNothing] closure should not have a
+ // parent context and therefore not transitively refer [rp].
+ rp.sendPort.send(captureNothing);
+ }
+
+ nested();
+}
+
+testNormalNestedEnclosingFunction2(ReceivePort rp) {
+ final invalidObject = ClassWithNativeFields();
+ final normalObject = Object();
+
+ captureInvalidObject() {
+ local() => invalidObject;
+ return local;
+ }
+
+ captureNormalObject() {
+ local() => normalObject;
+ return local;
+ }
+
+ captureNothing() => 42;
+
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureInvalidObject));
+
+ // TODO(http://dartbug.com/36983): Avoid capturing more than needed.
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureNormalObject));
+
+ // Should not throw, since the [captureNothing] closure should not have a
+ // parent context and therefore not transitively refer [rp].
+ rp.sendPort.send(captureNothing);
+}
diff --git a/runtime/tests/vm/dart_2/isolates/closures_without_captured_variables_test.dart b/runtime/tests/vm/dart_2/isolates/closures_without_captured_variables_test.dart
new file mode 100644
index 0000000..3f714b6
--- /dev/null
+++ b/runtime/tests/vm/dart_2/isolates/closures_without_captured_variables_test.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// VMOptions=--enable-isolate-groups
+
+// The tests in this file will only succeed when isolate groups are enabled
+// (hence the VMOptions above).
+
+// @dart=2.9
+
+import 'dart:async';
+import 'dart:isolate';
+
+import 'package:expect/expect.dart';
+
+import 'fast_object_copy_test.dart' show ClassWithNativeFields;
+
+main() async {
+ final rp = ReceivePort();
+
+ testNormalEnclosingFunction(rp);
+ testNormalNestedEnclosingFunction(rp);
+ testNormalNestedEnclosingFunction2(rp);
+
+ final si = StreamIterator(rp);
+ for (int i = 0; i < 3; ++i) {
+ Expect.isTrue(await si.moveNext());
+ Expect.equals(42, (si.current)());
+ }
+ si.cancel(); // closes the port
+}
+
+testNormalEnclosingFunction(ReceivePort rp) {
+ final invalidObject = ClassWithNativeFields();
+ final normalObject = Object();
+
+ captureInvalidObject() => invalidObject;
+ captureNormalObject() => normalObject;
+ captureNothing() => 42;
+
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureInvalidObject));
+
+ // TODO(http://dartbug.com/36983): Avoid capturing more than needed.
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureNormalObject));
+
+ // Should not throw, since the [captureNothing] closure should not have a
+ // parent context and therefore not transitively refer [rp].
+ rp.sendPort.send(captureNothing);
+}
+
+testNormalNestedEnclosingFunction(ReceivePort rp) {
+ final invalidObject = ClassWithNativeFields();
+ final normalObject = Object();
+ nested() {
+ captureInvalidObject() => invalidObject;
+ captureNormalObject() => normalObject;
+ captureNothing() => 42;
+
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureInvalidObject));
+
+ // TODO(http://dartbug.com/36983): Avoid capturing more than needed.
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureNormalObject));
+
+ // Should not throw, since the [captureNothing] closure should not have a
+ // parent context and therefore not transitively refer [rp].
+ rp.sendPort.send(captureNothing);
+ }
+
+ nested();
+}
+
+testNormalNestedEnclosingFunction2(ReceivePort rp) {
+ final invalidObject = ClassWithNativeFields();
+ final normalObject = Object();
+
+ captureInvalidObject() {
+ local() => invalidObject;
+ return local;
+ }
+
+ captureNormalObject() {
+ local() => normalObject;
+ return local;
+ }
+
+ captureNothing() => 42;
+
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureInvalidObject));
+
+ // TODO(http://dartbug.com/36983): Avoid capturing more than needed.
+ Expect.throwsArgumentError(() => rp.sendPort.send(captureNormalObject));
+
+ // Should not throw, since the [captureNothing] closure should not have a
+ // parent context and therefore not transitively refer [rp].
+ rp.sendPort.send(captureNothing);
+}
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 870c86f..7464bef 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -5252,7 +5252,7 @@
bool has_valid_annotation,
bool has_pragma,
intptr_t func_decl_offset) {
- intptr_t offset = ReaderOffset();
+ const intptr_t offset = ReaderOffset();
FunctionNodeHelper function_node_helper(this);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
@@ -5385,7 +5385,11 @@
Fragment instructions;
instructions += Constant(function);
- instructions += LoadLocal(parsed_function()->current_context_var());
+ if (scopes()->IsClosureWithEmptyContext(offset)) {
+ instructions += NullConstant();
+ } else {
+ instructions += LoadLocal(parsed_function()->current_context_var());
+ }
instructions += flow_graph_builder_->AllocateClosure();
LocalVariable* closure = MakeTemporary();
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 7d7ab7e..f103f14 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -1492,7 +1492,7 @@
void ScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) {
// "Peek" ahead into the function node
- intptr_t offset = helper_.ReaderOffset();
+ const intptr_t offset = helper_.ReaderOffset();
FunctionNodeHelper function_node_helper(&helper_);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
@@ -1534,6 +1534,14 @@
VisitFunctionNode(); // read function node.
+ // Remember if this closure and all closures nested within it don't
+ // capture any variables from outer scopes.
+ if (scope_->function_level() == 1) {
+ if (scope_->NumCapturedVariables() == 0) {
+ result_->closure_offsets_without_captures.Add(offset);
+ }
+ }
+
ExitScope(function_node_helper.position_, function_node_helper.end_position_);
depth_ = saved_depth_state;
current_function_scope_ = saved_function_scope;
diff --git a/runtime/vm/compiler/frontend/scope_builder.h b/runtime/vm/compiler/frontend/scope_builder.h
index 1531508..9805939 100644
--- a/runtime/vm/compiler/frontend/scope_builder.h
+++ b/runtime/vm/compiler/frontend/scope_builder.h
@@ -189,6 +189,15 @@
yield_context_variable(NULL),
raw_variable_counter_(0) {}
+ bool IsClosureWithEmptyContext(intptr_t function_node_offset) {
+ for (intptr_t i = 0; i < closure_offsets_without_captures.length(); ++i) {
+ if (closure_offsets_without_captures[i] == function_node_offset) {
+ return true;
+ }
+ }
+ return false;
+ }
+
IntMap<LocalVariable*> locals;
IntMap<LocalScope*> scopes;
GrowableArray<FunctionScope> function_scopes;
@@ -230,6 +239,10 @@
// For-in iterators, one per for-in nesting level.
GrowableArray<LocalVariable*> iterator_variables;
+ // Remembers closure function kernel offsets that do not capture any
+ // variables.
+ GrowableArray<intptr_t> closure_offsets_without_captures;
+
private:
DISALLOW_COPY_AND_ASSIGN(ScopeBuildingResult);
};
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 8dc8f37..d6555b3 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -1774,31 +1774,10 @@
}
private:
- friend class DisabledNoActiveIsolateScope;
Thread* thread_;
Isolate* saved_isolate_;
};
-// Can be used inside a [NoActiveIsolateScope] to set the current isolate.
-class DisabledNoActiveIsolateScope : public StackResource {
- public:
- explicit DisabledNoActiveIsolateScope(NoActiveIsolateScope* scope)
- : StackResource(Thread::Current()),
- thread_(static_cast<Thread*>(thread())),
- scope_(scope) {
- ASSERT(thread_->isolate() == nullptr);
- thread_->isolate_ = scope_->saved_isolate_;
- }
- ~DisabledNoActiveIsolateScope() {
- ASSERT(thread_->isolate_ == scope_->saved_isolate_);
- thread_->isolate_ = nullptr;
- }
-
- private:
- Thread* thread_;
- NoActiveIsolateScope* scope_;
-};
-
} // namespace dart
#endif // RUNTIME_VM_ISOLATE_H_
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 2aec2d0..3c538f6 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -189,7 +189,7 @@
: program_(program),
thread_(Thread::Current()),
zone_(thread_->zone()),
- isolate_(thread_->isolate()),
+ no_active_isolate_scope_(),
patch_classes_(Array::ZoneHandle(zone_)),
active_class_(),
library_kernel_offset_(-1), // Set to the correct value in LoadLibrary
@@ -462,7 +462,7 @@
: program_(NULL),
thread_(Thread::Current()),
zone_(thread_->zone()),
- isolate_(thread_->isolate()),
+ no_active_isolate_scope_(),
patch_classes_(Array::ZoneHandle(zone_)),
library_kernel_offset_(data_program_offset),
kernel_binary_version_(kernel_binary_version),
@@ -646,8 +646,6 @@
}
void KernelLoader::LoadLibrary(const Library& library) {
- NoActiveIsolateScope no_active_isolate_scope;
-
// This will be invoked by VM bootstrapping code.
SafepointWriteRwLocker ml(thread_, thread_->isolate_group()->program_lock());
@@ -874,8 +872,6 @@
"not allowed");
}
- NoActiveIsolateScope no_active_isolate_scope;
-
// Read library index.
library_kernel_offset_ = library_offset(index);
correction_offset_ = library_kernel_offset_;
@@ -1690,8 +1686,6 @@
}
void KernelLoader::FinishLoading(const Class& klass) {
- NoActiveIsolateScope no_active_isolate_scope;
-
ASSERT(klass.IsTopLevel() || (klass.kernel_offset() > 0));
Zone* zone = Thread::Current()->zone();
diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h
index 4fe570f..573711f 100644
--- a/runtime/vm/kernel_loader.h
+++ b/runtime/vm/kernel_loader.h
@@ -375,7 +375,7 @@
Thread* thread_;
Zone* zone_;
- Isolate* isolate_;
+ NoActiveIsolateScope no_active_isolate_scope_;
Array& patch_classes_;
ActiveClass active_class_;
// This is the offset of the current library within
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 3577ccb..8344d4b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -13639,11 +13639,13 @@
zone, String::New("Kernel isolate returned ill-formed kernel.")));
}
- kernel::KernelLoader loader(kernel_pgm.get(),
- /*uri_to_source_table=*/nullptr);
- auto& result = Object::Handle(
- zone, loader.LoadExpressionEvaluationFunction(library_url, klass));
- kernel_pgm.reset();
+ auto& result = Object::Handle(zone);
+ {
+ kernel::KernelLoader loader(kernel_pgm.get(),
+ /*uri_to_source_table=*/nullptr);
+ result = loader.LoadExpressionEvaluationFunction(library_url, klass);
+ kernel_pgm.reset();
+ }
if (result.IsError()) return result.ptr();
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index c6e6c9d..52c680f 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -245,7 +245,9 @@
DART_FORCE_INLINE
ObjectPtr AllocateObject(intptr_t cid, intptr_t size) {
#if defined(DART_COMPRESSED_POINTERS)
- const bool compressed = true;
+ // TODO(rmacnak): Can be changed unconditionally to `true` once Contexts
+ // are compressed.
+ const bool compressed = cid != kContextCid;
#else
const bool compressed = false;
#endif
@@ -1161,8 +1163,9 @@
UntagContext(to)->num_variables_ = UntagContext(from)->num_variables_;
- Base::ForwardCompressedPointer(from, to,
- OFFSET_OF(UntaggedContext, parent_));
+ // TODO(rmacnak): Should use ForwardCompressedPointer once contexts are
+ // compressed.
+ Base::ForwardPointer(from, to, OFFSET_OF(UntaggedContext, parent_));
Base::ForwardContextPointers(
length, from, to, Context::variable_offset(0),
Context::variable_offset(0) + kWordSize * length);
diff --git a/tests/language/explicit_type_instantiation_parsing_test.dart b/tests/language/explicit_type_instantiation_parsing_test.dart
new file mode 100644
index 0000000..4397a2c
--- /dev/null
+++ b/tests/language/explicit_type_instantiation_parsing_test.dart
@@ -0,0 +1,321 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// SharedOptions=--enable-experiment=constructor-tearoffs
+
+// Test parsing around ambiguities in grammar for explicit type instantiation.
+//
+// If an expression is followed by a `<`
+// which is then followed by potential type arguments and a `>`,
+// it is parsed as type arguments if the next token is one of
+// `)`, `}`, `]`, `:`, `;`, `,`, `(`, `.`, `==` or `!=`.
+// Otherwise it's (attempted) parsed as a `<` infix operator.
+// This decision is made no matter whether what otherwise follows
+// is valid for that choice.
+
+typedef X<_> = Class;
+
+typedef Z<_, __> = Class;
+
+const Object? v = null;
+const dynamic d = null;
+
+class Class {
+ Class([_]);
+ Class.named([_]);
+
+ static Class get instance => Class();
+
+ Class get value => this;
+ Class call([_]) => this;
+}
+
+int f1<X>([_]) => 0;
+int f2<X, Y>([_]) => 0;
+int f3<X, Y, Z>([_]) => 0;
+
+// Type of the instantiation of the functions above.
+typedef F = int Function([Object? _]);
+
+void expect1<T extends Object?>(T? a) {}
+void expect2(Object? a, Object? b) {}
+void expect3(Object? a, Object? b, Object? c) {}
+void expect4(Object? a, Object? b, Object? c, Object? d) {}
+
+// Anything goes!
+// We only care about parsing here, if it parses,
+// all objects support all the operations.
+extension <T extends Object?> on T {
+ T get self => this;
+ dynamic get any => null;
+ Object? operator *(_) => null;
+ Object? operator -(_) => null;
+ Object? operator <(_) => null;
+ Object? operator >(_) => null;
+ Object? operator >>(_) => null;
+ Object? operator >>>(_) => null;
+ Object? operator [](_) => null;
+ Object? call<R, S>([_]) => null;
+ bool get asBool => true;
+ int get prop => 0;
+ set prop(int _) {}
+}
+
+void main() {
+ Object? as = "gotcha!"; // Built-in identifier declared as variable.
+
+ // Validly parsed as type instantiation.
+ // Continuation tokens are: `(`, `.`, `==` and `!=`.
+ expect1<Class>(Z<X, X>(2));
+ expect1<Class>(Z<X, X>.named(2));
+ expect1<Function>(Z<X, X>.named); // constructor tear-off
+ expect1<bool>(Z<X, X> == Class);
+ expect1<bool>(Z<X, X> != Class);
+ // Stop tokens are `)`, `,`, `}`, `]`, `:` and `;`.
+ expect1<Type>(Z<X, X>);
+ expect1<Type>(Z<X, X>,);
+ expect1<Set<Type>>({Z<X, X>});
+ expect1<List<Type>>([Z<X, X>]);
+ expect1<Type>(v.asBool ? Z<X, X> : int);
+ expect1<Map<Type, int>>({Z<X, X>: 1});
+ {
+ Type _ = Z<X, X>;
+ }
+
+ // Validly parsed as generic function instantiation.
+ expect1<int>(f2<X, X>(1));
+ expect1<F>(f2<X, X>.self);
+ expect1<int>(f2<X, X>.self());
+ expect1<bool>(f2<X, X> == null);
+ expect1<bool>(f2<X, X> != null);
+
+ expect1<F>(f2<X, X>);
+ expect1<F>(f2<X, X>,);
+ expect1<Set<F>>({f2<X, X>});
+ expect1<List<F>>([f2<X, X>]);
+ expect1<F>(v.asBool ? f2<X, X> : ([_]) => 2);
+ expect1<Map<F, int>>({f2<X, X> : 2});
+ {
+ F _ = f2<X, X>;
+ }
+
+ // Also works if ending in `>>` or `>>>`
+ expect1<Class>(Z<X, Z<X, X>>(2));
+ expect1<Class>(Z<X, Z<X, X>>.named(2));
+ expect1<Function>(Z<X, Z<X, X>>.named); // constructor tear-off
+ expect1<bool>(Z<X, Z<X, X>> == Class);
+ expect1<bool>(Z<X, Z<X, X>> != Class);
+ // Stop tokens are `)`, `,`, `}`, `]`, `:` and `;`.
+ expect1<Type>(Z<X, Z<X, X>>);
+ expect1<Type>(Z<X, Z<X, X>>,);
+ expect1<Set<Type>>({Z<X, Z<X, X>>});
+ expect1<List<Type>>([Z<X, Z<X, X>>]);
+ expect1<Type>(v.asBool ? Z<X, Z<X, X>> : int);
+ expect1<Map<Type, int>>({Z<X, Z<X, X>> : 1});
+ {
+ Type _ = Z<X, Z<X, X>>;
+ }
+
+ // Validly parsed as generic function instantiation.
+ expect1<int>(f2<X, Z<X, X>>(1));
+ expect1<F>(f2<X, Z<X, X>>.self);
+ expect1<int>(f2<X, Z<X, X>>.self());
+ expect1<bool>(f2<X, Z<X, X>> == null);
+ expect1<bool>(f2<X, Z<X, X>> != null);
+
+ expect1<F>(f2<X, Z<X, X>>);
+ expect1<F>(f2<X, Z<X, X>>,);
+ expect1<Set<F>>({f2<X, Z<X, X>>});
+ expect1<List<F>>([f2<X, Z<X, X>>]);
+ expect1<F>(v.asBool ? f2<X, Z<X, X>> : ([_]) => 2);
+ expect1<Map<F, int>>({f2<X, Z<X, X>> : 2});
+ {
+ F _ = f2<X, Z<X, X>>;
+ }
+
+ expect1<Class>(Z<X, Z<X, Z<X, X>>>(2));
+ expect1<Class>(Z<X, Z<X, Z<X, X>>>.named(2));
+ expect1<Function>(Z<X, Z<X, Z<X, X>>>.named); // constructor tear-off
+ expect1<bool>(Z<X, Z<X, Z<X, X>>> == Class);
+ expect1<bool>(Z<X, Z<X, Z<X, X>>> != Class);
+ // Stop tokens are `)`, `,`, `}`, `]`, `:` and `;`.
+ expect1<Type>(Z<X, Z<X, Z<X, X>>>);
+ expect1<Type>(Z<X, Z<X, Z<X, X>>>,);
+ expect1<Set<Type>>({Z<X, Z<X, Z<X, X>>>});
+ expect1<List<Type>>([Z<X, Z<X, Z<X, X>>>]);
+ expect1<Type>(v.asBool ? Z<X, Z<X, Z<X, X>>> : int);
+ expect1<Map<Type, int>>({Z<X, Z<X, Z<X, X>>>: 1});
+ {
+ Type _ = Z<X, Z<X, Z<X, X>>>;
+ }
+
+ // Validly parsed as generic function instantiation.
+ expect1<int>(f2<X, Z<X, Z<X, X>>>(1));
+ expect1<F>(f2<X, Z<X, Z<X, X>>>.self);
+ expect1<int>(f2<X, Z<X, Z<X, X>>>.self());
+ expect1<bool>(f2<X, Z<X, Z<X, X>>> == null);
+ expect1<bool>(f2<X, Z<X, Z<X, X>>> != null);
+
+ expect1<F>(f2<X, Z<X, Z<X, X>>>);
+ expect1<F>(f2<X, Z<X, Z<X, X>>>,);
+ expect1<Set<F>>({f2<X, Z<X, Z<X, X>>>});
+ expect1<List<F>>([f2<X, Z<X, Z<X, X>>>]);
+ expect1<F>(v.asBool ? f2<X, Z<X, Z<X, X>>> : ([_]) => 2);
+ expect1<Map<F, int>>({f2<X, Z<X, Z<X, X>>> : 2});
+ {
+ F _ = f2<X, Z<X, Z<X, X>>>;
+ }
+
+ // Parsed as instantiation, can't access statics on instantiated type literal.
+ expect1<Class>(Z<X, X>.instance);
+ // ^^^^^^^^
+ // [cfe] Cannot access static member on an instantiated generic class.
+ // [analyzer] unspecified
+
+
+ // Not valid <typeList> inside `<..>`, so always parsed as operators.
+ // The expect2 function requires two arguments, so it would be a type
+ // error to parse as type arguments.
+ expect2(Z < X, 2 > (2));
+ expect2(Z < 2, X > (2));
+ expect2(Z < X, 2 > (2));
+ expect2(Z < X, v! > (2));
+ expect3(Z < X, Z < X, 2 >> (2));
+ expect4(Z < X, Z < X, Z < X, 2 >>> (2));
+ // `as` is a built-in identifier, so it cannot be a *type*,
+ // preventing the lookahead from `<` from matching <typeList>,
+ // and therefore it's parsed as an operator.
+ expect2(Z < X, as > (2));
+ expect3(Z < X, Z < X, as >> (2));
+ expect4(Z < X, Z < X, Z < X, as >>> (2));
+
+ // Validly parsed as operators due to disambiguation.
+ expect2(Z < X, X > X);
+ expect2(Z < X, X > 2);
+ expect2(Z < X, X > .2); // That `.` is part of the number literal, not a `.` token.
+ expect2(Z < X, X > -2);
+ expect2(Z < X, X > as);
+ expect2(Z < X, X > [1]);
+ expect2(Z < X, X > ![1].asBool);
+ expect2(Z < X, X > ++[1].prop);
+ expect2(Z < X, X > <int>[1]);
+
+ // Some would be valid as instantiation too, as proven by parenthefication.
+ expect1((Z<X, X>) - 2);
+ expect1((Z<X, X>)[1]);
+ expect1((Z<X, X>)![1].asBool); // ignore: unnecessary_non_null_assertion
+ // ^
+ // [cfe] Operand of null-aware operation '!' has type 'Type' which excludes null.
+
+ // Works if the type argument would end in `>>` or `>>>` too.
+ expect3(Z < X, Z < X, X >> X);
+ expect3(Z < X, Z < X, X >> 2);
+ expect3(Z < X, Z < X, X >> .2);
+ expect3(Z < X, Z < X, X >> -2);
+ expect3(Z < X, Z < X, X >> as);
+ expect3(Z < X, Z < X, X >> [1]);
+ expect3(Z < X, Z < X, X >> ![1].asBool);
+ expect3(Z < X, Z < X, X >> ++[1].prop);
+
+ expect4(Z < X, Z < X, Z < X, X >>> X);
+ expect4(Z < X, Z < X, Z < X, X >>> 2);
+ expect4(Z < X, Z < X, Z < X, X >>> .2);
+ expect4(Z < X, Z < X, Z < X, X >>> -2);
+ expect4(Z < X, Z < X, Z < X, X >>> as);
+ expect4(Z < X, Z < X, Z < X, X >>> [1]);
+ expect4(Z < X, Z < X, Z < X, X >>> ![1].asBool);
+ expect4(Z < X, Z < X, Z < X, X >>> ++[1].prop);
+
+ // No valid parsing either way.
+
+ // Content of type arguments not valid types.
+ // Cannot parse as operators since grammar doesn't allow chaining.
+ X<2>(2);
+ // ^
+ // [cfe] A comparison expression can't be an operand of another comparison expression.
+ // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND
+
+ X<2>;
+ // ^
+ // [cfe] Expected an identifier, but got ';'.
+ // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
+ // ^
+ // [cfe] A comparison expression can't be an operand of another comparison expression.
+ // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND
+
+ X<2>.instance; // Not type argument.
+ // ^
+ // [cfe] Expected an identifier, but got '.'.
+ // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
+ // ^
+ // [cfe] A comparison expression can't be an operand of another comparison expression.
+ // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND
+
+ X<2>.any;
+ // ^
+ // [cfe] Expected an identifier, but got '.'.
+ // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
+ // ^
+ // [cfe] A comparison expression can't be an operand of another comparison expression.
+ // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND
+
+ // This would be invalid even if `X` had an `any` member. See next.
+ X<X>.any; // Invalid, Class does not have any static `any` member.
+ // ^^^
+ // [cfe] Member not found: 'any'.
+ // [analyzer] unspecified
+
+ X<X>.instance; // Does have static `instance` member, can't access this way.
+ // ^^^^^^^^
+ // [cfe] Cannot access static member on an instantiated generic class.
+ // [analyzer] unspecified
+
+ // Parse error.
+
+ X<X>2;
+ // ^
+ // [cfe] A comparison expression can't be an operand of another comparison expression.
+ // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND
+
+ // Doesn't parse as operators, would be valid if type arguments.
+
+ // The following `-` forces operators, but those can't parse like this.
+ X<X>-1;
+ // ^
+ // [cfe] A comparison expression can't be an operand of another comparison expression.
+ // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND
+
+ // Parsed as operators on function instantiation too (parsing doesn't know.)
+ f1<X> - 1;
+ // ^
+ // [cfe] A comparison expression can't be an operand of another comparison expression.
+ // [analyzer] SYNTACTIC_ERROR.EQUALITY_CANNOT_BE_EQUALITY_OPERAND
+
+ // Parsed as a generic instantiation, but `v` is not generic function or type.
+ // Would be valid if parsed as operators.
+ expect1(v < X, X > (2));
+ // ^^^^^^^^
+ // [cfe] unspecified
+ // [analyzer] unspecified
+
+ // Parsed as a generic instantiation, but `d` is not generic function or type.
+ // Being dynamic doesn't help.
+ // Would be valid if parsed as operators.
+ expect1(v < X, X > (2));
+ // ^^^^^^^^
+ // [cfe] unspecified
+ // [analyzer] unspecified
+
+ // Valid only if parenthesized.
+ expect1((Z < X, X >) * 2);
+
+ // Valid only if parenthesized.
+ expect1((Z < X, X >) < 4);
+
+ // Still can't instantiate something non-generic.
+ /**/ v<int>;
+ // ^^^^^
+ // [cfe] The static type of the explicit instantiation operand must be a generic function type but is 'Object?'.
+ // [analyzer] unspecified
+}
diff --git a/tests/lib/isolate/illegal_msg_function_test.dart b/tests/lib/isolate/illegal_msg_function_test.dart
index 5b5aa45..62cbe70 100644
--- a/tests/lib/isolate/illegal_msg_function_test.dart
+++ b/tests/lib/isolate/illegal_msg_function_test.dart
@@ -2,7 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// VMOptions=--enable-isolate-groups
// VMOptions=--no-enable-isolate-groups
library illegal_msg_function_test;
diff --git a/tests/lib_2/isolate/illegal_msg_function_test.dart b/tests/lib_2/isolate/illegal_msg_function_test.dart
index 5ea24f0..dbd3699 100644
--- a/tests/lib_2/isolate/illegal_msg_function_test.dart
+++ b/tests/lib_2/isolate/illegal_msg_function_test.dart
@@ -4,7 +4,6 @@
// @dart = 2.9
-// VMOptions=--enable-isolate-groups
// VMOptions=--no-enable-isolate-groups
library illegal_msg_function_test;
diff --git a/tools/VERSION b/tools/VERSION
index 061b57b..b52e166 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 78
+PRERELEASE 79
PRERELEASE_PATCH 0
\ No newline at end of file