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