Version 2.14.0-125.0.dev

Merge commit '8c4dcce5aa0c662ad9e61c55c96549bab164600a' into 'dev'
diff --git a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
index cf7f639..f05be5b 100644
--- a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
@@ -16,6 +16,8 @@
         Expression,
         Field,
         FieldInitializer,
+        InstanceAccessKind,
+        InstanceGet,
         IntLiteral,
         InterfaceType,
         ListLiteral,
@@ -436,8 +438,15 @@
     nameFieldBuilder.build(libraryBuilder);
     Field nameField = nameFieldBuilder.field;
     ProcedureBuilder toStringBuilder = firstMemberNamed("toString");
-    toStringBuilder.body = new ReturnStatement(
-        new PropertyGet(new ThisExpression(), nameField.name, nameField));
+    if (libraryBuilder
+        .loader.target.backendTarget.supportsNewMethodInvocationEncoding) {
+      toStringBuilder.body = new ReturnStatement(new InstanceGet(
+          InstanceAccessKind.Instance, new ThisExpression(), nameField.name,
+          interfaceTarget: nameField, resultType: nameField.type));
+    } else {
+      toStringBuilder.body = new ReturnStatement(
+          new PropertyGet(new ThisExpression(), nameField.name, nameField));
+    }
     List<Expression> values = <Expression>[];
     if (enumConstantInfos != null) {
       for (EnumConstantInfo enumConstantInfo in enumConstantInfos) {
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index ad072b3..6bb19ed 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -2947,12 +2947,12 @@
       expression ??= new FunctionInvocation(
           target.isNullableCallFunction
               ? FunctionAccessKind.Nullable
-              : (inferredFunctionType == unknownFunction
+              : (identical(inferredFunctionType, unknownFunction)
                   ? FunctionAccessKind.Function
                   : FunctionAccessKind.FunctionType),
           receiver,
           arguments,
-          functionType: inferredFunctionType == unknownFunction
+          functionType: identical(inferredFunctionType, unknownFunction)
               ? null
               : inferredFunctionType)
         ..fileOffset = fileOffset;
diff --git a/pkg/front_end/testcases/none/issue46003.dart b/pkg/front_end/testcases/none/issue46003.dart
new file mode 100644
index 0000000..b0b7271
--- /dev/null
+++ b/pkg/front_end/testcases/none/issue46003.dart
@@ -0,0 +1,22 @@
+// 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.md file.
+
+class A {
+  final Function() foo;
+  A(this.foo);
+
+  void bar() {
+    foo();
+
+    Function() x = foo;
+    x();
+
+    void Function() y = foo;
+    y();
+  }
+}
+
+main() {
+  A(() {}).bar();
+}
diff --git a/pkg/front_end/testcases/none/issue46003.dart.strong.expect b/pkg/front_end/testcases/none/issue46003.dart.strong.expect
new file mode 100644
index 0000000..cbdc1a0
--- /dev/null
+++ b/pkg/front_end/testcases/none/issue46003.dart.strong.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  final field () → dynamic foo;
+  constructor •(() → dynamic foo) → self::A
+    : self::A::foo = foo, super core::Object::•()
+    ;
+  method bar() → void {
+    this.{self::A::foo}{() → dynamic}(){() → dynamic};
+    () → dynamic x = this.{self::A::foo}{() → dynamic};
+    x(){() → dynamic};
+    () → void y = this.{self::A::foo}{() → dynamic};
+    y(){() → void};
+  }
+}
+static method main() → dynamic {
+  new self::A::•(() → Null {}).{self::A::bar}(){() → void};
+}
diff --git a/pkg/front_end/testcases/none/issue46003.dart.strong.transformed.expect b/pkg/front_end/testcases/none/issue46003.dart.strong.transformed.expect
new file mode 100644
index 0000000..cbdc1a0
--- /dev/null
+++ b/pkg/front_end/testcases/none/issue46003.dart.strong.transformed.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  final field () → dynamic foo;
+  constructor •(() → dynamic foo) → self::A
+    : self::A::foo = foo, super core::Object::•()
+    ;
+  method bar() → void {
+    this.{self::A::foo}{() → dynamic}(){() → dynamic};
+    () → dynamic x = this.{self::A::foo}{() → dynamic};
+    x(){() → dynamic};
+    () → void y = this.{self::A::foo}{() → dynamic};
+    y(){() → void};
+  }
+}
+static method main() → dynamic {
+  new self::A::•(() → Null {}).{self::A::bar}(){() → void};
+}
diff --git a/pkg/front_end/testcases/none/issue46003.dart.textual_outline.expect b/pkg/front_end/testcases/none/issue46003.dart.textual_outline.expect
new file mode 100644
index 0000000..71689d1
--- /dev/null
+++ b/pkg/front_end/testcases/none/issue46003.dart.textual_outline.expect
@@ -0,0 +1,7 @@
+class A {
+  final Function() foo;
+  A(this.foo);
+  void bar() {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/none/issue46003.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/none/issue46003.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..68ea14c
--- /dev/null
+++ b/pkg/front_end/testcases/none/issue46003.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+class A {
+  A(this.foo);
+  final Function() foo;
+  void bar() {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/none/issue46003.dart.weak.expect b/pkg/front_end/testcases/none/issue46003.dart.weak.expect
new file mode 100644
index 0000000..cbdc1a0
--- /dev/null
+++ b/pkg/front_end/testcases/none/issue46003.dart.weak.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  final field () → dynamic foo;
+  constructor •(() → dynamic foo) → self::A
+    : self::A::foo = foo, super core::Object::•()
+    ;
+  method bar() → void {
+    this.{self::A::foo}{() → dynamic}(){() → dynamic};
+    () → dynamic x = this.{self::A::foo}{() → dynamic};
+    x(){() → dynamic};
+    () → void y = this.{self::A::foo}{() → dynamic};
+    y(){() → void};
+  }
+}
+static method main() → dynamic {
+  new self::A::•(() → Null {}).{self::A::bar}(){() → void};
+}
diff --git a/pkg/front_end/testcases/none/issue46003.dart.weak.outline.expect b/pkg/front_end/testcases/none/issue46003.dart.weak.outline.expect
new file mode 100644
index 0000000..f30d3b1
--- /dev/null
+++ b/pkg/front_end/testcases/none/issue46003.dart.weak.outline.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  final field () → dynamic foo;
+  constructor •(() → dynamic foo) → self::A
+    ;
+  method bar() → void
+    ;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/none/issue46003.dart.weak.transformed.expect b/pkg/front_end/testcases/none/issue46003.dart.weak.transformed.expect
new file mode 100644
index 0000000..cbdc1a0
--- /dev/null
+++ b/pkg/front_end/testcases/none/issue46003.dart.weak.transformed.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  final field () → dynamic foo;
+  constructor •(() → dynamic foo) → self::A
+    : self::A::foo = foo, super core::Object::•()
+    ;
+  method bar() → void {
+    this.{self::A::foo}{() → dynamic}(){() → dynamic};
+    () → dynamic x = this.{self::A::foo}{() → dynamic};
+    x(){() → dynamic};
+    () → void y = this.{self::A::foo}{() → dynamic};
+    y(){() → void};
+  }
+}
+static method main() → dynamic {
+  new self::A::•(() → Null {}).{self::A::bar}(){() → void};
+}
diff --git a/pkg/test_runner/test/experiment_test.dart b/pkg/test_runner/test/experiment_test.dart
new file mode 100644
index 0000000..1dffb4a
--- /dev/null
+++ b/pkg/test_runner/test/experiment_test.dart
@@ -0,0 +1,16 @@
+// 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.
+
+// Test that the experiment in the 'SharedOptions' comment is passed by the
+// test runner.
+
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+// SharedOptions=--enable-experiment=test-experiment
+main() {
+  Expect.isTrue(Platform.executableArguments
+      .contains("--enable-experiment=test-experiment"));
+}
diff --git a/runtime/tests/concurrency/run_stress_test_shards.dart b/runtime/tests/concurrency/run_stress_test_shards.dart
index e719c4a..3157880 100644
--- a/runtime/tests/concurrency/run_stress_test_shards.dart
+++ b/runtime/tests/concurrency/run_stress_test_shards.dart
@@ -124,13 +124,24 @@
   final shards = int.parse(options['shards']);
   final shard = int.parse(options['shard']) - 1;
 
-  final thisShardsConfigurations = [];
-  for (int i = 0; i < configurations.length; i++) {
-    if ((i % shards) == shard) {
-      thisShardsConfigurations.add(configurations[i]);
+  // Tasks will eventually be killed if they do not have any output for some
+  // time. So we'll explicitly print something every 4 minutes.
+  final sw = Stopwatch()..start();
+  final timer = Timer.periodic(const Duration(minutes: 4), (_) {
+    print('[${sw.elapsed}] ... still working ...');
+  });
+
+  try {
+    final thisShardsConfigurations = [];
+    for (int i = 0; i < configurations.length; i++) {
+      if ((i % shards) == shard) {
+        thisShardsConfigurations.add(configurations[i]);
+      }
     }
-  }
-  for (final config in thisShardsConfigurations) {
-    await config.runTest();
+    for (final config in thisShardsConfigurations) {
+      await config.runTest();
+    }
+  } finally {
+    timer.cancel();
   }
 }
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 1f688e0..0795592 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -6438,6 +6438,17 @@
     __ b(deopt, CS);
   }
 }
+  
+static bool CanBePairOfImmediateOperands(Value* value,
+                                         compiler::Operand* low,
+                                         compiler::Operand* high) {
+  int64_t imm;
+  if (value->BindsToConstant() && compiler::HasIntegerValue(value->BoundConstant(), &imm)) {
+    return compiler::Operand::CanHold(Utils::Low32Bits(imm), low) &&
+      compiler::Operand::CanHold(Utils::High32Bits(imm), high);
+  }
+  return false;
+}
 
 LocationSummary* BinaryInt64OpInstr::MakeLocationSummary(Zone* zone,
                                                          bool opt) const {
@@ -6447,8 +6458,17 @@
       LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
   summary->set_in(0, Location::Pair(Location::RequiresRegister(),
                                     Location::RequiresRegister()));
-  summary->set_in(1, Location::Pair(Location::RequiresRegister(),
-                                    Location::RequiresRegister()));
+
+  compiler::Operand o;
+  if (CanBePairOfImmediateOperands(right(), &o, &o) &&
+      (op_kind() == Token::kBIT_AND || op_kind() == Token::kBIT_OR ||
+       op_kind() == Token::kBIT_XOR || op_kind() == Token::kADD ||
+       op_kind() == Token::kSUB)) {
+    summary->set_in(1, Location::Constant(right()->definition()->AsConstant()));
+  } else {
+    summary->set_in(1, Location::Pair(Location::RequiresRegister(),
+                                      Location::RequiresRegister()));
+  }
   summary->set_out(0, Location::Pair(Location::RequiresRegister(),
                                      Location::RequiresRegister()));
   if (op_kind() == Token::kMUL) {
@@ -6461,15 +6481,22 @@
   PairLocation* left_pair = locs()->in(0).AsPairLocation();
   Register left_lo = left_pair->At(0).reg();
   Register left_hi = left_pair->At(1).reg();
-  PairLocation* right_pair = locs()->in(1).AsPairLocation();
-  Register right_lo = right_pair->At(0).reg();
-  Register right_hi = right_pair->At(1).reg();
   PairLocation* out_pair = locs()->out(0).AsPairLocation();
   Register out_lo = out_pair->At(0).reg();
   Register out_hi = out_pair->At(1).reg();
   ASSERT(!can_overflow());
   ASSERT(!CanDeoptimize());
 
+  compiler::Operand right_lo, right_hi;
+  if (locs()->in(1).IsConstant()) {
+    const bool ok = CanBePairOfImmediateOperands(right(), &right_lo, &right_hi);
+    RELEASE_ASSERT(ok);
+  } else {
+    PairLocation* right_pair = locs()->in(1).AsPairLocation();
+    right_lo = compiler::Operand(right_pair->At(0).reg());
+    right_hi = compiler::Operand(right_pair->At(1).reg());
+  }
+
   switch (op_kind()) {
     case Token::kBIT_AND: {
       __ and_(out_lo, left_lo, compiler::Operand(right_lo));
@@ -6497,12 +6524,15 @@
       break;
     }
     case Token::kMUL: {
+      PairLocation* right_pair = locs()->in(1).AsPairLocation();
+      Register right_lo_reg = right_pair->At(0).reg();
+      Register right_hi_reg = right_pair->At(1).reg();
       // Compute 64-bit a * b as:
       //     a_l * b_l + (a_h * b_l + a_l * b_h) << 32
       Register temp = locs()->temp(0).reg();
-      __ mul(temp, left_lo, right_hi);
-      __ mla(out_hi, left_hi, right_lo, temp);
-      __ umull(out_lo, temp, left_lo, right_lo);
+      __ mul(temp, left_lo, right_hi_reg);
+      __ mla(out_hi, left_hi, right_lo_reg, temp);
+      __ umull(out_lo, temp, left_lo, right_lo_reg);
       __ add(out_hi, out_hi, compiler::Operand(temp));
       break;
     }
diff --git a/tests/language/generic_methods/generic_invocation_all_types_test.dart b/tests/language/generic_methods/generic_invocation_all_types_test.dart
new file mode 100644
index 0000000..bad31e8
--- /dev/null
+++ b/tests/language/generic_methods/generic_invocation_all_types_test.dart
@@ -0,0 +1,50 @@
+// 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.
+
+// This test verifies that `f<T, U>(0)` is properly parsed as a generic
+// invocation, for all type syntaxes that may appear as T and U.
+
+// Note: it doesn't really matter what we import here; it's just a handy library
+// that has declares a types so that we can refer a type via a prefix.
+import 'dart:async' as prefix;
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+  C([Object? x = absent, Object? y = absent])
+      : super('new C${SyntaxTracker.args(x, y)}');
+}
+
+/// Helper function to work around the fact that not all types can be expressed
+/// as type literals.
+Type typeOf<T>() => T;
+
+main() {
+  SyntaxTracker.known[C] = 'C';
+  SyntaxTracker.known[typeOf<C?>()] = 'C?';
+  SyntaxTracker.known[prefix.Zone] = 'prefix.Zone';
+  SyntaxTracker.known[typeOf<prefix.Zone?>()] = 'prefix.Zone?';
+  SyntaxTracker.known[dynamic] = 'dynamic';
+  SyntaxTracker.known[typeOf<List<C>>()] = 'List<C>';
+  SyntaxTracker.known[typeOf<List<C>?>()] = 'List<C>?';
+  SyntaxTracker.known[typeOf<void Function()>()] = 'void Function()';
+  SyntaxTracker.known[typeOf<void Function()?>()] = 'void Function()?';
+  checkSyntax(f(f<C, C>(0)), 'f(f<C, C>(0))');
+  checkSyntax(f(f<C?, C>(0)), 'f(f<C?, C>(0))');
+  checkSyntax(f(f<dynamic, C>(0)), 'f(f<dynamic, C>(0))');
+  checkSyntax(f(f<prefix.Zone, C>(0)), 'f(f<prefix.Zone, C>(0))');
+  checkSyntax(f(f<prefix.Zone?, C>(0)), 'f(f<prefix.Zone?, C>(0))');
+  checkSyntax(f(f<List<C>, C>(0)), 'f(f<List<C>, C>(0))');
+  checkSyntax(f(f<List<C>?, C>(0)), 'f(f<List<C>?, C>(0))');
+  checkSyntax(f(f<void Function(), C>(0)), 'f(f<void Function(), C>(0))');
+  checkSyntax(f(f<void Function()?, C>(0)), 'f(f<void Function()?, C>(0))');
+  checkSyntax(f(f<C, C?>(0)), 'f(f<C, C?>(0))');
+  checkSyntax(f(f<C, dynamic>(0)), 'f(f<C, dynamic>(0))');
+  checkSyntax(f(f<C, prefix.Zone>(0)), 'f(f<C, prefix.Zone>(0))');
+  checkSyntax(f(f<C, prefix.Zone?>(0)), 'f(f<C, prefix.Zone?>(0))');
+  checkSyntax(f(f<C, List<C>>(0)), 'f(f<C, List<C>>(0))');
+  checkSyntax(f(f<C, List<C>?>(0)), 'f(f<C, List<C>?>(0))');
+  checkSyntax(f(f<C, void Function()>(0)), 'f(f<C, void Function()>(0))');
+  checkSyntax(f(f<C, void Function()?>(0)), 'f(f<C, void Function()?>(0))');
+}
diff --git a/tests/language/generic_methods/non_generic_invocation_all_first_types_test.dart b/tests/language/generic_methods/non_generic_invocation_all_first_types_test.dart
new file mode 100644
index 0000000..a384927
--- /dev/null
+++ b/tests/language/generic_methods/non_generic_invocation_all_first_types_test.dart
@@ -0,0 +1,71 @@
+// 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.
+
+// This test verifies that `f<EXPR,b>(x)` is properly parsed as a pair of
+// expressions separated by a `,`, for all types of expressions that may appear
+// as EXPR that can't be parsed as types.
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+  C([Object? x = absent, Object? y = absent])
+      : super('new C${SyntaxTracker.args(x, y)}');
+
+  C.syntax(String s) : super(s);
+}
+
+class ThisTest extends C {
+  ThisTest() : super.syntax('this');
+
+  void test() {
+    checkSyntax(f(x < this, C > (x)), 'f((x < this), (C > x))');
+    // Note: SyntaxTracker can't see the parens around `this` in the line below
+    checkSyntax(f(x < (this), C > (x)), 'f((x < this), (C > x))');
+  }
+}
+
+main() {
+  SyntaxTracker.known[C] = 'C';
+  SyntaxTracker.known[#x] = '#x';
+  checkSyntax(
+      f(x < x.getter.getter, C > (x)), 'f((x < x.getter.getter), (C > x))');
+  checkSyntax(f(x < C(), C > (x)), 'f((x < new C()), (C > x))');
+  checkSyntax(f(x < new C(), C > (x)), 'f((x < new C()), (C > x))');
+  checkSyntax(f(x < f(), C > (x)), 'f((x < f()), (C > x))');
+  checkSyntax(f(x < x.method(), C > (x)), 'f((x < x.method()), (C > x))');
+  checkSyntax(f(x < x[0](), C > (x)), 'f((x < x[0]()), (C > x))');
+  checkSyntax(f(x < #x, C > (x)), 'f((x < #x), (C > x))');
+  checkSyntax(f(x < null, C > (x)), 'f((x < null), (C > x))');
+  checkSyntax(f(x < 0, C > (x)), 'f((x < 0), (C > x))');
+  checkSyntax(f(x < 0.5, C > (x)), 'f((x < 0.5), (C > x))');
+  checkSyntax(f(x < [], C > (x)), 'f((x < []), (C > x))');
+  checkSyntax(f(x < [0], C > (x)), 'f((x < [0]), (C > x))');
+  checkSyntax(f(x < {}, C > (x)), 'f((x < {}), (C > x))');
+  checkSyntax(f(x < {0}, C > (x)), 'f((x < {0}), (C > x))');
+  checkSyntax(f(x < {0: 0}, C > (x)), 'f((x < { 0: 0 }), (C > x))');
+  checkSyntax(f(x < true, C > (x)), 'f((x < true), (C > x))');
+  checkSyntax(f(x < "s", C > (x)), 'f((x < "s"), (C > x))');
+  checkSyntax(f(x < r"s", C > (x)), 'f((x < "s"), (C > x))');
+  checkSyntax(f(x < x[0], C > (x)), 'f((x < x[0]), (C > x))');
+  // Note: SyntaxTracker can't see the `!` in the line below
+  checkSyntax(f(x < x!, C > (x)), 'f((x < x), (C > x))');
+  // Note: SyntaxTracker can't see the parens around `x` in the line below
+  checkSyntax(f(x < (x), C > (x)), 'f((x < x), (C > x))');
+  checkSyntax(f(x < -x, C > (x)), 'f((x < (-x)), (C > x))');
+  checkSyntax(f(x < !true, C > (x)), 'f((x < false), (C > x))');
+  checkSyntax(f(x < !(true), C > (x)), 'f((x < false), (C > x))');
+  checkSyntax(f(x < ~x, C > (x)), 'f((x < (~x)), (C > x))');
+  checkSyntax(f(x < x * x, C > (x)), 'f((x < (x * x)), (C > x))');
+  checkSyntax(f(x < x / x, C > (x)), 'f((x < (x / x)), (C > x))');
+  checkSyntax(f(x < x ~/ x, C > (x)), 'f((x < (x ~/ x)), (C > x))');
+  checkSyntax(f(x < x % x, C > (x)), 'f((x < (x % x)), (C > x))');
+  checkSyntax(f(x < x + x, C > (x)), 'f((x < (x + x)), (C > x))');
+  checkSyntax(f(x < x - x, C > (x)), 'f((x < (x - x)), (C > x))');
+  checkSyntax(f(x < x << x, C > (x)), 'f((x < (x << x)), (C > x))');
+  checkSyntax(f(x < x >> x, C > (x)), 'f((x < (x >> x)), (C > x))');
+  checkSyntax(f(x < x & x, C > (x)), 'f((x < (x & x)), (C > x))');
+  checkSyntax(f(x < x ^ x, C > (x)), 'f((x < (x ^ x)), (C > x))');
+  checkSyntax(f(x < x | x, C > (x)), 'f((x < (x | x)), (C > x))');
+  ThisTest().test();
+}
diff --git a/tests/language/generic_methods/non_generic_invocation_all_second_types_test.dart b/tests/language/generic_methods/non_generic_invocation_all_second_types_test.dart
new file mode 100644
index 0000000..d59058d
--- /dev/null
+++ b/tests/language/generic_methods/non_generic_invocation_all_second_types_test.dart
@@ -0,0 +1,85 @@
+// 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.
+
+// This test verifies that `f<a,EXPR>(x)` is properly parsed as a pair of
+// expressions separated by a `,`, for all types of expressions that may appear
+// as EXPR that can't be parsed as types.
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+  C([Object? x = absent, Object? y = absent])
+      : super('new C${SyntaxTracker.args(x, y)}');
+
+  C.syntax(String s) : super(s);
+
+  Object? operator >(Object? other) =>
+      SyntaxTracker('(${syntax(this)} > ${syntax(other)})');
+}
+
+class ThisTest extends C {
+  ThisTest() : super.syntax('this');
+
+  void test() {
+    checkSyntax(f(x < C, this > (x)), 'f((x < C), (this > x))');
+    // Note: SyntaxTracker can't see the parens around `this` in the line below
+    checkSyntax(f(x < C, (this) > (x)), 'f((x < C), (this > x))');
+  }
+}
+
+class SuperTest extends C {
+  SuperTest() : super.syntax('super');
+
+  void test() {
+    checkSyntax(f(x < C, super > (x)), 'f((x < C), (super > x))');
+  }
+}
+
+main() {
+  const y = 123;
+  SyntaxTracker.known[C] = 'C';
+  SyntaxTracker.known[#x] = '#x';
+  SyntaxTracker.known[y] = 'y';
+  checkSyntax(
+      f(x < C, x.getter.getter > (x)), 'f((x < C), (x.getter.getter > x))');
+  checkSyntax(f(x < C, C() > (x)), 'f((x < C), (new C() > x))');
+  checkSyntax(f(x < C, new C() > (x)), 'f((x < C), (new C() > x))');
+  checkSyntax(f(x < C, f() > (x)), 'f((x < C), (f() > x))');
+  checkSyntax(f(x < C, x.method() > (x)), 'f((x < C), (x.method() > x))');
+  checkSyntax(f(x < C, x[0]() > (x)), 'f((x < C), (x[0]() > x))');
+  checkSyntax(f(x < C, #x > (x)), 'f((x < C), (#x > x))');
+  checkSyntax(f(x < C, null > (x)), 'f((x < C), (null > x))');
+  checkSyntax(f(x < C, 0 > (y)), 'f((x < C), false)');
+  checkSyntax(f(x < C, 0.5 > (y)), 'f((x < C), false)');
+  checkSyntax(f(x < C, [] > (x)), 'f((x < C), ([] > x))');
+  checkSyntax(f(x < C, [0] > (x)), 'f((x < C), ([0] > x))');
+  checkSyntax(f(x < C, {} > (x)), 'f((x < C), ({} > x))');
+  checkSyntax(f(x < C, {0} > (x)), 'f((x < C), ({0} > x))');
+  checkSyntax(f(x < C, {0: 0} > (x)), 'f((x < C), ({ 0: 0 } > x))');
+  checkSyntax(f(x < C, true > (x)), 'f((x < C), (true > x))');
+  checkSyntax(f(x < C, "s" > (x)), 'f((x < C), ("s" > x))');
+  checkSyntax(f(x < C, r"s" > (x)), 'f((x < C), ("s" > x))');
+  checkSyntax(f(x < C, x[0] > (x)), 'f((x < C), (x[0] > x))');
+  // Note: SyntaxTracker can't see the `!` in the line below
+  checkSyntax(f(x < C, x! > (x)), 'f((x < C), (x > x))');
+  // Note: SyntaxTracker can't see the parens around `x` in the line below
+  checkSyntax(f(x < C, (x) > (x)), 'f((x < C), (x > x))');
+  checkSyntax(f(x < C, -x > (x)), 'f((x < C), ((-x) > x))');
+  checkSyntax(f(x < C, !true > (x)), 'f((x < C), (false > x))');
+  checkSyntax(f(x < C, !(true) > (x)), 'f((x < C), (false > x))');
+  checkSyntax(f(x < C, ~x > (x)), 'f((x < C), ((~x) > x))');
+  checkSyntax(f(x < C, x * x > (x)), 'f((x < C), ((x * x) > x))');
+  checkSyntax(f(x < C, x / x > (x)), 'f((x < C), ((x / x) > x))');
+  checkSyntax(f(x < C, x ~/ x > (x)), 'f((x < C), ((x ~/ x) > x))');
+  checkSyntax(f(x < C, x % x > (x)), 'f((x < C), ((x % x) > x))');
+  checkSyntax(f(x < C, x + x > (x)), 'f((x < C), ((x + x) > x))');
+  checkSyntax(f(x < C, x - x > (x)), 'f((x < C), ((x - x) > x))');
+  checkSyntax(f(x < C, x << x > (x)), 'f((x < C), ((x << x) > x))');
+  checkSyntax(f(x < C, x >> x > (x)), 'f((x < C), ((x >> x) > x))');
+  checkSyntax(f(x < C, x & x > (x)), 'f((x < C), ((x & x) > x))');
+  checkSyntax(f(x < C, x ^ x > (x)), 'f((x < C), ((x ^ x) > x))');
+  checkSyntax(f(x < C, x | x > (x)), 'f((x < C), ((x | x) > x))');
+  ThisTest().test();
+  SuperTest().test();
+}
diff --git a/tests/language/string/overflow_test.dart b/tests/language/string/overflow_test.dart
index 8b01e19..f93428a 100644
--- a/tests/language/string/overflow_test.dart
+++ b/tests/language/string/overflow_test.dart
@@ -9,13 +9,15 @@
 
 main() {
   String a = "a";
-  for (; a.length < 256 * 1024 * 1024;) a = a + a;
 
   var exception_thrown = false;
   try {
-    var concat = "$a$a$a$a$a$a$a$a";
+    while (true) {
+      a = "$a$a";
+    }
   } on OutOfMemoryError catch (exc) {
     exception_thrown = true;
   }
   Expect.isTrue(exception_thrown);
+  Expect.isTrue(a.startsWith('aaaaa') && a.length > 1024);
 }
diff --git a/tests/language/syntax_helper.dart b/tests/language/syntax_helper.dart
index 7cf4147..586edc0 100644
--- a/tests/language/syntax_helper.dart
+++ b/tests/language/syntax_helper.dart
@@ -51,7 +51,7 @@
       '(${[x, y].where((v) => v is! Absent).join(', ')})';
 
   static String typeArgs(Type T, Type U) =>
-      T.toString() == 'dynamic' ? '' : '<${syntax(T)}, ${syntax(U)}>';
+      T == dynamic && U == dynamic ? '' : '<${syntax(T)}, ${syntax(U)}>';
 
   /// Simple objects with known syntactic representations.  Tests can add to
   /// this map.
diff --git a/tests/language_2/generic_methods/generic_invocation_all_types_test.dart b/tests/language_2/generic_methods/generic_invocation_all_types_test.dart
new file mode 100644
index 0000000..f37efda
--- /dev/null
+++ b/tests/language_2/generic_methods/generic_invocation_all_types_test.dart
@@ -0,0 +1,38 @@
+// 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.
+
+// This test verifies that `f<T, U>(0)` is properly parsed as a generic
+// invocation, for all type syntaxes that may appear as T and U.
+
+// Note: it doesn't really matter what we import here; it's just a handy library
+// that has declares a types so that we can refer a type via a prefix.
+import 'dart:async' as prefix;
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+  C([Object x = absent, Object y = absent])
+      : super('new C${SyntaxTracker.args(x, y)}');
+}
+
+/// Helper function to work around the fact that not all types can be expressed
+/// as type literals.
+Type typeOf<T>() => T;
+
+main() {
+  SyntaxTracker.known[C] = 'C';
+  SyntaxTracker.known[prefix.Zone] = 'prefix.Zone';
+  SyntaxTracker.known[dynamic] = 'dynamic';
+  SyntaxTracker.known[typeOf<List<C>>()] = 'List<C>';
+  SyntaxTracker.known[typeOf<void Function()>()] = 'void Function()';
+  checkSyntax(f(f<C, C>(0)), 'f(f<C, C>(0))');
+  checkSyntax(f(f<dynamic, C>(0)), 'f(f<dynamic, C>(0))');
+  checkSyntax(f(f<prefix.Zone, C>(0)), 'f(f<prefix.Zone, C>(0))');
+  checkSyntax(f(f<List<C>, C>(0)), 'f(f<List<C>, C>(0))');
+  checkSyntax(f(f<void Function(), C>(0)), 'f(f<void Function(), C>(0))');
+  checkSyntax(f(f<C, dynamic>(0)), 'f(f<C, dynamic>(0))');
+  checkSyntax(f(f<C, prefix.Zone>(0)), 'f(f<C, prefix.Zone>(0))');
+  checkSyntax(f(f<C, List<C>>(0)), 'f(f<C, List<C>>(0))');
+  checkSyntax(f(f<C, void Function()>(0)), 'f(f<C, void Function()>(0))');
+}
diff --git a/tests/language_2/generic_methods/non_generic_invocation_all_first_types_test.dart b/tests/language_2/generic_methods/non_generic_invocation_all_first_types_test.dart
new file mode 100644
index 0000000..59a76a8
--- /dev/null
+++ b/tests/language_2/generic_methods/non_generic_invocation_all_first_types_test.dart
@@ -0,0 +1,69 @@
+// 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.
+
+// This test verifies that `f<EXPR,b>(x)` is properly parsed as a pair of
+// expressions separated by a `,`, for all types of expressions that may appear
+// as EXPR that can't be parsed as types.
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+  C([Object x = absent, Object y = absent])
+      : super('new C${SyntaxTracker.args(x, y)}');
+
+  C.syntax(String s) : super(s);
+}
+
+class ThisTest extends C {
+  ThisTest() : super.syntax('this');
+
+  void test() {
+    checkSyntax(f(x < this, C > (x)), 'f((x < this), (C > x))');
+    // Note: SyntaxTracker can't see the parens around `this` in the line below
+    checkSyntax(f(x < (this), C > (x)), 'f((x < this), (C > x))');
+  }
+}
+
+main() {
+  SyntaxTracker.known[C] = 'C';
+  SyntaxTracker.known[#x] = '#x';
+  checkSyntax(
+      f(x < x.getter.getter, C > (x)), 'f((x < x.getter.getter), (C > x))');
+  checkSyntax(f(x < C(), C > (x)), 'f((x < new C()), (C > x))');
+  checkSyntax(f(x < new C(), C > (x)), 'f((x < new C()), (C > x))');
+  checkSyntax(f(x < f(), C > (x)), 'f((x < f()), (C > x))');
+  checkSyntax(f(x < x.method(), C > (x)), 'f((x < x.method()), (C > x))');
+  checkSyntax(f(x < x[0](), C > (x)), 'f((x < x[0]()), (C > x))');
+  checkSyntax(f(x < #x, C > (x)), 'f((x < #x), (C > x))');
+  checkSyntax(f(x < null, C > (x)), 'f((x < null), (C > x))');
+  checkSyntax(f(x < 0, C > (x)), 'f((x < 0), (C > x))');
+  checkSyntax(f(x < 0.5, C > (x)), 'f((x < 0.5), (C > x))');
+  checkSyntax(f(x < [], C > (x)), 'f((x < []), (C > x))');
+  checkSyntax(f(x < [0], C > (x)), 'f((x < [0]), (C > x))');
+  checkSyntax(f(x < {}, C > (x)), 'f((x < {}), (C > x))');
+  checkSyntax(f(x < {0}, C > (x)), 'f((x < {0}), (C > x))');
+  checkSyntax(f(x < {0: 0}, C > (x)), 'f((x < { 0: 0 }), (C > x))');
+  checkSyntax(f(x < true, C > (x)), 'f((x < true), (C > x))');
+  checkSyntax(f(x < "s", C > (x)), 'f((x < "s"), (C > x))');
+  checkSyntax(f(x < r"s", C > (x)), 'f((x < "s"), (C > x))');
+  checkSyntax(f(x < x[0], C > (x)), 'f((x < x[0]), (C > x))');
+  // Note: SyntaxTracker can't see the parens around `x` in the line below
+  checkSyntax(f(x < (x), C > (x)), 'f((x < x), (C > x))');
+  checkSyntax(f(x < -x, C > (x)), 'f((x < (-x)), (C > x))');
+  checkSyntax(f(x < !true, C > (x)), 'f((x < false), (C > x))');
+  checkSyntax(f(x < !(true), C > (x)), 'f((x < false), (C > x))');
+  checkSyntax(f(x < ~x, C > (x)), 'f((x < (~x)), (C > x))');
+  checkSyntax(f(x < x * x, C > (x)), 'f((x < (x * x)), (C > x))');
+  checkSyntax(f(x < x / x, C > (x)), 'f((x < (x / x)), (C > x))');
+  checkSyntax(f(x < x ~/ x, C > (x)), 'f((x < (x ~/ x)), (C > x))');
+  checkSyntax(f(x < x % x, C > (x)), 'f((x < (x % x)), (C > x))');
+  checkSyntax(f(x < x + x, C > (x)), 'f((x < (x + x)), (C > x))');
+  checkSyntax(f(x < x - x, C > (x)), 'f((x < (x - x)), (C > x))');
+  checkSyntax(f(x < x << x, C > (x)), 'f((x < (x << x)), (C > x))');
+  checkSyntax(f(x < x >> x, C > (x)), 'f((x < (x >> x)), (C > x))');
+  checkSyntax(f(x < x & x, C > (x)), 'f((x < (x & x)), (C > x))');
+  checkSyntax(f(x < x ^ x, C > (x)), 'f((x < (x ^ x)), (C > x))');
+  checkSyntax(f(x < x | x, C > (x)), 'f((x < (x | x)), (C > x))');
+  ThisTest().test();
+}
diff --git a/tests/language_2/generic_methods/non_generic_invocation_all_second_types_test.dart b/tests/language_2/generic_methods/non_generic_invocation_all_second_types_test.dart
new file mode 100644
index 0000000..2036af2
--- /dev/null
+++ b/tests/language_2/generic_methods/non_generic_invocation_all_second_types_test.dart
@@ -0,0 +1,83 @@
+// 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.
+
+// This test verifies that `f<a,EXPR>(x)` is properly parsed as a pair of
+// expressions separated by a `,`, for all types of expressions that may appear
+// as EXPR that can't be parsed as types.
+
+import '../syntax_helper.dart';
+
+class C extends SyntaxTracker {
+  C([Object x = absent, Object y = absent])
+      : super('new C${SyntaxTracker.args(x, y)}');
+
+  C.syntax(String s) : super(s);
+
+  Object operator >(Object other) =>
+      SyntaxTracker('(${syntax(this)} > ${syntax(other)})');
+}
+
+class ThisTest extends C {
+  ThisTest() : super.syntax('this');
+
+  void test() {
+    checkSyntax(f(x < C, this > (x)), 'f((x < C), (this > x))');
+    // Note: SyntaxTracker can't see the parens around `this` in the line below
+    checkSyntax(f(x < C, (this) > (x)), 'f((x < C), (this > x))');
+  }
+}
+
+class SuperTest extends C {
+  SuperTest() : super.syntax('super');
+
+  void test() {
+    checkSyntax(f(x < C, super > (x)), 'f((x < C), (super > x))');
+  }
+}
+
+main() {
+  const y = 123;
+  SyntaxTracker.known[C] = 'C';
+  SyntaxTracker.known[#x] = '#x';
+  SyntaxTracker.known[y] = 'y';
+  checkSyntax(
+      f(x < C, x.getter.getter > (x)), 'f((x < C), (x.getter.getter > x))');
+  checkSyntax(f(x < C, C() > (x)), 'f((x < C), (new C() > x))');
+  checkSyntax(f(x < C, new C() > (x)), 'f((x < C), (new C() > x))');
+  checkSyntax(f(x < C, f() > (x)), 'f((x < C), (f() > x))');
+  checkSyntax(f(x < C, x.method() > (x)), 'f((x < C), (x.method() > x))');
+  checkSyntax(f(x < C, x[0]() > (x)), 'f((x < C), (x[0]() > x))');
+  checkSyntax(f(x < C, #x > (x)), 'f((x < C), (#x > x))');
+  checkSyntax(f(x < C, null > (x)), 'f((x < C), (null > x))');
+  checkSyntax(f(x < C, 0 > (y)), 'f((x < C), false)');
+  checkSyntax(f(x < C, 0.5 > (y)), 'f((x < C), false)');
+  checkSyntax(f(x < C, [] > (x)), 'f((x < C), ([] > x))');
+  checkSyntax(f(x < C, [0] > (x)), 'f((x < C), ([0] > x))');
+  checkSyntax(f(x < C, {} > (x)), 'f((x < C), ({} > x))');
+  checkSyntax(f(x < C, {0} > (x)), 'f((x < C), ({0} > x))');
+  checkSyntax(f(x < C, {0: 0} > (x)), 'f((x < C), ({ 0: 0 } > x))');
+  checkSyntax(f(x < C, true > (x)), 'f((x < C), (true > x))');
+  checkSyntax(f(x < C, "s" > (x)), 'f((x < C), ("s" > x))');
+  checkSyntax(f(x < C, r"s" > (x)), 'f((x < C), ("s" > x))');
+  checkSyntax(f(x < C, x[0] > (x)), 'f((x < C), (x[0] > x))');
+  // Note: SyntaxTracker can't see the parens around `x` in the line below
+  checkSyntax(f(x < C, (x) > (x)), 'f((x < C), (x > x))');
+  checkSyntax(f(x < C, -x > (x)), 'f((x < C), ((-x) > x))');
+  checkSyntax(f(x < C, !true > (x)), 'f((x < C), (false > x))');
+  checkSyntax(f(x < C, !(true) > (x)), 'f((x < C), (false > x))');
+  checkSyntax(f(x < C, ~x > (x)), 'f((x < C), ((~x) > x))');
+  checkSyntax(f(x < C, x * x > (x)), 'f((x < C), ((x * x) > x))');
+  checkSyntax(f(x < C, x / x > (x)), 'f((x < C), ((x / x) > x))');
+  checkSyntax(f(x < C, x ~/ x > (x)), 'f((x < C), ((x ~/ x) > x))');
+  checkSyntax(f(x < C, x % x > (x)), 'f((x < C), ((x % x) > x))');
+  checkSyntax(f(x < C, x + x > (x)), 'f((x < C), ((x + x) > x))');
+  checkSyntax(f(x < C, x - x > (x)), 'f((x < C), ((x - x) > x))');
+  checkSyntax(f(x < C, x << x > (x)), 'f((x < C), ((x << x) > x))');
+  checkSyntax(f(x < C, x >> x > (x)), 'f((x < C), ((x >> x) > x))');
+  checkSyntax(f(x < C, x & x > (x)), 'f((x < C), ((x & x) > x))');
+  checkSyntax(f(x < C, x ^ x > (x)), 'f((x < C), ((x ^ x) > x))');
+  checkSyntax(f(x < C, x | x > (x)), 'f((x < C), ((x | x) > x))');
+  ThisTest().test();
+  SuperTest().test();
+}
diff --git a/tests/language_2/string/overflow_test.dart b/tests/language_2/string/overflow_test.dart
index ad58981..ad4a332 100644
--- a/tests/language_2/string/overflow_test.dart
+++ b/tests/language_2/string/overflow_test.dart
@@ -11,13 +11,15 @@
 
 main() {
   String a = "a";
-  for (; a.length < 256 * 1024 * 1024;) a = a + a;
 
   var exception_thrown = false;
   try {
-    var concat = "$a$a$a$a$a$a$a$a";
+    while (true) {
+      a = "$a$a";
+    }
   } on OutOfMemoryError catch (exc) {
     exception_thrown = true;
   }
   Expect.isTrue(exception_thrown);
+  Expect.isTrue(a.startsWith('aaaaa') && a.length > 1024);
 }
diff --git a/tests/language_2/syntax_helper.dart b/tests/language_2/syntax_helper.dart
index 1c65097..bf6f995 100644
--- a/tests/language_2/syntax_helper.dart
+++ b/tests/language_2/syntax_helper.dart
@@ -51,7 +51,7 @@
       '(${[x, y].where((v) => v is! Absent).join(', ')})';
 
   static String typeArgs(Type T, Type U) =>
-      T.toString() == 'dynamic' ? '' : '<${syntax(T)}, ${syntax(U)}>';
+      T == dynamic && U == dynamic ? '' : '<${syntax(T)}, ${syntax(U)}>';
 
   /// Simple objects with known syntactic representations.  Tests can add to
   /// this map.
diff --git a/tools/VERSION b/tools/VERSION
index a47d6f5..83dfa8e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 124
+PRERELEASE 125
 PRERELEASE_PATCH 0
\ No newline at end of file