[cfe] Add check for constructor tear off on abstract classes

+ add test for inferred types on constructor tear offs
+ avoid creating tear off lowering for abstract classes

Change-Id: I0d7921690ecea090f96a9811d25248e7e02582b4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206082
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 1229ded..289ec96 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -12,6 +12,15 @@
 part of _fe_analyzer_shared.messages.codes;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeAbstractClassConstructorTearOff =
+    messageAbstractClassConstructorTearOff;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageAbstractClassConstructorTearOff = const MessageCode(
+    "AbstractClassConstructorTearOff",
+    message: r"""Constructors on abstract classes can't be torn off.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)>
     templateAbstractClassInstantiation =
     const Template<Message Function(String name)>(
diff --git a/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart b/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
index a82100b..d659697 100644
--- a/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
@@ -126,7 +126,8 @@
       this.charOpenParenOffset,
       int charEndOffset,
       Member? referenceFrom,
-      [String? nativeMethodName])
+      {String? nativeMethodName,
+      required bool forAbstractClassOrEnum})
       : _constructor = new Constructor(new FunctionNode(null),
             name: new Name(name, compilationUnit.library),
             fileUri: compilationUnit.fileUri,
@@ -136,7 +137,8 @@
           ..fileEndOffset = charEndOffset
           ..isNonNullableByDefault = compilationUnit.isNonNullableByDefault,
         _constructorTearOff = createConstructorTearOffProcedure(
-            name, compilationUnit, charOffset),
+            name, compilationUnit, charOffset,
+            forAbstractClassOrEnum: forAbstractClassOrEnum),
         super(metadata, modifiers, returnType, name, typeVariables, formals,
             compilationUnit, charOffset, nativeMethodName);
 
diff --git a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
index 9d294ec..8947531 100644
--- a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
@@ -288,7 +288,8 @@
         charOffset,
         charOffset,
         charEndOffset,
-        constructorReference);
+        constructorReference,
+        forAbstractClassOrEnum: true);
     constructors[""] = constructorBuilder;
     FieldBuilder valuesBuilder = new SourceFieldBuilder(
         /* metadata = */ null,
diff --git a/pkg/front_end/lib/src/fasta/kernel/constructor_tearoff_lowering.dart b/pkg/front_end/lib/src/fasta/kernel/constructor_tearoff_lowering.dart
index c14c0b7..33aa5d1 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constructor_tearoff_lowering.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constructor_tearoff_lowering.dart
@@ -21,9 +21,11 @@
 ///
 /// If constructor tear off lowering is not enabled, `null` is returned.
 Procedure? createConstructorTearOffProcedure(
-    String name, SourceLibraryBuilder compilationUnit, int fileOffset) {
-  if (compilationUnit
-      .loader.target.backendTarget.isConstructorTearOffLoweringEnabled) {
+    String name, SourceLibraryBuilder compilationUnit, int fileOffset,
+    {required bool forAbstractClassOrEnum}) {
+  if (!forAbstractClassOrEnum &&
+      compilationUnit
+          .loader.target.backendTarget.isConstructorTearOffLoweringEnabled) {
     return new Procedure(constructorTearOffName(name, compilationUnit.library),
         ProcedureKind.Method, new FunctionNode(null),
         fileUri: compilationUnit.fileUri, isStatic: true)
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 7150059..9789db1 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -3091,6 +3091,7 @@
   @override
   buildPropertyAccess(
       IncompleteSendGenerator send, int operatorOffset, bool isNullAware) {
+    int nameOffset = offsetForToken(send.token);
     Name name = send.name;
     Arguments? arguments = send.arguments;
 
@@ -3106,8 +3107,7 @@
     if (declarationBuilder is DeclarationBuilder) {
       DeclarationBuilder declaration = declarationBuilder;
       Builder? member = declaration.findStaticBuilder(
-          name.text, offsetForToken(send.token), _uri, _helper.libraryBuilder);
-
+          name.text, nameOffset, _uri, _helper.libraryBuilder);
       Generator generator;
       if (member == null) {
         // If we find a setter, [member] is an [AccessErrorBuilder], not null.
@@ -3115,10 +3115,16 @@
           if (_helper.enableConstructorTearOffsInLibrary &&
               declarationBuilder is ClassBuilder) {
             MemberBuilder? constructor =
-                declarationBuilder.findConstructorOrFactory(name.text,
-                    offsetForToken(send.token), _uri, _helper.libraryBuilder);
+                declarationBuilder.findConstructorOrFactory(
+                    name.text, nameOffset, _uri, _helper.libraryBuilder);
             Member? tearOff = constructor?.readTarget;
             if (tearOff is Constructor) {
+              if (declarationBuilder.isAbstract) {
+                return _helper.buildProblem(
+                    messageAbstractClassConstructorTearOff,
+                    nameOffset,
+                    name.text.length);
+              }
               return _helper.forest
                   .createConstructorTearOff(token.charOffset, tearOff);
             } else if (tearOff is Procedure) {
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 851d924..2531ba7 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -810,7 +810,9 @@
       ..isNonNullableByDefault =
           enclosingClass.enclosingLibrary.isNonNullableByDefault;
     Procedure? constructorTearOff = createConstructorTearOffProcedure(
-        '', classBuilder.library, constructor.fileOffset);
+        '', classBuilder.library, constructor.fileOffset,
+        forAbstractClassOrEnum:
+            enclosingClass.isAbstract || enclosingClass.isEnum);
     if (constructorTearOff != null) {
       buildConstructorTearOffProcedure(constructorTearOff, constructor,
           classBuilder.cls, classBuilder.library);
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index ec8bc6f..4cc93c2 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -105,6 +105,7 @@
 
   final bool enableNative;
   final bool stringExpectedAfterNative;
+  bool inAbstractClass = false;
   bool inConstructor = false;
   bool inConstructorName = false;
   int importIndex = 0;
@@ -470,6 +471,7 @@
     libraryBuilder.currentTypeParameterScopeBuilder
         .markAsClassDeclaration(name.lexeme, name.charOffset, typeVariables);
     libraryBuilder.setCurrentClassName(name.lexeme);
+    inAbstractClass = abstractToken != null;
     push(abstractToken != null ? abstractMask : 0);
   }
 
@@ -584,6 +586,7 @@
       supertype.typeVariables = typeVariables;
     }
     List<MetadataBuilder>? metadata = pop() as List<MetadataBuilder>?;
+    inAbstractClass = false;
     checkEmpty(beginToken.charOffset);
     if (name is ParserRecovery) {
       libraryBuilder
@@ -1240,7 +1243,8 @@
           formalsOffset,
           endToken.charOffset,
           nativeMethodName,
-          beginInitializers: beginInitializers);
+          beginInitializers: beginInitializers,
+          forAbstractClass: inAbstractClass);
     } else {
       if (isConst) {
         // TODO(danrubel): consider removing this
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 78cf330..b9eed88 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -2324,7 +2324,8 @@
       int charOpenParenOffset,
       int charEndOffset,
       String? nativeMethodName,
-      {Token? beginInitializers}) {
+      {Token? beginInitializers,
+      required bool forAbstractClass}) {
     Member? referenceFrom;
     if (_currentClassReferencesFromIndexed != null) {
       referenceFrom = _currentClassReferencesFromIndexed!.lookupConstructor(
@@ -2344,7 +2345,8 @@
         charOpenParenOffset,
         charEndOffset,
         referenceFrom,
-        nativeMethodName);
+        nativeMethodName: nativeMethodName,
+        forAbstractClassOrEnum: forAbstractClass);
     checkTypeVariables(typeVariables, constructorBuilder);
     addBuilder(constructorName, constructorBuilder, charOffset,
         getterReference: referenceFrom?.reference);
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 18c7bae..f6e79b9a 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -5,6 +5,7 @@
 # Note that test/spelling: Status will have no effect. Spelling errors can
 # always be fixed by either spelling correctly or updating the dictionary.
 
+AbstractClassConstructorTearOff/analyzerCode: Fail
 AbstractClassInstantiation/example: Fail
 AbstractExtensionField/analyzerCode: Fail
 AbstractExtensionField/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 8045d59..84ed8c2 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -5232,3 +5232,10 @@
   script: |
     f<X>() {}
     main() => f<int, String>;
+
+AbstractClassConstructorTearOff:
+  template: "Constructors on abstract classes can't be torn off."
+  experiments: constructor-tearoffs
+  script: |
+    abstract class Class {}
+    main() => Class.new;
diff --git a/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart
new file mode 100644
index 0000000..31fcc39
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart
@@ -0,0 +1,21 @@
+// 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.
+
+class ConcreteClass {}
+abstract class AbstractClass {}
+mixin Mixin {}
+class NamedMixinApplication = Object with Mixin;
+abstract class AbstractNamedMixinApplication = Object with Mixin;
+extension Extension on int {}
+
+test() {
+  ConcreteClass.new; // ok
+  AbstractClass.new; // error
+  Mixin.new; // error
+  NamedMixinApplication.new; // ok
+  AbstractNamedMixinApplication.new; // error
+  Extension.new; // error
+}
+
+main() {}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.strong.expect
new file mode 100644
index 0000000..e8c1280
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.strong.expect
@@ -0,0 +1,64 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:14:17: Error: Constructors on abstract classes can't be torn off.
+//   AbstractClass.new; // error
+//                 ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:15:9: Error: Getter not found: 'new'.
+//   Mixin.new; // error
+//         ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:17:33: Error: Constructors on abstract classes can't be torn off.
+//   AbstractNamedMixinApplication.new; // error
+//                                 ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:18:13: Error: Getter not found: 'new'.
+//   Extension.new; // error
+//             ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class ConcreteClass extends core::Object {
+  synthetic constructor •() → self::ConcreteClass
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractClass extends core::Object {
+  synthetic constructor •() → self::AbstractClass
+    : super core::Object::•()
+    ;
+}
+abstract class Mixin extends core::Object /*isMixinDeclaration*/  {
+}
+class NamedMixinApplication = core::Object with self::Mixin /*hasConstConstructor*/  {
+  const synthetic constructor •() → self::NamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractNamedMixinApplication = core::Object with self::Mixin /*hasConstConstructor*/  {
+  const synthetic constructor •() → self::AbstractNamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+extension Extension on core::int {
+}
+static method test() → dynamic {
+  self::ConcreteClass::•;
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:14:17: Error: Constructors on abstract classes can't be torn off.
+  AbstractClass.new; // error
+                ^^^";
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:15:9: Error: Getter not found: 'new'.
+  Mixin.new; // error
+        ^^^";
+  self::NamedMixinApplication::•;
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:17:33: Error: Constructors on abstract classes can't be torn off.
+  AbstractNamedMixinApplication.new; // error
+                                ^^^";
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:18:13: Error: Getter not found: 'new'.
+  Extension.new; // error
+            ^^^";
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.strong.transformed.expect
new file mode 100644
index 0000000..b86fac8
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.strong.transformed.expect
@@ -0,0 +1,64 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:14:17: Error: Constructors on abstract classes can't be torn off.
+//   AbstractClass.new; // error
+//                 ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:15:9: Error: Getter not found: 'new'.
+//   Mixin.new; // error
+//         ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:17:33: Error: Constructors on abstract classes can't be torn off.
+//   AbstractNamedMixinApplication.new; // error
+//                                 ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:18:13: Error: Getter not found: 'new'.
+//   Extension.new; // error
+//             ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class ConcreteClass extends core::Object {
+  synthetic constructor •() → self::ConcreteClass
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractClass extends core::Object {
+  synthetic constructor •() → self::AbstractClass
+    : super core::Object::•()
+    ;
+}
+abstract class Mixin extends core::Object /*isMixinDeclaration*/  {
+}
+class NamedMixinApplication extends core::Object implements self::Mixin /*isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •() → self::NamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractNamedMixinApplication extends core::Object implements self::Mixin /*isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •() → self::AbstractNamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+extension Extension on core::int {
+}
+static method test() → dynamic {
+  self::ConcreteClass::•;
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:14:17: Error: Constructors on abstract classes can't be torn off.
+  AbstractClass.new; // error
+                ^^^";
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:15:9: Error: Getter not found: 'new'.
+  Mixin.new; // error
+        ^^^";
+  self::NamedMixinApplication::•;
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:17:33: Error: Constructors on abstract classes can't be torn off.
+  AbstractNamedMixinApplication.new; // error
+                                ^^^";
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:18:13: Error: Getter not found: 'new'.
+  Extension.new; // error
+            ^^^";
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.textual_outline.expect
new file mode 100644
index 0000000..a955952
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.textual_outline.expect
@@ -0,0 +1,12 @@
+class ConcreteClass {}
+
+abstract class AbstractClass {}
+
+mixin Mixin {}
+class NamedMixinApplication = Object with Mixin;
+abstract class AbstractNamedMixinApplication = Object with Mixin;
+
+extension Extension on int {}
+
+test() {}
+main() {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..f9d5a03
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+abstract class AbstractClass {}
+
+abstract class AbstractNamedMixinApplication = Object with Mixin;
+
+class ConcreteClass {}
+
+class NamedMixinApplication = Object with Mixin;
+
+extension Extension on int {}
+
+main() {}
+mixin Mixin {}
+test() {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.expect
new file mode 100644
index 0000000..e8c1280
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.expect
@@ -0,0 +1,64 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:14:17: Error: Constructors on abstract classes can't be torn off.
+//   AbstractClass.new; // error
+//                 ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:15:9: Error: Getter not found: 'new'.
+//   Mixin.new; // error
+//         ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:17:33: Error: Constructors on abstract classes can't be torn off.
+//   AbstractNamedMixinApplication.new; // error
+//                                 ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:18:13: Error: Getter not found: 'new'.
+//   Extension.new; // error
+//             ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class ConcreteClass extends core::Object {
+  synthetic constructor •() → self::ConcreteClass
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractClass extends core::Object {
+  synthetic constructor •() → self::AbstractClass
+    : super core::Object::•()
+    ;
+}
+abstract class Mixin extends core::Object /*isMixinDeclaration*/  {
+}
+class NamedMixinApplication = core::Object with self::Mixin /*hasConstConstructor*/  {
+  const synthetic constructor •() → self::NamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractNamedMixinApplication = core::Object with self::Mixin /*hasConstConstructor*/  {
+  const synthetic constructor •() → self::AbstractNamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+extension Extension on core::int {
+}
+static method test() → dynamic {
+  self::ConcreteClass::•;
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:14:17: Error: Constructors on abstract classes can't be torn off.
+  AbstractClass.new; // error
+                ^^^";
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:15:9: Error: Getter not found: 'new'.
+  Mixin.new; // error
+        ^^^";
+  self::NamedMixinApplication::•;
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:17:33: Error: Constructors on abstract classes can't be torn off.
+  AbstractNamedMixinApplication.new; // error
+                                ^^^";
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:18:13: Error: Getter not found: 'new'.
+  Extension.new; // error
+            ^^^";
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.outline.expect
new file mode 100644
index 0000000..23490ce
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.outline.expect
@@ -0,0 +1,30 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class ConcreteClass extends core::Object {
+  synthetic constructor •() → self::ConcreteClass
+    ;
+}
+abstract class AbstractClass extends core::Object {
+  synthetic constructor •() → self::AbstractClass
+    ;
+}
+abstract class Mixin extends core::Object /*isMixinDeclaration*/  {
+}
+class NamedMixinApplication = core::Object with self::Mixin /*hasConstConstructor*/  {
+  const synthetic constructor •() → self::NamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractNamedMixinApplication = core::Object with self::Mixin /*hasConstConstructor*/  {
+  const synthetic constructor •() → self::AbstractNamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+extension Extension on core::int {
+}
+static method test() → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.transformed.expect
new file mode 100644
index 0000000..b86fac8
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart.weak.transformed.expect
@@ -0,0 +1,64 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:14:17: Error: Constructors on abstract classes can't be torn off.
+//   AbstractClass.new; // error
+//                 ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:15:9: Error: Getter not found: 'new'.
+//   Mixin.new; // error
+//         ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:17:33: Error: Constructors on abstract classes can't be torn off.
+//   AbstractNamedMixinApplication.new; // error
+//                                 ^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:18:13: Error: Getter not found: 'new'.
+//   Extension.new; // error
+//             ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class ConcreteClass extends core::Object {
+  synthetic constructor •() → self::ConcreteClass
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractClass extends core::Object {
+  synthetic constructor •() → self::AbstractClass
+    : super core::Object::•()
+    ;
+}
+abstract class Mixin extends core::Object /*isMixinDeclaration*/  {
+}
+class NamedMixinApplication extends core::Object implements self::Mixin /*isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •() → self::NamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+abstract class AbstractNamedMixinApplication extends core::Object implements self::Mixin /*isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •() → self::AbstractNamedMixinApplication
+    : super core::Object::•()
+    ;
+}
+extension Extension on core::int {
+}
+static method test() → dynamic {
+  self::ConcreteClass::•;
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:14:17: Error: Constructors on abstract classes can't be torn off.
+  AbstractClass.new; // error
+                ^^^";
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:15:9: Error: Getter not found: 'new'.
+  Mixin.new; // error
+        ^^^";
+  self::NamedMixinApplication::•;
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:17:33: Error: Constructors on abstract classes can't be torn off.
+  AbstractNamedMixinApplication.new; // error
+                                ^^^";
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/abstract_class_constructor_tear_off.dart:18:13: Error: Getter not found: 'new'.
+  Extension.new; // error
+            ^^^";
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart
new file mode 100644
index 0000000..fc8cba1
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart
@@ -0,0 +1,73 @@
+// 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.
+
+final bool inSoundMode = <int?>[] is! List<int>;
+
+main() {
+  print('inSoundMode: $inSoundMode');
+  testInferred();
+}
+
+class Class1 {
+  int field;
+
+  Class1(this.field);
+}
+
+abstract class Interface2 {
+  int get field;
+}
+
+class Class2 implements Interface2 {
+  final field;
+
+  Class2(this.field);
+}
+
+testInferred() {
+  var f1a = Class1.new;
+  expect(true, f1a is Class1 Function(int));
+  expect(false, f1a is Class1 Function(String));
+  var c1a = f1a(0);
+  expect(true, c1a is Class1);
+  () {
+    f1a(''); // error
+  };
+
+  dynamic f1b = Class1.new;
+  var c1b = f1b(0);
+  expect(true, c1b is Class1);
+  throws(() => f1b(''));
+
+  var f2a = Class2.new;
+  expect(true, f2a is Class2 Function(int));
+  expect(false, f2a is Class2 Function(String));
+  var c2a = f2a(0);
+  expect(true, c2a is Class2);
+  () {
+    f2a(''); // error
+  };
+
+  dynamic f2b = Class2.new;
+  var c2b = f2b(0);
+  expect(true, c2b is Class2);
+  throws(() => f2b(''));
+}
+
+expect(expected, actual) {
+  if (expected != actual) throw 'Expected $expected, actual $actual';
+}
+
+throws(Function() f, {bool inSoundModeOnly: false}) {
+  try {
+    f();
+  } catch (e) {
+    print('Thrown: $e');
+    return;
+  }
+  if (!inSoundMode && inSoundModeOnly) {
+    return;
+  }
+  throw 'Expected exception';
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.strong.expect
new file mode 100644
index 0000000..edd7977
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.strong.expect
@@ -0,0 +1,95 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:35:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+//     f1a(''); // error
+//         ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:49:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+//     f2a(''); // error
+//         ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  field core::int field;
+  constructor •(core::int field) → self::Class1
+    : self::Class1::field = field, super core::Object::•()
+    ;
+  static method _#new#tearOff(core::int field) → self::Class1
+    return new self::Class1::•(field);
+}
+abstract class Interface2 extends core::Object {
+  synthetic constructor •() → self::Interface2
+    : super core::Object::•()
+    ;
+  abstract get field() → core::int;
+}
+class Class2 extends core::Object implements self::Interface2 {
+  final field core::int field;
+  constructor •(core::int field) → self::Class2
+    : self::Class2::field = field, super core::Object::•()
+    ;
+  static method _#new#tearOff(core::int field) → self::Class2
+    return new self::Class2::•(field);
+}
+static final field core::bool inSoundMode = !(<core::int?>[] is{ForNonNullableByDefault} core::List<core::int>);
+static method main() → dynamic {
+  core::print("inSoundMode: ${self::inSoundMode}");
+  self::testInferred();
+}
+static method testInferred() → dynamic {
+  (core::int) → self::Class1 f1a = #C1;
+  self::expect(true, f1a is{ForNonNullableByDefault} (core::int) → self::Class1);
+  self::expect(false, f1a is{ForNonNullableByDefault} (core::String) → self::Class1);
+  self::Class1 c1a = f1a(0){(core::int) → self::Class1};
+  self::expect(true, c1a is{ForNonNullableByDefault} self::Class1);
+  () → Null {
+    f1a(let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:35:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+    f1a(''); // error
+        ^" in "" as{TypeError,ForNonNullableByDefault} core::int){(core::int) → self::Class1};
+  };
+  dynamic f1b = #C1;
+  dynamic c1b = f1b{dynamic}.call(0);
+  self::expect(true, c1b is{ForNonNullableByDefault} self::Class1);
+  self::throws(() → dynamic => f1b{dynamic}.call(""));
+  (core::int) → self::Class2 f2a = #C2;
+  self::expect(true, f2a is{ForNonNullableByDefault} (core::int) → self::Class2);
+  self::expect(false, f2a is{ForNonNullableByDefault} (core::String) → self::Class2);
+  self::Class2 c2a = f2a(0){(core::int) → self::Class2};
+  self::expect(true, c2a is{ForNonNullableByDefault} self::Class2);
+  () → Null {
+    f2a(let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:49:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+    f2a(''); // error
+        ^" in "" as{TypeError,ForNonNullableByDefault} core::int){(core::int) → self::Class2};
+  };
+  dynamic f2b = #C2;
+  dynamic c2b = f2b{dynamic}.call(0);
+  self::expect(true, c2b is{ForNonNullableByDefault} self::Class2);
+  self::throws(() → dynamic => f2b{dynamic}.call(""));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C3}) → dynamic {
+  try {
+    f(){() → dynamic};
+  }
+  on core::Object catch(final core::Object e) {
+    core::print("Thrown: ${e}");
+    return;
+  }
+  if(!self::inSoundMode && inSoundModeOnly) {
+    return;
+  }
+  throw "Expected exception";
+}
+
+constants  {
+  #C1 = tearoff self::Class1::_#new#tearOff
+  #C2 = tearoff self::Class2::_#new#tearOff
+  #C3 = false
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.strong.transformed.expect
new file mode 100644
index 0000000..9297490
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.strong.transformed.expect
@@ -0,0 +1,95 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:35:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+//     f1a(''); // error
+//         ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:49:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+//     f2a(''); // error
+//         ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  field core::int field;
+  constructor •(core::int field) → self::Class1
+    : self::Class1::field = field, super core::Object::•()
+    ;
+  static method _#new#tearOff(core::int field) → self::Class1
+    return new self::Class1::•(field);
+}
+abstract class Interface2 extends core::Object {
+  synthetic constructor •() → self::Interface2
+    : super core::Object::•()
+    ;
+  abstract get field() → core::int;
+}
+class Class2 extends core::Object implements self::Interface2 {
+  final field core::int field;
+  constructor •(core::int field) → self::Class2
+    : self::Class2::field = field, super core::Object::•()
+    ;
+  static method _#new#tearOff(core::int field) → self::Class2
+    return new self::Class2::•(field);
+}
+static final field core::bool inSoundMode = !(core::_GrowableList::•<core::int?>(0) is{ForNonNullableByDefault} core::List<core::int>);
+static method main() → dynamic {
+  core::print("inSoundMode: ${self::inSoundMode}");
+  self::testInferred();
+}
+static method testInferred() → dynamic {
+  (core::int) → self::Class1 f1a = #C1;
+  self::expect(true, f1a is{ForNonNullableByDefault} (core::int) → self::Class1);
+  self::expect(false, f1a is{ForNonNullableByDefault} (core::String) → self::Class1);
+  self::Class1 c1a = f1a(0){(core::int) → self::Class1};
+  self::expect(true, c1a is{ForNonNullableByDefault} self::Class1);
+  () → Null {
+    f1a(let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:35:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+    f1a(''); // error
+        ^" in "" as{TypeError,ForNonNullableByDefault} core::int){(core::int) → self::Class1};
+  };
+  dynamic f1b = #C1;
+  dynamic c1b = f1b{dynamic}.call(0);
+  self::expect(true, c1b is{ForNonNullableByDefault} self::Class1);
+  self::throws(() → dynamic => f1b{dynamic}.call(""));
+  (core::int) → self::Class2 f2a = #C2;
+  self::expect(true, f2a is{ForNonNullableByDefault} (core::int) → self::Class2);
+  self::expect(false, f2a is{ForNonNullableByDefault} (core::String) → self::Class2);
+  self::Class2 c2a = f2a(0){(core::int) → self::Class2};
+  self::expect(true, c2a is{ForNonNullableByDefault} self::Class2);
+  () → Null {
+    f2a(let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:49:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+    f2a(''); // error
+        ^" in "" as{TypeError,ForNonNullableByDefault} core::int){(core::int) → self::Class2};
+  };
+  dynamic f2b = #C2;
+  dynamic c2b = f2b{dynamic}.call(0);
+  self::expect(true, c2b is{ForNonNullableByDefault} self::Class2);
+  self::throws(() → dynamic => f2b{dynamic}.call(""));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C3}) → dynamic {
+  try {
+    f(){() → dynamic};
+  }
+  on core::Object catch(final core::Object e) {
+    core::print("Thrown: ${e}");
+    return;
+  }
+  if(!self::inSoundMode && inSoundModeOnly) {
+    return;
+  }
+  throw "Expected exception";
+}
+
+constants  {
+  #C1 = tearoff self::Class1::_#new#tearOff
+  #C2 = tearoff self::Class2::_#new#tearOff
+  #C3 = false
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.textual_outline.expect
new file mode 100644
index 0000000..9e086db
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.textual_outline.expect
@@ -0,0 +1,20 @@
+final bool inSoundMode = <int?>[] is! List<int>;
+main() {}
+
+class Class1 {
+  int field;
+  Class1(this.field);
+}
+
+abstract class Interface2 {
+  int get field;
+}
+
+class Class2 implements Interface2 {
+  final field;
+  Class2(this.field);
+}
+
+testInferred() {}
+expect(expected, actual) {}
+throws(Function() f, {bool inSoundModeOnly: false}) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..8944ae7
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.textual_outline_modelled.expect
@@ -0,0 +1,19 @@
+abstract class Interface2 {
+  int get field;
+}
+
+class Class1 {
+  Class1(this.field);
+  int field;
+}
+
+class Class2 implements Interface2 {
+  Class2(this.field);
+  final field;
+}
+
+expect(expected, actual) {}
+final bool inSoundMode = <int?>[] is! List<int>;
+main() {}
+testInferred() {}
+throws(Function() f, {bool inSoundModeOnly: false}) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.expect
new file mode 100644
index 0000000..edd7977
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.expect
@@ -0,0 +1,95 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:35:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+//     f1a(''); // error
+//         ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:49:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+//     f2a(''); // error
+//         ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  field core::int field;
+  constructor •(core::int field) → self::Class1
+    : self::Class1::field = field, super core::Object::•()
+    ;
+  static method _#new#tearOff(core::int field) → self::Class1
+    return new self::Class1::•(field);
+}
+abstract class Interface2 extends core::Object {
+  synthetic constructor •() → self::Interface2
+    : super core::Object::•()
+    ;
+  abstract get field() → core::int;
+}
+class Class2 extends core::Object implements self::Interface2 {
+  final field core::int field;
+  constructor •(core::int field) → self::Class2
+    : self::Class2::field = field, super core::Object::•()
+    ;
+  static method _#new#tearOff(core::int field) → self::Class2
+    return new self::Class2::•(field);
+}
+static final field core::bool inSoundMode = !(<core::int?>[] is{ForNonNullableByDefault} core::List<core::int>);
+static method main() → dynamic {
+  core::print("inSoundMode: ${self::inSoundMode}");
+  self::testInferred();
+}
+static method testInferred() → dynamic {
+  (core::int) → self::Class1 f1a = #C1;
+  self::expect(true, f1a is{ForNonNullableByDefault} (core::int) → self::Class1);
+  self::expect(false, f1a is{ForNonNullableByDefault} (core::String) → self::Class1);
+  self::Class1 c1a = f1a(0){(core::int) → self::Class1};
+  self::expect(true, c1a is{ForNonNullableByDefault} self::Class1);
+  () → Null {
+    f1a(let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:35:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+    f1a(''); // error
+        ^" in "" as{TypeError,ForNonNullableByDefault} core::int){(core::int) → self::Class1};
+  };
+  dynamic f1b = #C1;
+  dynamic c1b = f1b{dynamic}.call(0);
+  self::expect(true, c1b is{ForNonNullableByDefault} self::Class1);
+  self::throws(() → dynamic => f1b{dynamic}.call(""));
+  (core::int) → self::Class2 f2a = #C2;
+  self::expect(true, f2a is{ForNonNullableByDefault} (core::int) → self::Class2);
+  self::expect(false, f2a is{ForNonNullableByDefault} (core::String) → self::Class2);
+  self::Class2 c2a = f2a(0){(core::int) → self::Class2};
+  self::expect(true, c2a is{ForNonNullableByDefault} self::Class2);
+  () → Null {
+    f2a(let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:49:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+    f2a(''); // error
+        ^" in "" as{TypeError,ForNonNullableByDefault} core::int){(core::int) → self::Class2};
+  };
+  dynamic f2b = #C2;
+  dynamic c2b = f2b{dynamic}.call(0);
+  self::expect(true, c2b is{ForNonNullableByDefault} self::Class2);
+  self::throws(() → dynamic => f2b{dynamic}.call(""));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C3}) → dynamic {
+  try {
+    f(){() → dynamic};
+  }
+  on core::Object catch(final core::Object e) {
+    core::print("Thrown: ${e}");
+    return;
+  }
+  if(!self::inSoundMode && inSoundModeOnly) {
+    return;
+  }
+  throw "Expected exception";
+}
+
+constants  {
+  #C1 = tearoff self::Class1::_#new#tearOff
+  #C2 = tearoff self::Class2::_#new#tearOff
+  #C3 = false
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.outline.expect
new file mode 100644
index 0000000..802525c
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.outline.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  field core::int field;
+  constructor •(core::int field) → self::Class1
+    ;
+  static method _#new#tearOff(core::int field) → self::Class1
+    return new self::Class1::•(field);
+}
+abstract class Interface2 extends core::Object {
+  synthetic constructor •() → self::Interface2
+    ;
+  abstract get field() → core::int;
+}
+class Class2 extends core::Object implements self::Interface2 {
+  final field core::int field;
+  constructor •(core::int field) → self::Class2
+    ;
+  static method _#new#tearOff(core::int field) → self::Class2
+    return new self::Class2::•(field);
+}
+static final field core::bool inSoundMode;
+static method main() → dynamic
+  ;
+static method testInferred() → dynamic
+  ;
+static method expect(dynamic expected, dynamic actual) → dynamic
+  ;
+static method throws(() → dynamic f, {core::bool inSoundModeOnly}) → dynamic
+  ;
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.transformed.expect
new file mode 100644
index 0000000..9297490
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart.weak.transformed.expect
@@ -0,0 +1,95 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:35:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+//     f1a(''); // error
+//         ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:49:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+//     f2a(''); // error
+//         ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  field core::int field;
+  constructor •(core::int field) → self::Class1
+    : self::Class1::field = field, super core::Object::•()
+    ;
+  static method _#new#tearOff(core::int field) → self::Class1
+    return new self::Class1::•(field);
+}
+abstract class Interface2 extends core::Object {
+  synthetic constructor •() → self::Interface2
+    : super core::Object::•()
+    ;
+  abstract get field() → core::int;
+}
+class Class2 extends core::Object implements self::Interface2 {
+  final field core::int field;
+  constructor •(core::int field) → self::Class2
+    : self::Class2::field = field, super core::Object::•()
+    ;
+  static method _#new#tearOff(core::int field) → self::Class2
+    return new self::Class2::•(field);
+}
+static final field core::bool inSoundMode = !(core::_GrowableList::•<core::int?>(0) is{ForNonNullableByDefault} core::List<core::int>);
+static method main() → dynamic {
+  core::print("inSoundMode: ${self::inSoundMode}");
+  self::testInferred();
+}
+static method testInferred() → dynamic {
+  (core::int) → self::Class1 f1a = #C1;
+  self::expect(true, f1a is{ForNonNullableByDefault} (core::int) → self::Class1);
+  self::expect(false, f1a is{ForNonNullableByDefault} (core::String) → self::Class1);
+  self::Class1 c1a = f1a(0){(core::int) → self::Class1};
+  self::expect(true, c1a is{ForNonNullableByDefault} self::Class1);
+  () → Null {
+    f1a(let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:35:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+    f1a(''); // error
+        ^" in "" as{TypeError,ForNonNullableByDefault} core::int){(core::int) → self::Class1};
+  };
+  dynamic f1b = #C1;
+  dynamic c1b = f1b{dynamic}.call(0);
+  self::expect(true, c1b is{ForNonNullableByDefault} self::Class1);
+  self::throws(() → dynamic => f1b{dynamic}.call(""));
+  (core::int) → self::Class2 f2a = #C2;
+  self::expect(true, f2a is{ForNonNullableByDefault} (core::int) → self::Class2);
+  self::expect(false, f2a is{ForNonNullableByDefault} (core::String) → self::Class2);
+  self::Class2 c2a = f2a(0){(core::int) → self::Class2};
+  self::expect(true, c2a is{ForNonNullableByDefault} self::Class2);
+  () → Null {
+    f2a(let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/inferred_constructor_tear_off.dart:49:9: Error: The argument type 'String' can't be assigned to the parameter type 'int'.
+    f2a(''); // error
+        ^" in "" as{TypeError,ForNonNullableByDefault} core::int){(core::int) → self::Class2};
+  };
+  dynamic f2b = #C2;
+  dynamic c2b = f2b{dynamic}.call(0);
+  self::expect(true, c2b is{ForNonNullableByDefault} self::Class2);
+  self::throws(() → dynamic => f2b{dynamic}.call(""));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C3}) → dynamic {
+  try {
+    f(){() → dynamic};
+  }
+  on core::Object catch(final core::Object e) {
+    core::print("Thrown: ${e}");
+    return;
+  }
+  if(!self::inSoundMode && inSoundModeOnly) {
+    return;
+  }
+  throw "Expected exception";
+}
+
+constants  {
+  #C1 = tearoff self::Class1::_#new#tearOff
+  #C2 = tearoff self::Class2::_#new#tearOff
+  #C3 = false
+}
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 322bacf..a694e4f 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -6,6 +6,7 @@
 # the round trip for Kernel textual serialization where the initial binary
 # Kernel files are produced by compiling Dart code via Fasta.
 
+constructor_tearoffs/abstract_class_constructor_tear_off: TextSerializationFailure
 constructor_tearoffs/generic_tearoff_with_context: TextSerializationFailure
 constructor_tearoffs/generic_tearoff_without_context: TextSerializationFailure
 constructor_tearoffs/instantiation: TypeCheckError