[cfe] Remove a side effect from computeConstCanonicalType

Closes #44857.

Bug: https://github.com/dart-lang/sdk/issues/44857
Change-Id: I1082fded9a0b06cbcf30c484975b0cf464db75a4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182781
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/testcases/nnbd/issue44857.dart b/pkg/front_end/testcases/nnbd/issue44857.dart
new file mode 100644
index 0000000..96ac4d5
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44857.dart
@@ -0,0 +1,11 @@
+// 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.
+
+typedef F = Never Function(void Function<T extends Never>(T));
+
+void main() {
+  const c = F;
+  print("Are $c, $F identical?");
+  print(identical(c, F));
+}
diff --git a/pkg/front_end/testcases/nnbd/issue44857.dart.outline.expect b/pkg/front_end/testcases/nnbd/issue44857.dart.outline.expect
new file mode 100644
index 0000000..82e5613
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44857.dart.outline.expect
@@ -0,0 +1,6 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+
+typedef F = (<T extends Never = dynamic>(T) → void) → Never;
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue44857.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue44857.dart.strong.expect
new file mode 100644
index 0000000..4a6c15d
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44857.dart.strong.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef F = (<T extends Never = dynamic>(T) → void) → Never;
+static method main() → void {
+  core::print("Are ${#C1}, ${#C1} identical?");
+  core::print(core::identical(#C1, #C1));
+}
+
+constants  {
+  #C1 = TypeLiteralConstant((<T extends Never = dynamic>(Never) → void) → Never)
+}
diff --git a/pkg/front_end/testcases/nnbd/issue44857.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue44857.dart.strong.transformed.expect
new file mode 100644
index 0000000..3ddde37
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44857.dart.strong.transformed.expect
@@ -0,0 +1,17 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef F = (<T extends Never = dynamic>(T) → void) → Never;
+static method main() → void {
+  core::print("Are ${#C1}, ${#C1} identical?");
+  core::print(core::identical(#C1, #C1));
+}
+
+constants  {
+  #C1 = TypeLiteralConstant((<T extends Never = dynamic>(Never) → void) → Never)
+}
+
+Extra constant evaluation status:
+Evaluated: StaticInvocation @ org-dartlang-testcase:///issue44857.dart:10:9 -> BoolConstant(true)
+Extra constant evaluation: evaluated: 4, effectively constant: 1
diff --git a/pkg/front_end/testcases/nnbd/issue44857.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue44857.dart.textual_outline.expect
new file mode 100644
index 0000000..cb283ab
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44857.dart.textual_outline.expect
@@ -0,0 +1,2 @@
+typedef F = Never Function(void Function<T extends Never>(T));
+void main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue44857.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue44857.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..cb283ab
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44857.dart.textual_outline_modelled.expect
@@ -0,0 +1,2 @@
+typedef F = Never Function(void Function<T extends Never>(T));
+void main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue44857.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue44857.dart.weak.expect
new file mode 100644
index 0000000..ad063b4
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44857.dart.weak.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef F = (<T extends Never = dynamic>(T) → void) → Never;
+static method main() → void {
+  core::print("Are ${#C1}, ${#C1} identical?");
+  core::print(core::identical(#C1, #C1));
+}
+
+constants  {
+  #C1 = TypeLiteralConstant((<T extends Never* = Never*>(Never*) →* void) →* Never*)
+}
diff --git a/pkg/front_end/testcases/nnbd/issue44857.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue44857.dart.weak.transformed.expect
new file mode 100644
index 0000000..dd62920
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44857.dart.weak.transformed.expect
@@ -0,0 +1,17 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef F = (<T extends Never = dynamic>(T) → void) → Never;
+static method main() → void {
+  core::print("Are ${#C1}, ${#C1} identical?");
+  core::print(core::identical(#C1, #C1));
+}
+
+constants  {
+  #C1 = TypeLiteralConstant((<T extends Never* = Never*>(Never*) →* void) →* Never*)
+}
+
+Extra constant evaluation status:
+Evaluated: StaticInvocation @ org-dartlang-testcase:///issue44857.dart:10:9 -> BoolConstant(true)
+Extra constant evaluation: evaluated: 4, effectively constant: 1
diff --git a/pkg/kernel/lib/src/const_canonical_type.dart b/pkg/kernel/lib/src/const_canonical_type.dart
index 9a1dd42..186c980 100644
--- a/pkg/kernel/lib/src/const_canonical_type.dart
+++ b/pkg/kernel/lib/src/const_canonical_type.dart
@@ -112,14 +112,15 @@
     assert(type.declaredNullability == Nullability.nonNullable);
 
     List<TypeParameter> canonicalizedTypeParameters;
-    Map<TypeParameter, DartType> substitutionMap;
+    Substitution substitution;
     if (type.typeParameters.isEmpty) {
       canonicalizedTypeParameters = const <TypeParameter>[];
-      substitutionMap = const <TypeParameter, DartType>{};
+      substitution = null;
     } else {
-      substitutionMap = <TypeParameter, DartType>{};
-      canonicalizedTypeParameters =
-          new List<TypeParameter>.of(type.typeParameters, growable: false);
+      FreshTypeParameters freshTypeParameters =
+          getFreshTypeParameters(type.typeParameters);
+      substitution = freshTypeParameters.substitution;
+      canonicalizedTypeParameters = freshTypeParameters.freshTypeParameters;
       for (TypeParameter parameter in canonicalizedTypeParameters) {
         parameter.bound = computeConstCanonicalType(parameter.bound, coreTypes,
             isNonNullableByDefault: isNonNullableByDefault);
@@ -130,11 +131,6 @@
       for (int i = 0; i < canonicalizedTypeParameters.length; ++i) {
         canonicalizedTypeParameters[i].defaultType = defaultTypes[i];
       }
-      for (int i = 0; i < canonicalizedTypeParameters.length; ++i) {
-        substitutionMap[canonicalizedTypeParameters[i]] =
-            new TypeParameterType.forAlphaRenaming(
-                type.typeParameters[i], canonicalizedTypeParameters[i]);
-      }
     }
 
     List<DartType> canonicalizedPositionalParameters;
@@ -147,8 +143,8 @@
         DartType canonicalized = computeConstCanonicalType(
             canonicalizedPositionalParameters[i], coreTypes,
             isNonNullableByDefault: isNonNullableByDefault);
-        if (substitutionMap.isNotEmpty) {
-          canonicalized = substitute(canonicalized, substitutionMap);
+        if (substitution != null) {
+          canonicalized = substitution.substituteType(canonicalized);
         }
         canonicalizedPositionalParameters[i] = canonicalized;
       }
@@ -164,8 +160,8 @@
         DartType canonicalized = computeConstCanonicalType(
             canonicalizedNamedParameters[i].type, coreTypes,
             isNonNullableByDefault: isNonNullableByDefault);
-        if (substitutionMap.isNotEmpty) {
-          canonicalized = substitute(canonicalized, substitutionMap);
+        if (substitution != null) {
+          canonicalized = substitution.substituteType(canonicalized);
         }
         canonicalizedNamedParameters[i] = new NamedType(
             canonicalizedNamedParameters[i].name, canonicalized,
@@ -176,9 +172,9 @@
     DartType canonicalizedReturnType = computeConstCanonicalType(
         type.returnType, coreTypes,
         isNonNullableByDefault: isNonNullableByDefault);
-    if (substitutionMap.isNotEmpty) {
+    if (substitution != null) {
       canonicalizedReturnType =
-          substitute(canonicalizedReturnType, substitutionMap);
+          substitution.substituteType(canonicalizedReturnType);
     }
 
     // Canonicalize typedef type, just in case.