Version 2.15.0-168.0.dev

Merge commit '8c41cc95378475755d530f10011a434f536d4141' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart b/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart
index c2abc80..6f0eed7 100644
--- a/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart
+++ b/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart
@@ -244,6 +244,10 @@
     if (element is MethodElement && element.isStatic) {
       return;
     }
+    if (element is TypeParameterElement &&
+        featureSet.isEnabled(Feature.constructor_tearoffs)) {
+      return;
+    }
     nodes.add(node);
   }
 
diff --git a/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart b/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart
index e9e0a9a..0d881c1 100644
--- a/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart
@@ -13,7 +13,6 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(IsConstantTypeExpressionTest);
     defineReflectiveTests(PotentiallyConstantTest);
-    defineReflectiveTests(PotentiallyConstantWithoutNullSafetyTest);
   });
 }
 
@@ -216,6 +215,18 @@
 ''', () => _xInitializer());
   }
 
+  test_asExpression_typeParameter_29() async {
+    await _assertNotConst(r'''
+// @dart = 2.9
+const a = 0;
+class A<T> {
+  m() {
+    var x = a as T;
+  }
+}
+''', () => _xInitializer(), () => [findNode.namedType('T;')]);
+  }
+
   test_asExpression_typeParameter_nested() async {
     await _assertConst(r'''
 const a = 0;
@@ -518,6 +529,18 @@
 ''', () => _xInitializer());
   }
 
+  test_isExpression_typeParameter_29() async {
+    await _assertNotConst(r'''
+// @dart = 2.9
+const a = 0;
+class A<T> {
+  m() {
+    var x = a is T;
+  }
+}
+''', () => _xInitializer(), () => [findNode.namedType('T;')]);
+  }
+
   test_isExpression_typeParameter_nested() async {
     await _assertConst(r'''
 const a = 0;
@@ -1097,6 +1120,25 @@
 ''', () => _xInitializer());
   }
 
+  test_simpleIdentifier_typeParameter_class() async {
+    await _assertConst(r'''
+class A<T> {
+  final Object f;
+  A() : f = T;
+}
+''', () => findNode.simple('T;'));
+  }
+
+  test_simpleIdentifier_typeParameter_class_214() async {
+    await _assertNotConst(r'''
+// @dart = 2.14
+class A<T> {
+  final Object f;
+  A() : f = T;
+}
+''', () => findNode.simple('T;'), () => [findNode.simple('T;')]);
+  }
+
   test_spreadElement() async {
     await _assertConst(r'''
 const a = [0, 1, 2];
@@ -1162,46 +1204,3 @@
     return findNode.variableDeclaration('x = ').initializer!;
   }
 }
-
-@reflectiveTest
-class PotentiallyConstantWithoutNullSafetyTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
-  test_asExpression_typeParameter() async {
-    await _assertNotConst(r'''
-const a = 0;
-class A<T> {
-  m() {
-    var x = a as T;
-  }
-}
-''', () => _xInitializer(), () => [findNode.namedType('T;')]);
-  }
-
-  test_isExpression_typeParameter() async {
-    await _assertNotConst(r'''
-const a = 0;
-class A<T> {
-  m() {
-    var x = a is T;
-  }
-}
-''', () => _xInitializer(), () => [findNode.namedType('T;')]);
-  }
-
-  _assertNotConst(String code, AstNode Function() getNode,
-      List<AstNode> Function() getNotConstList) async {
-    await resolveTestCode(code);
-    var node = getNode();
-    var notConstList = getNotPotentiallyConstants(
-      node,
-      featureSet: featureSet,
-    );
-
-    var expectedNotConst = getNotConstList();
-    expect(notConstList, unorderedEquals(expectedNotConst));
-  }
-
-  Expression _xInitializer() {
-    return findNode.variableDeclaration('x = ').initializer!;
-  }
-}
diff --git a/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart b/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart
new file mode 100644
index 0000000..9baf000
--- /dev/null
+++ b/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart
@@ -0,0 +1,18 @@
+// 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 we emit EqualityCompare rather than StrictCompare+BoxInt64
+// when comparing non-nullable integer to a Smi.
+
+// MatchIL[AOT]=factorial
+// __ GraphEntry
+// __ FunctionEntry
+// __ CheckStackOverflow
+// __ Branch(EqualityCompare)
+@pragma('vm:never-inline')
+int factorial(int value) => value == 1 ? value : value * factorial(value - 1);
+
+void main() {
+  print(factorial(4));
+}
diff --git a/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart b/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart
new file mode 100644
index 0000000..9baf000
--- /dev/null
+++ b/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart
@@ -0,0 +1,18 @@
+// 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 we emit EqualityCompare rather than StrictCompare+BoxInt64
+// when comparing non-nullable integer to a Smi.
+
+// MatchIL[AOT]=factorial
+// __ GraphEntry
+// __ FunctionEntry
+// __ CheckStackOverflow
+// __ Branch(EqualityCompare)
+@pragma('vm:never-inline')
+int factorial(int value) => value == 1 ? value : value * factorial(value - 1);
+
+void main() {
+  print(factorial(4));
+}
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc
index 6fa1d01..a013cb2 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.cc
+++ b/runtime/vm/compiler/aot/aot_call_specializer.cc
@@ -465,23 +465,35 @@
 
     switch (op_kind) {
       case Token::kEQ:
-      case Token::kNE:
-        if (left_type->IsNull() || left_type->IsNullableSmi() ||
-            right_type->IsNull() || right_type->IsNullableSmi()) {
+      case Token::kNE: {
+        // If both arguments are nullable Smi or one of the arguments is
+        // a null or Smi and the other argument is nullable then emit
+        // StrictCompare (all arguments are going to be boxed anyway).
+        // Otherwise prefer EqualityCompare to avoid redundant boxing.
+        const bool left_is_null_or_smi =
+            left_type->IsNull() || left_type->IsNullableSmi();
+        const bool right_is_null_or_smi =
+            right_type->IsNull() || right_type->IsNullableSmi();
+        const bool both_are_nullable_smis =
+            left_type->IsNullableSmi() && right_type->IsNullableSmi();
+        const bool either_can_be_null =
+            left_type->is_nullable() || right_type->is_nullable();
+        if (both_are_nullable_smis ||
+            ((left_is_null_or_smi || right_is_null_or_smi) &&
+             either_can_be_null)) {
           replacement = new (Z) StrictCompareInstr(
               instr->source(),
               (op_kind == Token::kEQ) ? Token::kEQ_STRICT : Token::kNE_STRICT,
               left_value->CopyWithType(Z), right_value->CopyWithType(Z),
               /*needs_number_check=*/false, DeoptId::kNone);
         } else {
-          const bool null_aware =
-              left_type->is_nullable() || right_type->is_nullable();
           replacement = new (Z) EqualityCompareInstr(
               instr->source(), op_kind, left_value->CopyWithType(Z),
               right_value->CopyWithType(Z), kMintCid, DeoptId::kNone,
-              null_aware, Instruction::kNotSpeculative);
+              /*null_aware=*/either_can_be_null, Instruction::kNotSpeculative);
         }
         break;
+      }
       case Token::kLT:
       case Token::kLTE:
       case Token::kGT:
diff --git a/tools/VERSION b/tools/VERSION
index d113f0b..0fdd90e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 167
+PRERELEASE 168
 PRERELEASE_PATCH 0
\ No newline at end of file