[cfe] Implement TypedefTearOffConstant.== using assumptions

Change-Id: Id329259ce95ceaa8c4a7418de487c8595f5ea7e9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/205522
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 4a2284a..a8a2a04 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -13025,15 +13025,19 @@
     if (other.tearOffConstant != tearOffConstant) return false;
     if (other.parameters.length != parameters.length) return false;
     if (parameters.isNotEmpty) {
-      Map<TypeParameter, DartType> substitutionMap =
-          <TypeParameter, DartType>{};
-      for (int i = 0; i < parameters.length; ++i) {
-        substitutionMap[parameters[i]] = new TypeParameterType.forAlphaRenaming(
-            other.parameters[i], parameters[i]);
+      Assumptions assumptions = new Assumptions();
+      for (int index = 0; index < parameters.length; index++) {
+        assumptions.assume(parameters[index], other.parameters[index]);
       }
-      Substitution substitution = Substitution.fromMap(substitutionMap);
-      for (int i = 0; i < parameters.length; ++i) {
-        if (types[i] != substitution.substituteType(other.types[i])) {
+      for (int index = 0; index < parameters.length; index++) {
+        if (!parameters[index]
+            .bound
+            .equals(other.parameters[index].bound, assumptions)) {
+          return false;
+        }
+      }
+      for (int i = 0; i < types.length; ++i) {
+        if (!types[i].equals(other.types[i], assumptions)) {
           return false;
         }
       }
diff --git a/pkg/kernel/test/constant_equals_test.dart b/pkg/kernel/test/constant_equals_test.dart
new file mode 100644
index 0000000..689ae71
--- /dev/null
+++ b/pkg/kernel/test/constant_equals_test.dart
@@ -0,0 +1,165 @@
+// 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.
+
+import 'package:kernel/ast.dart';
+
+testEquals(Constant a, Constant b) {
+  if (a != b) {
+    throw 'Expected $a and $b to be equal.';
+  }
+}
+
+testNotEquals(Constant a, Constant b) {
+  if (a == b) {
+    throw 'Expected $a and $b to be not equal.';
+  }
+}
+
+main() {
+  Uri uri = Uri.parse('test:uri');
+  Procedure procedure1 = new Procedure(
+      new Name('foo'), ProcedureKind.Method, new FunctionNode(null),
+      fileUri: uri, isStatic: true);
+  Procedure procedure2 = new Procedure(
+      new Name('foo'), ProcedureKind.Method, new FunctionNode(null),
+      fileUri: uri, isStatic: true);
+  Procedure procedure3 = new Procedure(
+      new Name('foo'),
+      ProcedureKind.Method,
+      new FunctionNode(null, typeParameters: [
+        new TypeParameter('X', const DynamicType(), const DynamicType())
+      ]),
+      fileUri: uri,
+      isStatic: true);
+
+  TearOffConstant tearOffConstant1a = new StaticTearOffConstant(procedure1);
+  TearOffConstant tearOffConstant1b = new StaticTearOffConstant(procedure1);
+  TearOffConstant tearOffConstant2 = new StaticTearOffConstant(procedure2);
+  TearOffConstant tearOffConstant3 = new StaticTearOffConstant(procedure3);
+
+  // foo() {}
+  // const a = foo;
+  // const b = foo;
+  // a == b
+  testEquals(tearOffConstant1a, tearOffConstant1b);
+
+  // foo() {} // from lib1;
+  // foo() {} // from lib2;
+  // lib1.foo != lib2.foo
+  testNotEquals(tearOffConstant1a, tearOffConstant2);
+
+  // foo() {}
+  // typedef F = foo;
+  // typedef G = foo;
+  // F == G
+  testEquals(new TypedefTearOffConstant([], tearOffConstant1a, []),
+      new TypedefTearOffConstant([], tearOffConstant1b, []));
+
+  // foo() {} // from lib1;
+  // foo() {} // from lib2;
+  // typedef F = lib1.foo;
+  // typedef G = lib2.foo;
+  // F != G
+  testNotEquals(new TypedefTearOffConstant([], tearOffConstant1a, []),
+      new TypedefTearOffConstant([], tearOffConstant2, []));
+
+  // foo() {}
+  // typedef F<T> = foo;
+  // typedef G<S> = foo;
+  // F == G
+  testEquals(
+      new TypedefTearOffConstant(
+          [new TypeParameter('T', const DynamicType(), const DynamicType())],
+          tearOffConstant1a,
+          []),
+      new TypedefTearOffConstant(
+          [new TypeParameter('S', const DynamicType(), const DynamicType())],
+          tearOffConstant1b,
+          []));
+
+  // foo() {}
+  // typedef F<T1, T2> = foo;
+  // typedef G<S> = foo;
+  // F != G
+  testNotEquals(
+      new TypedefTearOffConstant(
+          [
+            new TypeParameter('T1', const DynamicType(), const DynamicType()),
+            new TypeParameter('T2', const DynamicType(), const DynamicType())
+          ],
+          tearOffConstant1a,
+          []),
+      new TypedefTearOffConstant(
+          [new TypeParameter('S', const DynamicType(), const DynamicType())],
+          tearOffConstant1b,
+          []));
+
+  // foo() {}
+  // typedef F<T extends void> = foo;
+  // typedef G<S> = foo;
+  // F != G
+  testNotEquals(
+      new TypedefTearOffConstant(
+          [new TypeParameter('T', const VoidType(), const DynamicType())],
+          tearOffConstant1a,
+          []),
+      new TypedefTearOffConstant(
+          [new TypeParameter('S', const DynamicType(), const DynamicType())],
+          tearOffConstant1b,
+          []));
+  {
+    TypeParameter typeParameter1 =
+        new TypeParameter('T', const DynamicType(), const DynamicType());
+    TypeParameter typeParameter2 =
+        new TypeParameter('S', const DynamicType(), const DynamicType());
+
+    // foo<X>() {}
+    // typedef F<T> = foo<T>;
+    // typedef G<S> = foo<S>;
+    // F == G
+    testEquals(
+        new TypedefTearOffConstant([typeParameter1], tearOffConstant3,
+            [new TypeParameterType(typeParameter1, Nullability.nullable)]),
+        new TypedefTearOffConstant([typeParameter2], tearOffConstant3,
+            [new TypeParameterType(typeParameter2, Nullability.nullable)]));
+  }
+  {
+    TypeParameter typeParameter1a =
+        new TypeParameter('T1', const DynamicType(), const DynamicType());
+    TypeParameter typeParameter1b =
+        new TypeParameter('T2', const DynamicType(), const DynamicType());
+    TypeParameter typeParameter2a =
+        new TypeParameter('S1', const DynamicType(), const DynamicType());
+    TypeParameter typeParameter2b =
+        new TypeParameter('S2', const DynamicType(), const DynamicType());
+
+    // foo<X>() {}
+    // typedef F<T1, T2> = foo<T1>;
+    // typedef G<S1, S2> = foo<S1>;
+    // F == G
+    testEquals(
+        new TypedefTearOffConstant(
+            [typeParameter1a, typeParameter1b],
+            tearOffConstant3,
+            [new TypeParameterType(typeParameter1a, Nullability.nullable)]),
+        new TypedefTearOffConstant(
+            [typeParameter2a, typeParameter2b],
+            tearOffConstant3,
+            [new TypeParameterType(typeParameter2a, Nullability.nullable)]));
+
+    // foo<X>() {}
+    // typedef F<T1, T2> = foo<T1>;
+    // typedef G<S1, S2> = foo<S2>;
+    // F != G
+    testNotEquals(
+        new TypedefTearOffConstant(
+            [typeParameter1a, typeParameter1b],
+            tearOffConstant3,
+            [new TypeParameterType(typeParameter1a, Nullability.nullable)]),
+        new TypedefTearOffConstant(
+            [typeParameter2a, typeParameter2b],
+            tearOffConstant3,
+            [new TypeParameterType(typeParameter2b, Nullability.nullable)]));
+  }
+}