[cfe] Eagerly canonicalize instantiations

With the constructor-tearoff feature, effectively constant
instantiations are canonicalized.

Change-Id: I4e83fef47e5a404da19667ccfb1d13a3b00d632f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/204960
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 1c7f034..737f649 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -86,6 +86,7 @@
     required bool desugarSets,
     required bool enableTripleShift,
     required bool enableConstFunctions,
+    required bool enableConstructorTearOff,
     required bool errorOnUnevaluatedConstant,
     CoreTypes? coreTypes,
     ClassHierarchy? hierarchy}) {
@@ -99,6 +100,8 @@
   assert(enableConstFunctions != null);
   // ignore: unnecessary_null_comparison
   assert(errorOnUnevaluatedConstant != null);
+  // ignore: unnecessary_null_comparison
+  assert(enableConstructorTearOff != null);
   coreTypes ??= new CoreTypes(component);
   hierarchy ??= new ClassHierarchy(component, coreTypes);
 
@@ -110,7 +113,8 @@
       enableTripleShift: enableTripleShift,
       enableConstFunctions: enableConstFunctions,
       errorOnUnevaluatedConstant: errorOnUnevaluatedConstant,
-      evaluateAnnotations: evaluateAnnotations);
+      evaluateAnnotations: evaluateAnnotations,
+      enableConstructorTearOff: enableConstructorTearOff);
   return component;
 }
 
@@ -124,7 +128,8 @@
     {required bool evaluateAnnotations,
     required bool enableTripleShift,
     required bool enableConstFunctions,
-    required bool errorOnUnevaluatedConstant}) {
+    required bool errorOnUnevaluatedConstant,
+    required bool enableConstructorTearOff}) {
   // ignore: unnecessary_null_comparison
   assert(evaluateAnnotations != null);
   // ignore: unnecessary_null_comparison
@@ -133,12 +138,15 @@
   assert(enableConstFunctions != null);
   // ignore: unnecessary_null_comparison
   assert(errorOnUnevaluatedConstant != null);
+  // ignore: unnecessary_null_comparison
+  assert(enableConstructorTearOff != null);
   final ConstantsTransformer constantsTransformer = new ConstantsTransformer(
       backend,
       environmentDefines,
       evaluateAnnotations,
       enableTripleShift,
       enableConstFunctions,
+      enableConstructorTearOff,
       errorOnUnevaluatedConstant,
       typeEnvironment,
       errorReporter,
@@ -156,10 +164,11 @@
     TypeEnvironment typeEnvironment,
     ErrorReporter errorReporter,
     EvaluationMode evaluationMode,
-    {bool evaluateAnnotations: true,
-    bool enableTripleShift: false,
-    bool enableConstFunctions: false,
-    bool errorOnUnevaluatedConstant: false}) {
+    {required bool evaluateAnnotations,
+    required bool enableTripleShift,
+    required bool enableConstFunctions,
+    required bool enableConstructorTearOff,
+    required bool errorOnUnevaluatedConstant}) {
   // ignore: unnecessary_null_comparison
   assert(evaluateAnnotations != null);
   // ignore: unnecessary_null_comparison
@@ -168,12 +177,15 @@
   assert(enableConstFunctions != null);
   // ignore: unnecessary_null_comparison
   assert(errorOnUnevaluatedConstant != null);
+  // ignore: unnecessary_null_comparison
+  assert(enableConstructorTearOff != null);
   final ConstantsTransformer constantsTransformer = new ConstantsTransformer(
       backend,
       environmentDefines,
       evaluateAnnotations,
       enableTripleShift,
       enableConstFunctions,
+      enableConstructorTearOff,
       errorOnUnevaluatedConstant,
       typeEnvironment,
       errorReporter,
@@ -360,6 +372,7 @@
   final bool evaluateAnnotations;
   final bool enableTripleShift;
   final bool enableConstFunctions;
+  final bool enableConstructorTearOff;
   final bool errorOnUnevaluatedConstant;
 
   ConstantsTransformer(
@@ -368,6 +381,7 @@
       this.evaluateAnnotations,
       this.enableTripleShift,
       this.enableConstFunctions,
+      this.enableConstructorTearOff,
       this.errorOnUnevaluatedConstant,
       this.typeEnvironment,
       ErrorReporter errorReporter,
@@ -696,6 +710,18 @@
   }
 
   @override
+  TreeNode visitInstantiation(Instantiation node, TreeNode? removalSentinel) {
+    Instantiation result =
+        super.visitInstantiation(node, removalSentinel) as Instantiation;
+    if (enableConstructorTearOff &&
+        result.expression is ConstantExpression &&
+        result.typeArguments.every(isInstantiated)) {
+      return evaluateAndTransformWithContext(node, result);
+    }
+    return node;
+  }
+
+  @override
   TreeNode visitSwitchCase(SwitchCase node, TreeNode? removalSentinel) {
     transformExpressions(node.expressions, node);
     return super.visitSwitchCase(node, removalSentinel);
@@ -872,9 +898,6 @@
   final bool enableTripleShift;
   final bool enableConstFunctions;
 
-  final bool Function(DartType) isInstantiated =
-      new IsInstantiatedVisitor().isInstantiated;
-
   final Map<Constant, Constant> canonicalizationCache;
   final Map<Node, Constant?> nodeCache;
   final CloneVisitorNotMembers cloner = new CloneVisitorNotMembers();
@@ -4479,6 +4502,10 @@
   }
 }
 
+bool isInstantiated(DartType type) {
+  return type.accept(new IsInstantiatedVisitor());
+}
+
 class IsInstantiatedVisitor extends DartTypeVisitor<bool> {
   final _availableVariables = new Set<TypeParameter>();
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index eb47f3f..3fe1f73 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -1214,6 +1214,8 @@
             isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
         enableConstFunctions:
             isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
+        enableConstructorTearOff:
+            isExperimentEnabledGlobally(ExperimentalFlag.constructorTearoffs),
         errorOnUnevaluatedConstant: errorOnUnevaluatedConstant);
     ticker.logMs("Evaluated constants");
 
@@ -1255,18 +1257,21 @@
     constants.EvaluationMode evaluationMode = _getConstantEvaluationMode();
 
     constants.transformProcedure(
-        procedure,
-        backendTarget.constantsBackend(loader.coreTypes),
-        environmentDefines,
-        environment,
-        new KernelConstantErrorReporter(loader),
-        evaluationMode,
-        evaluateAnnotations: true,
-        enableTripleShift:
-            isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
-        enableConstFunctions:
-            isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
-        errorOnUnevaluatedConstant: errorOnUnevaluatedConstant);
+      procedure,
+      backendTarget.constantsBackend(loader.coreTypes),
+      environmentDefines,
+      environment,
+      new KernelConstantErrorReporter(loader),
+      evaluationMode,
+      evaluateAnnotations: true,
+      enableTripleShift:
+          isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
+      enableConstFunctions:
+          isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
+      enableConstructorTearOff:
+          isExperimentEnabledGlobally(ExperimentalFlag.constructorTearoffs),
+      errorOnUnevaluatedConstant: errorOnUnevaluatedConstant,
+    );
     ticker.logMs("Evaluated constants");
 
     backendTarget.performTransformationsOnProcedure(
diff --git a/pkg/front_end/test/constant_evaluator_benchmark.dart b/pkg/front_end/test/constant_evaluator_benchmark.dart
index c1eb8fb..4595cf8 100644
--- a/pkg/front_end/test/constant_evaluator_benchmark.dart
+++ b/pkg/front_end/test/constant_evaluator_benchmark.dart
@@ -103,6 +103,8 @@
                 .isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
             enableConstFunctions: target
                 .isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
+            enableConstructorTearOff: target.isExperimentEnabledGlobally(
+                ExperimentalFlag.constructorTearoffs),
             errorOnUnevaluatedConstant:
                 incrementalCompiler.context.options.errorOnUnevaluatedConstant);
         print("Transformed constants with $environmentDefinesDescription"
diff --git a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.strong.expect
index 912e906..efd5a53 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.strong.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.strong.expect
@@ -12,8 +12,8 @@
   method inst<T extends core::Object? = dynamic>(self::C::inst::T% value) → self::C::inst::T%
     return value;
   method method() → void {
-    (core::int) → core::int f1 = #C1<core::int>;
-    core::String f1TypeName = (#C1<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C2;
+    core::String f1TypeName = (#C2).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -26,8 +26,8 @@
   method minst<T extends core::Object? = dynamic>(self::M::minst::T% value) → self::M::minst::T%
     return value;
   method mmethod() → void {
-    (core::int) → core::int f1 = #C2<core::int>;
-    core::String f1TypeName = (#C2<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C4;
+    core::String f1TypeName = (#C4).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -68,8 +68,8 @@
 static method Ext|get#einst(lowered final self::C #this) → <T extends core::Object? = dynamic>(T%) → T%
   return <T extends core::Object? = dynamic>(T% value) → T% => self::Ext|einst<T%>(#this, value);
 static method Ext|emethod(lowered final self::C #this) → void {
-  (core::int) → core::int f1 = #C3<core::int>;
-  core::String f1TypeName = (#C3<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+  (core::int) → core::int f1 = #C6;
+  core::String f1TypeName = (#C6).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
   (core::int) → core::int f2 = self::Ext|get#einst(#this)<core::int>;
   core::String f2TypeName = (self::Ext|get#einst(#this)<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
   (core::int) → core::int f3 = self::Ext|get#einst(#this)<core::int>;
@@ -78,8 +78,8 @@
 static method Ext|get#emethod(lowered final self::C #this) → () → void
   return () → void => self::Ext|emethod(#this);
 static method main() → void {
-  core::Type t1 = #C4;
-  core::Type t2 = #C5;
+  core::Type t1 = #C7;
+  core::Type t2 = #C8;
   function local<T extends core::Object? = dynamic>(T% value) → T%
     return value;
   (core::int) → core::int f3 = local<core::int>;
@@ -87,14 +87,17 @@
   (core::int) → core::int f4 = d.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
   (core::int) → core::int f5 = d.{self::_D&C&M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
   (core::int) → core::int f6 = self::Ext|get#einst(d)<core::int>;
-  core::String typeName = (#C4).{core::Type::toString}(){() → core::String};
+  core::String typeName = (#C7).{core::Type::toString}(){() → core::String};
   core::String functionTypeName = (local<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
 }
 
 constants  {
   #C1 = tearoff self::C::stat
-  #C2 = tearoff self::M::mstat
-  #C3 = tearoff self::Ext|estat
-  #C4 = TypeLiteralConstant(core::List<core::int>)
-  #C5 = TypeLiteralConstant(core::List<core::List<core::int>>)
+  #C2 = partial-instantiation self::C::stat <core::int>
+  #C3 = tearoff self::M::mstat
+  #C4 = partial-instantiation self::M::mstat <core::int>
+  #C5 = tearoff self::Ext|estat
+  #C6 = partial-instantiation self::Ext|estat <core::int>
+  #C7 = TypeLiteralConstant(core::List<core::int>)
+  #C8 = TypeLiteralConstant(core::List<core::List<core::int>>)
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.strong.transformed.expect
index 3ed377b..ba6e6f0 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.strong.transformed.expect
@@ -12,8 +12,8 @@
   method inst<T extends core::Object? = dynamic>(self::C::inst::T% value) → self::C::inst::T%
     return value;
   method method() → void {
-    (core::int) → core::int f1 = #C1<core::int>;
-    core::String f1TypeName = (#C1<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C2;
+    core::String f1TypeName = (#C2).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -26,8 +26,8 @@
   method minst<T extends core::Object? = dynamic>(self::M::minst::T% value) → self::M::minst::T%
     return value;
   method mmethod() → void {
-    (core::int) → core::int f1 = #C2<core::int>;
-    core::String f1TypeName = (#C2<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C4;
+    core::String f1TypeName = (#C4).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -41,8 +41,8 @@
   method minst<T extends core::Object? = dynamic>(self::_D&C&M::minst::T% value) → self::_D&C&M::minst::T%
     return value;
   method mmethod() → void {
-    (core::int) → core::int f1 = #C2<core::int>;
-    core::String f1TypeName = (#C2<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C4;
+    core::String f1TypeName = (#C4).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -76,8 +76,8 @@
 static method Ext|get#einst(lowered final self::C #this) → <T extends core::Object? = dynamic>(T%) → T%
   return <T extends core::Object? = dynamic>(T% value) → T% => self::Ext|einst<T%>(#this, value);
 static method Ext|emethod(lowered final self::C #this) → void {
-  (core::int) → core::int f1 = #C3<core::int>;
-  core::String f1TypeName = (#C3<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+  (core::int) → core::int f1 = #C6;
+  core::String f1TypeName = (#C6).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
   (core::int) → core::int f2 = self::Ext|get#einst(#this)<core::int>;
   core::String f2TypeName = (self::Ext|get#einst(#this)<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
   (core::int) → core::int f3 = self::Ext|get#einst(#this)<core::int>;
@@ -86,8 +86,8 @@
 static method Ext|get#emethod(lowered final self::C #this) → () → void
   return () → void => self::Ext|emethod(#this);
 static method main() → void {
-  core::Type t1 = #C4;
-  core::Type t2 = #C5;
+  core::Type t1 = #C7;
+  core::Type t2 = #C8;
   function local<T extends core::Object? = dynamic>(T% value) → T%
     return value;
   (core::int) → core::int f3 = local<core::int>;
@@ -95,25 +95,17 @@
   (core::int) → core::int f4 = d.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
   (core::int) → core::int f5 = d.{self::_D&C&M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
   (core::int) → core::int f6 = self::Ext|get#einst(d)<core::int>;
-  core::String typeName = (#C4).{core::Type::toString}(){() → core::String};
+  core::String typeName = (#C7).{core::Type::toString}(){() → core::String};
   core::String functionTypeName = (local<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
 }
 
 constants  {
   #C1 = tearoff self::C::stat
-  #C2 = tearoff self::M::mstat
-  #C3 = tearoff self::Ext|estat
-  #C4 = TypeLiteralConstant(core::List<core::int>)
-  #C5 = TypeLiteralConstant(core::List<core::List<core::int>>)
+  #C2 = partial-instantiation self::C::stat <core::int>
+  #C3 = tearoff self::M::mstat
+  #C4 = partial-instantiation self::M::mstat <core::int>
+  #C5 = tearoff self::Ext|estat
+  #C6 = partial-instantiation self::Ext|estat <core::int>
+  #C7 = TypeLiteralConstant(core::List<core::int>)
+  #C8 = TypeLiteralConstant(core::List<core::List<core::int>>)
 }
-
-Extra constant evaluation status:
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:13:18 -> PartialInstantiationConstant(C.stat<int>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:14:26 -> PartialInstantiationConstant(C.stat<int>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:26:19 -> PartialInstantiationConstant(M.mstat<int>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:27:27 -> PartialInstantiationConstant(M.mstat<int>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:26:19 -> PartialInstantiationConstant(M.mstat<int>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:27:27 -> PartialInstantiationConstant(M.mstat<int>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:39:19 -> PartialInstantiationConstant(Ext|estat<int>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:40:27 -> PartialInstantiationConstant(Ext|estat<int>)
-Extra constant evaluation: evaluated: 120, effectively constant: 8
diff --git a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.weak.expect
index 56a00ef..70f8b63 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.weak.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.weak.expect
@@ -12,8 +12,8 @@
   method inst<T extends core::Object? = dynamic>(self::C::inst::T% value) → self::C::inst::T%
     return value;
   method method() → void {
-    (core::int) → core::int f1 = #C1<core::int>;
-    core::String f1TypeName = (#C1<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C2;
+    core::String f1TypeName = (#C2).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -26,8 +26,8 @@
   method minst<T extends core::Object? = dynamic>(self::M::minst::T% value) → self::M::minst::T%
     return value;
   method mmethod() → void {
-    (core::int) → core::int f1 = #C2<core::int>;
-    core::String f1TypeName = (#C2<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C4;
+    core::String f1TypeName = (#C4).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -68,8 +68,8 @@
 static method Ext|get#einst(lowered final self::C #this) → <T extends core::Object? = dynamic>(T%) → T%
   return <T extends core::Object? = dynamic>(T% value) → T% => self::Ext|einst<T%>(#this, value);
 static method Ext|emethod(lowered final self::C #this) → void {
-  (core::int) → core::int f1 = #C3<core::int>;
-  core::String f1TypeName = (#C3<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+  (core::int) → core::int f1 = #C6;
+  core::String f1TypeName = (#C6).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
   (core::int) → core::int f2 = self::Ext|get#einst(#this)<core::int>;
   core::String f2TypeName = (self::Ext|get#einst(#this)<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
   (core::int) → core::int f3 = self::Ext|get#einst(#this)<core::int>;
@@ -78,8 +78,8 @@
 static method Ext|get#emethod(lowered final self::C #this) → () → void
   return () → void => self::Ext|emethod(#this);
 static method main() → void {
-  core::Type t1 = #C4;
-  core::Type t2 = #C5;
+  core::Type t1 = #C7;
+  core::Type t2 = #C8;
   function local<T extends core::Object? = dynamic>(T% value) → T%
     return value;
   (core::int) → core::int f3 = local<core::int>;
@@ -87,14 +87,17 @@
   (core::int) → core::int f4 = d.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
   (core::int) → core::int f5 = d.{self::_D&C&M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
   (core::int) → core::int f6 = self::Ext|get#einst(d)<core::int>;
-  core::String typeName = (#C4).{core::Type::toString}(){() → core::String};
+  core::String typeName = (#C7).{core::Type::toString}(){() → core::String};
   core::String functionTypeName = (local<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
 }
 
 constants  {
   #C1 = tearoff self::C::stat
-  #C2 = tearoff self::M::mstat
-  #C3 = tearoff self::Ext|estat
-  #C4 = TypeLiteralConstant(core::List<core::int*>*)
-  #C5 = TypeLiteralConstant(core::List<core::List<core::int*>*>*)
+  #C2 = partial-instantiation self::C::stat <core::int*>
+  #C3 = tearoff self::M::mstat
+  #C4 = partial-instantiation self::M::mstat <core::int*>
+  #C5 = tearoff self::Ext|estat
+  #C6 = partial-instantiation self::Ext|estat <core::int*>
+  #C7 = TypeLiteralConstant(core::List<core::int*>*)
+  #C8 = TypeLiteralConstant(core::List<core::List<core::int*>*>*)
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.weak.transformed.expect
index bb7ef4c..217f0b6 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation.dart.weak.transformed.expect
@@ -12,8 +12,8 @@
   method inst<T extends core::Object? = dynamic>(self::C::inst::T% value) → self::C::inst::T%
     return value;
   method method() → void {
-    (core::int) → core::int f1 = #C1<core::int>;
-    core::String f1TypeName = (#C1<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C2;
+    core::String f1TypeName = (#C2).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -26,8 +26,8 @@
   method minst<T extends core::Object? = dynamic>(self::M::minst::T% value) → self::M::minst::T%
     return value;
   method mmethod() → void {
-    (core::int) → core::int f1 = #C2<core::int>;
-    core::String f1TypeName = (#C2<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C4;
+    core::String f1TypeName = (#C4).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -41,8 +41,8 @@
   method minst<T extends core::Object? = dynamic>(self::_D&C&M::minst::T% value) → self::_D&C&M::minst::T%
     return value;
   method mmethod() → void {
-    (core::int) → core::int f1 = #C2<core::int>;
-    core::String f1TypeName = (#C2<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+    (core::int) → core::int f1 = #C4;
+    core::String f1TypeName = (#C4).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f2 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
     core::String f2TypeName = (this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
     (core::int) → core::int f3 = this.{self::M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
@@ -76,8 +76,8 @@
 static method Ext|get#einst(lowered final self::C #this) → <T extends core::Object? = dynamic>(T%) → T%
   return <T extends core::Object? = dynamic>(T% value) → T% => self::Ext|einst<T%>(#this, value);
 static method Ext|emethod(lowered final self::C #this) → void {
-  (core::int) → core::int f1 = #C3<core::int>;
-  core::String f1TypeName = (#C3<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
+  (core::int) → core::int f1 = #C6;
+  core::String f1TypeName = (#C6).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
   (core::int) → core::int f2 = self::Ext|get#einst(#this)<core::int>;
   core::String f2TypeName = (self::Ext|get#einst(#this)<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
   (core::int) → core::int f3 = self::Ext|get#einst(#this)<core::int>;
@@ -86,8 +86,8 @@
 static method Ext|get#emethod(lowered final self::C #this) → () → void
   return () → void => self::Ext|emethod(#this);
 static method main() → void {
-  core::Type t1 = #C4;
-  core::Type t2 = #C5;
+  core::Type t1 = #C7;
+  core::Type t2 = #C8;
   function local<T extends core::Object? = dynamic>(T% value) → T%
     return value;
   (core::int) → core::int f3 = local<core::int>;
@@ -95,25 +95,17 @@
   (core::int) → core::int f4 = d.{self::C::inst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
   (core::int) → core::int f5 = d.{self::_D&C&M::minst}{<T extends core::Object? = dynamic>(T%) → T%}<core::int>;
   (core::int) → core::int f6 = self::Ext|get#einst(d)<core::int>;
-  core::String typeName = (#C4).{core::Type::toString}(){() → core::String};
+  core::String typeName = (#C7).{core::Type::toString}(){() → core::String};
   core::String functionTypeName = (local<core::int>).{core::Object::runtimeType}{core::Type}.{core::Type::toString}(){() → core::String};
 }
 
 constants  {
   #C1 = tearoff self::C::stat
-  #C2 = tearoff self::M::mstat
-  #C3 = tearoff self::Ext|estat
-  #C4 = TypeLiteralConstant(core::List<core::int*>*)
-  #C5 = TypeLiteralConstant(core::List<core::List<core::int*>*>*)
+  #C2 = partial-instantiation self::C::stat <core::int*>
+  #C3 = tearoff self::M::mstat
+  #C4 = partial-instantiation self::M::mstat <core::int*>
+  #C5 = tearoff self::Ext|estat
+  #C6 = partial-instantiation self::Ext|estat <core::int*>
+  #C7 = TypeLiteralConstant(core::List<core::int*>*)
+  #C8 = TypeLiteralConstant(core::List<core::List<core::int*>*>*)
 }
-
-Extra constant evaluation status:
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:13:18 -> PartialInstantiationConstant(C.stat<int*>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:14:26 -> PartialInstantiationConstant(C.stat<int*>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:26:19 -> PartialInstantiationConstant(M.mstat<int*>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:27:27 -> PartialInstantiationConstant(M.mstat<int*>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:26:19 -> PartialInstantiationConstant(M.mstat<int*>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:27:27 -> PartialInstantiationConstant(M.mstat<int*>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:39:19 -> PartialInstantiationConstant(Ext|estat<int*>)
-Evaluated: Instantiation @ org-dartlang-testcase:///explicit_instantiation.dart:40:27 -> PartialInstantiationConstant(Ext|estat<int*>)
-Extra constant evaluation: evaluated: 120, effectively constant: 8
diff --git a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.strong.expect
index 1724a92..7d47228 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.strong.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.strong.expect
@@ -57,7 +57,7 @@
 
 static field <X extends core::Object? = dynamic>(X%) → X% a = #C1;
 static field (core::int) → core::int b = self::a<core::int>;
-static field (core::int) → core::int c = #C1<core::int>;
+static field (core::int) → core::int c = #C2;
 static field invalid-type d = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:25:11: Error: Too many type arguments: 1 allowed, but 2 found.
 Try removing the extra type arguments.
 var d = id<int, String>; // error - too many args
@@ -74,7 +74,7 @@
 Try changing the operand or remove the type arguments.
 var g = main<int>; // error - non-generic function type operand
             ^";
-static field (core::String) → core::String h = #C2<core::String>;
+static field (core::String) → core::String h = #C4;
 static method id<X extends core::Object? = dynamic>(self::id::X% x) → self::id::X%
   return x;
 static method method<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → void {}
@@ -83,7 +83,7 @@
 static method test() → dynamic {
   <X extends core::Object? = dynamic>(X%) → X% a = #C1;
   (core::int) → core::int b = a<core::int>;
-  (core::int) → core::int c = #C1<core::int>;
+  (core::int) → core::int c = #C2;
   invalid-type d = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:15:13: Error: Too many type arguments: 1 allowed, but 2 found.
 Try removing the extra type arguments.
   var d = id<int, String>; // error - too many args
@@ -100,11 +100,13 @@
 Try changing the operand or remove the type arguments.
   var g = main<int>; // error - non-generic function type operand
               ^";
-  (core::String) → core::String h = #C2<core::String>;
+  (core::String) → core::String h = #C4;
 }
 static method main() → dynamic {}
 
 constants  {
   #C1 = tearoff self::id
-  #C2 = tearoff self::boundedMethod
+  #C2 = partial-instantiation self::id <core::int>
+  #C3 = tearoff self::boundedMethod
+  #C4 = partial-instantiation self::boundedMethod <core::String>
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.strong.transformed.expect
new file mode 100644
index 0000000..7d47228
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.strong.transformed.expect
@@ -0,0 +1,112 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:15:13: Error: Too many type arguments: 1 allowed, but 2 found.
+// Try removing the extra type arguments.
+//   var d = id<int, String>; // error - too many args
+//             ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:16:17: Error: Too few type arguments: 2 required, 1 given.
+// Try adding the missing type arguments.
+//   var e = method<int>; // error - too few args
+//                 ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:17:12: Error: The static type of the explicit instantiation operand must be a generic function type but is 'int'.
+// Try changing the operand or remove the type arguments.
+//   var f = 0<int>; // error - non-function type operand
+//            ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:18:15: Error: The static type of the explicit instantiation operand must be a generic function type but is 'dynamic Function()'.
+// Try changing the operand or remove the type arguments.
+//   var g = main<int>; // error - non-generic function type operand
+//               ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:19:24: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'X' on 'X Function<X extends num>(X)'.
+// Try changing type arguments so that they conform to the bounds.
+//   var h = boundedMethod<String>; // error - invalid bound
+//                        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:25:11: Error: Too many type arguments: 1 allowed, but 2 found.
+// Try removing the extra type arguments.
+// var d = id<int, String>; // error - too many args
+//           ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:26:15: Error: Too few type arguments: 2 required, 1 given.
+// Try adding the missing type arguments.
+// var e = method<int>; // error - too few args
+//               ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:27:10: Error: The static type of the explicit instantiation operand must be a generic function type but is 'int'.
+// Try changing the operand or remove the type arguments.
+// var f = 0<int>; // error - non-function type operand
+//          ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:28:13: Error: The static type of the explicit instantiation operand must be a generic function type but is 'dynamic Function()'.
+// Try changing the operand or remove the type arguments.
+// var g = main<int>; // error - non-generic function type operand
+//             ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:29:22: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'X' on 'X Function<X extends num>(X)'.
+// Try changing type arguments so that they conform to the bounds.
+// var h = boundedMethod<String>; // error - invalid bound
+//                      ^
+//
+import self as self;
+import "dart:core" as core;
+
+static field <X extends core::Object? = dynamic>(X%) → X% a = #C1;
+static field (core::int) → core::int b = self::a<core::int>;
+static field (core::int) → core::int c = #C2;
+static field invalid-type d = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:25:11: Error: Too many type arguments: 1 allowed, but 2 found.
+Try removing the extra type arguments.
+var d = id<int, String>; // error - too many args
+          ^";
+static field invalid-type e = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:26:15: Error: Too few type arguments: 2 required, 1 given.
+Try adding the missing type arguments.
+var e = method<int>; // error - too few args
+              ^";
+static field invalid-type f = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:27:10: Error: The static type of the explicit instantiation operand must be a generic function type but is 'int'.
+Try changing the operand or remove the type arguments.
+var f = 0<int>; // error - non-function type operand
+         ^";
+static field invalid-type g = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:28:13: Error: The static type of the explicit instantiation operand must be a generic function type but is 'dynamic Function()'.
+Try changing the operand or remove the type arguments.
+var g = main<int>; // error - non-generic function type operand
+            ^";
+static field (core::String) → core::String h = #C4;
+static method id<X extends core::Object? = dynamic>(self::id::X% x) → self::id::X%
+  return x;
+static method method<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → void {}
+static method boundedMethod<X extends core::num>(self::boundedMethod::X x) → self::boundedMethod::X
+  return x;
+static method test() → dynamic {
+  <X extends core::Object? = dynamic>(X%) → X% a = #C1;
+  (core::int) → core::int b = a<core::int>;
+  (core::int) → core::int c = #C2;
+  invalid-type d = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:15:13: Error: Too many type arguments: 1 allowed, but 2 found.
+Try removing the extra type arguments.
+  var d = id<int, String>; // error - too many args
+            ^";
+  invalid-type e = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:16:17: Error: Too few type arguments: 2 required, 1 given.
+Try adding the missing type arguments.
+  var e = method<int>; // error - too few args
+                ^";
+  invalid-type f = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:17:12: Error: The static type of the explicit instantiation operand must be a generic function type but is 'int'.
+Try changing the operand or remove the type arguments.
+  var f = 0<int>; // error - non-function type operand
+           ^";
+  invalid-type g = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:18:15: Error: The static type of the explicit instantiation operand must be a generic function type but is 'dynamic Function()'.
+Try changing the operand or remove the type arguments.
+  var g = main<int>; // error - non-generic function type operand
+              ^";
+  (core::String) → core::String h = #C4;
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = tearoff self::id
+  #C2 = partial-instantiation self::id <core::int>
+  #C3 = tearoff self::boundedMethod
+  #C4 = partial-instantiation self::boundedMethod <core::String>
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.weak.expect
index 1724a92..c32e295 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.weak.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.weak.expect
@@ -57,7 +57,7 @@
 
 static field <X extends core::Object? = dynamic>(X%) → X% a = #C1;
 static field (core::int) → core::int b = self::a<core::int>;
-static field (core::int) → core::int c = #C1<core::int>;
+static field (core::int) → core::int c = #C2;
 static field invalid-type d = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:25:11: Error: Too many type arguments: 1 allowed, but 2 found.
 Try removing the extra type arguments.
 var d = id<int, String>; // error - too many args
@@ -74,7 +74,7 @@
 Try changing the operand or remove the type arguments.
 var g = main<int>; // error - non-generic function type operand
             ^";
-static field (core::String) → core::String h = #C2<core::String>;
+static field (core::String) → core::String h = #C4;
 static method id<X extends core::Object? = dynamic>(self::id::X% x) → self::id::X%
   return x;
 static method method<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → void {}
@@ -83,7 +83,7 @@
 static method test() → dynamic {
   <X extends core::Object? = dynamic>(X%) → X% a = #C1;
   (core::int) → core::int b = a<core::int>;
-  (core::int) → core::int c = #C1<core::int>;
+  (core::int) → core::int c = #C2;
   invalid-type d = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:15:13: Error: Too many type arguments: 1 allowed, but 2 found.
 Try removing the extra type arguments.
   var d = id<int, String>; // error - too many args
@@ -100,11 +100,13 @@
 Try changing the operand or remove the type arguments.
   var g = main<int>; // error - non-generic function type operand
               ^";
-  (core::String) → core::String h = #C2<core::String>;
+  (core::String) → core::String h = #C4;
 }
 static method main() → dynamic {}
 
 constants  {
   #C1 = tearoff self::id
-  #C2 = tearoff self::boundedMethod
+  #C2 = partial-instantiation self::id <core::int*>
+  #C3 = tearoff self::boundedMethod
+  #C4 = partial-instantiation self::boundedMethod <core::String*>
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.weak.transformed.expect
new file mode 100644
index 0000000..c32e295
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart.weak.transformed.expect
@@ -0,0 +1,112 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:15:13: Error: Too many type arguments: 1 allowed, but 2 found.
+// Try removing the extra type arguments.
+//   var d = id<int, String>; // error - too many args
+//             ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:16:17: Error: Too few type arguments: 2 required, 1 given.
+// Try adding the missing type arguments.
+//   var e = method<int>; // error - too few args
+//                 ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:17:12: Error: The static type of the explicit instantiation operand must be a generic function type but is 'int'.
+// Try changing the operand or remove the type arguments.
+//   var f = 0<int>; // error - non-function type operand
+//            ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:18:15: Error: The static type of the explicit instantiation operand must be a generic function type but is 'dynamic Function()'.
+// Try changing the operand or remove the type arguments.
+//   var g = main<int>; // error - non-generic function type operand
+//               ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:19:24: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'X' on 'X Function<X extends num>(X)'.
+// Try changing type arguments so that they conform to the bounds.
+//   var h = boundedMethod<String>; // error - invalid bound
+//                        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:25:11: Error: Too many type arguments: 1 allowed, but 2 found.
+// Try removing the extra type arguments.
+// var d = id<int, String>; // error - too many args
+//           ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:26:15: Error: Too few type arguments: 2 required, 1 given.
+// Try adding the missing type arguments.
+// var e = method<int>; // error - too few args
+//               ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:27:10: Error: The static type of the explicit instantiation operand must be a generic function type but is 'int'.
+// Try changing the operand or remove the type arguments.
+// var f = 0<int>; // error - non-function type operand
+//          ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:28:13: Error: The static type of the explicit instantiation operand must be a generic function type but is 'dynamic Function()'.
+// Try changing the operand or remove the type arguments.
+// var g = main<int>; // error - non-generic function type operand
+//             ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:29:22: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'X' on 'X Function<X extends num>(X)'.
+// Try changing type arguments so that they conform to the bounds.
+// var h = boundedMethod<String>; // error - invalid bound
+//                      ^
+//
+import self as self;
+import "dart:core" as core;
+
+static field <X extends core::Object? = dynamic>(X%) → X% a = #C1;
+static field (core::int) → core::int b = self::a<core::int>;
+static field (core::int) → core::int c = #C2;
+static field invalid-type d = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:25:11: Error: Too many type arguments: 1 allowed, but 2 found.
+Try removing the extra type arguments.
+var d = id<int, String>; // error - too many args
+          ^";
+static field invalid-type e = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:26:15: Error: Too few type arguments: 2 required, 1 given.
+Try adding the missing type arguments.
+var e = method<int>; // error - too few args
+              ^";
+static field invalid-type f = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:27:10: Error: The static type of the explicit instantiation operand must be a generic function type but is 'int'.
+Try changing the operand or remove the type arguments.
+var f = 0<int>; // error - non-function type operand
+         ^";
+static field invalid-type g = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:28:13: Error: The static type of the explicit instantiation operand must be a generic function type but is 'dynamic Function()'.
+Try changing the operand or remove the type arguments.
+var g = main<int>; // error - non-generic function type operand
+            ^";
+static field (core::String) → core::String h = #C4;
+static method id<X extends core::Object? = dynamic>(self::id::X% x) → self::id::X%
+  return x;
+static method method<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → void {}
+static method boundedMethod<X extends core::num>(self::boundedMethod::X x) → self::boundedMethod::X
+  return x;
+static method test() → dynamic {
+  <X extends core::Object? = dynamic>(X%) → X% a = #C1;
+  (core::int) → core::int b = a<core::int>;
+  (core::int) → core::int c = #C2;
+  invalid-type d = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:15:13: Error: Too many type arguments: 1 allowed, but 2 found.
+Try removing the extra type arguments.
+  var d = id<int, String>; // error - too many args
+            ^";
+  invalid-type e = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:16:17: Error: Too few type arguments: 2 required, 1 given.
+Try adding the missing type arguments.
+  var e = method<int>; // error - too few args
+                ^";
+  invalid-type f = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:17:12: Error: The static type of the explicit instantiation operand must be a generic function type but is 'int'.
+Try changing the operand or remove the type arguments.
+  var f = 0<int>; // error - non-function type operand
+           ^";
+  invalid-type g = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/explicit_instantiation_errors.dart:18:15: Error: The static type of the explicit instantiation operand must be a generic function type but is 'dynamic Function()'.
+Try changing the operand or remove the type arguments.
+  var g = main<int>; // error - non-generic function type operand
+              ^";
+  (core::String) → core::String h = #C4;
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = tearoff self::id
+  #C2 = partial-instantiation self::id <core::int*>
+  #C3 = tearoff self::boundedMethod
+  #C4 = partial-instantiation self::boundedMethod <core::String*>
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart
new file mode 100644
index 0000000..6cb7494
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart
@@ -0,0 +1,54 @@
+// 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.
+
+T id<T>(T t) => t;
+
+int Function(int) implicitInstantiation = id;
+var explicitInstantiation = id<int>;
+const int Function(int) implicitConstInstantiation = id;
+const explicitConstInstantiation = id<int>;
+
+T Function(T) create<T>() => id<T>;
+
+main() {
+  expect(true, identical(implicitInstantiation, implicitInstantiation));
+  expect(true, identical(implicitInstantiation, explicitInstantiation));
+  expect(true, identical(implicitInstantiation, implicitConstInstantiation));
+  expect(true, identical(implicitInstantiation, explicitConstInstantiation));
+  expect(true, identical(implicitInstantiation, id<int>));
+  expect(false, identical(implicitInstantiation, id<String>));
+  expect(false, identical(implicitInstantiation, create<int>()));
+
+  expect(true, identical(explicitInstantiation, implicitInstantiation));
+  expect(true, identical(explicitInstantiation, explicitInstantiation));
+  expect(true, identical(explicitInstantiation, implicitConstInstantiation));
+  expect(true, identical(explicitInstantiation, explicitConstInstantiation));
+  expect(true, identical(explicitInstantiation, id<int>));
+  expect(false, identical(explicitInstantiation, id<String>));
+  expect(false, identical(explicitInstantiation, create<int>()));
+
+  expect(true, identical(implicitConstInstantiation, implicitInstantiation));
+  expect(true, identical(implicitConstInstantiation, explicitInstantiation));
+  expect(true, identical(implicitConstInstantiation,
+      implicitConstInstantiation));
+  expect(true, identical(implicitConstInstantiation,
+      explicitConstInstantiation));
+  expect(true, identical(implicitConstInstantiation, id<int>));
+  expect(false, identical(implicitConstInstantiation, id<String>));
+  expect(false, identical(implicitConstInstantiation, create<int>()));
+
+  expect(true, identical(explicitConstInstantiation, implicitInstantiation));
+  expect(true, identical(explicitConstInstantiation, explicitInstantiation));
+  expect(true, identical(explicitConstInstantiation,
+      implicitConstInstantiation));
+  expect(true, identical(explicitConstInstantiation,
+      explicitConstInstantiation));
+  expect(true, identical(explicitConstInstantiation, id<int>));
+  expect(false, identical(explicitConstInstantiation, id<String>));
+  expect(false, identical(explicitConstInstantiation, create<int>()));
+}
+
+expect(expected, actual) {
+  if (expected != actual) throw 'Expected $expected, actual $actual';
+}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.strong.expect
new file mode 100644
index 0000000..8df289d3
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.strong.expect
@@ -0,0 +1,52 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field (core::int) → core::int implicitInstantiation = #C2;
+static field (core::int) → core::int explicitInstantiation = #C2;
+static const field (core::int) → core::int implicitConstInstantiation = #C2;
+static const field (core::int) → core::int explicitConstInstantiation = #C2;
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method create<T extends core::Object? = dynamic>() → (self::create::T%) → self::create::T%
+  return #C1<self::create::T%>;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::implicitInstantiation, self::implicitInstantiation));
+  self::expect(true, core::identical(self::implicitInstantiation, self::explicitInstantiation));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(false, core::identical(self::implicitInstantiation, #C3));
+  self::expect(false, core::identical(self::implicitInstantiation, self::create<core::int>()));
+  self::expect(true, core::identical(self::explicitInstantiation, self::implicitInstantiation));
+  self::expect(true, core::identical(self::explicitInstantiation, self::explicitInstantiation));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(false, core::identical(self::explicitInstantiation, #C3));
+  self::expect(false, core::identical(self::explicitInstantiation, self::create<core::int>()));
+  self::expect(true, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, self::explicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, #C3));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+  self::expect(true, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, self::explicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, #C3));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+
+constants  {
+  #C1 = tearoff self::id
+  #C2 = partial-instantiation self::id <core::int>
+  #C3 = partial-instantiation self::id <core::String>
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.strong.transformed.expect
new file mode 100644
index 0000000..3afa4cf
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.strong.transformed.expect
@@ -0,0 +1,63 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field (core::int) → core::int implicitInstantiation = #C2;
+static field (core::int) → core::int explicitInstantiation = #C2;
+static const field (core::int) → core::int implicitConstInstantiation = #C2;
+static const field (core::int) → core::int explicitConstInstantiation = #C2;
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method create<T extends core::Object? = dynamic>() → (self::create::T%) → self::create::T%
+  return #C1<self::create::T%>;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::implicitInstantiation, self::implicitInstantiation));
+  self::expect(true, core::identical(self::implicitInstantiation, self::explicitInstantiation));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(false, core::identical(self::implicitInstantiation, #C3));
+  self::expect(false, core::identical(self::implicitInstantiation, self::create<core::int>()));
+  self::expect(true, core::identical(self::explicitInstantiation, self::implicitInstantiation));
+  self::expect(true, core::identical(self::explicitInstantiation, self::explicitInstantiation));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(false, core::identical(self::explicitInstantiation, #C3));
+  self::expect(false, core::identical(self::explicitInstantiation, self::create<core::int>()));
+  self::expect(true, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, self::explicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, #C3));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+  self::expect(true, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, self::explicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, #C3));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+
+constants  {
+  #C1 = tearoff self::id
+  #C2 = partial-instantiation self::id <core::int>
+  #C3 = partial-instantiation self::id <core::String>
+}
+
+Extra constant evaluation status:
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:33:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:35:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:37:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:38:17 -> BoolConstant(false)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:43:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:45:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:47:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:48:17 -> BoolConstant(false)
+Extra constant evaluation: evaluated: 92, effectively constant: 8
diff --git a/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.textual_outline.expect
new file mode 100644
index 0000000..558b2b1
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+T id<T>(T t) => t;
+int Function(int) implicitInstantiation = id;
+var explicitInstantiation = id<int>;
+const int Function(int) implicitConstInstantiation = id;
+const explicitConstInstantiation = id<int>;
+T Function(T) create<T>() => id<T>;
+main() {}
+expect(expected, actual) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.expect
new file mode 100644
index 0000000..c33a355
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.expect
@@ -0,0 +1,52 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field (core::int) → core::int implicitInstantiation = #C2;
+static field (core::int) → core::int explicitInstantiation = #C2;
+static const field (core::int) → core::int implicitConstInstantiation = #C2;
+static const field (core::int) → core::int explicitConstInstantiation = #C2;
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method create<T extends core::Object? = dynamic>() → (self::create::T%) → self::create::T%
+  return #C1<self::create::T%>;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::implicitInstantiation, self::implicitInstantiation));
+  self::expect(true, core::identical(self::implicitInstantiation, self::explicitInstantiation));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(false, core::identical(self::implicitInstantiation, #C3));
+  self::expect(false, core::identical(self::implicitInstantiation, self::create<core::int>()));
+  self::expect(true, core::identical(self::explicitInstantiation, self::implicitInstantiation));
+  self::expect(true, core::identical(self::explicitInstantiation, self::explicitInstantiation));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(false, core::identical(self::explicitInstantiation, #C3));
+  self::expect(false, core::identical(self::explicitInstantiation, self::create<core::int>()));
+  self::expect(true, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, self::explicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, #C3));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+  self::expect(true, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, self::explicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, #C3));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+
+constants  {
+  #C1 = tearoff self::id
+  #C2 = partial-instantiation self::id <core::int*>
+  #C3 = partial-instantiation self::id <core::String*>
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.outline.expect
new file mode 100644
index 0000000..d3f2e88
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.outline.expect
@@ -0,0 +1,22 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field (core::int) → core::int implicitInstantiation;
+static field (core::int) → core::int explicitInstantiation;
+static const field (core::int) → core::int implicitConstInstantiation = self::id<core::int>;
+static const field (core::int) → core::int explicitConstInstantiation = self::id<core::int>;
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  ;
+static method create<T extends core::Object? = dynamic>() → (self::create::T%) → self::create::T%
+  ;
+static method main() → dynamic
+  ;
+static method expect(dynamic expected, dynamic actual) → dynamic
+  ;
+
+
+Extra constant evaluation status:
+Evaluated: Instantiation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:9:54 -> PartialInstantiationConstant(id<int*>)
+Evaluated: Instantiation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:10:38 -> PartialInstantiationConstant(id<int*>)
+Extra constant evaluation: evaluated: 2, effectively constant: 2
diff --git a/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.transformed.expect
new file mode 100644
index 0000000..9cdfa3c
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/identical_instantiated_function_tearoffs.dart.weak.transformed.expect
@@ -0,0 +1,63 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field (core::int) → core::int implicitInstantiation = #C2;
+static field (core::int) → core::int explicitInstantiation = #C2;
+static const field (core::int) → core::int implicitConstInstantiation = #C2;
+static const field (core::int) → core::int explicitConstInstantiation = #C2;
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method create<T extends core::Object? = dynamic>() → (self::create::T%) → self::create::T%
+  return #C1<self::create::T%>;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::implicitInstantiation, self::implicitInstantiation));
+  self::expect(true, core::identical(self::implicitInstantiation, self::explicitInstantiation));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(true, core::identical(self::implicitInstantiation, #C2));
+  self::expect(false, core::identical(self::implicitInstantiation, #C3));
+  self::expect(false, core::identical(self::implicitInstantiation, self::create<core::int>()));
+  self::expect(true, core::identical(self::explicitInstantiation, self::implicitInstantiation));
+  self::expect(true, core::identical(self::explicitInstantiation, self::explicitInstantiation));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(true, core::identical(self::explicitInstantiation, #C2));
+  self::expect(false, core::identical(self::explicitInstantiation, #C3));
+  self::expect(false, core::identical(self::explicitInstantiation, self::create<core::int>()));
+  self::expect(true, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, self::explicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, #C3));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+  self::expect(true, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, self::explicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, #C3));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+
+constants  {
+  #C1 = tearoff self::id
+  #C2 = partial-instantiation self::id <core::int*>
+  #C3 = partial-instantiation self::id <core::String*>
+}
+
+Extra constant evaluation status:
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:33:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:35:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:37:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:38:17 -> BoolConstant(false)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:43:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:45:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:47:16 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:48:17 -> BoolConstant(false)
+Extra constant evaluation: evaluated: 92, effectively constant: 8
diff --git a/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart
new file mode 100644
index 0000000..955feff
--- /dev/null
+++ b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart
@@ -0,0 +1,34 @@
+// 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.
+
+// @dart=2.13
+
+// This test is similar to
+//
+//   constructor_tearoffs/identical_instantiated_function_tearoffs.dart
+//
+// but verifies that before the constructor-tearoffs experiment was enabled,
+// instantiations in non-constant context were not canonicalized.
+
+T id<T>(T t) => t;
+
+int Function(int) implicitInstantiation = id;
+const int Function(int) implicitConstInstantiation = id;
+
+T Function(T) create<T>() => id;
+
+main() {
+  expect(true, identical(implicitInstantiation, implicitInstantiation));
+  expect(false, identical(implicitInstantiation, implicitConstInstantiation));
+  expect(false, identical(implicitInstantiation, create<int>()));
+
+  expect(false, identical(implicitConstInstantiation, implicitInstantiation));
+  expect(
+      true, identical(implicitConstInstantiation, implicitConstInstantiation));
+  expect(false, identical(implicitConstInstantiation, create<int>()));
+}
+
+expect(expected, actual) {
+  if (expected != actual) throw 'Expected $expected, actual $actual';
+}
diff --git a/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.textual_outline.expect b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.textual_outline.expect
new file mode 100644
index 0000000..90e26eb
--- /dev/null
+++ b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.textual_outline.expect
@@ -0,0 +1,7 @@
+// @dart = 2.13
+T id<T>(T t) => t;
+int Function(int) implicitInstantiation = id;
+const int Function(int) implicitConstInstantiation = id;
+T Function(T) create<T>() => id;
+main() {}
+expect(expected, actual) {}
diff --git a/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..a0e9251
--- /dev/null
+++ b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+// @dart = 2.13
+T Function(T) create<T>() => id;
+T id<T>(T t) => t;
+const int Function(int) implicitConstInstantiation = id;
+expect(expected, actual) {}
+int Function(int) implicitInstantiation = id;
+main() {}
diff --git a/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.expect b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.expect
new file mode 100644
index 0000000..daf6f36
--- /dev/null
+++ b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.expect
@@ -0,0 +1,27 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field (core::int) → core::int implicitInstantiation = #C1<core::int>;
+static const field (core::int) → core::int implicitConstInstantiation = #C2;
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method create<T extends core::Object? = dynamic>() → (self::create::T%) → self::create::T%
+  return #C1<self::create::T%>;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::implicitInstantiation, self::implicitInstantiation));
+  self::expect(false, core::identical(self::implicitInstantiation, #C2));
+  self::expect(false, core::identical(self::implicitInstantiation, self::create<core::int>()));
+  self::expect(false, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+
+constants  {
+  #C1 = tearoff self::id
+  #C2 = partial-instantiation self::id <core::int*>
+}
diff --git a/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.outline.expect b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.outline.expect
new file mode 100644
index 0000000..02a352f
--- /dev/null
+++ b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.outline.expect
@@ -0,0 +1,19 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field (core::int) → core::int implicitInstantiation;
+static const field (core::int) → core::int implicitConstInstantiation = self::id<core::int>;
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  ;
+static method create<T extends core::Object? = dynamic>() → (self::create::T%) → self::create::T%
+  ;
+static method main() → dynamic
+  ;
+static method expect(dynamic expected, dynamic actual) → dynamic
+  ;
+
+
+Extra constant evaluation status:
+Evaluated: Instantiation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:17:54 -> PartialInstantiationConstant(id<int*>)
+Extra constant evaluation: evaluated: 1, effectively constant: 1
diff --git a/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.transformed.expect b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.transformed.expect
new file mode 100644
index 0000000..ad67316
--- /dev/null
+++ b/pkg/front_end/testcases/general/identical_instantiated_function_tearoffs.dart.weak.transformed.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field (core::int) → core::int implicitInstantiation = #C1<core::int>;
+static const field (core::int) → core::int implicitConstInstantiation = #C2;
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method create<T extends core::Object? = dynamic>() → (self::create::T%) → self::create::T%
+  return #C1<self::create::T%>;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::implicitInstantiation, self::implicitInstantiation));
+  self::expect(false, core::identical(self::implicitInstantiation, #C2));
+  self::expect(false, core::identical(self::implicitInstantiation, self::create<core::int>()));
+  self::expect(false, core::identical(#C2, self::implicitInstantiation));
+  self::expect(true, core::identical(#C2, #C2));
+  self::expect(false, core::identical(#C2, self::create<core::int>()));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+
+constants  {
+  #C1 = tearoff self::id
+  #C2 = partial-instantiation self::id <core::int*>
+}
+
+Extra constant evaluation status:
+Evaluated: StaticInvocation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:28:13 -> BoolConstant(true)
+Evaluated: Instantiation @ org-dartlang-testcase:///identical_instantiated_function_tearoffs.dart:16:43 -> PartialInstantiationConstant(id<int*>)
+Extra constant evaluation: evaluated: 30, effectively constant: 2
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index ecea6cc..cfb0715 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -8,7 +8,6 @@
 
 dart2js/late_statics: SemiFuzzFailure # dartbug.com/45854
 
-constructor_tearoffs/explicit_instantiation_errors: TypeCheckError
 constructor_tearoffs/redirecting_constructors: RuntimeError
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
 extension_types/issue45775: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index eb861df..c7df9c9 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -6,7 +6,6 @@
 # the round trip for Kernel textual serialization where the initial binary
 # Kernel files are produced by compiling Dart code via Fasta.
 
-constructor_tearoffs/explicit_instantiation_errors: TypeCheckError
 constructor_tearoffs/redirecting_constructors: RuntimeError
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
 extension_types/issue45775: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 8d66bab..29acb62 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -26,6 +26,7 @@
 constructor_tearoffs/explicit_instantiation_errors: FormatterCrash
 constructor_tearoffs/generic_tearoff_with_context: FormatterCrash
 constructor_tearoffs/generic_tearoff_without_context: FormatterCrash
+constructor_tearoffs/identical_instantiated_function_tearoffs: FormatterCrash
 constructor_tearoffs/instantiation: FormatterCrash
 constructor_tearoffs/nongeneric_tearoff_with_context: FormatterCrash
 constructor_tearoffs/nongeneric_tearoff_without_context: FormatterCrash
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index 96f77b8..82c2ba6 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -11,7 +11,6 @@
 regress/utf_16_le_content.crash: SemiFuzzCrash
 dart2js/late_statics: SemiFuzzFailure # dartbug.com/45854
 
-constructor_tearoffs/explicit_instantiation_errors: TypeCheckError
 constructor_tearoffs/redirecting_constructors: RuntimeError
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
 extension_types/issue45775: ExpectationFileMismatchSerialized # Expected.