Version 2.14.0-344.0.dev

Merge commit 'b89c35472f7b5cf41be01111620f92c644cb4c74' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 666d9be..41a201a 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -2361,6 +2361,15 @@
   /// children of this element's parent.
   String get identifier => name!;
 
+  /// Return `true` if this element was created from a macro-generated code.
+  bool get isFromMacro {
+    return hasModifier(Modifier.IS_FROM_MACRO);
+  }
+
+  set isFromMacro(bool isFromMacro) {
+    setModifier(Modifier.IS_FROM_MACRO, isFromMacro);
+  }
+
   bool get isNonFunctionTypeAliasesEnabled {
     return library!.featureSet.isEnabled(Feature.nonfunction_type_aliases);
   }
@@ -4281,23 +4290,26 @@
   /// type being referred to is the return type.
   static const Modifier IMPLICIT_TYPE = Modifier('IMPLICIT_TYPE', 16);
 
+  /// Indicates that this element was created from macro-generated code.
+  static const Modifier IS_FROM_MACRO = Modifier('IS_FROM_MACRO', 17);
+
   /// Indicates that modifier 'lazy' was applied to the element.
-  static const Modifier LATE = Modifier('LATE', 17);
+  static const Modifier LATE = Modifier('LATE', 18);
 
   /// Indicates that a class is a mixin application.
-  static const Modifier MIXIN_APPLICATION = Modifier('MIXIN_APPLICATION', 18);
+  static const Modifier MIXIN_APPLICATION = Modifier('MIXIN_APPLICATION', 19);
 
   /// Indicates that the pseudo-modifier 'set' was applied to the element.
-  static const Modifier SETTER = Modifier('SETTER', 19);
+  static const Modifier SETTER = Modifier('SETTER', 20);
 
   /// Indicates that the modifier 'static' was applied to the element.
-  static const Modifier STATIC = Modifier('STATIC', 20);
+  static const Modifier STATIC = Modifier('STATIC', 21);
 
   /// Indicates that the element does not appear in the source code but was
   /// implicitly created. For example, if a class does not define any
   /// constructors, an implicit zero-argument constructor will be created and it
   /// will be marked as being synthetic.
-  static const Modifier SYNTHETIC = Modifier('SYNTHETIC', 21);
+  static const Modifier SYNTHETIC = Modifier('SYNTHETIC', 22);
 
   static const List<Modifier> values = [
     ABSTRACT,
@@ -4316,6 +4328,7 @@
     HAS_INITIALIZER,
     HAS_PART_OF_DIRECTIVE,
     IMPLICIT_TYPE,
+    IS_FROM_MACRO,
     LATE,
     MIXIN_APPLICATION,
     SETTER,
diff --git a/pkg/analyzer/lib/src/macro/api/code.dart b/pkg/analyzer/lib/src/macro/api/code.dart
new file mode 100644
index 0000000..0c06310
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/api/code.dart
@@ -0,0 +1,58 @@
+// 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.
+
+/// Combines [parts] into a [String].
+/// Must only contain [Code] or [String] instances.
+String _combineParts(List<Object> parts) {
+  var buffer = StringBuffer();
+  for (var part in parts) {
+    if (part is String) {
+      buffer.write(part);
+    } else if (part is Code) {
+      buffer.write(part.code);
+    } else if (part is List<Code>) {
+      buffer.write(part.map((p) => p.code).join());
+    } else {
+      throw UnsupportedError(
+        'Only String, Code, and List<Code> are allowed but got $part',
+      );
+    }
+  }
+  return buffer.toString();
+}
+
+/// The representation of a piece of code.
+abstract class Code {
+  String get code;
+
+  @override
+  String toString() => code;
+}
+
+/// A piece of code representing a syntactically valid declaration.
+class Declaration extends Code {
+  @override
+  final String code;
+
+  Declaration(this.code);
+
+  /// Creates a [Declaration] from [parts], which must be of type [Code],
+  /// `List<Code>`, or [String].
+  factory Declaration.fromParts(List<Object> parts) =>
+      Declaration(_combineParts(parts));
+}
+
+/// A piece of code that can't be parsed into a valid language construct in its
+/// current form. No validation or parsing is performed.
+class Fragment extends Code {
+  @override
+  final String code;
+
+  Fragment(this.code);
+
+  /// Creates a [Fragment] from [parts], which must be of type [Code],
+  /// `List<Code>`, or [String].
+  factory Fragment.fromParts(List<Object> parts) =>
+      Fragment(_combineParts(parts));
+}
diff --git a/pkg/analyzer/lib/src/macro/api/macro.dart b/pkg/analyzer/lib/src/macro/api/macro.dart
new file mode 100644
index 0000000..0f65d38
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/api/macro.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/dart/ast/ast.dart' as ast;
+import 'package:analyzer/src/macro/api/code.dart';
+
+/// The api used by [DeclarationMacro]s to contribute new declarations to the
+/// current class.
+///
+/// Note that this is available to macros that run directly on classes, as well
+/// as macros that run on any members of a class.
+abstract class ClassDeclarationBuilder implements DeclarationBuilder {
+  /// Adds a new declaration to the surrounding class.
+  void addToClass(Declaration declaration);
+}
+
+/// The api used by [DeclarationMacro]s to contribute new declarations to the
+/// current library.
+abstract class DeclarationBuilder {
+  /// Adds a new regular declaration to the surrounding library.
+  ///
+  /// Note that type declarations are not supported.
+  void addToLibrary(Declaration declaration);
+
+  /// Return the [Code] of the [node].
+  Code typeAnnotationCode(ast.TypeAnnotation node);
+}
+
+/// The marker interface for macros that are allowed to contribute new
+/// declarations to the program, including both top level and class level
+/// declarations.
+///
+/// These macros run after [TypeMacro] macros, but before [DefinitionMacro]
+/// macros.
+///
+/// These macros can resolve type annotations to specific declarations, and
+/// inspect type hierarchies, but they cannot inspect the declarations on those
+/// type annotations, since new declarations could still be added in this phase.
+abstract class DeclarationMacro implements Macro {}
+
+/// The marker interface for macros that are only allowed to implement or wrap
+/// existing declarations in the program. They cannot introduce any new
+/// declarations that are visible to the program, but are allowed to add
+/// declarations that only they can see.
+///
+/// These macros run after all other types of macros.
+///
+/// These macros can fully reflect on the program since the static shape is
+/// fully defined by the time they run.
+abstract class DefinitionMacro implements Macro {}
+
+/// The interface for [DeclarationMacro]s that can be applied to fields.
+abstract class FieldDeclarationMacro implements DeclarationMacro {
+  void visitFieldDeclaration(
+    ast.FieldDeclaration declaration,
+    ClassDeclarationBuilder builder,
+  );
+}
+
+/// The marker interface for all types of macros.
+abstract class Macro {}
+
+/// The marker interface for macros that are allowed to contribute new type
+/// declarations into the program.
+///
+/// These macros run before all other types of macros.
+///
+/// In exchange for the power to add new type declarations, these macros have
+/// limited introspections capabilities, since new types can be added in this
+/// phase you cannot follow type references back to their declarations.
+abstract class TypeMacro implements Macro {}
diff --git a/pkg/analyzer/lib/src/macro/builders/observable.dart b/pkg/analyzer/lib/src/macro/builders/observable.dart
new file mode 100644
index 0000000..6857d34
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/builders/observable.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/dart/ast/ast.dart' as ast;
+import 'package:analyzer/src/macro/api/code.dart';
+import 'package:analyzer/src/macro/api/macro.dart';
+
+class ObservableMacro implements FieldDeclarationMacro {
+  const ObservableMacro();
+
+  @override
+  void visitFieldDeclaration(
+    ast.FieldDeclaration node,
+    ClassDeclarationBuilder builder,
+  ) {
+    var typeNode = node.fields.type;
+    if (typeNode == null) {
+      throw ArgumentError('@observable can only annotate typed fields.');
+    }
+    var typeCode = builder.typeAnnotationCode(typeNode);
+
+    var fields = node.fields.variables;
+    for (var field in fields) {
+      var name = field.name.name;
+      if (!name.startsWith('_')) {
+        throw ArgumentError(
+          '@observable can only annotate private fields, and it will create '
+          'public getters and setters for them, but the public field '
+          '$name was annotated.',
+        );
+      }
+      var publicName = name.substring(1);
+
+      var getter = Declaration(
+        '$typeCode get $publicName => $name;',
+      );
+      builder.addToClass(getter);
+
+      var setter = Declaration('''
+set $publicName($typeCode val) {
+  print('Setting $publicName to \${val}');
+  $name = val;
+}''');
+      builder.addToClass(setter);
+    }
+  }
+}
diff --git a/pkg/analyzer/lib/src/macro/impl/macro.dart b/pkg/analyzer/lib/src/macro/impl/macro.dart
new file mode 100644
index 0000000..6459a6c
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/impl/macro.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.
+
+import 'package:analyzer/dart/analysis/utilities.dart';
+import 'package:analyzer/dart/ast/ast.dart' as ast;
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/src/dart/ast/ast.dart' as ast;
+import 'package:analyzer/src/macro/api/code.dart';
+import 'package:analyzer/src/macro/api/macro.dart';
+
+class ClassDeclarationBuilderImpl extends DeclarationBuilderImpl
+    implements ClassDeclarationBuilder {
+  final ast.ClassDeclarationImpl node;
+
+  ClassDeclarationBuilderImpl(this.node);
+
+  @override
+  void addToClass(Declaration declaration) {
+    // TODO(scheglov) feature set
+    // TODO(scheglov) throw if errors?
+    var parseResult = parseString(
+      content: 'class ${node.name.name} { $declaration }',
+    );
+    var parsedDeclarations = parseResult.unit.declarations;
+    var parsedClass = parsedDeclarations.single as ast.ClassDeclaration;
+    var parsedMember = parsedClass.members.single;
+    _resetOffsets(parsedMember);
+
+    node.members.add(parsedMember);
+  }
+
+  /// We parsed [node] in the context of some synthetic code string, its
+  /// current offsets are meaningless. So, we reset them for now.
+  static void _resetOffsets(ast.AstNode node) {
+    for (Token? t = node.beginToken;
+        t != null && t != node.endToken;
+        t = t.next) {
+      t.offset = -1;
+    }
+  }
+}
+
+class DeclarationBuilderImpl implements DeclarationBuilder {
+  @override
+  void addToLibrary(Declaration declaration) {
+    // TODO: implement addToLibrary
+  }
+
+  @override
+  Code typeAnnotationCode(ast.TypeAnnotation node) {
+    return Fragment(node.toSource());
+  }
+}
diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart
index 3737f6e..983c60a 100644
--- a/pkg/analyzer/lib/src/summary2/element_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/element_builder.dart
@@ -89,6 +89,34 @@
     }
   }
 
+  /// Build elements for [members] and add into the [element].
+  void buildMacroClassMembers(
+    ClassElementImpl element,
+    List<ClassMember> members,
+  ) {
+    var holder = _buildClassMembers(element, members);
+
+    for (var newElement in holder.propertyAccessors) {
+      newElement.isFromMacro = true;
+      element.accessors.add(newElement);
+    }
+
+    for (var newElement in holder.constructors) {
+      newElement.isFromMacro = true;
+      element.constructors.add(newElement);
+    }
+
+    for (var newElement in holder.properties.whereType<FieldElementImpl>()) {
+      newElement.isFromMacro = true;
+      element.fields.add(newElement);
+    }
+
+    for (var newElement in holder.methods) {
+      newElement.isFromMacro = true;
+      element.methods.add(newElement);
+    }
+  }
+
   @override
   void visitClassDeclaration(covariant ClassDeclarationImpl node) {
     var nameNode = node.name;
@@ -868,7 +896,7 @@
   }
 
   _EnclosingContext _buildClassMembers(
-      ElementImpl element, NodeList<ClassMember> members) {
+      ElementImpl element, List<ClassMember> members) {
     var hasConstConstructor = members.any((e) {
       return e is ConstructorDeclaration && e.constKeyword != null;
     });
diff --git a/pkg/analyzer/lib/src/summary2/element_flags.dart b/pkg/analyzer/lib/src/summary2/element_flags.dart
index e808000..f43df16 100644
--- a/pkg/analyzer/lib/src/summary2/element_flags.dart
+++ b/pkg/analyzer/lib/src/summary2/element_flags.dart
@@ -8,12 +8,14 @@
 
 class ClassElementFlags {
   static const int _isAbstract = 1 << 0;
-  static const int _isMixinApplication = 1 << 1;
-  static const int _isSimplyBounded = 1 << 2;
+  static const int _isFromMacro = 1 << 1;
+  static const int _isMixinApplication = 1 << 2;
+  static const int _isSimplyBounded = 1 << 3;
 
   static void read(SummaryDataReader reader, ClassElementImpl element) {
     var byte = reader.readByte();
     element.isAbstract = (byte & _isAbstract) != 0;
+    element.isFromMacro = (byte & _isFromMacro) != 0;
     element.isMixinApplication = (byte & _isMixinApplication) != 0;
     element.isSimplyBounded = (byte & _isSimplyBounded) != 0;
   }
@@ -21,6 +23,7 @@
   static void write(BufferedSink sink, ClassElementImpl element) {
     var result = 0;
     result |= element.isAbstract ? _isAbstract : 0;
+    result |= element.isFromMacro ? _isFromMacro : 0;
     result |= element.isMixinApplication ? _isMixinApplication : 0;
     result |= element.isSimplyBounded ? _isSimplyBounded : 0;
     sink.writeByte(result);
@@ -60,8 +63,9 @@
   static const int _isCovariant = 1 << 5;
   static const int _isExternal = 1 << 6;
   static const int _isFinal = 1 << 7;
-  static const int _isLate = 1 << 8;
-  static const int _isStatic = 1 << 9;
+  static const int _isFromMacro = 1 << 8;
+  static const int _isLate = 1 << 9;
+  static const int _isStatic = 1 << 10;
 
   static void read(SummaryDataReader reader, FieldElementImpl element) {
     var byte = reader.readUInt30();
@@ -73,6 +77,7 @@
     element.isCovariant = (byte & _isCovariant) != 0;
     element.isExternal = (byte & _isExternal) != 0;
     element.isFinal = (byte & _isFinal) != 0;
+    element.isFromMacro = (byte & _isFromMacro) != 0;
     element.isLate = (byte & _isLate) != 0;
     element.isStatic = (byte & _isStatic) != 0;
   }
@@ -87,6 +92,7 @@
     result |= element.isCovariant ? _isCovariant : 0;
     result |= element.isExternal ? _isExternal : 0;
     result |= element.isFinal ? _isFinal : 0;
+    result |= element.isFromMacro ? _isFromMacro : 0;
     result |= element.isLate ? _isLate : 0;
     result |= element.isStatic ? _isStatic : 0;
     sink.writeUInt30(result);
@@ -161,8 +167,9 @@
   static const int _isAbstract = 1 << 1;
   static const int _isAsynchronous = 1 << 2;
   static const int _isExternal = 1 << 3;
-  static const int _isGenerator = 1 << 4;
-  static const int _isStatic = 1 << 5;
+  static const int _isFromMacro = 1 << 4;
+  static const int _isGenerator = 1 << 5;
+  static const int _isStatic = 1 << 6;
 
   static void read(SummaryDataReader reader, MethodElementImpl element) {
     var byte = reader.readByte();
@@ -170,6 +177,7 @@
     element.isAbstract = (byte & _isAbstract) != 0;
     element.isAsynchronous = (byte & _isAsynchronous) != 0;
     element.isExternal = (byte & _isExternal) != 0;
+    element.isFromMacro = (byte & _isFromMacro) != 0;
     element.isGenerator = (byte & _isGenerator) != 0;
     element.isStatic = (byte & _isStatic) != 0;
   }
@@ -180,6 +188,7 @@
     result |= element.isAbstract ? _isAbstract : 0;
     result |= element.isAsynchronous ? _isAsynchronous : 0;
     result |= element.isExternal ? _isExternal : 0;
+    result |= element.isFromMacro ? _isFromMacro : 0;
     result |= element.isGenerator ? _isGenerator : 0;
     result |= element.isStatic ? _isStatic : 0;
     sink.writeByte(result);
@@ -217,8 +226,9 @@
   static const int _isAbstract = 1 << 3;
   static const int _isAsynchronous = 1 << 4;
   static const int _isExternal = 1 << 5;
-  static const int _isGenerator = 1 << 6;
-  static const int _isStatic = 1 << 7;
+  static const int _isFromMacro = 1 << 6;
+  static const int _isGenerator = 1 << 7;
+  static const int _isStatic = 1 << 8;
 
   static void read(
     SummaryDataReader reader,
@@ -231,6 +241,7 @@
     element.isAbstract = (byte & _isAbstract) != 0;
     element.isAsynchronous = (byte & _isAsynchronous) != 0;
     element.isExternal = (byte & _isExternal) != 0;
+    element.isFromMacro = (byte & _isFromMacro) != 0;
     element.isGenerator = (byte & _isGenerator) != 0;
     element.isStatic = (byte & _isStatic) != 0;
   }
@@ -243,6 +254,7 @@
     result |= element.isAbstract ? _isAbstract : 0;
     result |= element.isAsynchronous ? _isAsynchronous : 0;
     result |= element.isExternal ? _isExternal : 0;
+    result |= element.isFromMacro ? _isFromMacro : 0;
     result |= element.isGenerator ? _isGenerator : 0;
     result |= element.isStatic ? _isStatic : 0;
     sink.writeUInt30(result);
diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart
index ba1e230..e500aa1 100644
--- a/pkg/analyzer/lib/src/summary2/library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/library_builder.dart
@@ -9,6 +9,8 @@
 import 'package:analyzer/src/dart/ast/mixin_super_invoked_names.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
+import 'package:analyzer/src/macro/builders/observable.dart' as macro;
+import 'package:analyzer/src/macro/impl/macro.dart' as macro;
 import 'package:analyzer/src/summary2/combinator.dart';
 import 'package:analyzer/src/summary2/constructor_initializer_resolver.dart';
 import 'package:analyzer/src/summary2/default_value_resolver.dart';
@@ -159,18 +161,69 @@
 
   void resolveTypes(NodesToBuildType nodesToBuildType) {
     for (var linkingUnit in units) {
-      var resolver = ReferenceResolver(
-        linker,
-        nodesToBuildType,
-        linker.elementFactory,
-        element,
-        linkingUnit.reference,
-        linkingUnit.node.featureSet.isEnabled(Feature.non_nullable),
-      );
+      var resolver = _newTypeReferenceResolver(nodesToBuildType, linkingUnit);
       linkingUnit.node.accept(resolver);
     }
   }
 
+  /// Run built-in declaration macros.
+  void runDeclarationMacros() {
+    bool hasMacroAnnotation(ast.AnnotatedNode node, String name) {
+      for (var annotation in node.metadata) {
+        var nameNode = annotation.name;
+        if (nameNode is ast.SimpleIdentifier &&
+            annotation.arguments == null &&
+            annotation.constructorName == null &&
+            nameNode.name == name) {
+          var nameElement = element.scope.lookup(name).getter;
+          return nameElement != null &&
+              nameElement.library?.name == 'analyzer.macro.annotations';
+        }
+      }
+      return false;
+    }
+
+    for (var linkingUnit in units) {
+      for (var declaration in linkingUnit.node.declarations) {
+        if (declaration is ast.ClassDeclarationImpl) {
+          var members = declaration.members.toList();
+          for (var member in members) {
+            if (member is ast.FieldDeclarationImpl) {
+              if (hasMacroAnnotation(member, 'observable')) {
+                macro.ObservableMacro().visitFieldDeclaration(
+                  member,
+                  macro.ClassDeclarationBuilderImpl(declaration),
+                );
+              }
+            }
+          }
+
+          var newMembers = declaration.members.sublist(members.length);
+          if (newMembers.isNotEmpty) {
+            var elementBuilder = ElementBuilder(
+              libraryBuilder: this,
+              unitReference: linkingUnit.reference,
+              unitElement: linkingUnit.element,
+            );
+            var classElement = declaration.declaredElement as ClassElementImpl;
+            elementBuilder.buildMacroClassMembers(classElement, newMembers);
+
+            // TODO(scheglov) extract
+            {
+              var nodesToBuildType = NodesToBuildType();
+              var resolver =
+                  _newTypeReferenceResolver(nodesToBuildType, linkingUnit);
+              for (var newMember in newMembers) {
+                newMember.accept(resolver);
+              }
+              TypesBuilder(linker).build(nodesToBuildType);
+            }
+          }
+        }
+      }
+    }
+  }
+
   void storeExportScope() {
     exports = exportScope.map.values.toList();
     linker.elementFactory.setExportsOfLibrary('$uri', exports);
@@ -205,6 +258,21 @@
     }
   }
 
+  ReferenceResolver _newTypeReferenceResolver(
+    NodesToBuildType nodesToBuildType,
+    LinkingUnit linkingUnit,
+  ) {
+    /// TODO(scheglov) Do we need all these parameters?
+    return ReferenceResolver(
+      linker,
+      nodesToBuildType,
+      linker.elementFactory,
+      element,
+      linkingUnit.reference,
+      linkingUnit.node.featureSet.isEnabled(Feature.non_nullable),
+    );
+  }
+
   static void build(Linker linker, LinkInputLibrary inputLibrary) {
     var elementFactory = linker.elementFactory;
 
diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart
index f0b0174..d0fe559 100644
--- a/pkg/analyzer/lib/src/summary2/link.dart
+++ b/pkg/analyzer/lib/src/summary2/link.dart
@@ -93,6 +93,7 @@
     _createTypeSystem();
     _buildEnumChildren();
     _resolveTypes();
+    _runDeclarationMacros();
     _performTopLevelInference();
     _resolveConstructors();
     _resolveConstantInitializers();
@@ -213,6 +214,12 @@
     TypesBuilder(this).build(nodesToBuildType);
   }
 
+  void _runDeclarationMacros() {
+    for (var library in builders.values) {
+      library.runDeclarationMacros();
+    }
+  }
+
   void _writeLibraries() {
     var bundleWriter = BundleWriter(
       elementFactory.dynamicRef,
diff --git a/pkg/analyzer/test/src/dart/resolution/macro_test.dart b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
new file mode 100644
index 0000000..4dd8ebc
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../generated/elements_types_mixin.dart';
+import 'context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(MacroResolutionTest);
+  });
+}
+
+@reflectiveTest
+class MacroResolutionTest extends PubPackageResolutionTest
+    with ElementsTypesMixin {
+  @override
+  void setUp() {
+    super.setUp();
+
+    newFile('$testPackageLibPath/macro_annotations.dart', content: r'''
+library analyzer.macro.annotations;
+const observable = 0;
+''');
+  }
+
+  test_observable() async {
+    await assertErrorsInCode(r'''
+import 'macro_annotations.dart';
+
+class A {
+  @observable
+  int _foo = 0;
+}
+
+void f(A a) {
+  a.foo;
+  a.foo = 2;
+}
+''', [
+      error(HintCode.UNUSED_FIELD, 64, 4),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart
index 5f1c71d..08c23ad 100644
--- a/pkg/analyzer/test/src/dart/resolution/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart
@@ -42,6 +42,7 @@
 import 'library_element_test.dart' as library_element;
 import 'local_function_test.dart' as local_function;
 import 'local_variable_test.dart' as local_variable;
+import 'macro_test.dart' as macro;
 import 'metadata_test.dart' as metadata;
 import 'method_declaration_test.dart' as method_declaration;
 import 'method_invocation_test.dart' as method_invocation;
@@ -101,6 +102,7 @@
     library_element.main();
     local_function.main();
     local_variable.main();
+    macro.main();
     metadata.main();
     method_declaration.main();
     method_invocation.main();
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index acbd956..e7884a5 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -645,6 +645,8 @@
   }
 
   void _writePropertyAccessorElement(PropertyAccessorElement e) {
+    e as PropertyAccessorElementImpl;
+
     PropertyInducingElement variable = e.variable;
     expect(variable, isNotNull);
 
@@ -667,7 +669,7 @@
       }
     }
 
-    if (e.isSynthetic) {
+    if (e.isSynthetic || e.isFromMacro) {
       expect(e.nameOffset, -1);
     } else {
       expect(e.nameOffset, isPositive);
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index caee60d..8c5f16e 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -21246,6 +21246,58 @@
 ''');
   }
 
+  test_macro_observable() async {
+    addLibrarySource('/macro_annotations.dart', r'''
+library analyzer.macro.annotations;
+const observable = 0;
+''');
+    var library = await checkLibrary(r'''
+import 'macro_annotations.dart';
+class A {
+  @observable
+  int _f = 0;
+}
+''');
+    checkElementText(library, r'''
+library
+  imports
+    macro_annotations.dart
+  definingUnit
+    classes
+      class A @39
+        fields
+          _f @63
+            metadata
+              Annotation
+                atSign: @ @45
+                element: macro_annotations.dart::@getter::observable
+                name: SimpleIdentifier
+                  staticElement: macro_annotations.dart::@getter::observable
+                  staticType: null
+                  token: observable @46
+            type: int
+          synthetic f @-1
+            type: int
+        constructors
+          synthetic @-1
+        accessors
+          synthetic get _f @-1
+            returnType: int
+          synthetic set _f @-1
+            parameters
+              requiredPositional __f @-1
+                type: int
+            returnType: void
+          get f @-1
+            returnType: int
+          set f @-1
+            parameters
+              requiredPositional val @-1
+                type: int
+            returnType: void
+''');
+  }
+
   test_main_class() async {
     var library = await checkLibrary('class main {}');
     checkElementText(library, r'''
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index d7811e0..64b66be 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -139,6 +139,9 @@
   /// processed its initial paused state).
   Future<void> get debuggerInitialized => _debuggerInitializedCompleter.future;
 
+  bool get evaluateToStringInDebugViews =>
+      args.evaluateToStringInDebugViews ?? false;
+
   /// [attachRequest] is called by the client when it wants us to to attach to
   /// an existing app. This will only be called once (and only one of this or
   /// launchRequest will be called).
@@ -318,6 +321,15 @@
         sendResponse(protocols?.toJson());
         break;
 
+      /// Used to toggle debug settings such as whether SDK/Packages are
+      /// debuggable while the session is in progress.
+      case 'updateDebugOptions':
+        if (args != null) {
+          await _updateDebugOptions(args.args);
+        }
+        sendResponse(null);
+        break;
+
       default:
         await super.customRequest(request, args, sendResponse);
     }
@@ -421,7 +433,7 @@
       final resultString = await _converter.convertVmInstanceRefToDisplayString(
         thread,
         result,
-        allowCallingToString: true,
+        allowCallingToString: evaluateToStringInDebugViews,
       );
       // TODO(dantup): We may need to store `expression` with this data
       // to allow building nested evaluateNames.
@@ -845,7 +857,8 @@
             thread,
             variable.value,
             name: variable.name,
-            allowCallingToString: index <= maxToStringsPerEvaluation,
+            allowCallingToString: evaluateToStringInDebugViews &&
+                index <= maxToStringsPerEvaluation,
           );
         }
 
@@ -871,6 +884,7 @@
         variables.addAll(await _converter.convertVmInstanceToVariablesList(
           thread,
           object,
+          allowCallingToString: evaluateToStringInDebugViews,
           startItem: childStart,
           numItems: childCount,
         ));
@@ -972,8 +986,15 @@
       if (ref == null || ref.kind == vm.InstanceKind.kNull) {
         return null;
       }
-      // TODO(dantup): This should handle truncation and complex types.
-      return ref.valueAsString;
+      return _converter.convertVmInstanceRefToDisplayString(
+        thread,
+        ref,
+        // Always allow calling toString() here as the user expects the full
+        // string they logged regardless of the evaluateToStringInDebugViews
+        // setting.
+        allowCallingToString: true,
+        includeQuotesAroundString: false,
+      );
     }
 
     var loggerName = await asString(record.loggerName);
@@ -987,13 +1008,13 @@
     final prefix = '[$loggerName] ';
 
     if (message != null) {
-      sendPrefixedOutput('stdout', prefix, '$message\n');
+      sendPrefixedOutput('console', prefix, '$message\n');
     }
     if (error != null) {
-      sendPrefixedOutput('stderr', prefix, '$error\n');
+      sendPrefixedOutput('console', prefix, '$error\n');
     }
     if (stack != null) {
-      sendPrefixedOutput('stderr', prefix, '$stack\n');
+      sendPrefixedOutput('console', prefix, '$stack\n');
     }
   }
 
@@ -1009,7 +1030,29 @@
     // Notify IsolateManager if we'll be debugging so it knows whether to set
     // up breakpoints etc. when isolates are registered.
     final debug = !(args.noDebug ?? false);
-    _isolateManager.setDebugEnabled(debug);
+    _isolateManager.debug = debug;
+    _isolateManager.debugSdkLibraries = args.debugSdkLibraries ?? true;
+    _isolateManager.debugExternalPackageLibraries =
+        args.debugExternalPackageLibraries ?? true;
+  }
+
+  /// Updates the current debug options for the session.
+  ///
+  /// Clients may not know about all debug options, so anything not included
+  /// in the map will not be updated by this method.
+  Future<void> _updateDebugOptions(Map<String, Object?> args) async {
+    // TODO(dantup): Document this - it's a public API we expect to be used
+    //   by editors that can support it (although it will require custom
+    //   code as it's there's no DAP standard for this, or the settings it
+    //   toggles).
+    if (args.containsKey('debugSdkLibraries')) {
+      _isolateManager.debugSdkLibraries = args['debugSdkLibraries'] as bool;
+    }
+    if (args.containsKey('debugExternalPackageLibraries')) {
+      _isolateManager.debugExternalPackageLibraries =
+          args['debugExternalPackageLibraries'] as bool;
+    }
+    await _isolateManager.applyDebugOptions();
   }
 
   /// A wrapper around the same name function from package:vm_service that
diff --git a/pkg/dds/lib/src/dap/adapters/dart_cli.dart b/pkg/dds/lib/src/dap/adapters/dart_cli.dart
index b0fd701..c01214d 100644
--- a/pkg/dds/lib/src/dap/adapters/dart_cli.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart_cli.dart
@@ -53,7 +53,9 @@
           enableDds: enableDds,
           enableAuthCodes: enableAuthCodes,
           logger: logger,
-        );
+        ) {
+    channel.closed.then((_) => shutdown());
+  }
 
   Future<void> debuggerConnected(vm.VM vmInfo) async {
     if (!isAttach) {
diff --git a/pkg/dds/lib/src/dap/base_debug_adapter.dart b/pkg/dds/lib/src/dap/base_debug_adapter.dart
index cd2b99a..a74f1f3 100644
--- a/pkg/dds/lib/src/dap/base_debug_adapter.dart
+++ b/pkg/dds/lib/src/dap/base_debug_adapter.dart
@@ -65,14 +65,7 @@
     RawRequestArguments? args,
     void Function(Object?) sendResponse,
   ) async {
-    final response = Response(
-      success: false,
-      requestSeq: request.seq,
-      seq: _sequence++,
-      command: request.command,
-      message: 'Unknown command: ${request.command}',
-    );
-    sendResponse(response);
+    throw DebugAdapterException('Unknown command  ${request.command}');
   }
 
   Future<void> disconnectRequest(
@@ -251,7 +244,7 @@
     } else if (message is Response) {
       _handleIncomingResponse(message);
     } else {
-      throw Exception('Unknown Protocol message ${message.type}');
+      throw DebugAdapterException('Unknown Protocol message ${message.type}');
     }
   }
 
diff --git a/pkg/dds/lib/src/dap/isolate_manager.dart b/pkg/dds/lib/src/dap/isolate_manager.dart
index 99ee743..9c16536 100644
--- a/pkg/dds/lib/src/dap/isolate_manager.dart
+++ b/pkg/dds/lib/src/dap/isolate_manager.dart
@@ -23,15 +23,33 @@
   final Map<int, ThreadInfo> _threadsByThreadId = {};
   int _nextThreadNumber = 1;
 
-  /// Whether debugging is enabled.
+  /// Whether debugging is enabled for this session.
   ///
   /// This must be set before any isolates are spawned and controls whether
   /// breakpoints or exception pause modes are sent to the VM.
   ///
+  /// If false, requests to send breakpoints or exception pause mode will be
+  /// dropped. Other functionality (handling pause events, resuming, etc.) will
+  /// all still function.
+  ///
   /// This is used to support debug sessions that have VM Service connections
   /// but were run with noDebug: true (for example we may need a VM Service
   /// connection for a noDebug flutter app in order to support hot reload).
-  bool _debug = false;
+  bool debug = false;
+
+  /// Whether SDK libraries should be marked as debuggable.
+  ///
+  /// Calling [sendLibraryDebuggables] is required after changing this value to
+  /// apply changes. This allows applying both [debugSdkLibraries] and
+  /// [debugExternalPackageLibraries] in one step.
+  bool debugSdkLibraries = true;
+
+  /// Whether external package libraries should be marked as debuggable.
+  ///
+  /// Calling [sendLibraryDebuggables] is required after changing this value to
+  /// apply changes. This allows applying both [debugSdkLibraries] and
+  /// [debugExternalPackageLibraries] in one step.
+  bool debugExternalPackageLibraries = true;
 
   /// Tracks breakpoints last provided by the client so they can be sent to new
   /// isolates that appear after initial breakpoints were sent.
@@ -72,6 +90,18 @@
   /// not exited between accessing this list and trying to use the results.
   List<ThreadInfo> get threads => _threadsByIsolateId.values.toList();
 
+  /// Re-applies debug options to all isolates/libraries.
+  ///
+  /// This is required if options like debugSdkLibraries are modified, but is a
+  /// separate step to batch together changes to multiple options.
+  Future<void> applyDebugOptions() async {
+    await Future.wait(_threadsByThreadId.values.map(
+      // debuggable libraries is the only thing currently affected by these
+      // changable options.
+      (isolate) => _sendLibraryDebuggables(isolate.isolate),
+    ));
+  }
+
   Future<T> getObject<T extends vm.Response>(
       vm.IsolateRef isolate, vm.ObjRef object) async {
     final res = await _adapter.vmService?.getObject(isolate.id!, object.id!);
@@ -219,19 +249,6 @@
         .map((isolate) => _sendBreakpoints(isolate.isolate, uri: uri)));
   }
 
-  /// Sets whether debugging is enabled for this session.
-  ///
-  /// If not, requests to send breakpoints or exception pause mode will be
-  /// dropped. Other functionality (handling pause events, resuming, etc.) will
-  /// all still function.
-  ///
-  /// This is used to support debug sessions that have VM Service connections
-  /// but were run with noDebug: true (for example we may need a VM Service
-  /// connection for a noDebug flutter app in order to support hot reload).
-  void setDebugEnabled(bool debug) {
-    _debug = debug;
-  }
-
   /// Records exception pause mode as one of 'None', 'Unhandled' or 'All'. All
   /// existing isolates will be updated to reflect the new setting.
   Future<void> setExceptionPauseMode(String mode) async {
@@ -371,13 +388,13 @@
 
   /// Checks whether a library should be considered debuggable.
   ///
-  /// This usesthe settings from the launch arguments (debugSdkLibraries
-  /// and debugExternalPackageLibraries) against the type of library given.
+  /// Initial values are provided in the launch arguments, but may be updated
+  /// by the `updateDebugOptions` custom request.
   bool _libaryIsDebuggable(vm.LibraryRef library) {
     if (_isSdkLibrary(library)) {
-      return _adapter.args.debugSdkLibraries ?? false;
+      return debugSdkLibraries;
     } else if (_isExternalPackageLibrary(library)) {
-      return _adapter.args.debugExternalPackageLibraries ?? false;
+      return debugExternalPackageLibraries;
     } else {
       return true;
     }
@@ -391,7 +408,7 @@
   /// newly-created isolates).
   Future<void> _sendBreakpoints(vm.IsolateRef isolate, {String? uri}) async {
     final service = _adapter.vmService;
-    if (!_debug || service == null) {
+    if (!debug || service == null) {
       return;
     }
 
@@ -425,7 +442,7 @@
   /// Sets the exception pause mode for an individual isolate.
   Future<void> _sendExceptionPauseMode(vm.IsolateRef isolate) async {
     final service = _adapter.vmService;
-    if (!_debug || service == null) {
+    if (!debug || service == null) {
       return;
     }
 
@@ -436,7 +453,7 @@
   /// on the debug settings.
   Future<void> _sendLibraryDebuggables(vm.IsolateRef isolateRef) async {
     final service = _adapter.vmService;
-    if (!_debug || service == null) {
+    if (!debug || service == null) {
       return;
     }
 
diff --git a/pkg/dds/lib/src/dap/protocol_converter.dart b/pkg/dds/lib/src/dap/protocol_converter.dart
index 85ef5f8..6285121 100644
--- a/pkg/dds/lib/src/dap/protocol_converter.dart
+++ b/pkg/dds/lib/src/dap/protocol_converter.dart
@@ -57,21 +57,32 @@
     required bool allowCallingToString,
     bool includeQuotesAroundString = true,
   }) async {
-    final canCallToString = allowCallingToString &&
-        (_adapter.args.evaluateToStringInDebugViews ?? false);
+    final isTruncated = ref.valueAsStringIsTruncated ?? false;
+    if (ref.kind == vm.InstanceKind.kString && isTruncated) {
+      // Call toString() if allowed, otherwise (or if it returns null) fall back
+      // to the truncated value with "…" suffix.
+      var stringValue = allowCallingToString
+          ? await _callToString(
+              thread,
+              ref,
+              includeQuotesAroundString: includeQuotesAroundString,
+            )
+          : null;
+      stringValue ??= '${ref.valueAsString}…';
 
-    if (ref.kind == 'String' || ref.valueAsString != null) {
-      var stringValue = ref.valueAsString.toString();
-      if (ref.valueAsStringIsTruncated ?? false) {
-        stringValue = '$stringValue…';
-      }
-      if (ref.kind == 'String' && includeQuotesAroundString) {
-        stringValue = '"$stringValue"';
-      }
-      return stringValue;
+      return includeQuotesAroundString ? '"$stringValue"' : stringValue;
+    } else if (ref.kind == vm.InstanceKind.kString) {
+      // Untruncated strings.
+      return includeQuotesAroundString
+          ? '"${ref.valueAsString}"'
+          : ref.valueAsString.toString();
+    } else if (ref.valueAsString != null) {
+      return isTruncated
+          ? '${ref.valueAsString}…'
+          : ref.valueAsString.toString();
     } else if (ref.kind == 'PlainInstance') {
       var stringValue = ref.classRef?.name ?? '<unknown instance>';
-      if (canCallToString) {
+      if (allowCallingToString) {
         final toStringValue = await _callToString(
           thread,
           ref,
@@ -99,6 +110,7 @@
   Future<List<dap.Variable>> convertVmInstanceToVariablesList(
     ThreadInfo thread,
     vm.Instance instance, {
+    required bool allowCallingToString,
     int? startItem = 0,
     int? numItems,
   }) async {
@@ -112,7 +124,7 @@
         await convertVmResponseToVariable(
           thread,
           instance,
-          allowCallingToString: true,
+          allowCallingToString: allowCallingToString,
         )
       ];
     } else if (elements != null) {
@@ -121,10 +133,15 @@
       return Future.wait(elements
           .cast<vm.Response>()
           .sublist(start, numItems != null ? start + numItems : null)
-          .mapIndexed((index, response) async => convertVmResponseToVariable(
-              thread, response,
+          .mapIndexed(
+            (index, response) => convertVmResponseToVariable(
+              thread,
+              response,
               name: '${start + index}',
-              allowCallingToString: index <= maxToStringsPerEvaluation)));
+              allowCallingToString:
+                  allowCallingToString && index <= maxToStringsPerEvaluation,
+            ),
+          ));
     } else if (associations != null) {
       // For maps, create a variable for each entry (in the requested subset).
       // Use the keys and values to create a display string in the form
@@ -135,13 +152,14 @@
       return Future.wait(associations
           .sublist(start, numItems != null ? start + numItems : null)
           .mapIndexed((index, mapEntry) async {
-        final allowCallingToString = index <= maxToStringsPerEvaluation;
+        final callToString =
+            allowCallingToString && index <= maxToStringsPerEvaluation;
         final keyDisplay = await convertVmResponseToDisplayString(
             thread, mapEntry.key,
-            allowCallingToString: allowCallingToString);
+            allowCallingToString: callToString);
         final valueDisplay = await convertVmResponseToDisplayString(
             thread, mapEntry.value,
-            allowCallingToString: allowCallingToString);
+            allowCallingToString: callToString);
         return dap.Variable(
           name: '${start + index}',
           value: '$keyDisplay -> $valueDisplay',
@@ -154,7 +172,8 @@
           (index, field) async => convertVmResponseToVariable(
               thread, field.value,
               name: field.decl?.name ?? '<unnamed field>',
-              allowCallingToString: index <= maxToStringsPerEvaluation)));
+              allowCallingToString:
+                  allowCallingToString && index <= maxToStringsPerEvaluation)));
 
       // Also evaluate the getters if evaluateGettersInDebugViews=true enabled.
       final service = _adapter.vmService;
@@ -177,7 +196,8 @@
             thread,
             response,
             name: getterName,
-            allowCallingToString: index <= maxToStringsPerEvaluation,
+            allowCallingToString:
+                allowCallingToString && index <= maxToStringsPerEvaluation,
           );
         }
 
@@ -397,7 +417,7 @@
     if (service == null) {
       return null;
     }
-    final result = await service.invoke(
+    var result = await service.invoke(
       thread.isolate.id!,
       ref.id!,
       'toString',
@@ -405,10 +425,18 @@
       disableBreakpoints: true,
     );
 
+    // If the response is a string and is truncated, use getObject() to get the
+    // full value.
+    if (result is vm.InstanceRef &&
+        result.kind == 'String' &&
+        (result.valueAsStringIsTruncated ?? false)) {
+      result = await service.getObject(thread.isolate.id!, result.id!);
+    }
+
     return convertVmResponseToDisplayString(
       thread,
       result,
-      allowCallingToString: false,
+      allowCallingToString: false, // Don't allow recursing.
       includeQuotesAroundString: includeQuotesAroundString,
     );
   }
diff --git a/pkg/dds/lib/src/dap/protocol_stream_transformers.dart b/pkg/dds/lib/src/dap/protocol_stream_transformers.dart
index 6bd6caa..424fa84 100644
--- a/pkg/dds/lib/src/dap/protocol_stream_transformers.dart
+++ b/pkg/dds/lib/src/dap/protocol_stream_transformers.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 import 'dart:convert';
-import 'dart:typed_data';
 
 class InvalidEncodingError {
   final String headers;
@@ -107,28 +106,3 @@
   final String? encoding;
   ProtocolHeaders(this.rawHeaders, this.contentLength, this.encoding);
 }
-
-/// Transforms a stream of [Uint8List]s to [List<int>]s. Used because
-/// [ServerSocket] and [Socket] use [Uint8List] but stdin and stdout use
-/// [List<int>] and the LSP server needs to operate against both.
-class Uint8ListTransformer extends StreamTransformerBase<Uint8List, List<int>> {
-  // TODO(dantup): Is there a built-in (or better) way to do this?
-  @override
-  Stream<List<int>> bind(Stream<Uint8List> stream) {
-    late StreamSubscription<Uint8List> input;
-    late StreamController<List<int>> _output;
-    _output = StreamController<List<int>>(
-      onListen: () {
-        input = stream.listen(
-          (uints) => _output.add(uints),
-          onError: _output.addError,
-          onDone: _output.close,
-        );
-      },
-      onPause: () => input.pause(),
-      onResume: () => input.resume(),
-      onCancel: () => input.cancel(),
-    );
-    return _output.stream;
-  }
-}
diff --git a/pkg/dds/lib/src/dap/server.dart b/pkg/dds/lib/src/dap/server.dart
index f2530a3..8a38138 100644
--- a/pkg/dds/lib/src/dap/server.dart
+++ b/pkg/dds/lib/src/dap/server.dart
@@ -3,92 +3,45 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:io';
 
 import 'package:dds/src/dap/adapters/dart.dart';
-import 'package:pedantic/pedantic.dart';
 
 import 'adapters/dart_cli.dart';
 import 'logging.dart';
 import 'protocol_stream.dart';
-import 'protocol_stream_transformers.dart';
 
-/// A DAP server that binds to a port and runs in multi-session mode.
+/// A DAP server that communicates over a [ByteStreamServerChannel], usually
+/// constructed from the processes stdin/stdout streams.
+///
+/// The server runs in single-user mode and services only a single client. For
+/// multiple debug sessions, there would be multiple servers (and the editor
+/// would have a client for each of them).
 class DapServer {
-  final ServerSocket _socket;
+  final ByteStreamServerChannel channel;
+  late final DartDebugAdapter adapter;
   final bool ipv6;
   final bool enableDds;
   final bool enableAuthCodes;
   final Logger? logger;
-  final _channels = <ByteStreamServerChannel>{};
-  final _adapters = <DartDebugAdapter>{};
 
-  DapServer._(
-    this._socket, {
+  DapServer(
+    Stream<List<int>> _input,
+    StreamSink<List<int>> _output, {
     this.ipv6 = false,
     this.enableDds = true,
     this.enableAuthCodes = true,
     this.logger,
-  }) {
-    _socket.listen(_acceptConnection);
-  }
-
-  String get host => _socket.address.host;
-  int get port => _socket.port;
-
-  Future<void> stop() async {
-    _channels.forEach((client) => client.close());
-    await _socket.close();
-  }
-
-  void _acceptConnection(Socket client) {
-    final address = client.remoteAddress;
-    logger?.call('Accepted connection from $address');
-    client.done.then((_) {
-      logger?.call('Connection from $address closed');
-    });
-    _createAdapter(client.transform(Uint8ListTransformer()), client);
-  }
-
-  void _createAdapter(Stream<List<int>> _input, StreamSink<List<int>> _output) {
-    // TODO(dantup): This is hard-coded to DartCliDebugAdapter but will
-    //   ultimately need to support having a factory passed in to support
-    //   tests and/or being used in flutter_tools.
-    final channel = ByteStreamServerChannel(_input, _output, logger);
-    final adapter = DartCliDebugAdapter(
+  }) : channel = ByteStreamServerChannel(_input, _output, logger) {
+    adapter = DartCliDebugAdapter(
       channel,
       ipv6: ipv6,
       enableDds: enableDds,
       enableAuthCodes: enableAuthCodes,
       logger: logger,
     );
-    _channels.add(channel);
-    _adapters.add(adapter);
-    unawaited(channel.closed.then((_) {
-      _channels.remove(channel);
-      _adapters.remove(adapter);
-      adapter.shutdown();
-    }));
   }
 
-  /// Starts a DAP Server listening on [host]:[port].
-  static Future<DapServer> create({
-    String? host,
-    int port = 0,
-    bool ipv6 = false,
-    bool enableDdds = true,
-    bool enableAuthCodes = true,
-    Logger? logger,
-  }) async {
-    final hostFallback =
-        ipv6 ? InternetAddress.loopbackIPv6 : InternetAddress.loopbackIPv4;
-    final _socket = await ServerSocket.bind(host ?? hostFallback, port);
-    return DapServer._(
-      _socket,
-      ipv6: ipv6,
-      enableDds: enableDdds,
-      enableAuthCodes: enableAuthCodes,
-      logger: logger,
-    );
+  void stop() {
+    channel.close();
   }
 }
diff --git a/pkg/dds/test/dap/integration/debug_breakpoints_test.dart b/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
index 9a50b03..62cacbf 100644
--- a/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
+++ b/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
@@ -9,56 +9,61 @@
 import 'test_support.dart';
 
 main() {
-  testDap((dap) async {
-    group('debug mode breakpoints', () {
-      test('stops at a line breakpoint', () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(simpleBreakpointProgram);
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+  late DapTestSession dap;
+  setUp(() async {
+    dap = await DapTestSession.setUp();
+  });
+  tearDown(() => dap.tearDown());
 
-        await client.hitBreakpoint(testFile, breakpointLine);
-      });
+  group('debug mode breakpoints', () {
+    test('stops at a line breakpoint', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-      test('stops at a line breakpoint and can be resumed', () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(simpleBreakpointProgram);
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      await client.hitBreakpoint(testFile, breakpointLine);
+    });
 
-        // Hit the initial breakpoint.
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
+    test('stops at a line breakpoint and can be resumed', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        // Resume and expect termination (as the script will get to the end).
-        await Future.wait([
-          client.event('terminated'),
-          client.continue_(stop.threadId!),
-        ], eagerError: true);
-      });
+      // Hit the initial breakpoint.
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
 
-      test('stops at a line breakpoint and can step over (next)', () async {
-        final testFile = dap.createTestFile(r'''
+      // Resume and expect termination (as the script will get to the end).
+      await Future.wait([
+        client.event('terminated'),
+        client.continue_(stop.threadId!),
+      ], eagerError: true);
+    });
+
+    test('stops at a line breakpoint and can step over (next)', () async {
+      final testFile = dap.createTestFile(r'''
 void main(List<String> args) async {
   print('Hello!'); // BREAKPOINT
   print('Hello!'); // STEP
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
-        final stepLine = lineWith(testFile, '// STEP');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final stepLine = lineWith(testFile, '// STEP');
 
-        // Hit the initial breakpoint.
-        final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
+      // Hit the initial breakpoint.
+      final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
 
-        // Step and expect stopping on the next line with a 'step' stop type.
-        await Future.wait([
-          dap.client.expectStop('step', file: testFile, line: stepLine),
-          dap.client.next(stop.threadId!),
-        ], eagerError: true);
-      });
+      // Step and expect stopping on the next line with a 'step' stop type.
+      await Future.wait([
+        dap.client.expectStop('step', file: testFile, line: stepLine),
+        dap.client.next(stop.threadId!),
+      ], eagerError: true);
+    });
 
-      test(
-          'stops at a line breakpoint and can step over (next) an async boundary',
-          () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(r'''
+    test(
+        'stops at a line breakpoint and can step over (next) an async boundary',
+        () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(r'''
 Future<void> main(List<String> args) async {
   await asyncPrint('Hello!'); // BREAKPOINT
   await asyncPrint('Hello!'); // STEP
@@ -68,30 +73,30 @@
   await Future.delayed(const Duration(milliseconds: 1));
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
-        final stepLine = lineWith(testFile, '// STEP');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final stepLine = lineWith(testFile, '// STEP');
 
-        // Hit the initial breakpoint.
-        final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
+      // Hit the initial breakpoint.
+      final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
 
-        // The first step will move from `asyncPrint` to the `await`.
-        await Future.wait([
-          client.expectStop('step', file: testFile, line: breakpointLine),
-          client.next(stop.threadId!),
-        ], eagerError: true);
+      // The first step will move from `asyncPrint` to the `await`.
+      await Future.wait([
+        client.expectStop('step', file: testFile, line: breakpointLine),
+        client.next(stop.threadId!),
+      ], eagerError: true);
 
-        // The next step should go over the async boundary and to stepLine (if
-        // we did not correctly send kOverAsyncSuspension we would end up in
-        // the asyncPrint method).
-        await Future.wait([
-          client.expectStop('step', file: testFile, line: stepLine),
-          client.next(stop.threadId!),
-        ], eagerError: true);
-      });
+      // The next step should go over the async boundary and to stepLine (if
+      // we did not correctly send kOverAsyncSuspension we would end up in
+      // the asyncPrint method).
+      await Future.wait([
+        client.expectStop('step', file: testFile, line: stepLine),
+        client.next(stop.threadId!),
+      ], eagerError: true);
+    });
 
-      test('stops at a line breakpoint and can step in', () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(r'''
+    test('stops at a line breakpoint and can step in', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(r'''
 void main(List<String> args) async {
   log('Hello!'); // BREAKPOINT
 }
@@ -100,22 +105,22 @@
   print(message);
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
-        final stepLine = lineWith(testFile, '// STEP');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final stepLine = lineWith(testFile, '// STEP');
 
-        // Hit the initial breakpoint.
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      // Hit the initial breakpoint.
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
 
-        // Step and expect stopping in the inner function with a 'step' stop type.
-        await Future.wait([
-          client.expectStop('step', file: testFile, line: stepLine),
-          client.stepIn(stop.threadId!),
-        ], eagerError: true);
-      });
+      // Step and expect stopping in the inner function with a 'step' stop type.
+      await Future.wait([
+        client.expectStop('step', file: testFile, line: stepLine),
+        client.stepIn(stop.threadId!),
+      ], eagerError: true);
+    });
 
-      test('stops at a line breakpoint and can step out', () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(r'''
+    test('stops at a line breakpoint and can step out', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(r'''
 void main(List<String> args) async {
   log('Hello!');
   log('Hello!'); // STEP
@@ -125,87 +130,119 @@
   print(message); // BREAKPOINT
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
-        final stepLine = lineWith(testFile, '// STEP');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final stepLine = lineWith(testFile, '// STEP');
 
-        // Hit the initial breakpoint.
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      // Hit the initial breakpoint.
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
 
-        // Step and expect stopping in the inner function with a 'step' stop type.
-        await Future.wait([
-          client.expectStop('step', file: testFile, line: stepLine),
-          client.stepOut(stop.threadId!),
-        ], eagerError: true);
-      });
+      // Step and expect stopping in the inner function with a 'step' stop type.
+      await Future.wait([
+        client.expectStop('step', file: testFile, line: stepLine),
+        client.stepOut(stop.threadId!),
+      ], eagerError: true);
+    });
 
-      test('does not step into SDK code with debugSdkLibraries=false',
-          () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(r'''
+    test('does not step into SDK code with debugSdkLibraries=false', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(r'''
 void main(List<String> args) async {
   print('Hello!'); // BREAKPOINT
   print('Hello!'); // STEP
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
-        final stepLine = lineWith(testFile, '// STEP');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final stepLine = lineWith(testFile, '// STEP');
 
-        // Hit the initial breakpoint.
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      // Hit the initial breakpoint.
+      final stop = await client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        launch: () => client.launch(
+          testFile.path,
+          debugSdkLibraries: false,
+        ),
+      );
 
-        // Step in and expect stopping on the next line (don't go into print).
-        await Future.wait([
-          client.expectStop('step', file: testFile, line: stepLine),
-          client.stepIn(stop.threadId!),
-        ], eagerError: true);
-      });
+      // Step in and expect stopping on the next line (don't go into print).
+      await Future.wait([
+        client.expectStop('step', file: testFile, line: stepLine),
+        client.stepIn(stop.threadId!),
+      ], eagerError: true);
+    });
 
-      test('steps into SDK code with debugSdkLibraries=true', () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(r'''
+    test('steps into SDK code with debugSdkLibraries=true', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(r'''
 void main(List<String> args) async {
   print('Hello!'); // BREAKPOINT
   print('Hello!');
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        // Hit the initial breakpoint.
-        final stop = await dap.client.hitBreakpoint(
-          testFile,
-          breakpointLine,
-          launch: () => client.launch(
-            testFile.path,
-            debugSdkLibraries: true,
-          ),
-        );
+      // Hit the initial breakpoint.
+      final stop = await dap.client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        launch: () => client.launch(
+          testFile.path,
+          debugSdkLibraries: true,
+        ),
+      );
 
-        // Step in and expect to go into print.
-        await Future.wait([
-          client.expectStop('step', sourceName: 'dart:core/print.dart'),
-          client.stepIn(stop.threadId!),
-        ], eagerError: true);
+      // Step in and expect to go into print.
+      await Future.wait([
+        client.expectStop('step', sourceName: 'dart:core/print.dart'),
+        client.stepIn(stop.threadId!),
+      ], eagerError: true);
+    });
+
+    test(
+        'does not step into external package code with debugExternalPackageLibraries=false',
+        () {
+      // TODO(dantup): Support for debugExternalPackageLibraries
+    }, skip: true);
+
+    test(
+        'steps into external package code with debugExternalPackageLibraries=true',
+        () {
+      // TODO(dantup): Support for debugExternalPackageLibraries
+    }, skip: true);
+
+    test('allows changing debug settings during session', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(r'''
+void main(List<String> args) async {
+  print('Hello!'); // BREAKPOINT
+  print('Hello!'); // STEP
+}
+    ''');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final stepLine = lineWith(testFile, '// STEP');
+
+      // Start with debugSdkLibraryes _enabled_ and hit the breakpoint.
+      final stop = await client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        launch: () => client.launch(
+          testFile.path,
+          debugSdkLibraries: true,
+        ),
+      );
+
+      // Turn off debugSdkLibraries.
+      await client.custom('updateDebugOptions', {
+        'debugSdkLibraries': false,
       });
 
-      test(
-          'does not step into external package code with debugExternalPackageLibraries=false',
-          () {
-        // TODO(dantup): Support for debugExternalPackageLibraries
-      }, skip: true);
-
-      test(
-          'steps into external package code with debugExternalPackageLibraries=true',
-          () {
-        // TODO(dantup): Support for debugExternalPackageLibraries
-      }, skip: true);
-
-      test('allows changing debug settings during session', () {
-        // TODO(dantup): !
-        // Dart-Code's DAP has a custom method that allows an editor to change
-        // the debug settings (debugSdkLibraries/debugExternalPackageLibraries)
-        // during a debug session.
-      }, skip: true);
-      // These tests can be slow due to starting up the external server process.
-    }, timeout: Timeout.none);
-  });
+      // Step in and expect stopping on the next line (don't go into print
+      // because we turned off SDK debugging).
+      await Future.wait([
+        client.expectStop('step', file: testFile, line: stepLine),
+        client.stepIn(stop.threadId!),
+      ], eagerError: true);
+    });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
 }
diff --git a/pkg/dds/test/dap/integration/debug_eval_test.dart b/pkg/dds/test/dap/integration/debug_eval_test.dart
index 9b59310..ab24f0c 100644
--- a/pkg/dds/test/dap/integration/debug_eval_test.dart
+++ b/pkg/dds/test/dap/integration/debug_eval_test.dart
@@ -10,126 +10,131 @@
 import 'test_support.dart';
 
 main() {
-  testDap((dap) async {
-    group('debug mode evaluation', () {
-      test('evaluates expressions with simple results', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+  late DapTestSession dap;
+  setUp(() async {
+    dap = await DapTestSession.setUp();
+  });
+  tearDown(() => dap.tearDown());
+
+  group('debug mode evaluation', () {
+    test('evaluates expressions with simple results', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   var a = 1;
   var b = 2;
   var c = 'test';
   print('Hello!'); // BREAKPOINT
 }''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
-        await client.expectTopFrameEvalResult(stop.threadId!, 'a', '1');
-        await client.expectTopFrameEvalResult(stop.threadId!, 'a * b', '2');
-        await client.expectTopFrameEvalResult(stop.threadId!, 'c', '"test"');
-      });
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      await client.expectTopFrameEvalResult(stop.threadId!, 'a', '1');
+      await client.expectTopFrameEvalResult(stop.threadId!, 'a * b', '2');
+      await client.expectTopFrameEvalResult(stop.threadId!, 'c', '"test"');
+    });
 
-      test('evaluates expressions with complex results', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(simpleBreakpointProgram);
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+    test('evaluates expressions with complex results', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
-        final result = await client.expectTopFrameEvalResult(
-          stop.threadId!,
-          'DateTime(2000, 1, 1)',
-          'DateTime',
-        );
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      final result = await client.expectTopFrameEvalResult(
+        stop.threadId!,
+        'DateTime(2000, 1, 1)',
+        'DateTime',
+      );
 
-        // Check we got a variablesReference that maps on to the fields.
-        expect(result.variablesReference, greaterThan(0));
-        await client.expectVariables(
-          result.variablesReference,
-          '''
+      // Check we got a variablesReference that maps on to the fields.
+      expect(result.variablesReference, greaterThan(0));
+      await client.expectVariables(
+        result.variablesReference,
+        '''
             isUtc: false
           ''',
-        );
-      });
+      );
+    });
 
-      test(
-          'evaluates complex expressions expressions with evaluateToStringInDebugViews=true',
-          () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(simpleBreakpointProgram);
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+    test(
+        'evaluates complex expressions expressions with evaluateToStringInDebugViews=true',
+        () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(
-          testFile,
-          breakpointLine,
-          launch: () =>
-              client.launch(testFile.path, evaluateToStringInDebugViews: true),
-        );
+      final stop = await client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        launch: () =>
+            client.launch(testFile.path, evaluateToStringInDebugViews: true),
+      );
 
-        await client.expectTopFrameEvalResult(
-          stop.threadId!,
-          'DateTime(2000, 1, 1)',
-          'DateTime (2000-01-01 00:00:00.000)',
-        );
-      });
+      await client.expectTopFrameEvalResult(
+        stop.threadId!,
+        'DateTime(2000, 1, 1)',
+        'DateTime (2000-01-01 00:00:00.000)',
+      );
+    });
 
-      test(
-          'evaluates $threadExceptionExpression to the threads exception (simple type)',
-          () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test(
+        'evaluates $threadExceptionExpression to the threads exception (simple type)',
+        () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   throw 'my error';
 }''');
 
-        final stop = await client.hitException(testFile);
+      final stop = await client.hitException(testFile);
 
-        final result = await client.expectTopFrameEvalResult(
-          stop.threadId!,
-          threadExceptionExpression,
-          '"my error"',
-        );
-        expect(result.variablesReference, equals(0));
-      });
+      final result = await client.expectTopFrameEvalResult(
+        stop.threadId!,
+        threadExceptionExpression,
+        '"my error"',
+      );
+      expect(result.variablesReference, equals(0));
+    });
 
-      test(
-          'evaluates $threadExceptionExpression to the threads exception (complex type)',
-          () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test(
+        'evaluates $threadExceptionExpression to the threads exception (complex type)',
+        () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   throw Exception('my error');
 }''');
 
-        final stop = await client.hitException(testFile);
-        final result = await client.expectTopFrameEvalResult(
-          stop.threadId!,
-          threadExceptionExpression,
-          '_Exception',
-        );
-        expect(result.variablesReference, greaterThan(0));
-      });
+      final stop = await client.hitException(testFile);
+      final result = await client.expectTopFrameEvalResult(
+        stop.threadId!,
+        threadExceptionExpression,
+        '_Exception',
+      );
+      expect(result.variablesReference, greaterThan(0));
+    });
 
-      test(
-          'evaluates $threadExceptionExpression.x.y to x.y on the threads exception',
-          () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test(
+        'evaluates $threadExceptionExpression.x.y to x.y on the threads exception',
+        () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   throw Exception('12345');
 }
     ''');
 
-        final stop = await client.hitException(testFile);
-        await client.expectTopFrameEvalResult(
-          stop.threadId!,
-          '$threadExceptionExpression.message.length',
-          '5',
-        );
-      });
+      final stop = await client.hitException(testFile);
+      await client.expectTopFrameEvalResult(
+        stop.threadId!,
+        '$threadExceptionExpression.message.length',
+        '5',
+      );
+    });
 
-      test('can evaluate expressions in non-top frames', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test('can evaluate expressions in non-top frames', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   var a = 999;
   foo();
@@ -138,17 +143,16 @@
 void foo() {
   var a = 111; // BREAKPOINT
 }''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
-        final stack = await client.getValidStack(stop.threadId!,
-            startFrame: 0, numFrames: 2);
-        final secondFrameId = stack.stackFrames[1].id;
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      final stack = await client.getValidStack(stop.threadId!,
+          startFrame: 0, numFrames: 2);
+      final secondFrameId = stack.stackFrames[1].id;
 
-        await client.expectEvalResult(secondFrameId, 'a', '999');
-      });
+      await client.expectEvalResult(secondFrameId, 'a', '999');
+    });
 
-      // These tests can be slow due to starting up the external server process.
-    }, timeout: Timeout.none);
-  });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
 }
diff --git a/pkg/dds/test/dap/integration/debug_logging_test.dart b/pkg/dds/test/dap/integration/debug_logging_test.dart
index 1df090c..27578f5 100644
--- a/pkg/dds/test/dap/integration/debug_logging_test.dart
+++ b/pkg/dds/test/dap/integration/debug_logging_test.dart
@@ -4,13 +4,19 @@
 
 import 'package:test/test.dart';
 
+import 'test_client.dart';
 import 'test_support.dart';
 
 main() {
-  testDap((dap) async {
-    group('debug mode', () {
-      test('prints messages from dart:developer log()', () async {
-        final testFile = dap.createTestFile(r'''
+  late DapTestSession dap;
+  setUp(() async {
+    dap = await DapTestSession.setUp();
+  });
+  tearDown(() => dap.tearDown());
+
+  group('debug mode', () {
+    test('prints messages from dart:developer log()', () async {
+      final testFile = dap.createTestFile(r'''
 import 'dart:developer';
 
 void main(List<String> args) async {
@@ -19,19 +25,45 @@
 }
     ''');
 
-        var outputEvents = await dap.client.collectOutput(file: testFile);
+      var outputEvents = await dap.client.collectOutput(file: testFile);
 
-        // Skip the first line because it's the VM Service connection info.
-        final output = outputEvents.skip(1).map((e) => e.output).join();
-        expectLines(output, [
-          '[log] this is a test',
-          '      across two lines',
-          '[foo] this is a test',
-          '',
-          'Exited.',
-        ]);
-      });
-      // These tests can be slow due to starting up the external server process.
-    }, timeout: Timeout.none);
-  });
+      // Skip the first line because it's the VM Service connection info.
+      final output = outputEvents.skip(1).map((e) => e.output).join();
+      expectLines(output, [
+        '[log] this is a test',
+        '      across two lines',
+        '[foo] this is a test',
+        '',
+        'Exited.',
+      ]);
+    });
+
+    test('prints long messages from dart:developer log()', () async {
+      // Make a long message that's more than 255 chars (where the VM truncates
+      // log strings by default).
+      final longMessage = 'this is a test' * 20;
+      final testFile = dap.createTestFile('''
+import 'dart:developer';
+
+void main(List<String> args) async {
+  log('$longMessage');
+  // Prevent us exiting before the async log messages may have completed.
+  // The test will terminate the script early once the expectations are met.
+  await Future.delayed(const Duration(seconds: 30));
+}
+    ''');
+      final expectedLogMessage = '[log] $longMessage\n';
+
+      final consoleOutputs = dap.client.outputEvents
+          .where((event) => event.category == 'console')
+          .map((event) => event.output);
+
+      await Future.wait([
+        expectLater(consoleOutputs, emitsThrough(expectedLogMessage)),
+        dap.client.start(file: testFile),
+      ]);
+      await dap.client.terminate();
+    });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
 }
diff --git a/pkg/dds/test/dap/integration/debug_test.dart b/pkg/dds/test/dap/integration/debug_test.dart
index 7b80af0..41863f9 100644
--- a/pkg/dds/test/dap/integration/debug_test.dart
+++ b/pkg/dds/test/dap/integration/debug_test.dart
@@ -10,10 +10,15 @@
 import 'test_support.dart';
 
 main() {
-  testDap((dap) async {
-    group('debug mode', () {
-      test('runs a simple script', () async {
-        final testFile = dap.createTestFile(r'''
+  group('debug mode', () {
+    late DapTestSession dap;
+    setUp(() async {
+      dap = await DapTestSession.setUp();
+    });
+    tearDown(() => dap.tearDown());
+
+    test('runs a simple script', () async {
+      final testFile = dap.createTestFile(r'''
 void main(List<String> args) async {
   print('Hello!');
   print('World!');
@@ -21,100 +26,99 @@
 }
     ''');
 
-        final outputEvents = await dap.client.collectOutput(
-          launch: () => dap.client.launch(
-            testFile.path,
-            args: ['one', 'two'],
-          ),
-        );
+      final outputEvents = await dap.client.collectOutput(
+        launch: () => dap.client.launch(
+          testFile.path,
+          args: ['one', 'two'],
+        ),
+      );
 
-        // Expect a "console" output event that prints the URI of the VM Service
-        // the debugger connects to.
-        final vmConnection = outputEvents.first;
-        expect(vmConnection.output,
-            startsWith('Connecting to VM Service at ws://127.0.0.1:'));
-        expect(vmConnection.category, equals('console'));
+      // Expect a "console" output event that prints the URI of the VM Service
+      // the debugger connects to.
+      final vmConnection = outputEvents.first;
+      expect(vmConnection.output,
+          startsWith('Connecting to VM Service at ws://127.0.0.1:'));
+      expect(vmConnection.category, equals('console'));
 
-        // Expect the normal applications output.
-        final output = outputEvents.skip(1).map((e) => e.output).join();
-        expectLines(output, [
-          'Hello!',
-          'World!',
-          'args: [one, two]',
-          '',
-          'Exited.',
-        ]);
-      });
+      // Expect the normal applications output.
+      final output = outputEvents.skip(1).map((e) => e.output).join();
+      expectLines(output, [
+        'Hello!',
+        'World!',
+        'args: [one, two]',
+        '',
+        'Exited.',
+      ]);
+    });
 
-      test('provides a list of threads', () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(simpleBreakpointProgram);
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+    test('provides a list of threads', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        await client.hitBreakpoint(testFile, breakpointLine);
-        final response = await client.getValidThreads();
+      await client.hitBreakpoint(testFile, breakpointLine);
+      final response = await client.getValidThreads();
 
-        expect(response.threads, hasLength(1));
-        expect(response.threads.first.name, equals('main'));
-      });
+      expect(response.threads, hasLength(1));
+      expect(response.threads.first.name, equals('main'));
+    });
 
-      test('runs with DDS', () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(simpleBreakpointProgram);
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+    test('runs with DDS by default', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        await client.hitBreakpoint(testFile, breakpointLine);
-        expect(await client.ddsAvailable, isTrue);
-      });
-      // These tests can be slow due to starting up the external server process.
-    }, timeout: Timeout.none);
+      await client.hitBreakpoint(testFile, breakpointLine);
+      expect(await client.ddsAvailable, isTrue);
+    });
 
-    test('runs with auth codes enabled', () async {
+    test('runs with auth codes enabled by default', () async {
       final testFile = dap.createTestFile(emptyProgram);
 
       final outputEvents = await dap.client.collectOutput(file: testFile);
-      expect(_hasAuthCode(outputEvents.first), isTrue);
+      final vmServiceUri = _extractVmServiceUri(outputEvents.first);
+      expect(vmServiceUri.path, matches(vmServiceAuthCodePathPattern));
     });
-  });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
 
-  testDap((dap) async {
-    group('debug mode', () {
-      test('runs without DDS', () async {
-        final client = dap.client;
-        final testFile = dap.createTestFile(simpleBreakpointProgram);
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+  group('debug mode', () {
+    test('can run without DDS', () async {
+      final dap = await DapTestSession.setUp(additionalArgs: ['--no-dds']);
+      addTearDown(dap.tearDown);
 
-        await client.hitBreakpoint(testFile, breakpointLine);
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        expect(await client.ddsAvailable, isFalse);
-      });
+      await client.hitBreakpoint(testFile, breakpointLine);
 
-      test('runs with auth tokens disabled', () async {
-        final testFile = dap.createTestFile(emptyProgram);
+      expect(await client.ddsAvailable, isFalse);
+    });
 
-        final outputEvents = await dap.client.collectOutput(file: testFile);
-        expect(_hasAuthCode(outputEvents.first), isFalse);
-      });
-      // These tests can be slow due to starting up the external server process.
-    }, timeout: Timeout.none);
-  }, additionalArgs: ['--no-dds', '--no-auth-codes']);
+    test('can run without auth codes', () async {
+      final dap =
+          await DapTestSession.setUp(additionalArgs: ['--no-auth-codes']);
+      addTearDown(dap.tearDown);
 
-  testDap((dap) async {
-    group('debug mode', () {
-      test('can run with ipv6', () async {
-        final testFile = dap.createTestFile(emptyProgram);
+      final testFile = dap.createTestFile(emptyProgram);
+      final outputEvents = await dap.client.collectOutput(file: testFile);
+      final vmServiceUri = _extractVmServiceUri(outputEvents.first);
+      expect(vmServiceUri.path, isNot(matches(vmServiceAuthCodePathPattern)));
+    });
 
-        final outputEvents = await dap.client.collectOutput(file: testFile);
-        final vmServiceUri = _extractVmServiceUri(outputEvents.first);
+    test('can run with ipv6', () async {
+      final dap = await DapTestSession.setUp(additionalArgs: ['--ipv6']);
+      addTearDown(dap.tearDown);
 
-        // Check DAP server host.
-        expect(dap.server.host, equals('::1'));
-        // Check VM Service/DDS host.
-        expect(vmServiceUri.host, equals('::1'));
-      });
-      // These tests can be slow due to starting up the external server process.
-    }, timeout: Timeout.none);
-  }, additionalArgs: ['--ipv6']);
+      final testFile = dap.createTestFile(emptyProgram);
+      final outputEvents = await dap.client.collectOutput(file: testFile);
+      final vmServiceUri = _extractVmServiceUri(outputEvents.first);
+
+      expect(vmServiceUri.host, equals('::1'));
+    });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
 }
 
 /// Extracts the VM Service URI from the "Connecting to ..." banner output by
@@ -122,15 +126,6 @@
 Uri _extractVmServiceUri(OutputEventBody vmConnectionBanner) {
   // TODO(dantup): Change this to use the dart.debuggerUris custom event
   //   if implemented (whch VS Code also needs).
-  final vmServiceUriPattern = RegExp(r'Connecting to VM Service at ([^\s]+)\s');
   final match = vmServiceUriPattern.firstMatch(vmConnectionBanner.output);
   return Uri.parse(match!.group(1)!);
 }
-
-/// Checks for the presence of an auth token in a VM Service URI in the
-/// "Connecting to VM Service" [OutputEvent].
-bool _hasAuthCode(OutputEventBody vmConnectionBanner) {
-  final vmServiceUri = _extractVmServiceUri(vmConnectionBanner);
-  final authCodePattern = RegExp(r'^/[\w=]{5,15}/ws');
-  return authCodePattern.hasMatch(vmServiceUri.path);
-}
diff --git a/pkg/dds/test/dap/integration/debug_variables_test.dart b/pkg/dds/test/dap/integration/debug_variables_test.dart
index e04e36e..b5e357c 100644
--- a/pkg/dds/test/dap/integration/debug_variables_test.dart
+++ b/pkg/dds/test/dap/integration/debug_variables_test.dart
@@ -8,11 +8,16 @@
 import 'test_support.dart';
 
 main() {
-  testDap((dap) async {
-    group('debug mode variables', () {
-      test('provides variable list for frames', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+  late DapTestSession dap;
+  setUp(() async {
+    dap = await DapTestSession.setUp();
+  });
+  tearDown(() => dap.tearDown());
+
+  group('debug mode variables', () {
+    test('provides variable list for frames', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   final myVariable = 1;
   foo();
@@ -23,135 +28,135 @@
   print('Hello!'); // BREAKPOINT
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
-        final stack = await client.getValidStack(
-          stop.threadId!,
-          startFrame: 0,
-          numFrames: 2,
-        );
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      final stack = await client.getValidStack(
+        stop.threadId!,
+        startFrame: 0,
+        numFrames: 2,
+      );
 
-        // Check top two frames (in `foo` and in `main`).
-        await client.expectScopeVariables(
-          stack.stackFrames[0].id, // Top frame: foo
-          'Variables',
-          '''
+      // Check top two frames (in `foo` and in `main`).
+      await client.expectScopeVariables(
+        stack.stackFrames[0].id, // Top frame: foo
+        'Variables',
+        '''
             b: 2
           ''',
-        );
-        await client.expectScopeVariables(
-          stack.stackFrames[1].id, // Second frame: main
-          'Variables',
-          '''
+      );
+      await client.expectScopeVariables(
+        stack.stackFrames[1].id, // Second frame: main
+        'Variables',
+        '''
             args: List (0 items)
             myVariable: 1
           ''',
-        );
-      });
+      );
+    });
 
-      test('provides simple exception types for frames', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test('provides simple exception types for frames', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   throw 'my error';
 }
     ''');
 
-        final stop = await client.hitException(testFile);
-        final stack = await client.getValidStack(
-          stop.threadId!,
-          startFrame: 0,
-          numFrames: 1,
-        );
-        final topFrameId = stack.stackFrames.first.id;
+      final stop = await client.hitException(testFile);
+      final stack = await client.getValidStack(
+        stop.threadId!,
+        startFrame: 0,
+        numFrames: 1,
+      );
+      final topFrameId = stack.stackFrames.first.id;
 
-        // Check for an additional Scope named "Exceptions" that includes the
-        // exception.
-        await client.expectScopeVariables(
-          topFrameId,
-          'Exceptions',
-          '''
+      // Check for an additional Scope named "Exceptions" that includes the
+      // exception.
+      await client.expectScopeVariables(
+        topFrameId,
+        'Exceptions',
+        '''
             String: "my error"
           ''',
-        );
-      });
+      );
+    });
 
-      test('provides complex exception types frames', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test('provides complex exception types frames', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   throw ArgumentError.notNull('args');
 }
     ''');
 
-        final stop = await client.hitException(testFile);
-        final stack = await client.getValidStack(
-          stop.threadId!,
-          startFrame: 0,
-          numFrames: 1,
-        );
-        final topFrameId = stack.stackFrames.first.id;
+      final stop = await client.hitException(testFile);
+      final stack = await client.getValidStack(
+        stop.threadId!,
+        startFrame: 0,
+        numFrames: 1,
+      );
+      final topFrameId = stack.stackFrames.first.id;
 
-        // Check for an additional Scope named "Exceptions" that includes the
-        // exception.
-        await client.expectScopeVariables(
-          topFrameId,
-          'Exceptions',
-          // TODO(dantup): evaluateNames
-          '''
+      // Check for an additional Scope named "Exceptions" that includes the
+      // exception.
+      await client.expectScopeVariables(
+        topFrameId,
+        'Exceptions',
+        // TODO(dantup): evaluateNames
+        '''
             invalidValue: null
             message: "Must not be null"
             name: "args"
           ''',
-        );
-      });
+      );
+    });
 
-      test('includes simple variable fields', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test('includes simple variable fields', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   final myVariable = DateTime(2000, 1, 1);
   print('Hello!'); // BREAKPOINT
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
-        await client.expectLocalVariable(
-          stop.threadId!,
-          expectedName: 'myVariable',
-          expectedDisplayString: 'DateTime',
-          expectedVariables: '''
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      await client.expectLocalVariable(
+        stop.threadId!,
+        expectedName: 'myVariable',
+        expectedDisplayString: 'DateTime',
+        expectedVariables: '''
             isUtc: false
           ''',
-        );
-      });
+      );
+    });
 
-      test('includes variable getters when evaluateGettersInDebugViews=true',
-          () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test('includes variable getters when evaluateGettersInDebugViews=true',
+        () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   final myVariable = DateTime(2000, 1, 1);
   print('Hello!'); // BREAKPOINT
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(
-          testFile,
-          breakpointLine,
-          launch: () => client.launch(
-            testFile.path,
-            evaluateGettersInDebugViews: true,
-          ),
-        );
-        await client.expectLocalVariable(
-          stop.threadId!,
-          expectedName: 'myVariable',
-          expectedDisplayString: 'DateTime',
-          expectedVariables: '''
+      final stop = await client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        launch: () => client.launch(
+          testFile.path,
+          evaluateGettersInDebugViews: true,
+        ),
+      );
+      await client.expectLocalVariable(
+        stop.threadId!,
+        expectedName: 'myVariable',
+        expectedDisplayString: 'DateTime',
+        expectedVariables: '''
             day: 1
             hour: 0
             isUtc: false
@@ -165,72 +170,71 @@
             weekday: 6
             year: 2000
           ''',
-          ignore: {
-            // Don't check fields that may very based on timezone as it'll make
-            // these tests fragile, and this isn't really what's being tested.
-            'timeZoneName',
-            'microsecondsSinceEpoch',
-            'millisecondsSinceEpoch',
-          },
-        );
-      });
+        ignore: {
+          // Don't check fields that may very based on timezone as it'll make
+          // these tests fragile, and this isn't really what's being tested.
+          'timeZoneName',
+          'microsecondsSinceEpoch',
+          'millisecondsSinceEpoch',
+        },
+      );
+    });
 
-      test('renders a simple list', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test('renders a simple list', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   final myVariable = ["first", "second", "third"];
   print('Hello!'); // BREAKPOINT
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
-        await client.expectLocalVariable(
-          stop.threadId!,
-          expectedName: 'myVariable',
-          expectedDisplayString: 'List (3 items)',
-          // TODO(dantup): evaluateNames
-          expectedVariables: '''
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      await client.expectLocalVariable(
+        stop.threadId!,
+        expectedName: 'myVariable',
+        expectedDisplayString: 'List (3 items)',
+        // TODO(dantup): evaluateNames
+        expectedVariables: '''
             0: "first"
             1: "second"
             2: "third"
           ''',
-        );
-      });
+      );
+    });
 
-      test('renders a simple list subset', () async {
-        final client = dap.client;
-        final testFile = await dap.createTestFile(r'''
+    test('renders a simple list subset', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(r'''
 void main(List<String> args) {
   final myVariable = ["first", "second", "third"];
   print('Hello!'); // BREAKPOINT
 }
     ''');
-        final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
 
-        final stop = await client.hitBreakpoint(testFile, breakpointLine);
-        await client.expectLocalVariable(
-          stop.threadId!,
-          expectedName: 'myVariable',
-          expectedDisplayString: 'List (3 items)',
-          // TODO(dantup): evaluateNames
-          expectedVariables: '''
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      await client.expectLocalVariable(
+        stop.threadId!,
+        expectedName: 'myVariable',
+        expectedDisplayString: 'List (3 items)',
+        // TODO(dantup): evaluateNames
+        expectedVariables: '''
             1: "second"
           ''',
-          start: 1,
-          count: 1,
-        );
-      });
+        start: 1,
+        count: 1,
+      );
+    });
 
-      test('renders a simple map', () {
-        // TODO(dantup): Implement this (inc evaluateNames)
-      }, skip: true);
+    test('renders a simple map', () {
+      // TODO(dantup): Implement this (inc evaluateNames)
+    }, skip: true);
 
-      test('renders a simple map subset', () {
-        // TODO(dantup): Implement this (inc evaluateNames)
-      }, skip: true);
-      // These tests can be slow due to starting up the external server process.
-    }, timeout: Timeout.none);
-  });
+    test('renders a simple map subset', () {
+      // TODO(dantup): Implement this (inc evaluateNames)
+    }, skip: true);
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
 }
diff --git a/pkg/dds/test/dap/integration/no_debug_test.dart b/pkg/dds/test/dap/integration/no_debug_test.dart
index e9f5dd2..bb0af1d 100644
--- a/pkg/dds/test/dap/integration/no_debug_test.dart
+++ b/pkg/dds/test/dap/integration/no_debug_test.dart
@@ -4,13 +4,19 @@
 
 import 'package:test/test.dart';
 
+import 'test_client.dart';
 import 'test_support.dart';
 
 main() {
-  testDap((dap) async {
-    group('noDebug mode', () {
-      test('runs a simple script', () async {
-        final testFile = dap.createTestFile(r'''
+  late DapTestSession dap;
+  setUp(() async {
+    dap = await DapTestSession.setUp();
+  });
+  tearDown(() => dap.tearDown());
+
+  group('noDebug mode', () {
+    test('runs a simple script', () async {
+      final testFile = dap.createTestFile(r'''
 void main(List<String> args) async {
   print('Hello!');
   print('World!');
@@ -18,24 +24,23 @@
 }
     ''');
 
-        final outputEvents = await dap.client.collectOutput(
-          launch: () => dap.client.launch(
-            testFile.path,
-            noDebug: true,
-            args: ['one', 'two'],
-          ),
-        );
+      final outputEvents = await dap.client.collectOutput(
+        launch: () => dap.client.launch(
+          testFile.path,
+          noDebug: true,
+          args: ['one', 'two'],
+        ),
+      );
 
-        final output = outputEvents.map((e) => e.output).join();
-        expectLines(output, [
-          'Hello!',
-          'World!',
-          'args: [one, two]',
-          '',
-          'Exited.',
-        ]);
-      });
-      // These tests can be slow due to starting up the external server process.
-    }, timeout: Timeout.none);
-  });
+      final output = outputEvents.map((e) => e.output).join();
+      expectLines(output, [
+        'Hello!',
+        'World!',
+        'args: [one, two]',
+        '',
+        'Exited.',
+      ]);
+    });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
 }
diff --git a/pkg/dds/test/dap/integration/test_client.dart b/pkg/dds/test/dap/integration/test_client.dart
index c130e00..bfa05c6 100644
--- a/pkg/dds/test/dap/integration/test_client.dart
+++ b/pkg/dds/test/dap/integration/test_client.dart
@@ -10,7 +10,6 @@
 import 'package:dds/src/dap/logging.dart';
 import 'package:dds/src/dap/protocol_generated.dart';
 import 'package:dds/src/dap/protocol_stream.dart';
-import 'package:dds/src/dap/protocol_stream_transformers.dart';
 import 'package:test/test.dart';
 import 'package:vm_service/vm_service.dart' as vm;
 
@@ -22,19 +21,17 @@
 /// Methods on this class should map directly to protocol methods. Additional
 /// helpers are available in [DapTestClientExtension].
 class DapTestClient {
-  final Socket _socket;
   final ByteStreamServerChannel _channel;
   late final StreamSubscription<String> _subscription;
 
   final Logger? _logger;
   final bool captureVmServiceTraffic;
-  final _requestWarningDuration = const Duration(seconds: 2);
+  final _requestWarningDuration = const Duration(seconds: 5);
   final Map<int, _OutgoingRequest> _pendingRequests = {};
   final _eventController = StreamController<Event>.broadcast();
   int _seq = 1;
 
   DapTestClient._(
-    this._socket,
     this._channel,
     this._logger, {
     this.captureVmServiceTraffic = false,
@@ -57,21 +54,6 @@
   Stream<OutputEventBody> get outputEvents => events('output')
       .map((e) => OutputEventBody.fromJson(e.body as Map<String, Object?>));
 
-  /// Collects all output events until the program terminates.
-  Future<List<OutputEventBody>> collectOutput(
-      {File? file, Future<Response> Function()? launch}) async {
-    final outputEventsFuture = outputEvents.toList();
-
-    // Launch script and wait for termination.
-    await Future.wait([
-      event('terminated'),
-      initialize(),
-      launch?.call() ?? this.launch(file!.path),
-    ], eagerError: true);
-
-    return outputEventsFuture;
-  }
-
   /// Sends a continue request for the given thread.
   ///
   /// Returns a Future that completes when the server returns a corresponding
@@ -210,6 +192,19 @@
       sendRequest(StackTraceArguments(
           threadId: threadId, startFrame: startFrame, levels: numFrames));
 
+  /// Initializes the debug adapter and launches [file] or calls the custom
+  /// [launch] method.
+  Future<void> start({
+    File? file,
+    Future<Response> Function()? launch,
+  }) {
+    // Launch script and wait for termination.
+    return Future.wait([
+      initialize(),
+      launch?.call() ?? this.launch(file!.path),
+    ], eagerError: true);
+  }
+
   /// Sends a stepIn request for the given thread.
   ///
   /// Returns a Future that completes when the server returns a corresponding
@@ -226,7 +221,6 @@
 
   Future<void> stop() async {
     _channel.close();
-    await _socket.close();
     await _subscription.cancel();
   }
 
@@ -305,16 +299,12 @@
   /// Creates a [DapTestClient] that connects the server listening on
   /// [host]:[port].
   static Future<DapTestClient> connect(
-    String host,
-    int port, {
+    DapTestServer server, {
     bool captureVmServiceTraffic = false,
     Logger? logger,
   }) async {
-    final socket = await Socket.connect(host, port);
-    final channel = ByteStreamServerChannel(
-        socket.transform(Uint8ListTransformer()), socket, logger);
-
-    return DapTestClient._(socket, channel, logger,
+    final channel = ByteStreamServerChannel(server.stream, server.sink, logger);
+    return DapTestClient._(channel, logger,
         captureVmServiceTraffic: captureVmServiceTraffic);
   }
 }
@@ -441,6 +431,21 @@
     return ThreadsResponseBody.fromJson(response.body as Map<String, Object?>);
   }
 
+  /// Collects all output events until the program terminates.
+  ///
+  /// These results include all events in the order they are recieved, including
+  /// console, stdout and stderr.
+  Future<List<OutputEventBody>> collectOutput({
+    File? file,
+    Future<Response> Function()? launch,
+  }) async {
+    final outputEventsFuture = outputEvents.toList();
+
+    await start(file: file, launch: launch);
+
+    return outputEventsFuture;
+  }
+
   /// A helper that fetches scopes for a frame, checks for one with the name
   /// [expectedName] and verifies its variables.
   Future<Scope> expectScopeVariables(
diff --git a/pkg/dds/test/dap/integration/test_server.dart b/pkg/dds/test/dap/integration/test_server.dart
index c2a11f7..dc96dc7 100644
--- a/pkg/dds/test/dap/integration/test_server.dart
+++ b/pkg/dds/test/dap/integration/test_server.dart
@@ -13,10 +13,9 @@
 import 'package:pedantic/pedantic.dart';
 
 abstract class DapTestServer {
-  List<String> get errorLogs;
-  String get host;
-  int get port;
   Future<void> stop();
+  StreamSink<List<int>> get sink;
+  Stream<List<int>> get stream;
 }
 
 /// An instance of a DAP server running in-process (to aid debugging).
@@ -25,22 +24,27 @@
 /// serialized and deserialized but it's not quite the same running out of
 /// process.
 class InProcessDapTestServer extends DapTestServer {
-  final DapServer _server;
+  late final DapServer _server;
+  final stdinController = StreamController<List<int>>();
+  final stdoutController = StreamController<List<int>>();
 
-  InProcessDapTestServer._(this._server);
+  StreamSink<List<int>> get sink => stdinController.sink;
+  Stream<List<int>> get stream => stdoutController.stream;
 
-  String get host => _server.host;
-  int get port => _server.port;
-  List<String> get errorLogs => const []; // In-proc errors just throw in-line.
+  InProcessDapTestServer._() {
+    _server = DapServer(stdinController.stream, stdoutController.sink);
+  }
 
   @override
   Future<void> stop() async {
-    await _server.stop();
+    _server.stop();
   }
 
-  static Future<InProcessDapTestServer> create({Logger? logger}) async {
-    final DapServer server = await DapServer.create(logger: logger);
-    return InProcessDapTestServer._(server);
+  static Future<InProcessDapTestServer> create({
+    Logger? logger,
+    List<String>? additionalArgs,
+  }) async {
+    return InProcessDapTestServer._();
   }
 }
 
@@ -52,28 +56,22 @@
 class OutOfProcessDapTestServer extends DapTestServer {
   var _isShuttingDown = false;
   final Process _process;
-  final int port;
-  final String host;
-  final List<String> _errors = [];
 
-  List<String> get errorLogs => _errors;
+  StreamSink<List<int>> get sink => _process.stdin;
+  Stream<List<int>> get stream => _process.stdout;
 
   OutOfProcessDapTestServer._(
     this._process,
-    this.host,
-    this.port,
     Logger? logger,
   ) {
     // Treat anything written to stderr as the DAP crashing and fail the test.
     _process.stderr.transform(utf8.decoder).listen((error) {
       logger?.call(error);
-      _errors.add(error);
       throw error;
     });
     unawaited(_process.exitCode.then((code) {
       final message = 'Out-of-process DAP server terminated with code $code';
       logger?.call(message);
-      _errors.add(message);
       if (!_isShuttingDown && code != 0) {
         throw message;
       }
@@ -107,27 +105,6 @@
       ],
     );
 
-    final startedCompleter = Completer<void>();
-    late String host;
-    late int port;
-
-    // Scrape the `started` event to get the host/port. Any other output
-    // should be sent to the logger (as it may be verbose output for diagnostic
-    // purposes).
-    _process.stdout.transform(utf8.decoder).listen((text) {
-      if (!startedCompleter.isCompleted) {
-        final event = jsonDecode(text);
-        if (event['state'] == 'started') {
-          host = event['dapHost'];
-          port = event['dapPort'];
-          startedCompleter.complete();
-          return;
-        }
-      }
-      logger?.call(text);
-    });
-    await startedCompleter.future;
-
-    return OutOfProcessDapTestServer._(_process, host, port, logger);
+    return OutOfProcessDapTestServer._(_process, logger);
   }
 }
diff --git a/pkg/dds/test/dap/integration/test_support.dart b/pkg/dds/test/dap/integration/test_support.dart
index 45460b2..a44a791 100644
--- a/pkg/dds/test/dap/integration/test_support.dart
+++ b/pkg/dds/test/dap/integration/test_support.dart
@@ -12,20 +12,6 @@
 import 'test_client.dart';
 import 'test_server.dart';
 
-/// A logger to use to log all traffic (both DAP and VM) to stdout.
-///
-/// If the enviroment variable is `DAP_TEST_VERBOSE` then `print` will be used,
-/// otherwise there will be no verbose logging.
-///
-///   DAP_TEST_VERBOSE=true pub run test --chain-stack-traces test/dap/integration
-///
-///
-/// When using the out-of-process DAP, this causes `--verbose` to be passed to
-/// the server which causes it to write all traffic to `stdout` which is then
-/// picked up by [OutOfProcessDapTestServer] and passed to this logger.
-final logger =
-    Platform.environment['DAP_TEST_VERBOSE'] == 'true' ? print : null;
-
 /// Whether to run the DAP server in-process with the tests, or externally in
 /// another process.
 ///
@@ -35,6 +21,14 @@
 /// simplified in VS Code by using a launch config with custom CodeLens links).
 final useInProcessDap = Platform.environment['DAP_TEST_INTERNAL'] == 'true';
 
+/// A [RegExp] that matches the `path` part of a VM Service URI that contains
+/// an authentication token.
+final vmServiceAuthCodePathPattern = RegExp(r'^/[\w_\-=]{5,15}/ws$');
+
+/// A [RegExp] that matches the "Connecting to VM Service" banner that is sent
+/// as the first output event for a debug session.
+final vmServiceUriPattern = RegExp(r'Connecting to VM Service at ([^\s]+)\s');
+
 /// Expects [actual] to equal the lines [expected], ignoring differences in line
 /// endings.
 void expectLines(String actual, List<String> expected) {
@@ -45,33 +39,13 @@
 int lineWith(File file, String searchText) =>
     file.readAsLinesSync().indexWhere((line) => line.contains(searchText)) + 1;
 
-/// A helper function to wrap all tests in a library with setup/teardown functions
-/// to start a shared server for all tests in the library and an individual
-/// client for each test.
-testDap(
-  Future<void> Function(DapTestSession session) tests, {
-  List<String>? additionalArgs,
-}) {
-  final session = DapTestSession(additionalArgs: additionalArgs);
-
-  setUpAll(session.setUpAll);
-  tearDownAll(session.tearDownAll);
-  setUp(session.setUp);
-  tearDown(session.tearDown);
-
-  return tests(session);
-}
-
-/// A helper class provided to DAP integration tests run with [testDap] to
-/// easily share setup/teardown without sharing state across tests from different
-/// files.
+/// A helper class containing the DAP server/client for DAP integration tests.
 class DapTestSession {
-  late DapTestServer server;
-  late DapTestClient client;
+  DapTestServer server;
+  DapTestClient client;
   final _testFolders = <Directory>[];
-  final List<String>? additionalArgs;
 
-  DapTestSession({this.additionalArgs});
+  DapTestSession._(this.server, this.client);
 
   /// Creates a file in a temporary folder to be used as an application for testing.
   ///
@@ -84,60 +58,32 @@
     return testFile;
   }
 
-  Future<void> setUp() async {
-    client = await _startClient(server);
+  static Future<DapTestSession> setUp({List<String>? additionalArgs}) async {
+    final server = await _startServer(additionalArgs: additionalArgs);
+    final client = await DapTestClient.connect(server);
+    return DapTestSession._(server, client);
   }
 
-  Future<void> setUpAll() async {
-    server = await _startServer(logger: logger, additionalArgs: additionalArgs);
-  }
-
-  Future<void> tearDown() => client.stop();
-
-  Future<void> tearDownAll() async {
+  Future<void> tearDown() async {
+    await client.stop();
     await server.stop();
 
     // Clean up any temp folders created during the test runs.
-    _testFolders.forEach((dir) => dir.deleteSync(recursive: true));
-  }
-
-  /// Creates and connects a new [DapTestClient] to [server].
-  Future<DapTestClient> _startClient(DapTestServer server) async {
-    // Since we don't get a signal from the DAP server when it's ready and we
-    // just started it, add a short retry to connections.
-    // Since the bots can be quite slow, it may take 6-7 seconds for the server
-    // to initially start up (including compilation).
-    var attempt = 1;
-    while (attempt++ <= 100) {
-      try {
-        return await DapTestClient.connect(server.host, server.port);
-      } catch (e) {
-        await Future.delayed(const Duration(milliseconds: 200));
-      }
-    }
-
-    final errorMessage = StringBuffer();
-    errorMessage.writeln(
-      'Failed to connect to DAP server on port ${server.port}'
-      ' after $attempt attempts. Did the server start correctly?',
-    );
-
-    final serverErrorLogs = server.errorLogs;
-    if (serverErrorLogs.isNotEmpty) {
-      errorMessage.writeln('Server errors:');
-      errorMessage.writeAll(serverErrorLogs);
-    }
-
-    throw Exception(errorMessage.toString());
+    _testFolders
+      ..forEach((dir) => dir.deleteSync(recursive: true))
+      ..clear();
   }
 
   /// Starts a DAP server that can be shared across tests.
-  Future<DapTestServer> _startServer({
+  static Future<DapTestServer> _startServer({
     Logger? logger,
     List<String>? additionalArgs,
   }) async {
     return useInProcessDap
-        ? await InProcessDapTestServer.create(logger: logger)
+        ? await InProcessDapTestServer.create(
+            logger: logger,
+            additionalArgs: additionalArgs,
+          )
         : await OutOfProcessDapTestServer.create(
             logger: logger,
             additionalArgs: additionalArgs,
diff --git a/pkg/dds/tool/dap/run_server.dart b/pkg/dds/tool/dap/run_server.dart
index 5fd19f2..cfddd48 100644
--- a/pkg/dds/tool/dap/run_server.dart
+++ b/pkg/dds/tool/dap/run_server.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'dart:convert';
+import 'dart:async';
 import 'dart:io';
 
 import 'package:args/command_runner.dart';
@@ -12,7 +12,7 @@
   // TODO(dantup): "dap_tool" is a placeholder and will likely eventually be a
   // "dart" command.
   final runner = CommandRunner('dap_tool', 'Dart DAP Tool')
-    ..addCommand(DapCommand());
+    ..addCommand(DapCommand(stdin, stdout.nonBlocking));
 
   try {
     await runner.run(arguments);
@@ -23,33 +23,22 @@
 }
 
 class DapCommand extends Command {
-  static const argHost = 'host';
-  static const argPort = 'port';
   static const argIpv6 = 'ipv6';
   static const argDds = 'dds';
   static const argAuthCodes = 'auth-codes';
   static const argVerbose = 'verbose';
 
+  final Stream<List<int>> _inputStream;
+  final StreamSink<List<int>> _outputSink;
+
   @override
   final String description = 'Start a DAP debug server.';
 
   @override
   final String name = 'dap';
 
-  DapCommand() {
+  DapCommand(this._inputStream, this._outputSink) {
     argParser
-      ..addOption(
-        argHost,
-        help: 'The hostname/IP to bind the server to. If not supplied, will'
-            ' use the appropriate loopback address depending on whether'
-            ' --ipv6 is set',
-      )
-      ..addOption(
-        argPort,
-        abbr: 'p',
-        defaultsTo: '0',
-        help: 'The port to bind the server to',
-      )
       ..addFlag(
         argIpv6,
         help: 'Whether to bind DAP/VM Service/DDS to IPv6 addresses',
@@ -63,33 +52,21 @@
         argAuthCodes,
         defaultsTo: true,
         help: 'Whether to enable authentication codes for VM Services',
-      )
-      ..addFlag(
-        argVerbose,
-        abbr: 'v',
-        help: 'Whether to print diagnostic output to stdout',
       );
   }
 
   Future<void> run() async {
     final args = argResults!;
-    final port = int.parse(args[argPort]);
-    final host = args[argHost];
     final ipv6 = args[argIpv6] as bool;
 
-    final server = await DapServer.create(
-      host: host,
-      port: port,
+    final server = DapServer(
+      _inputStream,
+      _outputSink,
       ipv6: ipv6,
-      enableDdds: args[argDds],
+      enableDds: args[argDds],
       enableAuthCodes: args[argAuthCodes],
-      logger: args[argVerbose] ? print : null,
     );
 
-    stdout.write(jsonEncode({
-      'state': 'started',
-      'dapHost': server.host,
-      'dapPort': server.port,
-    }));
+    await server.channel.closed;
   }
 }
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index 8a3759b..716a5a2 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -124,7 +124,7 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes);
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes);
 
   /// Registers a constructor redirection for this class and returns true if
   /// this redirection gives rise to a cycle that has not been reported before.
@@ -345,11 +345,11 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     void build(String ignore, Builder declaration) {
       MemberBuilder member = declaration as MemberBuilder;
-      member.buildOutlineExpressions(
-          library, coreTypes, delayedActionPerformers, clonedFunctionNodes);
+      member.buildOutlineExpressions(library, coreTypes,
+          delayedActionPerformers, synthesizedFunctionNodes);
     }
 
     MetadataBuilder.buildAnnotations(
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 b0a984c..e49c0c7 100644
--- a/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
@@ -20,7 +20,7 @@
     show ExpressionGeneratorHelper;
 import '../kernel/kernel_builder.dart'
     show isRedirectingGenerativeConstructorImplementation;
-import '../kernel/kernel_target.dart' show ClonedFunctionNode;
+import '../kernel/kernel_target.dart' show SynthesizedFunctionNode;
 
 import '../loader.dart' show Loader;
 
@@ -252,9 +252,9 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     super.buildOutlineExpressions(
-        library, coreTypes, delayedActionPerformers, clonedFunctionNodes);
+        library, coreTypes, delayedActionPerformers, synthesizedFunctionNodes);
 
     // For modular compilation purposes we need to include initializers
     // for const constructors into the outline.
@@ -471,13 +471,14 @@
 
 class SyntheticConstructorBuilder extends DillConstructorBuilder {
   MemberBuilderImpl? _origin;
-  ClonedFunctionNode? _clonedFunctionNode;
+  SynthesizedFunctionNode? _synthesizedFunctionNode;
 
   SyntheticConstructorBuilder(SourceClassBuilder parent,
       Constructor constructor, Procedure? constructorTearOff,
-      {MemberBuilderImpl? origin, ClonedFunctionNode? clonedFunctionNode})
+      {MemberBuilderImpl? origin,
+      SynthesizedFunctionNode? synthesizedFunctionNode})
       : _origin = origin,
-        _clonedFunctionNode = clonedFunctionNode,
+        _synthesizedFunctionNode = synthesizedFunctionNode,
         super(constructor, constructorTearOff, parent);
 
   @override
@@ -485,7 +486,7 @@
       SourceLibraryBuilder libraryBuilder,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     if (_origin != null) {
       // Ensure that default value expressions have been created for [_origin].
       LibraryBuilder originLibraryBuilder = _origin!.library;
@@ -493,10 +494,10 @@
         // If [_origin] is from a source library, we need to build the default
         // values and initializers first.
         _origin!.buildOutlineExpressions(originLibraryBuilder, coreTypes,
-            delayedActionPerformers, clonedFunctionNodes);
+            delayedActionPerformers, synthesizedFunctionNodes);
       }
-      _clonedFunctionNode!.cloneDefaultValues();
-      _clonedFunctionNode = null;
+      _synthesizedFunctionNode!.cloneDefaultValues();
+      _synthesizedFunctionNode = null;
       _origin = null;
     }
   }
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 6c173ef..463174c 100644
--- a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
@@ -523,7 +523,7 @@
       SourceLibraryBuilder libraryBuilder,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     List<Expression> values = <Expression>[];
     if (enumConstantInfos != null) {
       for (EnumConstantInfo? enumConstantInfo in enumConstantInfos!) {
@@ -568,7 +568,7 @@
       }
     }
     super.buildOutlineExpressions(
-        library, coreTypes, delayedActionPerformers, clonedFunctionNodes);
+        library, coreTypes, delayedActionPerformers, synthesizedFunctionNodes);
   }
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/builder/extension_builder.dart b/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
index d52ae6f..9435193 100644
--- a/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
@@ -38,7 +38,7 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes);
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes);
 
   /// Looks up extension member by [name] taking privacy into account.
   ///
diff --git a/pkg/front_end/lib/src/fasta/builder/factory_builder.dart b/pkg/front_end/lib/src/fasta/builder/factory_builder.dart
index 4ca72bb..4646515 100644
--- a/pkg/front_end/lib/src/fasta/builder/factory_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/factory_builder.dart
@@ -341,10 +341,9 @@
     }
     updatePrivateMemberName(_procedureInternal, libraryBuilder);
     if (_factoryTearOff != null) {
-      _tearOffTypeParameters = buildRedirectingFactoryTearOffProcedure(
-          _factoryTearOff!,
-          _procedure,
-          library as SourceLibraryBuilder);
+      _tearOffTypeParameters =
+          buildRedirectingFactoryTearOffProcedureParameters(
+              _factoryTearOff!, _procedure, library);
     }
     return _procedureInternal;
   }
@@ -354,9 +353,9 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     super.buildOutlineExpressions(
-        library, coreTypes, delayedActionPerformers, clonedFunctionNodes);
+        library, coreTypes, delayedActionPerformers, synthesizedFunctionNodes);
     RedirectingFactoryBody redirectingFactoryBody =
         _procedure.function.body as RedirectingFactoryBody;
     List<DartType>? typeArguments = redirectingFactoryBody.typeArguments;
@@ -416,14 +415,13 @@
       }
       member.function!.body = new RedirectingFactoryBody(target, typeArguments);
     }
-    if (_factoryTearOff != null) {
-      clonedFunctionNodes.add(buildRedirectingFactoryTearOffBody(
+    if (_factoryTearOff != null &&
+        (target is Constructor || target is Procedure && target.isFactory)) {
+      synthesizedFunctionNodes.add(buildRedirectingFactoryTearOffBody(
           _factoryTearOff!,
-          target as Constructor,
+          target!,
           typeArguments ?? [],
           _tearOffTypeParameters!));
-      delayedActionPerformers.add(new _CopyDefaultValues(
-          _factoryTearOff!, redirectingFactoryBody.target!.function!));
     }
   }
 
@@ -445,18 +443,3 @@
     return getRedirectingFactoryBody(_procedure)!.typeArguments;
   }
 }
-
-class _CopyDefaultValues implements DelayedActionPerformer {
-  final Procedure _tearOff;
-  final FunctionNode _function;
-
-  _CopyDefaultValues(this._tearOff, this._function);
-
-  @override
-  bool get hasDelayedActions => true;
-
-  @override
-  void performDelayedActions() {
-    copyTearOffDefaultValues(_tearOff, _function);
-  }
-}
diff --git a/pkg/front_end/lib/src/fasta/builder/field_builder.dart b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
index b1ae35f..4fd424a 100644
--- a/pkg/front_end/lib/src/fasta/builder/field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
@@ -400,7 +400,7 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     _fieldEncoding.completeSignature(coreTypes);
 
     for (Annotatable annotatable in _fieldEncoding.annotatables) {
diff --git a/pkg/front_end/lib/src/fasta/builder/function_builder.dart b/pkg/front_end/lib/src/fasta/builder/function_builder.dart
index 638c80f..e4cab78 100644
--- a/pkg/front_end/lib/src/fasta/builder/function_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/function_builder.dart
@@ -480,7 +480,7 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     if (!_hasBuiltOutlineExpressions) {
       DeclarationBuilder? classOrExtensionBuilder =
           isClassMember || isExtensionMember
diff --git a/pkg/front_end/lib/src/fasta/builder/member_builder.dart b/pkg/front_end/lib/src/fasta/builder/member_builder.dart
index c7d1f9d..87167ef 100644
--- a/pkg/front_end/lib/src/fasta/builder/member_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/member_builder.dart
@@ -80,7 +80,7 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes);
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes);
 
   /// Returns the [ClassMember]s for the non-setter members created for this
   /// member builder.
@@ -187,7 +187,7 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {}
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {}
 
   /// Builds the core AST structures for this member as needed for the outline.
   void buildMembers(
diff --git a/pkg/front_end/lib/src/fasta/dill/dill_extension_builder.dart b/pkg/front_end/lib/src/fasta/dill/dill_extension_builder.dart
index 0c5d28f..c9aba98 100644
--- a/pkg/front_end/lib/src/fasta/dill/dill_extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/dill/dill_extension_builder.dart
@@ -109,7 +109,7 @@
       LibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     // TODO(johnniwinther): Remove the need for this.
   }
 }
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 f7603ef..2383c40 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
@@ -4,6 +4,7 @@
 
 import 'package:kernel/ast.dart';
 import 'package:kernel/type_algebra.dart';
+import '../builder/library_builder.dart';
 import '../builder/member_builder.dart';
 import '../source/source_library_builder.dart';
 import 'kernel_api.dart';
@@ -61,7 +62,7 @@
 void buildConstructorTearOffProcedure(Procedure tearOff, Member constructor,
     Class enclosingClass, SourceLibraryBuilder libraryBuilder) {
   assert(constructor is Constructor ||
-      (constructor is Procedure && constructor.kind == ProcedureKind.Factory));
+      (constructor is Procedure && constructor.isFactory));
 
   int fileOffset = tearOff.fileOffset;
 
@@ -84,19 +85,7 @@
   Substitution substitution = freshTypeParameters.substitution;
   _createParameters(tearOff, function, substitution);
   Arguments arguments = _createArguments(tearOff, typeArguments, fileOffset);
-
-  Expression constructorInvocation;
-  if (constructor is Constructor) {
-    constructorInvocation = new ConstructorInvocation(constructor, arguments)
-      ..fileOffset = tearOff.fileOffset;
-  } else {
-    constructorInvocation =
-        new StaticInvocation(constructor as Procedure, arguments)
-          ..fileOffset = tearOff.fileOffset;
-  }
-  tearOff.function.body = new ReturnStatement(constructorInvocation)
-    ..fileOffset = tearOff.fileOffset
-    ..parent = tearOff.function;
+  _createTearOffBody(tearOff, constructor, arguments);
   tearOff.function.fileOffset = tearOff.fileOffset;
   tearOff.function.fileEndOffset = tearOff.fileOffset;
   updatePrivateMemberName(tearOff, libraryBuilder);
@@ -157,10 +146,10 @@
 
 /// Creates the parameters for the redirecting factory [tearOff] based on the
 /// [redirectingConstructor] declaration.
-FreshTypeParameters buildRedirectingFactoryTearOffProcedure(
+FreshTypeParameters buildRedirectingFactoryTearOffProcedureParameters(
     Procedure tearOff,
     Procedure redirectingConstructor,
-    SourceLibraryBuilder libraryBuilder) {
+    LibraryBuilder libraryBuilder) {
   assert(redirectingConstructor.isRedirectingFactory);
   FunctionNode function = redirectingConstructor.function;
   FreshTypeParameters freshTypeParameters =
@@ -176,11 +165,11 @@
 /// Creates the body for the redirecting factory [tearOff] with the target
 /// [constructor] and [typeArguments].
 ///
-/// Returns the [ClonedFunctionNode] object need to perform default value
+/// Returns the [SynthesizedFunctionNode] object need to perform default value
 /// computation.
-ClonedFunctionNode buildRedirectingFactoryTearOffBody(
+SynthesizedFunctionNode buildRedirectingFactoryTearOffBody(
     Procedure tearOff,
-    Constructor constructor,
+    Member target,
     List<DartType> typeArguments,
     FreshTypeParameters freshTypeParameters) {
   int fileOffset = tearOff.fileOffset;
@@ -196,18 +185,13 @@
   }
 
   Arguments arguments = _createArguments(tearOff, typeArguments, fileOffset);
-  Expression constructorInvocation =
-      new ConstructorInvocation(constructor, arguments)
-        ..fileOffset = tearOff.fileOffset;
-  tearOff.function.body = new ReturnStatement(constructorInvocation)
-    ..fileOffset = tearOff.fileOffset
-    ..parent = tearOff.function;
-
-  return new ClonedFunctionNode(
+  _createTearOffBody(tearOff, target, arguments);
+  return new SynthesizedFunctionNode(
       new Map<TypeParameter, DartType>.fromIterables(
-          constructor.enclosingClass.typeParameters, typeArguments),
-      constructor.function,
-      tearOff.function);
+          target.enclosingClass!.typeParameters, typeArguments),
+      target.function!,
+      tearOff.function,
+      identicalSignatures: false);
 }
 
 /// Creates the synthesized name to use for the lowering of the tear off of a
@@ -363,3 +347,20 @@
     ..fileOffset = tearOff.fileOffset;
   return arguments;
 }
+
+/// Creates the tear of body for [tearOff] which calls [target] with
+/// [arguments].
+void _createTearOffBody(Procedure tearOff, Member target, Arguments arguments) {
+  assert(target is Constructor || (target is Procedure && target.isFactory));
+  Expression constructorInvocation;
+  if (target is Constructor) {
+    constructorInvocation = new ConstructorInvocation(target, arguments)
+      ..fileOffset = tearOff.fileOffset;
+  } else {
+    constructorInvocation = new StaticInvocation(target as Procedure, arguments)
+      ..fileOffset = tearOff.fileOffset;
+  }
+  tearOff.function.body = new ReturnStatement(constructorInvocation)
+    ..fileOffset = tearOff.fileOffset
+    ..parent = tearOff.function;
+}
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 a49ae77..7363aa8 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -139,7 +139,8 @@
   final bool errorOnUnevaluatedConstant =
       CompilerContext.current.options.errorOnUnevaluatedConstant;
 
-  final List<ClonedFunctionNode> clonedFunctionNodes = <ClonedFunctionNode>[];
+  final List<SynthesizedFunctionNode> synthesizedFunctionNodes =
+      <SynthesizedFunctionNode>[];
 
   KernelTarget(this.fileSystem, this.includeComments, DillTarget dillTarget,
       UriTranslator uriTranslator)
@@ -336,7 +337,8 @@
       loader.checkAbstractMembers(myClasses);
       loader.addNoSuchMethodForwarders(myClasses);
       loader.checkMixins(myClasses);
-      loader.buildOutlineExpressions(loader.coreTypes, clonedFunctionNodes);
+      loader.buildOutlineExpressions(
+          loader.coreTypes, synthesizedFunctionNodes);
       loader.checkTypes();
       loader.checkRedirectingFactories(myClasses);
       loader.checkMainMethods();
@@ -757,16 +759,17 @@
         returnType: makeConstructorReturnType(cls));
     SuperInitializer initializer = new SuperInitializer(
         constructor, new Arguments(positional, named: named));
-    ClonedFunctionNode clonedFunctionNode =
-        new ClonedFunctionNode(substitutionMap, constructor.function, function);
+    SynthesizedFunctionNode synthesizedFunctionNode =
+        new SynthesizedFunctionNode(
+            substitutionMap, constructor.function, function);
     if (!isConst) {
       // For constant constructors default values are computed and cloned part
       // of the outline expression and therefore passed to the
       // [SyntheticConstructorBuilder] below.
       //
       // For non-constant constructors default values are cloned as part of the
-      // full compilation using [clonedFunctionNodes].
-      clonedFunctionNodes.add(clonedFunctionNode);
+      // full compilation using [synthesizedFunctionNodes].
+      synthesizedFunctionNodes.add(synthesizedFunctionNode);
     }
     return new SyntheticConstructorBuilder(
         classBuilder,
@@ -785,14 +788,15 @@
         // cloned function nodes to ensure that the default values are computed
         // and cloned for the outline.
         origin: isConst ? memberBuilder : null,
-        clonedFunctionNode: isConst ? clonedFunctionNode : null);
+        synthesizedFunctionNode: isConst ? synthesizedFunctionNode : null);
   }
 
   void finishClonedParameters() {
-    for (ClonedFunctionNode clonedFunctionNode in clonedFunctionNodes) {
-      clonedFunctionNode.cloneDefaultValues();
+    for (SynthesizedFunctionNode synthesizedFunctionNode
+        in synthesizedFunctionNodes) {
+      synthesizedFunctionNode.cloneDefaultValues();
     }
-    clonedFunctionNodes.clear();
+    synthesizedFunctionNodes.clear();
     ticker.logMs("Cloned default values of formals");
   }
 
@@ -1424,12 +1428,36 @@
   }
 }
 
-class ClonedFunctionNode {
+/// Data for clone default values for synthesized function nodes once the
+/// original default values have been computed.
+///
+/// This is used for constructors in unnamed mixin application, which are
+/// created from the constructors in the superclass, and for tear off lowerings
+/// for redirecting factories, which are created from the effective target
+/// constructor.
+class SynthesizedFunctionNode {
+  /// Type parameter map from type parameters in scope [_original] to types
+  /// in scope of [_synthesized].
+  // TODO(johnniwinther): Is this ever needed? Should occurrence of type
+  //  variable types in default values be a compile time error?
   final Map<TypeParameter, DartType> _typeSubstitution;
-  final FunctionNode _original;
-  final FunctionNode _clone;
 
-  ClonedFunctionNode(this._typeSubstitution, this._original, this._clone);
+  /// The original function node.
+  final FunctionNode _original;
+
+  /// The synthesized function node.
+  final FunctionNode _synthesized;
+
+  /// If `true`, the [_synthesized] is guaranteed to have the same parameters in
+  /// the same order as [_original]. Otherwise [_original] is only guaranteed to
+  /// be callable from [_synthesized], meaning that is has at most the same
+  /// number of positional parameters and a, possibly reordered, subset of the
+  /// named parameters.
+  final bool identicalSignatures;
+
+  SynthesizedFunctionNode(
+      this._typeSubstitution, this._original, this._synthesized,
+      {this.identicalSignatures: true});
 
   void cloneDefaultValues() {
     // TODO(ahe): It is unclear if it is legal to use type variables in
@@ -1449,13 +1477,35 @@
       }
     }
 
-    for (int i = 0; i < _original.positionalParameters.length; i++) {
-      cloneInitializer(
-          _original.positionalParameters[i], _clone.positionalParameters[i]);
+    // For mixin application constructors, the argument count is the same, but
+    // for redirecting tear off lowerings, the argument count of the tear off
+    // can be less than that of the redirection target.
+
+    assert(_synthesized.positionalParameters.length <=
+        _original.positionalParameters.length);
+    for (int i = 0; i < _synthesized.positionalParameters.length; i++) {
+      cloneInitializer(_original.positionalParameters[i],
+          _synthesized.positionalParameters[i]);
     }
 
-    for (int i = 0; i < _original.namedParameters.length; i++) {
-      cloneInitializer(_original.namedParameters[i], _clone.namedParameters[i]);
+    if (identicalSignatures) {
+      assert(_synthesized.namedParameters.length ==
+          _original.namedParameters.length);
+      for (int i = 0; i < _synthesized.namedParameters.length; i++) {
+        cloneInitializer(
+            _original.namedParameters[i], _synthesized.namedParameters[i]);
+      }
+    } else if (_synthesized.namedParameters.isNotEmpty) {
+      Map<String, VariableDeclaration> originalParameters = {};
+      for (int i = 0; i < _original.namedParameters.length; i++) {
+        originalParameters[_original.namedParameters[i].name!] =
+            _original.namedParameters[i];
+      }
+      for (int i = 0; i < _synthesized.namedParameters.length; i++) {
+        cloneInitializer(
+            originalParameters[_synthesized.namedParameters[i].name!]!,
+            _synthesized.namedParameters[i]);
+      }
     }
   }
 }
diff --git a/pkg/front_end/lib/src/fasta/scope.dart b/pkg/front_end/lib/src/fasta/scope.dart
index 9427c99..4027001 100644
--- a/pkg/front_end/lib/src/fasta/scope.dart
+++ b/pkg/front_end/lib/src/fasta/scope.dart
@@ -811,7 +811,7 @@
       LibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     throw new UnsupportedError(
         'AmbiguousMemberBuilder.buildOutlineExpressions');
   }
diff --git a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
index 30f4fff..d6a21ef 100644
--- a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
@@ -276,7 +276,7 @@
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
       List<DelayedActionPerformer> delayedActionPerformers,
-      List<ClonedFunctionNode> clonedFunctionNodes) {
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     MetadataBuilder.buildAnnotations(isPatch ? origin.extension : extension,
         metadata, library, this, null, fileUri);
     if (typeParameters != null) {
@@ -288,8 +288,8 @@
 
     void build(String ignore, Builder declaration) {
       MemberBuilder member = declaration as MemberBuilder;
-      member.buildOutlineExpressions(
-          library, coreTypes, delayedActionPerformers, clonedFunctionNodes);
+      member.buildOutlineExpressions(library, coreTypes,
+          delayedActionPerformers, synthesizedFunctionNodes);
     }
 
     scope.forEach(build);
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 843754e..db885ca 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -92,7 +92,8 @@
 import '../kernel/kernel_builder.dart'
     show ClassHierarchyBuilder, ClassMember, DelayedCheck;
 
-import '../kernel/kernel_target.dart' show ClonedFunctionNode, KernelTarget;
+import '../kernel/kernel_target.dart'
+    show SynthesizedFunctionNode, KernelTarget;
 
 import '../kernel/body_builder.dart' show BodyBuilder;
 
@@ -1182,8 +1183,8 @@
     ticker.logMs("Checked mixin declaration applications");
   }
 
-  void buildOutlineExpressions(
-      CoreTypes coreTypes, List<ClonedFunctionNode> clonedFunctionNodes) {
+  void buildOutlineExpressions(CoreTypes coreTypes,
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     List<DelayedActionPerformer> delayedActionPerformers =
         <DelayedActionPerformer>[];
     for (LibraryBuilder library in builders.values) {
@@ -1194,13 +1195,13 @@
           Builder declaration = iterator.current;
           if (declaration is ClassBuilder) {
             declaration.buildOutlineExpressions(library, coreTypes,
-                delayedActionPerformers, clonedFunctionNodes);
+                delayedActionPerformers, synthesizedFunctionNodes);
           } else if (declaration is ExtensionBuilder) {
             declaration.buildOutlineExpressions(library, coreTypes,
-                delayedActionPerformers, clonedFunctionNodes);
+                delayedActionPerformers, synthesizedFunctionNodes);
           } else if (declaration is MemberBuilder) {
             declaration.buildOutlineExpressions(library, coreTypes,
-                delayedActionPerformers, clonedFunctionNodes);
+                delayedActionPerformers, synthesizedFunctionNodes);
           } else if (declaration is TypeAliasBuilder) {
             declaration.buildOutlineExpressions(
                 library, coreTypes, delayedActionPerformers);
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 07ed5c1..e8b4e79 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -1018,6 +1018,7 @@
 remover
 renames
 render
+reordered
 reparse
 repeating
 replacements
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 922e0ac..a51d1da 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -111,10 +111,13 @@
 c1b
 c1c
 c1d
+c1e
+c1f
 c2a
 c2b
 c2c
 c2d
+c2e
 c3a
 c3b
 c3c
@@ -125,6 +128,10 @@
 c4b
 c4c
 c4d
+c4e
+c4f
+c4g
+c4h
 c59cdee365b94ce066344840f9e3412d642019b
 c5a
 c5b
@@ -133,6 +140,9 @@
 c6a
 c6b
 c6c
+c7a
+c8a
+c8b
 ca
 cafebabe
 calloc
@@ -163,6 +173,10 @@
 class5b
 class5c
 class5d
+class7a
+class7b
+class8a
+class8b
 cloneable
 cm
 cmd
@@ -331,6 +345,9 @@
 f2
 f2a
 f2b
+f2c
+f2d
+f2e
 f3
 f3a
 f3b
@@ -342,10 +359,15 @@
 f4a
 f4b
 f4c
+f4d
+f4e
+f4f
 f5a
 f5b
 f6a
 f6b
+f7a
+f8a
 fac
 faking
 falling
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart
index e9ac4b7..ff03d6e 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart
@@ -27,6 +27,16 @@
 
   expect(true, identical(f2a, f2b));
 
+  var f2c = Class2.redirect;
+  var c2c = f2c();
+  expect(true, c2c is Class2);
+
+  dynamic f2d = Class2.redirect;
+  var c2d = f2d();
+  expect(true, c2d is Class2);
+
+  expect(true, identical(f2c, f2d));
+
   var f3a = Class3.new;
   var c3a = f3a(42);
   expect(42, c3a.field);
@@ -66,6 +76,31 @@
   expect(false, c4d is Class4<int>);
   throws(() => f4c<int, String>());
 
+  var f4d = Class4.redirect;
+  var c4e = f4d();
+  expect(true, c4e is Class4<dynamic>);
+  expect(false, c4e is Class4<int>);
+  var c4f = f4d<int>();
+  expect(true, c4f is Class4<int>);
+  expect(false, c4f is Class4<String>);
+  () {
+    f4d<int, String>(); // error
+  };
+
+  var f4e = f4d<int>;
+  var c4g = f4e();
+  expect(true, c4g is Class4<int>);
+  expect(false, c4g is Class4<String>);
+  () {
+    f4e<int>(); // error
+  };
+
+  dynamic f4f = Class4.redirect;
+  var c4h = f4f();
+  expect(true, c4h is Class4<dynamic>);
+  expect(false, c4h is Class4<int>);
+  throws(() => f4f<int, String>());
+
   var f5a = Class5.new;
   var c5a = f5a();
   expect(true, c5a is Class5<num>);
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.strong.expect
index 5f219e3..1806fdc 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.strong.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.strong.expect
@@ -2,29 +2,37 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:34:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:44:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:35:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:45:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:52:8: Error: Expected 1 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:62:8: Error: Expected 1 type arguments.
 //     f4a<int, String>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:60:8: Error: Expected 0 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:70:8: Error: Expected 0 type arguments.
 //     f4b<int>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:77:8: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:87:8: Error: Expected 1 type arguments.
+//     f4d<int, String>(); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:95:8: Error: Expected 0 type arguments.
+//     f4e<int>(); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:112:8: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
 // Try changing type arguments so that they conform to the bounds.
 //     f5a<String>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:78:8: Error: Expected 1 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:113:8: Error: Expected 1 type arguments.
 //     f5a<int, String>(); // error
 //        ^
 //
@@ -50,24 +58,31 @@
   dynamic c2b = f2b{dynamic}.call();
   self::expect(true, c2b is{ForNonNullableByDefault} mai::Class2);
   self::expect(true, core::identical(f2a, f2b));
-  (core::int) → mai::Class3 f3a = #C3;
+  () → mai::Class2 f2c = #C3;
+  mai::Class2 c2c = f2c(){() → mai::Class2};
+  self::expect(true, c2c is{ForNonNullableByDefault} mai::Class2);
+  dynamic f2d = #C3;
+  dynamic c2d = f2d{dynamic}.call();
+  self::expect(true, c2d is{ForNonNullableByDefault} mai::Class2);
+  self::expect(true, core::identical(f2c, f2d));
+  (core::int) → mai::Class3 f3a = #C4;
   mai::Class3 c3a = f3a(42){(core::int) → mai::Class3};
   self::expect(42, c3a.{mai::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:34:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:44:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:35:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:45:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
   };
-  dynamic f3b = #C3;
+  dynamic f3b = #C4;
   dynamic c3b = f3b{dynamic}.call(87);
   self::expect(87, c3b{dynamic}.field);
   self::throws(() → dynamic => f3b{dynamic}.call());
   self::throws(() → dynamic => f3b{dynamic}.call(42, 87));
-  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4a = #C4;
+  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4a = #C5;
   mai::Class4<dynamic> c4a = f4a<dynamic>(){() → mai::Class4<dynamic>};
   self::expect(true, c4a is{ForNonNullableByDefault} mai::Class4<dynamic>);
   self::expect(false, c4a is{ForNonNullableByDefault} mai::Class4<core::int>);
@@ -75,7 +90,7 @@
   self::expect(true, c4b is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::expect(false, c4b is{ForNonNullableByDefault} mai::Class4<core::String>);
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:52:8: Error: Expected 1 type arguments.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:62:8: Error: Expected 1 type arguments.
     f4a<int, String>(); // error
        ^" in f4a{<inapplicable>}.<core::int, core::String>();
   };
@@ -84,16 +99,42 @@
   self::expect(true, c4c is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::expect(false, c4c is{ForNonNullableByDefault} mai::Class4<core::String>);
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:60:8: Error: Expected 0 type arguments.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:70:8: Error: Expected 0 type arguments.
     f4b<int>(); // error
        ^" in f4b{<inapplicable>}.<core::int>();
   };
-  dynamic f4c = #C4;
+  dynamic f4c = #C5;
   dynamic c4d = f4c{dynamic}.call();
   self::expect(true, c4d is{ForNonNullableByDefault} mai::Class4<dynamic>);
   self::expect(false, c4d is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::throws(() → dynamic => f4c{dynamic}.call<core::int, core::String>());
-  <T extends core::num>() → mai::Class5<T> f5a = #C5;
+  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4d = #C6;
+  mai::Class4<dynamic> c4e = f4d<dynamic>(){() → mai::Class4<dynamic>};
+  self::expect(true, c4e is{ForNonNullableByDefault} mai::Class4<dynamic>);
+  self::expect(false, c4e is{ForNonNullableByDefault} mai::Class4<core::int>);
+  mai::Class4<core::int> c4f = f4d<core::int>(){() → mai::Class4<core::int>};
+  self::expect(true, c4f is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::expect(false, c4f is{ForNonNullableByDefault} mai::Class4<core::String>);
+  () → Null {
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:87:8: Error: Expected 1 type arguments.
+    f4d<int, String>(); // error
+       ^" in f4d{<inapplicable>}.<core::int, core::String>();
+  };
+  () → mai::Class4<core::int> f4e = f4d<core::int>;
+  mai::Class4<core::int> c4g = f4e(){() → mai::Class4<core::int>};
+  self::expect(true, c4g is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::expect(false, c4g is{ForNonNullableByDefault} mai::Class4<core::String>);
+  () → Null {
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:95:8: Error: Expected 0 type arguments.
+    f4e<int>(); // error
+       ^" in f4e{<inapplicable>}.<core::int>();
+  };
+  dynamic f4f = #C6;
+  dynamic c4h = f4f{dynamic}.call();
+  self::expect(true, c4h is{ForNonNullableByDefault} mai::Class4<dynamic>);
+  self::expect(false, c4h is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::throws(() → dynamic => f4f{dynamic}.call<core::int, core::String>());
+  <T extends core::num>() → mai::Class5<T> f5a = #C7;
   mai::Class5<core::num> c5a = f5a<core::num>(){() → mai::Class5<core::num>};
   self::expect(true, c5a is{ForNonNullableByDefault} mai::Class5<core::num>);
   self::expect(false, c5a is{ForNonNullableByDefault} mai::Class5<core::int>);
@@ -102,11 +143,11 @@
   self::expect(false, c5b is{ForNonNullableByDefault} mai::Class5<core::double>);
   () → Null {
     f5a<core::String>(){() → mai::Class5<core::String>};
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:78:8: Error: Expected 1 type arguments.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:113:8: Error: Expected 1 type arguments.
     f5a<int, String>(); // error
        ^" in f5a{<inapplicable>}.<core::int, core::String>();
   };
-  dynamic f5b = #C5;
+  dynamic f5b = #C7;
   dynamic c5c = f5b{dynamic}.call();
   self::expect(true, c5c is{ForNonNullableByDefault} mai::Class5<core::num>);
   self::expect(false, c5c is{ForNonNullableByDefault} mai::Class5<core::int>);
@@ -120,7 +161,7 @@
   if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
     throw "Expected ${expected}, actual ${actual}";
 }
-static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C6}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C8}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -146,11 +187,16 @@
     return new mai::Class1::•();
 }
 class Class2 extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::Class2::redirect]/*isLegacy*/;
   constructor named() → mai::Class2
     : super core::Object::•()
     ;
   static method _#named#tearOff() → mai::Class2
     return new mai::Class2::named();
+  static factory redirect() → mai::Class2
+    let dynamic #redirecting_factory = mai::Class2::named in invalid-expression;
+  static method _#redirect#tearOff() → mai::Class2
+    return new mai::Class2::named();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -161,6 +207,7 @@
     return new mai::Class3::•(field);
 }
 class Class4<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::Class4::redirect]/*isLegacy*/;
   constructor _() → mai::Class4<mai::Class4::T%>
     : super core::Object::•()
     ;
@@ -170,6 +217,10 @@
     return new mai::Class4::_<mai::Class4::•::T%>();
   static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::_#new#tearOff::T%>
     return mai::Class4::•<mai::Class4::_#new#tearOff::T%>();
+  static factory redirect<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::redirect::T%>
+    let dynamic #redirecting_factory = mai::Class4::_ in let mai::Class4::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::_#redirect#tearOff::T%>
+    return new mai::Class4::_<mai::Class4::_#redirect#tearOff::T%>();
 }
 class Class5<T extends core::num> extends core::Object {
   constructor _() → mai::Class5<mai::Class5::T>
@@ -186,8 +237,10 @@
 constants  {
   #C1 = static-tearoff mai::Class1::_#new#tearOff
   #C2 = static-tearoff mai::Class2::_#named#tearOff
-  #C3 = static-tearoff mai::Class3::_#new#tearOff
-  #C4 = static-tearoff mai::Class4::_#new#tearOff
-  #C5 = static-tearoff mai::Class5::_#new#tearOff
-  #C6 = false
+  #C3 = static-tearoff mai::Class2::_#redirect#tearOff
+  #C4 = static-tearoff mai::Class3::_#new#tearOff
+  #C5 = static-tearoff mai::Class4::_#new#tearOff
+  #C6 = static-tearoff mai::Class4::_#redirect#tearOff
+  #C7 = static-tearoff mai::Class5::_#new#tearOff
+  #C8 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.strong.transformed.expect
index 42623ded..9edd33a 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.strong.transformed.expect
@@ -2,29 +2,37 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:34:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:44:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:35:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:45:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:52:8: Error: Expected 1 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:62:8: Error: Expected 1 type arguments.
 //     f4a<int, String>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:60:8: Error: Expected 0 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:70:8: Error: Expected 0 type arguments.
 //     f4b<int>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:77:8: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:87:8: Error: Expected 1 type arguments.
+//     f4d<int, String>(); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:95:8: Error: Expected 0 type arguments.
+//     f4e<int>(); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:112:8: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
 // Try changing type arguments so that they conform to the bounds.
 //     f5a<String>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:78:8: Error: Expected 1 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:113:8: Error: Expected 1 type arguments.
 //     f5a<int, String>(); // error
 //        ^
 //
@@ -50,24 +58,31 @@
   dynamic c2b = f2b{dynamic}.call();
   self::expect(true, c2b is{ForNonNullableByDefault} mai::Class2);
   self::expect(true, core::identical(f2a, f2b));
-  (core::int) → mai::Class3 f3a = #C3;
+  () → mai::Class2 f2c = #C3;
+  mai::Class2 c2c = f2c(){() → mai::Class2};
+  self::expect(true, c2c is{ForNonNullableByDefault} mai::Class2);
+  dynamic f2d = #C3;
+  dynamic c2d = f2d{dynamic}.call();
+  self::expect(true, c2d is{ForNonNullableByDefault} mai::Class2);
+  self::expect(true, core::identical(f2c, f2d));
+  (core::int) → mai::Class3 f3a = #C4;
   mai::Class3 c3a = f3a(42){(core::int) → mai::Class3};
   self::expect(42, c3a.{mai::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:34:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:44:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:35:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:45:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
   };
-  dynamic f3b = #C3;
+  dynamic f3b = #C4;
   dynamic c3b = f3b{dynamic}.call(87);
   self::expect(87, c3b{dynamic}.field);
   self::throws(() → dynamic => f3b{dynamic}.call());
   self::throws(() → dynamic => f3b{dynamic}.call(42, 87));
-  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4a = #C4;
+  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4a = #C5;
   mai::Class4<dynamic> c4a = f4a<dynamic>(){() → mai::Class4<dynamic>};
   self::expect(true, c4a is{ForNonNullableByDefault} mai::Class4<dynamic>);
   self::expect(false, c4a is{ForNonNullableByDefault} mai::Class4<core::int>);
@@ -75,7 +90,7 @@
   self::expect(true, c4b is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::expect(false, c4b is{ForNonNullableByDefault} mai::Class4<core::String>);
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:52:8: Error: Expected 1 type arguments.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:62:8: Error: Expected 1 type arguments.
     f4a<int, String>(); // error
        ^" in f4a{<inapplicable>}.<core::int, core::String>();
   };
@@ -84,16 +99,42 @@
   self::expect(true, c4c is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::expect(false, c4c is{ForNonNullableByDefault} mai::Class4<core::String>);
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:60:8: Error: Expected 0 type arguments.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:70:8: Error: Expected 0 type arguments.
     f4b<int>(); // error
        ^" in f4b{<inapplicable>}.<core::int>();
   };
-  dynamic f4c = #C4;
+  dynamic f4c = #C5;
   dynamic c4d = f4c{dynamic}.call();
   self::expect(true, c4d is{ForNonNullableByDefault} mai::Class4<dynamic>);
   self::expect(false, c4d is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::throws(() → dynamic => f4c{dynamic}.call<core::int, core::String>());
-  <T extends core::num>() → mai::Class5<T> f5a = #C5;
+  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4d = #C6;
+  mai::Class4<dynamic> c4e = f4d<dynamic>(){() → mai::Class4<dynamic>};
+  self::expect(true, c4e is{ForNonNullableByDefault} mai::Class4<dynamic>);
+  self::expect(false, c4e is{ForNonNullableByDefault} mai::Class4<core::int>);
+  mai::Class4<core::int> c4f = f4d<core::int>(){() → mai::Class4<core::int>};
+  self::expect(true, c4f is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::expect(false, c4f is{ForNonNullableByDefault} mai::Class4<core::String>);
+  () → Null {
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:87:8: Error: Expected 1 type arguments.
+    f4d<int, String>(); // error
+       ^" in f4d{<inapplicable>}.<core::int, core::String>();
+  };
+  () → mai::Class4<core::int> f4e = f4d<core::int>;
+  mai::Class4<core::int> c4g = f4e(){() → mai::Class4<core::int>};
+  self::expect(true, c4g is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::expect(false, c4g is{ForNonNullableByDefault} mai::Class4<core::String>);
+  () → Null {
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:95:8: Error: Expected 0 type arguments.
+    f4e<int>(); // error
+       ^" in f4e{<inapplicable>}.<core::int>();
+  };
+  dynamic f4f = #C6;
+  dynamic c4h = f4f{dynamic}.call();
+  self::expect(true, c4h is{ForNonNullableByDefault} mai::Class4<dynamic>);
+  self::expect(false, c4h is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::throws(() → dynamic => f4f{dynamic}.call<core::int, core::String>());
+  <T extends core::num>() → mai::Class5<T> f5a = #C7;
   mai::Class5<core::num> c5a = f5a<core::num>(){() → mai::Class5<core::num>};
   self::expect(true, c5a is{ForNonNullableByDefault} mai::Class5<core::num>);
   self::expect(false, c5a is{ForNonNullableByDefault} mai::Class5<core::int>);
@@ -102,11 +143,11 @@
   self::expect(false, c5b is{ForNonNullableByDefault} mai::Class5<core::double>);
   () → Null {
     f5a<core::String>(){() → mai::Class5<core::String>};
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:78:8: Error: Expected 1 type arguments.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:113:8: Error: Expected 1 type arguments.
     f5a<int, String>(); // error
        ^" in f5a{<inapplicable>}.<core::int, core::String>();
   };
-  dynamic f5b = #C5;
+  dynamic f5b = #C7;
   dynamic c5c = f5b{dynamic}.call();
   self::expect(true, c5c is{ForNonNullableByDefault} mai::Class5<core::num>);
   self::expect(false, c5c is{ForNonNullableByDefault} mai::Class5<core::int>);
@@ -120,7 +161,7 @@
   if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
     throw "Expected ${expected}, actual ${actual}";
 }
-static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C6}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C8}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -146,11 +187,16 @@
     return new mai::Class1::•();
 }
 class Class2 extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::Class2::redirect]/*isLegacy*/;
   constructor named() → mai::Class2
     : super core::Object::•()
     ;
   static method _#named#tearOff() → mai::Class2
     return new mai::Class2::named();
+  static factory redirect() → mai::Class2
+    let Never #redirecting_factory = mai::Class2::named in invalid-expression;
+  static method _#redirect#tearOff() → mai::Class2
+    return new mai::Class2::named();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -161,6 +207,7 @@
     return new mai::Class3::•(field);
 }
 class Class4<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::Class4::redirect]/*isLegacy*/;
   constructor _() → mai::Class4<mai::Class4::T%>
     : super core::Object::•()
     ;
@@ -170,6 +217,10 @@
     return new mai::Class4::_<mai::Class4::•::T%>();
   static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::_#new#tearOff::T%>
     return mai::Class4::•<mai::Class4::_#new#tearOff::T%>();
+  static factory redirect<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::redirect::T%>
+    let Never #redirecting_factory = mai::Class4::_ in let mai::Class4::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::_#redirect#tearOff::T%>
+    return new mai::Class4::_<mai::Class4::_#redirect#tearOff::T%>();
 }
 class Class5<T extends core::num> extends core::Object {
   constructor _() → mai::Class5<mai::Class5::T>
@@ -186,8 +237,10 @@
 constants  {
   #C1 = static-tearoff mai::Class1::_#new#tearOff
   #C2 = static-tearoff mai::Class2::_#named#tearOff
-  #C3 = static-tearoff mai::Class3::_#new#tearOff
-  #C4 = static-tearoff mai::Class4::_#new#tearOff
-  #C5 = static-tearoff mai::Class5::_#new#tearOff
-  #C6 = false
+  #C3 = static-tearoff mai::Class2::_#redirect#tearOff
+  #C4 = static-tearoff mai::Class3::_#new#tearOff
+  #C5 = static-tearoff mai::Class4::_#new#tearOff
+  #C6 = static-tearoff mai::Class4::_#redirect#tearOff
+  #C7 = static-tearoff mai::Class5::_#new#tearOff
+  #C8 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.expect
index 5f219e3..1806fdc 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.expect
@@ -2,29 +2,37 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:34:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:44:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:35:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:45:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:52:8: Error: Expected 1 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:62:8: Error: Expected 1 type arguments.
 //     f4a<int, String>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:60:8: Error: Expected 0 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:70:8: Error: Expected 0 type arguments.
 //     f4b<int>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:77:8: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:87:8: Error: Expected 1 type arguments.
+//     f4d<int, String>(); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:95:8: Error: Expected 0 type arguments.
+//     f4e<int>(); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:112:8: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
 // Try changing type arguments so that they conform to the bounds.
 //     f5a<String>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:78:8: Error: Expected 1 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:113:8: Error: Expected 1 type arguments.
 //     f5a<int, String>(); // error
 //        ^
 //
@@ -50,24 +58,31 @@
   dynamic c2b = f2b{dynamic}.call();
   self::expect(true, c2b is{ForNonNullableByDefault} mai::Class2);
   self::expect(true, core::identical(f2a, f2b));
-  (core::int) → mai::Class3 f3a = #C3;
+  () → mai::Class2 f2c = #C3;
+  mai::Class2 c2c = f2c(){() → mai::Class2};
+  self::expect(true, c2c is{ForNonNullableByDefault} mai::Class2);
+  dynamic f2d = #C3;
+  dynamic c2d = f2d{dynamic}.call();
+  self::expect(true, c2d is{ForNonNullableByDefault} mai::Class2);
+  self::expect(true, core::identical(f2c, f2d));
+  (core::int) → mai::Class3 f3a = #C4;
   mai::Class3 c3a = f3a(42){(core::int) → mai::Class3};
   self::expect(42, c3a.{mai::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:34:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:44:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:35:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:45:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
   };
-  dynamic f3b = #C3;
+  dynamic f3b = #C4;
   dynamic c3b = f3b{dynamic}.call(87);
   self::expect(87, c3b{dynamic}.field);
   self::throws(() → dynamic => f3b{dynamic}.call());
   self::throws(() → dynamic => f3b{dynamic}.call(42, 87));
-  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4a = #C4;
+  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4a = #C5;
   mai::Class4<dynamic> c4a = f4a<dynamic>(){() → mai::Class4<dynamic>};
   self::expect(true, c4a is{ForNonNullableByDefault} mai::Class4<dynamic>);
   self::expect(false, c4a is{ForNonNullableByDefault} mai::Class4<core::int>);
@@ -75,7 +90,7 @@
   self::expect(true, c4b is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::expect(false, c4b is{ForNonNullableByDefault} mai::Class4<core::String>);
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:52:8: Error: Expected 1 type arguments.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:62:8: Error: Expected 1 type arguments.
     f4a<int, String>(); // error
        ^" in f4a{<inapplicable>}.<core::int, core::String>();
   };
@@ -84,16 +99,42 @@
   self::expect(true, c4c is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::expect(false, c4c is{ForNonNullableByDefault} mai::Class4<core::String>);
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:60:8: Error: Expected 0 type arguments.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:70:8: Error: Expected 0 type arguments.
     f4b<int>(); // error
        ^" in f4b{<inapplicable>}.<core::int>();
   };
-  dynamic f4c = #C4;
+  dynamic f4c = #C5;
   dynamic c4d = f4c{dynamic}.call();
   self::expect(true, c4d is{ForNonNullableByDefault} mai::Class4<dynamic>);
   self::expect(false, c4d is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::throws(() → dynamic => f4c{dynamic}.call<core::int, core::String>());
-  <T extends core::num>() → mai::Class5<T> f5a = #C5;
+  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4d = #C6;
+  mai::Class4<dynamic> c4e = f4d<dynamic>(){() → mai::Class4<dynamic>};
+  self::expect(true, c4e is{ForNonNullableByDefault} mai::Class4<dynamic>);
+  self::expect(false, c4e is{ForNonNullableByDefault} mai::Class4<core::int>);
+  mai::Class4<core::int> c4f = f4d<core::int>(){() → mai::Class4<core::int>};
+  self::expect(true, c4f is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::expect(false, c4f is{ForNonNullableByDefault} mai::Class4<core::String>);
+  () → Null {
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:87:8: Error: Expected 1 type arguments.
+    f4d<int, String>(); // error
+       ^" in f4d{<inapplicable>}.<core::int, core::String>();
+  };
+  () → mai::Class4<core::int> f4e = f4d<core::int>;
+  mai::Class4<core::int> c4g = f4e(){() → mai::Class4<core::int>};
+  self::expect(true, c4g is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::expect(false, c4g is{ForNonNullableByDefault} mai::Class4<core::String>);
+  () → Null {
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:95:8: Error: Expected 0 type arguments.
+    f4e<int>(); // error
+       ^" in f4e{<inapplicable>}.<core::int>();
+  };
+  dynamic f4f = #C6;
+  dynamic c4h = f4f{dynamic}.call();
+  self::expect(true, c4h is{ForNonNullableByDefault} mai::Class4<dynamic>);
+  self::expect(false, c4h is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::throws(() → dynamic => f4f{dynamic}.call<core::int, core::String>());
+  <T extends core::num>() → mai::Class5<T> f5a = #C7;
   mai::Class5<core::num> c5a = f5a<core::num>(){() → mai::Class5<core::num>};
   self::expect(true, c5a is{ForNonNullableByDefault} mai::Class5<core::num>);
   self::expect(false, c5a is{ForNonNullableByDefault} mai::Class5<core::int>);
@@ -102,11 +143,11 @@
   self::expect(false, c5b is{ForNonNullableByDefault} mai::Class5<core::double>);
   () → Null {
     f5a<core::String>(){() → mai::Class5<core::String>};
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:78:8: Error: Expected 1 type arguments.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:113:8: Error: Expected 1 type arguments.
     f5a<int, String>(); // error
        ^" in f5a{<inapplicable>}.<core::int, core::String>();
   };
-  dynamic f5b = #C5;
+  dynamic f5b = #C7;
   dynamic c5c = f5b{dynamic}.call();
   self::expect(true, c5c is{ForNonNullableByDefault} mai::Class5<core::num>);
   self::expect(false, c5c is{ForNonNullableByDefault} mai::Class5<core::int>);
@@ -120,7 +161,7 @@
   if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
     throw "Expected ${expected}, actual ${actual}";
 }
-static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C6}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C8}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -146,11 +187,16 @@
     return new mai::Class1::•();
 }
 class Class2 extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::Class2::redirect]/*isLegacy*/;
   constructor named() → mai::Class2
     : super core::Object::•()
     ;
   static method _#named#tearOff() → mai::Class2
     return new mai::Class2::named();
+  static factory redirect() → mai::Class2
+    let dynamic #redirecting_factory = mai::Class2::named in invalid-expression;
+  static method _#redirect#tearOff() → mai::Class2
+    return new mai::Class2::named();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -161,6 +207,7 @@
     return new mai::Class3::•(field);
 }
 class Class4<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::Class4::redirect]/*isLegacy*/;
   constructor _() → mai::Class4<mai::Class4::T%>
     : super core::Object::•()
     ;
@@ -170,6 +217,10 @@
     return new mai::Class4::_<mai::Class4::•::T%>();
   static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::_#new#tearOff::T%>
     return mai::Class4::•<mai::Class4::_#new#tearOff::T%>();
+  static factory redirect<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::redirect::T%>
+    let dynamic #redirecting_factory = mai::Class4::_ in let mai::Class4::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::_#redirect#tearOff::T%>
+    return new mai::Class4::_<mai::Class4::_#redirect#tearOff::T%>();
 }
 class Class5<T extends core::num> extends core::Object {
   constructor _() → mai::Class5<mai::Class5::T>
@@ -186,8 +237,10 @@
 constants  {
   #C1 = static-tearoff mai::Class1::_#new#tearOff
   #C2 = static-tearoff mai::Class2::_#named#tearOff
-  #C3 = static-tearoff mai::Class3::_#new#tearOff
-  #C4 = static-tearoff mai::Class4::_#new#tearOff
-  #C5 = static-tearoff mai::Class5::_#new#tearOff
-  #C6 = false
+  #C3 = static-tearoff mai::Class2::_#redirect#tearOff
+  #C4 = static-tearoff mai::Class3::_#new#tearOff
+  #C5 = static-tearoff mai::Class4::_#new#tearOff
+  #C6 = static-tearoff mai::Class4::_#redirect#tearOff
+  #C7 = static-tearoff mai::Class5::_#new#tearOff
+  #C8 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.outline.expect
index 2591204..a682b15 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.outline.expect
@@ -23,10 +23,15 @@
     return new self2::Class1::•();
 }
 class Class2 extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self2::Class2::redirect]/*isLegacy*/;
   constructor named() → self2::Class2
     ;
   static method _#named#tearOff() → self2::Class2
     return new self2::Class2::named();
+  static factory redirect() → self2::Class2
+    let dynamic #redirecting_factory = self2::Class2::named in invalid-expression;
+  static method _#redirect#tearOff() → self2::Class2
+    return new self2::Class2::named();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -36,6 +41,7 @@
     return new self2::Class3::•(field);
 }
 class Class4<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self2::Class4::redirect]/*isLegacy*/;
   constructor _() → self2::Class4<self2::Class4::T%>
     ;
   static method _#_#tearOff<T extends core::Object? = dynamic>() → self2::Class4<self2::Class4::_#_#tearOff::T%>
@@ -44,6 +50,10 @@
     ;
   static method _#new#tearOff<T extends core::Object? = dynamic>() → self2::Class4<self2::Class4::_#new#tearOff::T%>
     return self2::Class4::•<self2::Class4::_#new#tearOff::T%>();
+  static factory redirect<T extends core::Object? = dynamic>() → self2::Class4<self2::Class4::redirect::T%>
+    let dynamic #redirecting_factory = self2::Class4::_ in let self2::Class4::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → self2::Class4<self2::Class4::_#redirect#tearOff::T%>
+    return new self2::Class4::_<self2::Class4::_#redirect#tearOff::T%>();
 }
 class Class5<T extends core::num> extends core::Object {
   constructor _() → self2::Class5<self2::Class5::T>
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.transformed.expect
index 42623ded..9edd33a 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart.weak.transformed.expect
@@ -2,29 +2,37 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:34:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:44:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:35:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:45:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:52:8: Error: Expected 1 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:62:8: Error: Expected 1 type arguments.
 //     f4a<int, String>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:60:8: Error: Expected 0 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:70:8: Error: Expected 0 type arguments.
 //     f4b<int>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:77:8: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:87:8: Error: Expected 1 type arguments.
+//     f4d<int, String>(); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:95:8: Error: Expected 0 type arguments.
+//     f4e<int>(); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:112:8: Error: Type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
 // Try changing type arguments so that they conform to the bounds.
 //     f5a<String>(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:78:8: Error: Expected 1 type arguments.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:113:8: Error: Expected 1 type arguments.
 //     f5a<int, String>(); // error
 //        ^
 //
@@ -50,24 +58,31 @@
   dynamic c2b = f2b{dynamic}.call();
   self::expect(true, c2b is{ForNonNullableByDefault} mai::Class2);
   self::expect(true, core::identical(f2a, f2b));
-  (core::int) → mai::Class3 f3a = #C3;
+  () → mai::Class2 f2c = #C3;
+  mai::Class2 c2c = f2c(){() → mai::Class2};
+  self::expect(true, c2c is{ForNonNullableByDefault} mai::Class2);
+  dynamic f2d = #C3;
+  dynamic c2d = f2d{dynamic}.call();
+  self::expect(true, c2d is{ForNonNullableByDefault} mai::Class2);
+  self::expect(true, core::identical(f2c, f2d));
+  (core::int) → mai::Class3 f3a = #C4;
   mai::Class3 c3a = f3a(42){(core::int) → mai::Class3};
   self::expect(42, c3a.{mai::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:34:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:44:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:35:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:45:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
   };
-  dynamic f3b = #C3;
+  dynamic f3b = #C4;
   dynamic c3b = f3b{dynamic}.call(87);
   self::expect(87, c3b{dynamic}.field);
   self::throws(() → dynamic => f3b{dynamic}.call());
   self::throws(() → dynamic => f3b{dynamic}.call(42, 87));
-  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4a = #C4;
+  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4a = #C5;
   mai::Class4<dynamic> c4a = f4a<dynamic>(){() → mai::Class4<dynamic>};
   self::expect(true, c4a is{ForNonNullableByDefault} mai::Class4<dynamic>);
   self::expect(false, c4a is{ForNonNullableByDefault} mai::Class4<core::int>);
@@ -75,7 +90,7 @@
   self::expect(true, c4b is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::expect(false, c4b is{ForNonNullableByDefault} mai::Class4<core::String>);
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:52:8: Error: Expected 1 type arguments.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:62:8: Error: Expected 1 type arguments.
     f4a<int, String>(); // error
        ^" in f4a{<inapplicable>}.<core::int, core::String>();
   };
@@ -84,16 +99,42 @@
   self::expect(true, c4c is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::expect(false, c4c is{ForNonNullableByDefault} mai::Class4<core::String>);
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:60:8: Error: Expected 0 type arguments.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:70:8: Error: Expected 0 type arguments.
     f4b<int>(); // error
        ^" in f4b{<inapplicable>}.<core::int>();
   };
-  dynamic f4c = #C4;
+  dynamic f4c = #C5;
   dynamic c4d = f4c{dynamic}.call();
   self::expect(true, c4d is{ForNonNullableByDefault} mai::Class4<dynamic>);
   self::expect(false, c4d is{ForNonNullableByDefault} mai::Class4<core::int>);
   self::throws(() → dynamic => f4c{dynamic}.call<core::int, core::String>());
-  <T extends core::num>() → mai::Class5<T> f5a = #C5;
+  <T extends core::Object? = dynamic>() → mai::Class4<T%> f4d = #C6;
+  mai::Class4<dynamic> c4e = f4d<dynamic>(){() → mai::Class4<dynamic>};
+  self::expect(true, c4e is{ForNonNullableByDefault} mai::Class4<dynamic>);
+  self::expect(false, c4e is{ForNonNullableByDefault} mai::Class4<core::int>);
+  mai::Class4<core::int> c4f = f4d<core::int>(){() → mai::Class4<core::int>};
+  self::expect(true, c4f is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::expect(false, c4f is{ForNonNullableByDefault} mai::Class4<core::String>);
+  () → Null {
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:87:8: Error: Expected 1 type arguments.
+    f4d<int, String>(); // error
+       ^" in f4d{<inapplicable>}.<core::int, core::String>();
+  };
+  () → mai::Class4<core::int> f4e = f4d<core::int>;
+  mai::Class4<core::int> c4g = f4e(){() → mai::Class4<core::int>};
+  self::expect(true, c4g is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::expect(false, c4g is{ForNonNullableByDefault} mai::Class4<core::String>);
+  () → Null {
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:95:8: Error: Expected 0 type arguments.
+    f4e<int>(); // error
+       ^" in f4e{<inapplicable>}.<core::int>();
+  };
+  dynamic f4f = #C6;
+  dynamic c4h = f4f{dynamic}.call();
+  self::expect(true, c4h is{ForNonNullableByDefault} mai::Class4<dynamic>);
+  self::expect(false, c4h is{ForNonNullableByDefault} mai::Class4<core::int>);
+  self::throws(() → dynamic => f4f{dynamic}.call<core::int, core::String>());
+  <T extends core::num>() → mai::Class5<T> f5a = #C7;
   mai::Class5<core::num> c5a = f5a<core::num>(){() → mai::Class5<core::num>};
   self::expect(true, c5a is{ForNonNullableByDefault} mai::Class5<core::num>);
   self::expect(false, c5a is{ForNonNullableByDefault} mai::Class5<core::int>);
@@ -102,11 +143,11 @@
   self::expect(false, c5b is{ForNonNullableByDefault} mai::Class5<core::double>);
   () → Null {
     f5a<core::String>(){() → mai::Class5<core::String>};
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:78:8: Error: Expected 1 type arguments.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main.dart:113:8: Error: Expected 1 type arguments.
     f5a<int, String>(); // error
        ^" in f5a{<inapplicable>}.<core::int, core::String>();
   };
-  dynamic f5b = #C5;
+  dynamic f5b = #C7;
   dynamic c5c = f5b{dynamic}.call();
   self::expect(true, c5c is{ForNonNullableByDefault} mai::Class5<core::num>);
   self::expect(false, c5c is{ForNonNullableByDefault} mai::Class5<core::int>);
@@ -120,7 +161,7 @@
   if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
     throw "Expected ${expected}, actual ${actual}";
 }
-static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C6}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C8}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -146,11 +187,16 @@
     return new mai::Class1::•();
 }
 class Class2 extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::Class2::redirect]/*isLegacy*/;
   constructor named() → mai::Class2
     : super core::Object::•()
     ;
   static method _#named#tearOff() → mai::Class2
     return new mai::Class2::named();
+  static factory redirect() → mai::Class2
+    let Never #redirecting_factory = mai::Class2::named in invalid-expression;
+  static method _#redirect#tearOff() → mai::Class2
+    return new mai::Class2::named();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -161,6 +207,7 @@
     return new mai::Class3::•(field);
 }
 class Class4<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::Class4::redirect]/*isLegacy*/;
   constructor _() → mai::Class4<mai::Class4::T%>
     : super core::Object::•()
     ;
@@ -170,6 +217,10 @@
     return new mai::Class4::_<mai::Class4::•::T%>();
   static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::_#new#tearOff::T%>
     return mai::Class4::•<mai::Class4::_#new#tearOff::T%>();
+  static factory redirect<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::redirect::T%>
+    let Never #redirecting_factory = mai::Class4::_ in let mai::Class4::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::Class4<mai::Class4::_#redirect#tearOff::T%>
+    return new mai::Class4::_<mai::Class4::_#redirect#tearOff::T%>();
 }
 class Class5<T extends core::num> extends core::Object {
   constructor _() → mai::Class5<mai::Class5::T>
@@ -186,8 +237,10 @@
 constants  {
   #C1 = static-tearoff mai::Class1::_#new#tearOff
   #C2 = static-tearoff mai::Class2::_#named#tearOff
-  #C3 = static-tearoff mai::Class3::_#new#tearOff
-  #C4 = static-tearoff mai::Class4::_#new#tearOff
-  #C5 = static-tearoff mai::Class5::_#new#tearOff
-  #C6 = false
+  #C3 = static-tearoff mai::Class2::_#redirect#tearOff
+  #C4 = static-tearoff mai::Class3::_#new#tearOff
+  #C5 = static-tearoff mai::Class4::_#new#tearOff
+  #C6 = static-tearoff mai::Class4::_#redirect#tearOff
+  #C7 = static-tearoff mai::Class5::_#new#tearOff
+  #C8 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main_lib.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main_lib.dart
index f50d3cd..eed7823 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main_lib.dart
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/from_dill/main_lib.dart
@@ -6,6 +6,7 @@
 
 class Class2 {
   Class2.named();
+  factory Class2.redirect() = Class2.named;
 }
 
 class Class3 {
@@ -17,6 +18,7 @@
 class Class4<T> {
   Class4._();
   factory Class4() => new Class4<T>._();
+  factory Class4.redirect() = Class4._;
 }
 
 class Class5<T extends num> {
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart
new file mode 100644
index 0000000..ed7af3d1
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart
@@ -0,0 +1,14 @@
+// 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 Class {
+  Class._();
+  static Class constructor() => new Class._();
+  static Class Function() field = () => new Class._();
+
+  factory Class.a() = Class.nonexisting;
+  factory Class.b() = Class.constructor;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.strong.expect
new file mode 100644
index 0000000..d55e231
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.strong.expect
@@ -0,0 +1,33 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:10:23: Error: Redirection constructor target not found: 'Class.nonexisting'
+//   factory Class.a() = Class.nonexisting;
+//                       ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:11:23: Error: Redirection constructor target not found: 'Class.constructor'
+//   factory Class.b() = Class.constructor;
+//                       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  static field () → self::Class field = () → self::Class => new self::Class::_();
+  static final field dynamic _redirecting# = <dynamic>[self::Class::a, self::Class::b]/*isLegacy*/;
+  constructor _() → self::Class
+    : super core::Object::•()
+    ;
+  static method _#_#tearOff() → self::Class
+    return new self::Class::_();
+  static method constructor() → self::Class
+    return new self::Class::_();
+  static factory a() → self::Class
+    let dynamic #redirecting_factory = "Class.nonexisting" in invalid-expression;
+  static method _#a#tearOff() → self::Class;
+  static factory b() → self::Class
+    let dynamic #redirecting_factory = "Class.constructor" in invalid-expression;
+  static method _#b#tearOff() → self::Class;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.strong.transformed.expect
new file mode 100644
index 0000000..fe5e039
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.strong.transformed.expect
@@ -0,0 +1,33 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:10:23: Error: Redirection constructor target not found: 'Class.nonexisting'
+//   factory Class.a() = Class.nonexisting;
+//                       ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:11:23: Error: Redirection constructor target not found: 'Class.constructor'
+//   factory Class.b() = Class.constructor;
+//                       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  static field () → self::Class field = () → self::Class => new self::Class::_();
+  static final field dynamic _redirecting# = <dynamic>[self::Class::a, self::Class::b]/*isLegacy*/;
+  constructor _() → self::Class
+    : super core::Object::•()
+    ;
+  static method _#_#tearOff() → self::Class
+    return new self::Class::_();
+  static method constructor() → self::Class
+    return new self::Class::_();
+  static factory a() → self::Class
+    let core::String* #redirecting_factory = "Class.nonexisting" in invalid-expression;
+  static method _#a#tearOff() → self::Class;
+  static factory b() → self::Class
+    let core::String* #redirecting_factory = "Class.constructor" in invalid-expression;
+  static method _#b#tearOff() → self::Class;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.textual_outline.expect
new file mode 100644
index 0000000..1dc9515
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+class Class {
+  Class._();
+  static Class constructor() => new Class._();
+  static Class Function() field = () => new Class._();
+  factory Class.a() = Class.nonexisting;
+  factory Class.b() = Class.constructor;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..f765ff19
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+class Class {
+  Class._();
+  factory Class.a() = Class.nonexisting;
+  factory Class.b() = Class.constructor;
+  static Class Function() field = () => new Class._();
+  static Class constructor() => new Class._();
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.expect
new file mode 100644
index 0000000..d55e231
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.expect
@@ -0,0 +1,33 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:10:23: Error: Redirection constructor target not found: 'Class.nonexisting'
+//   factory Class.a() = Class.nonexisting;
+//                       ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:11:23: Error: Redirection constructor target not found: 'Class.constructor'
+//   factory Class.b() = Class.constructor;
+//                       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  static field () → self::Class field = () → self::Class => new self::Class::_();
+  static final field dynamic _redirecting# = <dynamic>[self::Class::a, self::Class::b]/*isLegacy*/;
+  constructor _() → self::Class
+    : super core::Object::•()
+    ;
+  static method _#_#tearOff() → self::Class
+    return new self::Class::_();
+  static method constructor() → self::Class
+    return new self::Class::_();
+  static factory a() → self::Class
+    let dynamic #redirecting_factory = "Class.nonexisting" in invalid-expression;
+  static method _#a#tearOff() → self::Class;
+  static factory b() → self::Class
+    let dynamic #redirecting_factory = "Class.constructor" in invalid-expression;
+  static method _#b#tearOff() → self::Class;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.outline.expect
new file mode 100644
index 0000000..7a6886a
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.outline.expect
@@ -0,0 +1,33 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:10:23: Error: Redirection constructor target not found: 'Class.nonexisting'
+//   factory Class.a() = Class.nonexisting;
+//                       ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:11:23: Error: Redirection constructor target not found: 'Class.constructor'
+//   factory Class.b() = Class.constructor;
+//                       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  static field () → self::Class field;
+  static final field dynamic _redirecting# = <dynamic>[self::Class::a, self::Class::b]/*isLegacy*/;
+  constructor _() → self::Class
+    ;
+  static method _#_#tearOff() → self::Class
+    return new self::Class::_();
+  static method constructor() → self::Class
+    ;
+  static factory a() → self::Class
+    let dynamic #redirecting_factory = "Class.nonexisting" in invalid-expression;
+  static method _#a#tearOff() → self::Class;
+  static factory b() → self::Class
+    let dynamic #redirecting_factory = "Class.constructor" in invalid-expression;
+  static method _#b#tearOff() → self::Class;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.transformed.expect
new file mode 100644
index 0000000..fe5e039
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart.weak.transformed.expect
@@ -0,0 +1,33 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:10:23: Error: Redirection constructor target not found: 'Class.nonexisting'
+//   factory Class.a() = Class.nonexisting;
+//                       ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/missing_redirecting_target.dart:11:23: Error: Redirection constructor target not found: 'Class.constructor'
+//   factory Class.b() = Class.constructor;
+//                       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  static field () → self::Class field = () → self::Class => new self::Class::_();
+  static final field dynamic _redirecting# = <dynamic>[self::Class::a, self::Class::b]/*isLegacy*/;
+  constructor _() → self::Class
+    : super core::Object::•()
+    ;
+  static method _#_#tearOff() → self::Class
+    return new self::Class::_();
+  static method constructor() → self::Class
+    return new self::Class::_();
+  static factory a() → self::Class
+    let core::String* #redirecting_factory = "Class.nonexisting" in invalid-expression;
+  static method _#a#tearOff() → self::Class;
+  static factory b() → self::Class
+    let core::String* #redirecting_factory = "Class.constructor" in invalid-expression;
+  static method _#b#tearOff() → self::Class;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart
index 75d63fa..765da64 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart
@@ -16,8 +16,9 @@
 }
 
 class Class2 {
-  Class2._();
-  factory Class2.named() = Class2._;
+    Class2.__();
+    factory Class2._() => Class2.__();
+    factory Class2.named() = Class2._;
 }
 
 testNoArgs() {
@@ -74,6 +75,22 @@
       Class6._;
 }
 
+class Class7a implements Class7b {
+  Class7a();
+}
+
+class Class7b {
+  factory Class7b() = Class7a;
+}
+
+class Class8a<T> implements Class8b<T> {
+  Class8a();
+}
+
+class Class8b<T> {
+  factory Class8b() = Class8a<T>;
+}
+
 testArgs() {
   var f3a = Class3.new;
   var c3a = f3a(42);
@@ -143,6 +160,20 @@
   throws(() => f6b(42), inSoundModeOnly: true);
   throws(() => f6b(42, 87), inSoundModeOnly: true);
   throws(() => f6b(field1: 87, field2: 87));
+
+  var f7a = Class7b.new;
+  var c7a = f7a();
+  expect(true, c7a is Class7a);
+  expect(true, c7a is Class7b);
+
+  var f8a = Class8b.new;
+  var c8a = f8a();
+  expect(true, c8a is Class8a);
+  expect(true, c8a is Class8b);
+  var c8b = f8a<int>();
+  expect(true, c8b is Class8a<int>);
+  expect(true, c8b is Class8b<int>);
+  expect(false, c8b is Class8b<String>);
 }
 
 expect(expected, actual) {
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.strong.expect
index f5b0f3e..a115126 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.strong.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.strong.expect
@@ -2,43 +2,43 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f4a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f5a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 // Try removing the extra positional arguments.
 //     f5a(42, 87, 123); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
 //     f6a(42); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f6a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(field1: 87, field2: 87); // error
 //        ^
 //
@@ -59,15 +59,19 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
     : super core::Object::•()
     ;
+  static method _#__#tearOff() → self::Class2
+    return new self::Class2::__();
+  static factory _() → self::Class2
+    return new self::Class2::__();
   static method _#_#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
   static factory named() → self::Class2
     let dynamic #redirecting_factory = self::Class2::_ in invalid-expression;
   static method _#named#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -124,6 +128,34 @@
   static method _#new#tearOff(core::int field1, {core::int? field2 = #C1, required core::int field3 = #C1}) → self::Class6
     return new self::Class6::_(field1, field2: field2, field3: field3);
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff() → self::Class7a
+    return new self::Class7a::•();
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let dynamic #redirecting_factory = self::Class7a::• in invalid-expression;
+  static method _#new#tearOff() → self::Class7b
+    return new self::Class7a::•();
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8a<self::Class8a::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8a::_#new#tearOff::T%>();
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let dynamic #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8b::_#new#tearOff::T%>();
+}
 static final field core::bool inSoundMode = !(<core::int?>[] is{ForNonNullableByDefault} core::List<core::int>);
 static method main() → dynamic {
   core::print("inSoundMode: ${self::inSoundMode}");
@@ -151,10 +183,10 @@
   self::Class3 c3a = f3a(42){(core::int) → self::Class3};
   self::expect(42, c3a.{self::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
@@ -170,7 +202,7 @@
   self::Class4 c4b = f4a(42){([core::int?]) → self::Class4};
   self::expect(42, c4b.{self::Class4::field}{core::int?});
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f4a(42, 87); // error
        ^" in f4a{<inapplicable>}.(42, 87);
@@ -185,10 +217,10 @@
   self::expect(87, c5b.{self::Class5::field1}{core::int});
   self::expect(42, c5b.{self::Class5::field2}{core::int?});
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
     f5a(); // error
        ^" in f5a{<inapplicable>}.();
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 Try removing the extra positional arguments.
     f5a(42, 87, 123); // error
        ^" in f5a{<inapplicable>}.(42, 87, 123);
@@ -202,17 +234,17 @@
   self::expect(null, c6a.{self::Class6::field2}{core::int?});
   self::expect(87, c6a.{self::Class6::field3}{core::int});
   () → Null {
-    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(); // error
        ^" in f6a{<inapplicable>}.();
-    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
     f6a(42); // error
        ^" in f6a{<inapplicable>}.(42);
-    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f6a(42, 87); // error
        ^" in f6a{<inapplicable>}.(42, 87);
-    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(field1: 87, field2: 87); // error
        ^" in f6a{<inapplicable>}.(field1: 87, field2: 87);
   };
@@ -229,12 +261,24 @@
   self::throws(() → dynamic => f6b{dynamic}.call(42), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(42, 87), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(field1: 87, field2: 87));
+  () → self::Class7b f7a = #C8;
+  self::Class7b c7a = f7a(){() → self::Class7b};
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7a);
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7b);
+  <T extends core::Object? = dynamic>() → self::Class8b<T%> f8a = #C9;
+  self::Class8b<dynamic> c8a = f8a<dynamic>(){() → self::Class8b<dynamic>};
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8a<dynamic>);
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8b<dynamic>);
+  self::Class8b<core::int> c8b = f8a<core::int>(){() → self::Class8b<core::int>};
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8a<core::int>);
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8b<core::int>);
+  self::expect(false, c8b is{ForNonNullableByDefault} self::Class8b<core::String>);
 }
 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 = #C8}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C10}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -256,5 +300,7 @@
   #C5 = static-tearoff self::Class4::_#new#tearOff
   #C6 = static-tearoff self::Class5::_#new#tearOff
   #C7 = static-tearoff self::Class6::_#new#tearOff
-  #C8 = false
+  #C8 = static-tearoff self::Class7b::_#new#tearOff
+  #C9 = static-tearoff self::Class8b::_#new#tearOff
+  #C10 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.strong.transformed.expect
index 59fb725..02a4614 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.strong.transformed.expect
@@ -2,43 +2,43 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f4a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f5a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 // Try removing the extra positional arguments.
 //     f5a(42, 87, 123); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
 //     f6a(42); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f6a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(field1: 87, field2: 87); // error
 //        ^
 //
@@ -59,15 +59,19 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
     : super core::Object::•()
     ;
+  static method _#__#tearOff() → self::Class2
+    return new self::Class2::__();
+  static factory _() → self::Class2
+    return new self::Class2::__();
   static method _#_#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
   static factory named() → self::Class2
-    let Never #redirecting_factory = self::Class2::_ in invalid-expression;
+    let () → self::Class2 #redirecting_factory = self::Class2::_ in invalid-expression;
   static method _#named#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -124,6 +128,34 @@
   static method _#new#tearOff(core::int field1, {core::int? field2 = #C1, required core::int field3 = #C1}) → self::Class6
     return new self::Class6::_(field1, field2: field2, field3: field3);
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff() → self::Class7a
+    return new self::Class7a::•();
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let Never #redirecting_factory = self::Class7a::• in invalid-expression;
+  static method _#new#tearOff() → self::Class7b
+    return new self::Class7a::•();
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8a<self::Class8a::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8a::_#new#tearOff::T%>();
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let Never #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8b::_#new#tearOff::T%>();
+}
 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}");
@@ -151,10 +183,10 @@
   self::Class3 c3a = f3a(42){(core::int) → self::Class3};
   self::expect(42, c3a.{self::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
@@ -170,7 +202,7 @@
   self::Class4 c4b = f4a(42){([core::int?]) → self::Class4};
   self::expect(42, c4b.{self::Class4::field}{core::int?});
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f4a(42, 87); // error
        ^" in f4a{<inapplicable>}.(42, 87);
@@ -185,10 +217,10 @@
   self::expect(87, c5b.{self::Class5::field1}{core::int});
   self::expect(42, c5b.{self::Class5::field2}{core::int?});
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
     f5a(); // error
        ^" in f5a{<inapplicable>}.();
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 Try removing the extra positional arguments.
     f5a(42, 87, 123); // error
        ^" in f5a{<inapplicable>}.(42, 87, 123);
@@ -202,17 +234,17 @@
   self::expect(null, c6a.{self::Class6::field2}{core::int?});
   self::expect(87, c6a.{self::Class6::field3}{core::int});
   () → Null {
-    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(); // error
        ^" in f6a{<inapplicable>}.();
-    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
     f6a(42); // error
        ^" in f6a{<inapplicable>}.(42);
-    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f6a(42, 87); // error
        ^" in f6a{<inapplicable>}.(42, 87);
-    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(field1: 87, field2: 87); // error
        ^" in f6a{<inapplicable>}.(field1: 87, field2: 87);
   };
@@ -229,12 +261,24 @@
   self::throws(() → dynamic => f6b{dynamic}.call(42), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(42, 87), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(field1: 87, field2: 87));
+  () → self::Class7b f7a = #C8;
+  self::Class7b c7a = f7a(){() → self::Class7b};
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7a);
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7b);
+  <T extends core::Object? = dynamic>() → self::Class8b<T%> f8a = #C9;
+  self::Class8b<dynamic> c8a = f8a<dynamic>(){() → self::Class8b<dynamic>};
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8a<dynamic>);
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8b<dynamic>);
+  self::Class8b<core::int> c8b = f8a<core::int>(){() → self::Class8b<core::int>};
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8a<core::int>);
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8b<core::int>);
+  self::expect(false, c8b is{ForNonNullableByDefault} self::Class8b<core::String>);
 }
 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 = #C8}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C10}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -256,5 +300,7 @@
   #C5 = static-tearoff self::Class4::_#new#tearOff
   #C6 = static-tearoff self::Class5::_#new#tearOff
   #C7 = static-tearoff self::Class6::_#new#tearOff
-  #C8 = false
+  #C8 = static-tearoff self::Class7b::_#new#tearOff
+  #C9 = static-tearoff self::Class8b::_#new#tearOff
+  #C10 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.textual_outline.expect
index 999b5d2..2fa25d6 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.textual_outline.expect
@@ -7,7 +7,8 @@
 }
 
 class Class2 {
-  Class2._();
+  Class2.__();
+  factory Class2._() => Class2.__();
   factory Class2.named() = Class2._;
 }
 
@@ -40,6 +41,22 @@
   factory Class6(int field1, {int? field2, required int field3}) = Class6._;
 }
 
+class Class7a implements Class7b {
+  Class7a();
+}
+
+class Class7b {
+  factory Class7b() = Class7a;
+}
+
+class Class8a<T> implements Class8b<T> {
+  Class8a();
+}
+
+class Class8b<T> {
+  factory Class8b() = Class8a<T>;
+}
+
 testArgs() {}
 expect(expected, actual) {}
 throws(Function() f, {bool inSoundModeOnly: false}) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.textual_outline_modelled.expect
index edb4f68..0500e74 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.textual_outline_modelled.expect
@@ -4,7 +4,8 @@
 }
 
 class Class2 {
-  Class2._();
+  Class2.__();
+  factory Class2._() => Class2.__();
   factory Class2.named() = Class2._;
 }
 
@@ -35,6 +36,22 @@
   final int field3;
 }
 
+class Class7a implements Class7b {
+  Class7a();
+}
+
+class Class7b {
+  factory Class7b() = Class7a;
+}
+
+class Class8a<T> implements Class8b<T> {
+  Class8a();
+}
+
+class Class8b<T> {
+  factory Class8b() = Class8a<T>;
+}
+
 expect(expected, actual) {}
 final bool inSoundMode = <int?>[] is! List<int>;
 main() {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.expect
index f5b0f3e..a115126 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.expect
@@ -2,43 +2,43 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f4a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f5a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 // Try removing the extra positional arguments.
 //     f5a(42, 87, 123); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
 //     f6a(42); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f6a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(field1: 87, field2: 87); // error
 //        ^
 //
@@ -59,15 +59,19 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
     : super core::Object::•()
     ;
+  static method _#__#tearOff() → self::Class2
+    return new self::Class2::__();
+  static factory _() → self::Class2
+    return new self::Class2::__();
   static method _#_#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
   static factory named() → self::Class2
     let dynamic #redirecting_factory = self::Class2::_ in invalid-expression;
   static method _#named#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -124,6 +128,34 @@
   static method _#new#tearOff(core::int field1, {core::int? field2 = #C1, required core::int field3 = #C1}) → self::Class6
     return new self::Class6::_(field1, field2: field2, field3: field3);
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff() → self::Class7a
+    return new self::Class7a::•();
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let dynamic #redirecting_factory = self::Class7a::• in invalid-expression;
+  static method _#new#tearOff() → self::Class7b
+    return new self::Class7a::•();
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8a<self::Class8a::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8a::_#new#tearOff::T%>();
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let dynamic #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8b::_#new#tearOff::T%>();
+}
 static final field core::bool inSoundMode = !(<core::int?>[] is{ForNonNullableByDefault} core::List<core::int>);
 static method main() → dynamic {
   core::print("inSoundMode: ${self::inSoundMode}");
@@ -151,10 +183,10 @@
   self::Class3 c3a = f3a(42){(core::int) → self::Class3};
   self::expect(42, c3a.{self::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
@@ -170,7 +202,7 @@
   self::Class4 c4b = f4a(42){([core::int?]) → self::Class4};
   self::expect(42, c4b.{self::Class4::field}{core::int?});
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f4a(42, 87); // error
        ^" in f4a{<inapplicable>}.(42, 87);
@@ -185,10 +217,10 @@
   self::expect(87, c5b.{self::Class5::field1}{core::int});
   self::expect(42, c5b.{self::Class5::field2}{core::int?});
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
     f5a(); // error
        ^" in f5a{<inapplicable>}.();
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 Try removing the extra positional arguments.
     f5a(42, 87, 123); // error
        ^" in f5a{<inapplicable>}.(42, 87, 123);
@@ -202,17 +234,17 @@
   self::expect(null, c6a.{self::Class6::field2}{core::int?});
   self::expect(87, c6a.{self::Class6::field3}{core::int});
   () → Null {
-    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(); // error
        ^" in f6a{<inapplicable>}.();
-    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
     f6a(42); // error
        ^" in f6a{<inapplicable>}.(42);
-    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f6a(42, 87); // error
        ^" in f6a{<inapplicable>}.(42, 87);
-    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(field1: 87, field2: 87); // error
        ^" in f6a{<inapplicable>}.(field1: 87, field2: 87);
   };
@@ -229,12 +261,24 @@
   self::throws(() → dynamic => f6b{dynamic}.call(42), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(42, 87), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(field1: 87, field2: 87));
+  () → self::Class7b f7a = #C8;
+  self::Class7b c7a = f7a(){() → self::Class7b};
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7a);
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7b);
+  <T extends core::Object? = dynamic>() → self::Class8b<T%> f8a = #C9;
+  self::Class8b<dynamic> c8a = f8a<dynamic>(){() → self::Class8b<dynamic>};
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8a<dynamic>);
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8b<dynamic>);
+  self::Class8b<core::int> c8b = f8a<core::int>(){() → self::Class8b<core::int>};
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8a<core::int>);
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8b<core::int>);
+  self::expect(false, c8b is{ForNonNullableByDefault} self::Class8b<core::String>);
 }
 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 = #C8}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C10}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -256,5 +300,7 @@
   #C5 = static-tearoff self::Class4::_#new#tearOff
   #C6 = static-tearoff self::Class5::_#new#tearOff
   #C7 = static-tearoff self::Class6::_#new#tearOff
-  #C8 = false
+  #C8 = static-tearoff self::Class7b::_#new#tearOff
+  #C9 = static-tearoff self::Class8b::_#new#tearOff
+  #C10 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.outline.expect
index f5e4544..0067daa 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.outline.expect
@@ -15,14 +15,18 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
+    ;
+  static method _#__#tearOff() → self::Class2
+    return new self::Class2::__();
+  static factory _() → self::Class2
     ;
   static method _#_#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
   static factory named() → self::Class2
     let dynamic #redirecting_factory = self::Class2::_ in invalid-expression;
   static method _#named#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -75,6 +79,32 @@
   static method _#new#tearOff(core::int field1, {core::int? field2, required core::int field3}) → self::Class6
     return new self::Class6::_(field1, field2: field2, field3: field3);
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    ;
+  static method _#new#tearOff() → self::Class7a
+    return new self::Class7a::•();
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let dynamic #redirecting_factory = self::Class7a::• in invalid-expression;
+  static method _#new#tearOff() → self::Class7b
+    return new self::Class7a::•();
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8a<self::Class8a::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8a::_#new#tearOff::T%>();
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let dynamic #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8b::_#new#tearOff::T%>();
+}
 static final field core::bool inSoundMode;
 static method main() → dynamic
   ;
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.transformed.expect
index 59fb725..02a4614 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart.weak.transformed.expect
@@ -2,43 +2,43 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f4a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f5a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 // Try removing the extra positional arguments.
 //     f5a(42, 87, 123); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
 //     f6a(42); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f6a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(field1: 87, field2: 87); // error
 //        ^
 //
@@ -59,15 +59,19 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
     : super core::Object::•()
     ;
+  static method _#__#tearOff() → self::Class2
+    return new self::Class2::__();
+  static factory _() → self::Class2
+    return new self::Class2::__();
   static method _#_#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
   static factory named() → self::Class2
-    let Never #redirecting_factory = self::Class2::_ in invalid-expression;
+    let () → self::Class2 #redirecting_factory = self::Class2::_ in invalid-expression;
   static method _#named#tearOff() → self::Class2
-    return new self::Class2::_();
+    return self::Class2::_();
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -124,6 +128,34 @@
   static method _#new#tearOff(core::int field1, {core::int? field2 = #C1, required core::int field3 = #C1}) → self::Class6
     return new self::Class6::_(field1, field2: field2, field3: field3);
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff() → self::Class7a
+    return new self::Class7a::•();
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let Never #redirecting_factory = self::Class7a::• in invalid-expression;
+  static method _#new#tearOff() → self::Class7b
+    return new self::Class7a::•();
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8a<self::Class8a::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8a::_#new#tearOff::T%>();
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let Never #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::_#new#tearOff::T%>
+    return new self::Class8a::•<self::Class8b::_#new#tearOff::T%>();
+}
 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}");
@@ -151,10 +183,10 @@
   self::Class3 c3a = f3a(42){(core::int) → self::Class3};
   self::expect(42, c3a.{self::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
@@ -170,7 +202,7 @@
   self::Class4 c4b = f4a(42){([core::int?]) → self::Class4};
   self::expect(42, c4b.{self::Class4::field}{core::int?});
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f4a(42, 87); // error
        ^" in f4a{<inapplicable>}.(42, 87);
@@ -185,10 +217,10 @@
   self::expect(87, c5b.{self::Class5::field1}{core::int});
   self::expect(42, c5b.{self::Class5::field2}{core::int?});
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
     f5a(); // error
        ^" in f5a{<inapplicable>}.();
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 Try removing the extra positional arguments.
     f5a(42, 87, 123); // error
        ^" in f5a{<inapplicable>}.(42, 87, 123);
@@ -202,17 +234,17 @@
   self::expect(null, c6a.{self::Class6::field2}{core::int?});
   self::expect(87, c6a.{self::Class6::field3}{core::int});
   () → Null {
-    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(); // error
        ^" in f6a{<inapplicable>}.();
-    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
     f6a(42); // error
        ^" in f6a{<inapplicable>}.(42);
-    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f6a(42, 87); // error
        ^" in f6a{<inapplicable>}.(42, 87);
-    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(field1: 87, field2: 87); // error
        ^" in f6a{<inapplicable>}.(field1: 87, field2: 87);
   };
@@ -229,12 +261,24 @@
   self::throws(() → dynamic => f6b{dynamic}.call(42), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(42, 87), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(field1: 87, field2: 87));
+  () → self::Class7b f7a = #C8;
+  self::Class7b c7a = f7a(){() → self::Class7b};
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7a);
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7b);
+  <T extends core::Object? = dynamic>() → self::Class8b<T%> f8a = #C9;
+  self::Class8b<dynamic> c8a = f8a<dynamic>(){() → self::Class8b<dynamic>};
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8a<dynamic>);
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8b<dynamic>);
+  self::Class8b<core::int> c8b = f8a<core::int>(){() → self::Class8b<core::int>};
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8a<core::int>);
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8b<core::int>);
+  self::expect(false, c8b is{ForNonNullableByDefault} self::Class8b<core::String>);
 }
 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 = #C8}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C10}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -256,5 +300,7 @@
   #C5 = static-tearoff self::Class4::_#new#tearOff
   #C6 = static-tearoff self::Class5::_#new#tearOff
   #C7 = static-tearoff self::Class6::_#new#tearOff
-  #C8 = false
+  #C8 = static-tearoff self::Class7b::_#new#tearOff
+  #C9 = static-tearoff self::Class8b::_#new#tearOff
+  #C10 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart
new file mode 100644
index 0000000..605ae05
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart
@@ -0,0 +1,152 @@
+// 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');
+  testDefaultValues();
+}
+
+class Class1 {
+  final int field1;
+  final int field2;
+
+  Class1.positional([this.field1 = 1, this.field2 = 2]);
+
+  factory Class1.redirectPositionalSame([int field1, int field2]) =
+      Class1.positional;
+
+  factory Class1.redirectPositionalFewer1([int field1]) = Class1.positional;
+
+  factory Class1.redirectPositionalFewer2() = Class1.positional;
+
+  Class1.named({this.field1 = 1, this.field2 = 2});
+
+  factory Class1.redirectNamedSame({int field1, int field2}) = Class1.named;
+
+  factory Class1.redirectNamedReorder({int field2, int field1}) = Class1.named;
+
+  factory Class1.redirectNamedFewer1({int field1}) = Class1.named;
+
+  factory Class1.redirectNamedFewer2({int field2}) = Class1.named;
+
+  factory Class1.redirectNamedFewer3() = Class1.named;
+}
+
+testDefaultValues() {
+  var f1a = Class1.redirectPositionalSame;
+  var c1a = f1a();
+  expect(1, c1a.field1);
+  expect(2, c1a.field2);
+  var c1b = f1a(42);
+  expect(42, c1b.field1);
+  expect(2, c1b.field2);
+  var c1c = f1a(42, 87);
+  expect(42, c1c.field1);
+  expect(87, c1c.field2);
+
+  var f1b = Class1.redirectPositionalFewer1;
+  var c1d = f1b();
+  expect(1, c1d.field1);
+  expect(2, c1d.field2);
+  var c1e = f1b(42);
+  expect(42, c1e.field1);
+  expect(2, c1e.field2);
+  () {
+    f1b(42, 87); // error
+  };
+
+  var f1c = Class1.redirectPositionalFewer2;
+  var c1f = f1c();
+  expect(1, c1f.field1);
+  expect(2, c1f.field2);
+  () {
+    f1c(42); // error
+    f1c(42, 87); // error
+  };
+
+  var f2a = Class1.redirectNamedSame;
+  var c2a = f2a();
+  expect(1, c2a.field1);
+  expect(2, c2a.field2);
+  var c2b = f2a(field1: 42);
+  expect(42, c2b.field1);
+  expect(2, c2b.field2);
+  var c2c = f2a(field1: 42, field2: 87);
+  expect(42, c2c.field1);
+  expect(87, c2c.field2);
+  var c2d = f2a(field2: 87);
+  expect(1, c2d.field1);
+  expect(87, c2d.field2);
+  var c2e = f2a(field2: 87, field1: 42);
+  expect(42, c2e.field1);
+  expect(87, c2e.field2);
+
+  var f2b = Class1.redirectNamedReorder;
+  var c3a = f2b();
+  expect(1, c3a.field1);
+  expect(2, c3a.field2);
+  var c3b = f2b(field1: 42);
+  expect(42, c3b.field1);
+  expect(2, c3b.field2);
+  var c3c = f2b(field1: 42, field2: 87);
+  expect(42, c3c.field1);
+  expect(87, c3c.field2);
+  var c3d = f2b(field2: 87);
+  expect(1, c3d.field1);
+  expect(87, c3d.field2);
+  var c3e = f2b(field2: 87, field1: 42);
+  expect(42, c3e.field1);
+  expect(87, c3e.field2);
+
+  var f2c = Class1.redirectNamedFewer1;
+  var c4a = f2c();
+  expect(1, c4a.field1);
+  expect(2, c4a.field2);
+  var c4b = f2c(field1: 42);
+  expect(42, c4b.field1);
+  expect(2, c4b.field2);
+  () {
+    f2c(field1: 42, field2: 87); // error
+  };
+
+  var f2d = Class1.redirectNamedFewer2;
+  var c5a = f2d();
+  expect(1, c5a.field1);
+  expect(2, c5a.field2);
+  var c5b = f2d(field2: 87);
+  expect(1, c5b.field1);
+  expect(87, c5b.field2);
+  () {
+    f2d(field1: 42, field2: 87); // error
+  };
+
+  var f2e = Class1.redirectNamedFewer3;
+  var c6a = f2e();
+  expect(1, c6a.field1);
+  expect(2, c6a.field2);
+  () {
+    f2e(field1: 42); // error
+    f2e(field2: 87); // error
+    f2e(field1: 42, field2: 87); // error
+  };
+}
+
+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/redirecting_factory_tear_off_default_values_complex.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.strong.expect
new file mode 100644
index 0000000..6d9d205
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.strong.expect
@@ -0,0 +1,236 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:58:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// Try removing the extra positional arguments.
+//     f1b(42, 87); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:66:8: Error: Too many positional arguments: 0 allowed, but 1 found.
+// Try removing the extra positional arguments.
+//     f1c(42); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:67:8: Error: Too many positional arguments: 0 allowed, but 2 found.
+// Try removing the extra positional arguments.
+//     f1c(42, 87); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:112:21: Error: No named parameter with the name 'field2'.
+//     f2c(field1: 42, field2: 87); // error
+//                     ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:123:9: Error: No named parameter with the name 'field1'.
+//     f2d(field1: 42, field2: 87); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:131:9: Error: No named parameter with the name 'field1'.
+//     f2e(field1: 42); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:132:9: Error: No named parameter with the name 'field2'.
+//     f2e(field2: 87); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:133:9: Error: No named parameter with the name 'field1'.
+//     f2e(field1: 42, field2: 87); // error
+//         ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  final field core::int field1;
+  final field core::int field2;
+  static final field dynamic _redirecting# = <dynamic>[self::Class1::redirectPositionalSame, self::Class1::redirectPositionalFewer1, self::Class1::redirectPositionalFewer2, self::Class1::redirectNamedSame, self::Class1::redirectNamedReorder, self::Class1::redirectNamedFewer1, self::Class1::redirectNamedFewer2, self::Class1::redirectNamedFewer3]/*isLegacy*/;
+  constructor positional([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    : self::Class1::field1 = field1, self::Class1::field2 = field2, super core::Object::•()
+    ;
+  constructor named({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    : self::Class1::field1 = field1, self::Class1::field2 = field2, super core::Object::•()
+    ;
+  static method _#positional#tearOff([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalSame([core::int field1 = #C3, core::int field2 = #C3]) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalSame#tearOff([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalFewer1([core::int field1 = #C3]) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer1#tearOff([core::int field1 = #C1]) → self::Class1
+    return new self::Class1::positional(field1);
+  static factory redirectPositionalFewer2() → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer2#tearOff() → self::Class1
+    return new self::Class1::positional();
+  static method _#named#tearOff({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedSame({core::int field1 = #C3, core::int field2 = #C3}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedSame#tearOff({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedReorder({core::int field2 = #C3, core::int field1 = #C3}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedReorder#tearOff({core::int field2 = #C2, core::int field1 = #C1}) → self::Class1
+    return new self::Class1::named(field2: field2, field1: field1);
+  static factory redirectNamedFewer1({core::int field1 = #C3}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer1#tearOff({core::int field1 = #C1}) → self::Class1
+    return new self::Class1::named(field1: field1);
+  static factory redirectNamedFewer2({core::int field2 = #C3}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer2#tearOff({core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field2: field2);
+  static factory redirectNamedFewer3() → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer3#tearOff() → self::Class1
+    return new self::Class1::named();
+}
+static final field core::bool inSoundMode = !(<core::int?>[] is{ForNonNullableByDefault} core::List<core::int>);
+static method main() → dynamic {
+  core::print("inSoundMode: ${self::inSoundMode}");
+  self::testDefaultValues();
+}
+static method testDefaultValues() → dynamic {
+  ([core::int, core::int]) → self::Class1 f1a = #C4;
+  self::Class1 c1a = f1a(){([core::int, core::int]) → self::Class1};
+  self::expect(1, c1a.{self::Class1::field1}{core::int});
+  self::expect(2, c1a.{self::Class1::field2}{core::int});
+  self::Class1 c1b = f1a(42){([core::int, core::int]) → self::Class1};
+  self::expect(42, c1b.{self::Class1::field1}{core::int});
+  self::expect(2, c1b.{self::Class1::field2}{core::int});
+  self::Class1 c1c = f1a(42, 87){([core::int, core::int]) → self::Class1};
+  self::expect(42, c1c.{self::Class1::field1}{core::int});
+  self::expect(87, c1c.{self::Class1::field2}{core::int});
+  ([core::int]) → self::Class1 f1b = #C5;
+  self::Class1 c1d = f1b(){([core::int]) → self::Class1};
+  self::expect(1, c1d.{self::Class1::field1}{core::int});
+  self::expect(2, c1d.{self::Class1::field2}{core::int});
+  self::Class1 c1e = f1b(42){([core::int]) → self::Class1};
+  self::expect(42, c1e.{self::Class1::field1}{core::int});
+  self::expect(2, c1e.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:58:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+Try removing the extra positional arguments.
+    f1b(42, 87); // error
+       ^" in f1b{<inapplicable>}.(42, 87);
+  };
+  () → self::Class1 f1c = #C6;
+  self::Class1 c1f = f1c(){() → self::Class1};
+  self::expect(1, c1f.{self::Class1::field1}{core::int});
+  self::expect(2, c1f.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:66:8: Error: Too many positional arguments: 0 allowed, but 1 found.
+Try removing the extra positional arguments.
+    f1c(42); // error
+       ^" in f1c{<inapplicable>}.(42);
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:67:8: Error: Too many positional arguments: 0 allowed, but 2 found.
+Try removing the extra positional arguments.
+    f1c(42, 87); // error
+       ^" in f1c{<inapplicable>}.(42, 87);
+  };
+  ({field1: core::int, field2: core::int}) → self::Class1 f2a = #C7;
+  self::Class1 c2a = f2a(){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c2a.{self::Class1::field1}{core::int});
+  self::expect(2, c2a.{self::Class1::field2}{core::int});
+  self::Class1 c2b = f2a(field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2b.{self::Class1::field1}{core::int});
+  self::expect(2, c2b.{self::Class1::field2}{core::int});
+  self::Class1 c2c = f2a(field1: 42, field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2c.{self::Class1::field1}{core::int});
+  self::expect(87, c2c.{self::Class1::field2}{core::int});
+  self::Class1 c2d = f2a(field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c2d.{self::Class1::field1}{core::int});
+  self::expect(87, c2d.{self::Class1::field2}{core::int});
+  self::Class1 c2e = f2a(field2: 87, field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2e.{self::Class1::field1}{core::int});
+  self::expect(87, c2e.{self::Class1::field2}{core::int});
+  ({field1: core::int, field2: core::int}) → self::Class1 f2b = #C8;
+  self::Class1 c3a = f2b(){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c3a.{self::Class1::field1}{core::int});
+  self::expect(2, c3a.{self::Class1::field2}{core::int});
+  self::Class1 c3b = f2b(field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3b.{self::Class1::field1}{core::int});
+  self::expect(2, c3b.{self::Class1::field2}{core::int});
+  self::Class1 c3c = f2b(field1: 42, field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3c.{self::Class1::field1}{core::int});
+  self::expect(87, c3c.{self::Class1::field2}{core::int});
+  self::Class1 c3d = f2b(field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c3d.{self::Class1::field1}{core::int});
+  self::expect(87, c3d.{self::Class1::field2}{core::int});
+  self::Class1 c3e = f2b(field2: 87, field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3e.{self::Class1::field1}{core::int});
+  self::expect(87, c3e.{self::Class1::field2}{core::int});
+  ({field1: core::int}) → self::Class1 f2c = #C9;
+  self::Class1 c4a = f2c(){({field1: core::int}) → self::Class1};
+  self::expect(1, c4a.{self::Class1::field1}{core::int});
+  self::expect(2, c4a.{self::Class1::field2}{core::int});
+  self::Class1 c4b = f2c(field1: 42){({field1: core::int}) → self::Class1};
+  self::expect(42, c4b.{self::Class1::field1}{core::int});
+  self::expect(2, c4b.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:112:21: Error: No named parameter with the name 'field2'.
+    f2c(field1: 42, field2: 87); // error
+                    ^^^^^^" in f2c{<inapplicable>}.(field1: 42, field2: 87);
+  };
+  ({field2: core::int}) → self::Class1 f2d = #C10;
+  self::Class1 c5a = f2d(){({field2: core::int}) → self::Class1};
+  self::expect(1, c5a.{self::Class1::field1}{core::int});
+  self::expect(2, c5a.{self::Class1::field2}{core::int});
+  self::Class1 c5b = f2d(field2: 87){({field2: core::int}) → self::Class1};
+  self::expect(1, c5b.{self::Class1::field1}{core::int});
+  self::expect(87, c5b.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:123:9: Error: No named parameter with the name 'field1'.
+    f2d(field1: 42, field2: 87); // error
+        ^^^^^^" in f2d{<inapplicable>}.(field1: 42, field2: 87);
+  };
+  () → self::Class1 f2e = #C11;
+  self::Class1 c6a = f2e(){() → self::Class1};
+  self::expect(1, c6a.{self::Class1::field1}{core::int});
+  self::expect(2, c6a.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:131:9: Error: No named parameter with the name 'field1'.
+    f2e(field1: 42); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field1: 42);
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:132:9: Error: No named parameter with the name 'field2'.
+    f2e(field2: 87); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field2: 87);
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:133:9: Error: No named parameter with the name 'field1'.
+    f2e(field1: 42, field2: 87); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field1: 42, field2: 87);
+  };
+}
+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 = #C12}) → 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 = 1
+  #C2 = 2
+  #C3 = null
+  #C4 = static-tearoff self::Class1::_#redirectPositionalSame#tearOff
+  #C5 = static-tearoff self::Class1::_#redirectPositionalFewer1#tearOff
+  #C6 = static-tearoff self::Class1::_#redirectPositionalFewer2#tearOff
+  #C7 = static-tearoff self::Class1::_#redirectNamedSame#tearOff
+  #C8 = static-tearoff self::Class1::_#redirectNamedReorder#tearOff
+  #C9 = static-tearoff self::Class1::_#redirectNamedFewer1#tearOff
+  #C10 = static-tearoff self::Class1::_#redirectNamedFewer2#tearOff
+  #C11 = static-tearoff self::Class1::_#redirectNamedFewer3#tearOff
+  #C12 = false
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.strong.transformed.expect
new file mode 100644
index 0000000..ae52285
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.strong.transformed.expect
@@ -0,0 +1,236 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:58:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// Try removing the extra positional arguments.
+//     f1b(42, 87); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:66:8: Error: Too many positional arguments: 0 allowed, but 1 found.
+// Try removing the extra positional arguments.
+//     f1c(42); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:67:8: Error: Too many positional arguments: 0 allowed, but 2 found.
+// Try removing the extra positional arguments.
+//     f1c(42, 87); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:112:21: Error: No named parameter with the name 'field2'.
+//     f2c(field1: 42, field2: 87); // error
+//                     ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:123:9: Error: No named parameter with the name 'field1'.
+//     f2d(field1: 42, field2: 87); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:131:9: Error: No named parameter with the name 'field1'.
+//     f2e(field1: 42); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:132:9: Error: No named parameter with the name 'field2'.
+//     f2e(field2: 87); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:133:9: Error: No named parameter with the name 'field1'.
+//     f2e(field1: 42, field2: 87); // error
+//         ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  final field core::int field1;
+  final field core::int field2;
+  static final field dynamic _redirecting# = <dynamic>[self::Class1::redirectPositionalSame, self::Class1::redirectPositionalFewer1, self::Class1::redirectPositionalFewer2, self::Class1::redirectNamedSame, self::Class1::redirectNamedReorder, self::Class1::redirectNamedFewer1, self::Class1::redirectNamedFewer2, self::Class1::redirectNamedFewer3]/*isLegacy*/;
+  constructor positional([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    : self::Class1::field1 = field1, self::Class1::field2 = field2, super core::Object::•()
+    ;
+  constructor named({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    : self::Class1::field1 = field1, self::Class1::field2 = field2, super core::Object::•()
+    ;
+  static method _#positional#tearOff([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalSame([core::int field1 = #C3, core::int field2 = #C3]) → self::Class1
+    let Never #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalSame#tearOff([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalFewer1([core::int field1 = #C3]) → self::Class1
+    let Never #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer1#tearOff([core::int field1 = #C1]) → self::Class1
+    return new self::Class1::positional(field1);
+  static factory redirectPositionalFewer2() → self::Class1
+    let Never #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer2#tearOff() → self::Class1
+    return new self::Class1::positional();
+  static method _#named#tearOff({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedSame({core::int field1 = #C3, core::int field2 = #C3}) → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedSame#tearOff({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedReorder({core::int field2 = #C3, core::int field1 = #C3}) → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedReorder#tearOff({core::int field2 = #C2, core::int field1 = #C1}) → self::Class1
+    return new self::Class1::named(field2: field2, field1: field1);
+  static factory redirectNamedFewer1({core::int field1 = #C3}) → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer1#tearOff({core::int field1 = #C1}) → self::Class1
+    return new self::Class1::named(field1: field1);
+  static factory redirectNamedFewer2({core::int field2 = #C3}) → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer2#tearOff({core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field2: field2);
+  static factory redirectNamedFewer3() → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer3#tearOff() → self::Class1
+    return new self::Class1::named();
+}
+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::testDefaultValues();
+}
+static method testDefaultValues() → dynamic {
+  ([core::int, core::int]) → self::Class1 f1a = #C4;
+  self::Class1 c1a = f1a(){([core::int, core::int]) → self::Class1};
+  self::expect(1, c1a.{self::Class1::field1}{core::int});
+  self::expect(2, c1a.{self::Class1::field2}{core::int});
+  self::Class1 c1b = f1a(42){([core::int, core::int]) → self::Class1};
+  self::expect(42, c1b.{self::Class1::field1}{core::int});
+  self::expect(2, c1b.{self::Class1::field2}{core::int});
+  self::Class1 c1c = f1a(42, 87){([core::int, core::int]) → self::Class1};
+  self::expect(42, c1c.{self::Class1::field1}{core::int});
+  self::expect(87, c1c.{self::Class1::field2}{core::int});
+  ([core::int]) → self::Class1 f1b = #C5;
+  self::Class1 c1d = f1b(){([core::int]) → self::Class1};
+  self::expect(1, c1d.{self::Class1::field1}{core::int});
+  self::expect(2, c1d.{self::Class1::field2}{core::int});
+  self::Class1 c1e = f1b(42){([core::int]) → self::Class1};
+  self::expect(42, c1e.{self::Class1::field1}{core::int});
+  self::expect(2, c1e.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:58:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+Try removing the extra positional arguments.
+    f1b(42, 87); // error
+       ^" in f1b{<inapplicable>}.(42, 87);
+  };
+  () → self::Class1 f1c = #C6;
+  self::Class1 c1f = f1c(){() → self::Class1};
+  self::expect(1, c1f.{self::Class1::field1}{core::int});
+  self::expect(2, c1f.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:66:8: Error: Too many positional arguments: 0 allowed, but 1 found.
+Try removing the extra positional arguments.
+    f1c(42); // error
+       ^" in f1c{<inapplicable>}.(42);
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:67:8: Error: Too many positional arguments: 0 allowed, but 2 found.
+Try removing the extra positional arguments.
+    f1c(42, 87); // error
+       ^" in f1c{<inapplicable>}.(42, 87);
+  };
+  ({field1: core::int, field2: core::int}) → self::Class1 f2a = #C7;
+  self::Class1 c2a = f2a(){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c2a.{self::Class1::field1}{core::int});
+  self::expect(2, c2a.{self::Class1::field2}{core::int});
+  self::Class1 c2b = f2a(field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2b.{self::Class1::field1}{core::int});
+  self::expect(2, c2b.{self::Class1::field2}{core::int});
+  self::Class1 c2c = f2a(field1: 42, field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2c.{self::Class1::field1}{core::int});
+  self::expect(87, c2c.{self::Class1::field2}{core::int});
+  self::Class1 c2d = f2a(field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c2d.{self::Class1::field1}{core::int});
+  self::expect(87, c2d.{self::Class1::field2}{core::int});
+  self::Class1 c2e = f2a(field2: 87, field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2e.{self::Class1::field1}{core::int});
+  self::expect(87, c2e.{self::Class1::field2}{core::int});
+  ({field1: core::int, field2: core::int}) → self::Class1 f2b = #C8;
+  self::Class1 c3a = f2b(){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c3a.{self::Class1::field1}{core::int});
+  self::expect(2, c3a.{self::Class1::field2}{core::int});
+  self::Class1 c3b = f2b(field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3b.{self::Class1::field1}{core::int});
+  self::expect(2, c3b.{self::Class1::field2}{core::int});
+  self::Class1 c3c = f2b(field1: 42, field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3c.{self::Class1::field1}{core::int});
+  self::expect(87, c3c.{self::Class1::field2}{core::int});
+  self::Class1 c3d = f2b(field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c3d.{self::Class1::field1}{core::int});
+  self::expect(87, c3d.{self::Class1::field2}{core::int});
+  self::Class1 c3e = f2b(field2: 87, field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3e.{self::Class1::field1}{core::int});
+  self::expect(87, c3e.{self::Class1::field2}{core::int});
+  ({field1: core::int}) → self::Class1 f2c = #C9;
+  self::Class1 c4a = f2c(){({field1: core::int}) → self::Class1};
+  self::expect(1, c4a.{self::Class1::field1}{core::int});
+  self::expect(2, c4a.{self::Class1::field2}{core::int});
+  self::Class1 c4b = f2c(field1: 42){({field1: core::int}) → self::Class1};
+  self::expect(42, c4b.{self::Class1::field1}{core::int});
+  self::expect(2, c4b.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:112:21: Error: No named parameter with the name 'field2'.
+    f2c(field1: 42, field2: 87); // error
+                    ^^^^^^" in f2c{<inapplicable>}.(field1: 42, field2: 87);
+  };
+  ({field2: core::int}) → self::Class1 f2d = #C10;
+  self::Class1 c5a = f2d(){({field2: core::int}) → self::Class1};
+  self::expect(1, c5a.{self::Class1::field1}{core::int});
+  self::expect(2, c5a.{self::Class1::field2}{core::int});
+  self::Class1 c5b = f2d(field2: 87){({field2: core::int}) → self::Class1};
+  self::expect(1, c5b.{self::Class1::field1}{core::int});
+  self::expect(87, c5b.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:123:9: Error: No named parameter with the name 'field1'.
+    f2d(field1: 42, field2: 87); // error
+        ^^^^^^" in f2d{<inapplicable>}.(field1: 42, field2: 87);
+  };
+  () → self::Class1 f2e = #C11;
+  self::Class1 c6a = f2e(){() → self::Class1};
+  self::expect(1, c6a.{self::Class1::field1}{core::int});
+  self::expect(2, c6a.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:131:9: Error: No named parameter with the name 'field1'.
+    f2e(field1: 42); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field1: 42);
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:132:9: Error: No named parameter with the name 'field2'.
+    f2e(field2: 87); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field2: 87);
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:133:9: Error: No named parameter with the name 'field1'.
+    f2e(field1: 42, field2: 87); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field1: 42, field2: 87);
+  };
+}
+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 = #C12}) → 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 = 1
+  #C2 = 2
+  #C3 = null
+  #C4 = static-tearoff self::Class1::_#redirectPositionalSame#tearOff
+  #C5 = static-tearoff self::Class1::_#redirectPositionalFewer1#tearOff
+  #C6 = static-tearoff self::Class1::_#redirectPositionalFewer2#tearOff
+  #C7 = static-tearoff self::Class1::_#redirectNamedSame#tearOff
+  #C8 = static-tearoff self::Class1::_#redirectNamedReorder#tearOff
+  #C9 = static-tearoff self::Class1::_#redirectNamedFewer1#tearOff
+  #C10 = static-tearoff self::Class1::_#redirectNamedFewer2#tearOff
+  #C11 = static-tearoff self::Class1::_#redirectNamedFewer3#tearOff
+  #C12 = false
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.textual_outline.expect
new file mode 100644
index 0000000..90fc9da
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.textual_outline.expect
@@ -0,0 +1,22 @@
+final bool inSoundMode = <int?>[] is! List<int>;
+main() {}
+
+class Class1 {
+  final int field1;
+  final int field2;
+  Class1.positional([this.field1 = 1, this.field2 = 2]);
+  factory Class1.redirectPositionalSame([int field1, int field2]) =
+      Class1.positional;
+  factory Class1.redirectPositionalFewer1([int field1]) = Class1.positional;
+  factory Class1.redirectPositionalFewer2() = Class1.positional;
+  Class1.named({this.field1 = 1, this.field2 = 2});
+  factory Class1.redirectNamedSame({int field1, int field2}) = Class1.named;
+  factory Class1.redirectNamedReorder({int field2, int field1}) = Class1.named;
+  factory Class1.redirectNamedFewer1({int field1}) = Class1.named;
+  factory Class1.redirectNamedFewer2({int field2}) = Class1.named;
+  factory Class1.redirectNamedFewer3() = Class1.named;
+}
+
+testDefaultValues() {}
+expect(expected, actual) {}
+throws(Function() f, {bool inSoundModeOnly: false}) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..5735aef
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.textual_outline_modelled.expect
@@ -0,0 +1,21 @@
+class Class1 {
+  Class1.named({this.field1 = 1, this.field2 = 2});
+  Class1.positional([this.field1 = 1, this.field2 = 2]);
+  factory Class1.redirectNamedFewer1({int field1}) = Class1.named;
+  factory Class1.redirectNamedFewer2({int field2}) = Class1.named;
+  factory Class1.redirectNamedFewer3() = Class1.named;
+  factory Class1.redirectNamedReorder({int field2, int field1}) = Class1.named;
+  factory Class1.redirectNamedSame({int field1, int field2}) = Class1.named;
+  factory Class1.redirectPositionalFewer1([int field1]) = Class1.positional;
+  factory Class1.redirectPositionalFewer2() = Class1.positional;
+  factory Class1.redirectPositionalSame([int field1, int field2]) =
+      Class1.positional;
+  final int field1;
+  final int field2;
+}
+
+expect(expected, actual) {}
+final bool inSoundMode = <int?>[] is! List<int>;
+main() {}
+testDefaultValues() {}
+throws(Function() f, {bool inSoundModeOnly: false}) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.weak.expect
new file mode 100644
index 0000000..6d9d205
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.weak.expect
@@ -0,0 +1,236 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:58:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// Try removing the extra positional arguments.
+//     f1b(42, 87); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:66:8: Error: Too many positional arguments: 0 allowed, but 1 found.
+// Try removing the extra positional arguments.
+//     f1c(42); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:67:8: Error: Too many positional arguments: 0 allowed, but 2 found.
+// Try removing the extra positional arguments.
+//     f1c(42, 87); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:112:21: Error: No named parameter with the name 'field2'.
+//     f2c(field1: 42, field2: 87); // error
+//                     ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:123:9: Error: No named parameter with the name 'field1'.
+//     f2d(field1: 42, field2: 87); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:131:9: Error: No named parameter with the name 'field1'.
+//     f2e(field1: 42); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:132:9: Error: No named parameter with the name 'field2'.
+//     f2e(field2: 87); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:133:9: Error: No named parameter with the name 'field1'.
+//     f2e(field1: 42, field2: 87); // error
+//         ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  final field core::int field1;
+  final field core::int field2;
+  static final field dynamic _redirecting# = <dynamic>[self::Class1::redirectPositionalSame, self::Class1::redirectPositionalFewer1, self::Class1::redirectPositionalFewer2, self::Class1::redirectNamedSame, self::Class1::redirectNamedReorder, self::Class1::redirectNamedFewer1, self::Class1::redirectNamedFewer2, self::Class1::redirectNamedFewer3]/*isLegacy*/;
+  constructor positional([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    : self::Class1::field1 = field1, self::Class1::field2 = field2, super core::Object::•()
+    ;
+  constructor named({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    : self::Class1::field1 = field1, self::Class1::field2 = field2, super core::Object::•()
+    ;
+  static method _#positional#tearOff([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalSame([core::int field1 = #C3, core::int field2 = #C3]) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalSame#tearOff([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalFewer1([core::int field1 = #C3]) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer1#tearOff([core::int field1 = #C1]) → self::Class1
+    return new self::Class1::positional(field1);
+  static factory redirectPositionalFewer2() → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer2#tearOff() → self::Class1
+    return new self::Class1::positional();
+  static method _#named#tearOff({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedSame({core::int field1 = #C3, core::int field2 = #C3}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedSame#tearOff({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedReorder({core::int field2 = #C3, core::int field1 = #C3}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedReorder#tearOff({core::int field2 = #C2, core::int field1 = #C1}) → self::Class1
+    return new self::Class1::named(field2: field2, field1: field1);
+  static factory redirectNamedFewer1({core::int field1 = #C3}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer1#tearOff({core::int field1 = #C1}) → self::Class1
+    return new self::Class1::named(field1: field1);
+  static factory redirectNamedFewer2({core::int field2 = #C3}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer2#tearOff({core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field2: field2);
+  static factory redirectNamedFewer3() → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer3#tearOff() → self::Class1
+    return new self::Class1::named();
+}
+static final field core::bool inSoundMode = !(<core::int?>[] is{ForNonNullableByDefault} core::List<core::int>);
+static method main() → dynamic {
+  core::print("inSoundMode: ${self::inSoundMode}");
+  self::testDefaultValues();
+}
+static method testDefaultValues() → dynamic {
+  ([core::int, core::int]) → self::Class1 f1a = #C4;
+  self::Class1 c1a = f1a(){([core::int, core::int]) → self::Class1};
+  self::expect(1, c1a.{self::Class1::field1}{core::int});
+  self::expect(2, c1a.{self::Class1::field2}{core::int});
+  self::Class1 c1b = f1a(42){([core::int, core::int]) → self::Class1};
+  self::expect(42, c1b.{self::Class1::field1}{core::int});
+  self::expect(2, c1b.{self::Class1::field2}{core::int});
+  self::Class1 c1c = f1a(42, 87){([core::int, core::int]) → self::Class1};
+  self::expect(42, c1c.{self::Class1::field1}{core::int});
+  self::expect(87, c1c.{self::Class1::field2}{core::int});
+  ([core::int]) → self::Class1 f1b = #C5;
+  self::Class1 c1d = f1b(){([core::int]) → self::Class1};
+  self::expect(1, c1d.{self::Class1::field1}{core::int});
+  self::expect(2, c1d.{self::Class1::field2}{core::int});
+  self::Class1 c1e = f1b(42){([core::int]) → self::Class1};
+  self::expect(42, c1e.{self::Class1::field1}{core::int});
+  self::expect(2, c1e.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:58:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+Try removing the extra positional arguments.
+    f1b(42, 87); // error
+       ^" in f1b{<inapplicable>}.(42, 87);
+  };
+  () → self::Class1 f1c = #C6;
+  self::Class1 c1f = f1c(){() → self::Class1};
+  self::expect(1, c1f.{self::Class1::field1}{core::int});
+  self::expect(2, c1f.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:66:8: Error: Too many positional arguments: 0 allowed, but 1 found.
+Try removing the extra positional arguments.
+    f1c(42); // error
+       ^" in f1c{<inapplicable>}.(42);
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:67:8: Error: Too many positional arguments: 0 allowed, but 2 found.
+Try removing the extra positional arguments.
+    f1c(42, 87); // error
+       ^" in f1c{<inapplicable>}.(42, 87);
+  };
+  ({field1: core::int, field2: core::int}) → self::Class1 f2a = #C7;
+  self::Class1 c2a = f2a(){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c2a.{self::Class1::field1}{core::int});
+  self::expect(2, c2a.{self::Class1::field2}{core::int});
+  self::Class1 c2b = f2a(field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2b.{self::Class1::field1}{core::int});
+  self::expect(2, c2b.{self::Class1::field2}{core::int});
+  self::Class1 c2c = f2a(field1: 42, field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2c.{self::Class1::field1}{core::int});
+  self::expect(87, c2c.{self::Class1::field2}{core::int});
+  self::Class1 c2d = f2a(field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c2d.{self::Class1::field1}{core::int});
+  self::expect(87, c2d.{self::Class1::field2}{core::int});
+  self::Class1 c2e = f2a(field2: 87, field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2e.{self::Class1::field1}{core::int});
+  self::expect(87, c2e.{self::Class1::field2}{core::int});
+  ({field1: core::int, field2: core::int}) → self::Class1 f2b = #C8;
+  self::Class1 c3a = f2b(){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c3a.{self::Class1::field1}{core::int});
+  self::expect(2, c3a.{self::Class1::field2}{core::int});
+  self::Class1 c3b = f2b(field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3b.{self::Class1::field1}{core::int});
+  self::expect(2, c3b.{self::Class1::field2}{core::int});
+  self::Class1 c3c = f2b(field1: 42, field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3c.{self::Class1::field1}{core::int});
+  self::expect(87, c3c.{self::Class1::field2}{core::int});
+  self::Class1 c3d = f2b(field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c3d.{self::Class1::field1}{core::int});
+  self::expect(87, c3d.{self::Class1::field2}{core::int});
+  self::Class1 c3e = f2b(field2: 87, field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3e.{self::Class1::field1}{core::int});
+  self::expect(87, c3e.{self::Class1::field2}{core::int});
+  ({field1: core::int}) → self::Class1 f2c = #C9;
+  self::Class1 c4a = f2c(){({field1: core::int}) → self::Class1};
+  self::expect(1, c4a.{self::Class1::field1}{core::int});
+  self::expect(2, c4a.{self::Class1::field2}{core::int});
+  self::Class1 c4b = f2c(field1: 42){({field1: core::int}) → self::Class1};
+  self::expect(42, c4b.{self::Class1::field1}{core::int});
+  self::expect(2, c4b.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:112:21: Error: No named parameter with the name 'field2'.
+    f2c(field1: 42, field2: 87); // error
+                    ^^^^^^" in f2c{<inapplicable>}.(field1: 42, field2: 87);
+  };
+  ({field2: core::int}) → self::Class1 f2d = #C10;
+  self::Class1 c5a = f2d(){({field2: core::int}) → self::Class1};
+  self::expect(1, c5a.{self::Class1::field1}{core::int});
+  self::expect(2, c5a.{self::Class1::field2}{core::int});
+  self::Class1 c5b = f2d(field2: 87){({field2: core::int}) → self::Class1};
+  self::expect(1, c5b.{self::Class1::field1}{core::int});
+  self::expect(87, c5b.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:123:9: Error: No named parameter with the name 'field1'.
+    f2d(field1: 42, field2: 87); // error
+        ^^^^^^" in f2d{<inapplicable>}.(field1: 42, field2: 87);
+  };
+  () → self::Class1 f2e = #C11;
+  self::Class1 c6a = f2e(){() → self::Class1};
+  self::expect(1, c6a.{self::Class1::field1}{core::int});
+  self::expect(2, c6a.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:131:9: Error: No named parameter with the name 'field1'.
+    f2e(field1: 42); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field1: 42);
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:132:9: Error: No named parameter with the name 'field2'.
+    f2e(field2: 87); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field2: 87);
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:133:9: Error: No named parameter with the name 'field1'.
+    f2e(field1: 42, field2: 87); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field1: 42, field2: 87);
+  };
+}
+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 = #C12}) → 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 = 1
+  #C2 = 2
+  #C3 = null
+  #C4 = static-tearoff self::Class1::_#redirectPositionalSame#tearOff
+  #C5 = static-tearoff self::Class1::_#redirectPositionalFewer1#tearOff
+  #C6 = static-tearoff self::Class1::_#redirectPositionalFewer2#tearOff
+  #C7 = static-tearoff self::Class1::_#redirectNamedSame#tearOff
+  #C8 = static-tearoff self::Class1::_#redirectNamedReorder#tearOff
+  #C9 = static-tearoff self::Class1::_#redirectNamedFewer1#tearOff
+  #C10 = static-tearoff self::Class1::_#redirectNamedFewer2#tearOff
+  #C11 = static-tearoff self::Class1::_#redirectNamedFewer3#tearOff
+  #C12 = false
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.weak.outline.expect
new file mode 100644
index 0000000..9ef35eb
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.weak.outline.expect
@@ -0,0 +1,58 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  final field core::int field1;
+  final field core::int field2;
+  static final field dynamic _redirecting# = <dynamic>[self::Class1::redirectPositionalSame, self::Class1::redirectPositionalFewer1, self::Class1::redirectPositionalFewer2, self::Class1::redirectNamedSame, self::Class1::redirectNamedReorder, self::Class1::redirectNamedFewer1, self::Class1::redirectNamedFewer2, self::Class1::redirectNamedFewer3]/*isLegacy*/;
+  constructor positional([core::int field1, core::int field2]) → self::Class1
+    ;
+  constructor named({core::int field1, core::int field2}) → self::Class1
+    ;
+  static method _#positional#tearOff([core::int field1, core::int field2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalSame([core::int field1, core::int field2]) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalSame#tearOff([core::int field1, core::int field2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalFewer1([core::int field1]) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer1#tearOff([core::int field1]) → self::Class1
+    return new self::Class1::positional(field1);
+  static factory redirectPositionalFewer2() → self::Class1
+    let dynamic #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer2#tearOff() → self::Class1
+    return new self::Class1::positional();
+  static method _#named#tearOff({core::int field1, core::int field2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedSame({core::int field1, core::int field2}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedSame#tearOff({core::int field1, core::int field2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedReorder({core::int field2, core::int field1}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedReorder#tearOff({core::int field2, core::int field1}) → self::Class1
+    return new self::Class1::named(field2: field2, field1: field1);
+  static factory redirectNamedFewer1({core::int field1}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer1#tearOff({core::int field1}) → self::Class1
+    return new self::Class1::named(field1: field1);
+  static factory redirectNamedFewer2({core::int field2}) → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer2#tearOff({core::int field2}) → self::Class1
+    return new self::Class1::named(field2: field2);
+  static factory redirectNamedFewer3() → self::Class1
+    let dynamic #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer3#tearOff() → self::Class1
+    return new self::Class1::named();
+}
+static final field core::bool inSoundMode;
+static method main() → dynamic
+  ;
+static method testDefaultValues() → 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/redirecting_factory_tear_off_default_values_complex.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.weak.transformed.expect
new file mode 100644
index 0000000..ae52285
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart.weak.transformed.expect
@@ -0,0 +1,236 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:58:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// Try removing the extra positional arguments.
+//     f1b(42, 87); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:66:8: Error: Too many positional arguments: 0 allowed, but 1 found.
+// Try removing the extra positional arguments.
+//     f1c(42); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:67:8: Error: Too many positional arguments: 0 allowed, but 2 found.
+// Try removing the extra positional arguments.
+//     f1c(42, 87); // error
+//        ^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:112:21: Error: No named parameter with the name 'field2'.
+//     f2c(field1: 42, field2: 87); // error
+//                     ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:123:9: Error: No named parameter with the name 'field1'.
+//     f2d(field1: 42, field2: 87); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:131:9: Error: No named parameter with the name 'field1'.
+//     f2e(field1: 42); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:132:9: Error: No named parameter with the name 'field2'.
+//     f2e(field2: 87); // error
+//         ^^^^^^
+//
+// pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:133:9: Error: No named parameter with the name 'field1'.
+//     f2e(field1: 42, field2: 87); // error
+//         ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+  final field core::int field1;
+  final field core::int field2;
+  static final field dynamic _redirecting# = <dynamic>[self::Class1::redirectPositionalSame, self::Class1::redirectPositionalFewer1, self::Class1::redirectPositionalFewer2, self::Class1::redirectNamedSame, self::Class1::redirectNamedReorder, self::Class1::redirectNamedFewer1, self::Class1::redirectNamedFewer2, self::Class1::redirectNamedFewer3]/*isLegacy*/;
+  constructor positional([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    : self::Class1::field1 = field1, self::Class1::field2 = field2, super core::Object::•()
+    ;
+  constructor named({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    : self::Class1::field1 = field1, self::Class1::field2 = field2, super core::Object::•()
+    ;
+  static method _#positional#tearOff([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalSame([core::int field1 = #C3, core::int field2 = #C3]) → self::Class1
+    let Never #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalSame#tearOff([core::int field1 = #C1, core::int field2 = #C2]) → self::Class1
+    return new self::Class1::positional(field1, field2);
+  static factory redirectPositionalFewer1([core::int field1 = #C3]) → self::Class1
+    let Never #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer1#tearOff([core::int field1 = #C1]) → self::Class1
+    return new self::Class1::positional(field1);
+  static factory redirectPositionalFewer2() → self::Class1
+    let Never #redirecting_factory = self::Class1::positional in invalid-expression;
+  static method _#redirectPositionalFewer2#tearOff() → self::Class1
+    return new self::Class1::positional();
+  static method _#named#tearOff({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedSame({core::int field1 = #C3, core::int field2 = #C3}) → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedSame#tearOff({core::int field1 = #C1, core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field1: field1, field2: field2);
+  static factory redirectNamedReorder({core::int field2 = #C3, core::int field1 = #C3}) → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedReorder#tearOff({core::int field2 = #C2, core::int field1 = #C1}) → self::Class1
+    return new self::Class1::named(field2: field2, field1: field1);
+  static factory redirectNamedFewer1({core::int field1 = #C3}) → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer1#tearOff({core::int field1 = #C1}) → self::Class1
+    return new self::Class1::named(field1: field1);
+  static factory redirectNamedFewer2({core::int field2 = #C3}) → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer2#tearOff({core::int field2 = #C2}) → self::Class1
+    return new self::Class1::named(field2: field2);
+  static factory redirectNamedFewer3() → self::Class1
+    let Never #redirecting_factory = self::Class1::named in invalid-expression;
+  static method _#redirectNamedFewer3#tearOff() → self::Class1
+    return new self::Class1::named();
+}
+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::testDefaultValues();
+}
+static method testDefaultValues() → dynamic {
+  ([core::int, core::int]) → self::Class1 f1a = #C4;
+  self::Class1 c1a = f1a(){([core::int, core::int]) → self::Class1};
+  self::expect(1, c1a.{self::Class1::field1}{core::int});
+  self::expect(2, c1a.{self::Class1::field2}{core::int});
+  self::Class1 c1b = f1a(42){([core::int, core::int]) → self::Class1};
+  self::expect(42, c1b.{self::Class1::field1}{core::int});
+  self::expect(2, c1b.{self::Class1::field2}{core::int});
+  self::Class1 c1c = f1a(42, 87){([core::int, core::int]) → self::Class1};
+  self::expect(42, c1c.{self::Class1::field1}{core::int});
+  self::expect(87, c1c.{self::Class1::field2}{core::int});
+  ([core::int]) → self::Class1 f1b = #C5;
+  self::Class1 c1d = f1b(){([core::int]) → self::Class1};
+  self::expect(1, c1d.{self::Class1::field1}{core::int});
+  self::expect(2, c1d.{self::Class1::field2}{core::int});
+  self::Class1 c1e = f1b(42){([core::int]) → self::Class1};
+  self::expect(42, c1e.{self::Class1::field1}{core::int});
+  self::expect(2, c1e.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:58:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+Try removing the extra positional arguments.
+    f1b(42, 87); // error
+       ^" in f1b{<inapplicable>}.(42, 87);
+  };
+  () → self::Class1 f1c = #C6;
+  self::Class1 c1f = f1c(){() → self::Class1};
+  self::expect(1, c1f.{self::Class1::field1}{core::int});
+  self::expect(2, c1f.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:66:8: Error: Too many positional arguments: 0 allowed, but 1 found.
+Try removing the extra positional arguments.
+    f1c(42); // error
+       ^" in f1c{<inapplicable>}.(42);
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:67:8: Error: Too many positional arguments: 0 allowed, but 2 found.
+Try removing the extra positional arguments.
+    f1c(42, 87); // error
+       ^" in f1c{<inapplicable>}.(42, 87);
+  };
+  ({field1: core::int, field2: core::int}) → self::Class1 f2a = #C7;
+  self::Class1 c2a = f2a(){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c2a.{self::Class1::field1}{core::int});
+  self::expect(2, c2a.{self::Class1::field2}{core::int});
+  self::Class1 c2b = f2a(field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2b.{self::Class1::field1}{core::int});
+  self::expect(2, c2b.{self::Class1::field2}{core::int});
+  self::Class1 c2c = f2a(field1: 42, field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2c.{self::Class1::field1}{core::int});
+  self::expect(87, c2c.{self::Class1::field2}{core::int});
+  self::Class1 c2d = f2a(field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c2d.{self::Class1::field1}{core::int});
+  self::expect(87, c2d.{self::Class1::field2}{core::int});
+  self::Class1 c2e = f2a(field2: 87, field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c2e.{self::Class1::field1}{core::int});
+  self::expect(87, c2e.{self::Class1::field2}{core::int});
+  ({field1: core::int, field2: core::int}) → self::Class1 f2b = #C8;
+  self::Class1 c3a = f2b(){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c3a.{self::Class1::field1}{core::int});
+  self::expect(2, c3a.{self::Class1::field2}{core::int});
+  self::Class1 c3b = f2b(field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3b.{self::Class1::field1}{core::int});
+  self::expect(2, c3b.{self::Class1::field2}{core::int});
+  self::Class1 c3c = f2b(field1: 42, field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3c.{self::Class1::field1}{core::int});
+  self::expect(87, c3c.{self::Class1::field2}{core::int});
+  self::Class1 c3d = f2b(field2: 87){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(1, c3d.{self::Class1::field1}{core::int});
+  self::expect(87, c3d.{self::Class1::field2}{core::int});
+  self::Class1 c3e = f2b(field2: 87, field1: 42){({field1: core::int, field2: core::int}) → self::Class1};
+  self::expect(42, c3e.{self::Class1::field1}{core::int});
+  self::expect(87, c3e.{self::Class1::field2}{core::int});
+  ({field1: core::int}) → self::Class1 f2c = #C9;
+  self::Class1 c4a = f2c(){({field1: core::int}) → self::Class1};
+  self::expect(1, c4a.{self::Class1::field1}{core::int});
+  self::expect(2, c4a.{self::Class1::field2}{core::int});
+  self::Class1 c4b = f2c(field1: 42){({field1: core::int}) → self::Class1};
+  self::expect(42, c4b.{self::Class1::field1}{core::int});
+  self::expect(2, c4b.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:112:21: Error: No named parameter with the name 'field2'.
+    f2c(field1: 42, field2: 87); // error
+                    ^^^^^^" in f2c{<inapplicable>}.(field1: 42, field2: 87);
+  };
+  ({field2: core::int}) → self::Class1 f2d = #C10;
+  self::Class1 c5a = f2d(){({field2: core::int}) → self::Class1};
+  self::expect(1, c5a.{self::Class1::field1}{core::int});
+  self::expect(2, c5a.{self::Class1::field2}{core::int});
+  self::Class1 c5b = f2d(field2: 87){({field2: core::int}) → self::Class1};
+  self::expect(1, c5b.{self::Class1::field1}{core::int});
+  self::expect(87, c5b.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:123:9: Error: No named parameter with the name 'field1'.
+    f2d(field1: 42, field2: 87); // error
+        ^^^^^^" in f2d{<inapplicable>}.(field1: 42, field2: 87);
+  };
+  () → self::Class1 f2e = #C11;
+  self::Class1 c6a = f2e(){() → self::Class1};
+  self::expect(1, c6a.{self::Class1::field1}{core::int});
+  self::expect(2, c6a.{self::Class1::field2}{core::int});
+  () → Null {
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:131:9: Error: No named parameter with the name 'field1'.
+    f2e(field1: 42); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field1: 42);
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:132:9: Error: No named parameter with the name 'field2'.
+    f2e(field2: 87); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field2: 87);
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/lowering/redirecting_factory_tear_off_default_values_complex.dart:133:9: Error: No named parameter with the name 'field1'.
+    f2e(field1: 42, field2: 87); // error
+        ^^^^^^" in f2e{<inapplicable>}.(field1: 42, field2: 87);
+  };
+}
+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 = #C12}) → 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 = 1
+  #C2 = 2
+  #C3 = null
+  #C4 = static-tearoff self::Class1::_#redirectPositionalSame#tearOff
+  #C5 = static-tearoff self::Class1::_#redirectPositionalFewer1#tearOff
+  #C6 = static-tearoff self::Class1::_#redirectPositionalFewer2#tearOff
+  #C7 = static-tearoff self::Class1::_#redirectNamedSame#tearOff
+  #C8 = static-tearoff self::Class1::_#redirectNamedReorder#tearOff
+  #C9 = static-tearoff self::Class1::_#redirectNamedFewer1#tearOff
+  #C10 = static-tearoff self::Class1::_#redirectNamedFewer2#tearOff
+  #C11 = static-tearoff self::Class1::_#redirectNamedFewer3#tearOff
+  #C12 = false
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart
index 75d63fa..4ac8aea 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart
+++ b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart
@@ -16,7 +16,8 @@
 }
 
 class Class2 {
-  Class2._();
+  Class2.__();
+  factory Class2._() => Class2.__();
   factory Class2.named() = Class2._;
 }
 
@@ -74,6 +75,22 @@
       Class6._;
 }
 
+class Class7a implements Class7b {
+  Class7a();
+}
+
+class Class7b {
+  factory Class7b() = Class7a;
+}
+
+class Class8a<T> implements Class8b<T> {
+  Class8a();
+}
+
+class Class8b<T> {
+  factory Class8b() = Class8a<T>;
+}
+
 testArgs() {
   var f3a = Class3.new;
   var c3a = f3a(42);
@@ -143,6 +160,20 @@
   throws(() => f6b(42), inSoundModeOnly: true);
   throws(() => f6b(42, 87), inSoundModeOnly: true);
   throws(() => f6b(field1: 87, field2: 87));
+
+  var f7a = Class7b.new;
+  var c7a = f7a();
+  expect(true, c7a is Class7a);
+  expect(true, c7a is Class7b);
+
+  var f8a = Class8b.new;
+  var c8a = f8a();
+  expect(true, c8a is Class8a);
+  expect(true, c8a is Class8b);
+  var c8b = f8a<int>();
+  expect(true, c8b is Class8a<int>);
+  expect(true, c8b is Class8b<int>);
+  expect(false, c8b is Class8b<String>);
 }
 
 expect(expected, actual) {
diff --git a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.strong.expect
index f2dd642..ce07a22 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.strong.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.strong.expect
@@ -2,43 +2,43 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f4a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f5a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 // Try removing the extra positional arguments.
 //     f5a(42, 87, 123); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
 //     f6a(42); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f6a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(field1: 87, field2: 87); // error
 //        ^
 //
@@ -55,9 +55,11 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
     : super core::Object::•()
     ;
+  static factory _() → self::Class2
+    return new self::Class2::__();
   static factory named() → self::Class2
     let dynamic #redirecting_factory = self::Class2::_ in invalid-expression;
 }
@@ -100,6 +102,26 @@
   static factory •(core::int field1, {core::int? field2 = #C1, required core::int field3 = #C1}) → self::Class6
     let dynamic #redirecting_factory = self::Class6::_ in invalid-expression;
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    : super core::Object::•()
+    ;
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let dynamic #redirecting_factory = self::Class7a::• in invalid-expression;
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    : super core::Object::•()
+    ;
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let dynamic #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+}
 static final field core::bool inSoundMode = !(<core::int?>[] is{ForNonNullableByDefault} core::List<core::int>);
 static method main() → dynamic {
   core::print("inSoundMode: ${self::inSoundMode}");
@@ -127,10 +149,10 @@
   self::Class3 c3a = f3a(42){(core::int) → self::Class3};
   self::expect(42, c3a.{self::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
@@ -146,7 +168,7 @@
   self::Class4 c4b = f4a(42){([core::int?]) → self::Class4};
   self::expect(42, c4b.{self::Class4::field}{core::int?});
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f4a(42, 87); // error
        ^" in f4a{<inapplicable>}.(42, 87);
@@ -161,10 +183,10 @@
   self::expect(87, c5b.{self::Class5::field1}{core::int});
   self::expect(42, c5b.{self::Class5::field2}{core::int?});
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
     f5a(); // error
        ^" in f5a{<inapplicable>}.();
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 Try removing the extra positional arguments.
     f5a(42, 87, 123); // error
        ^" in f5a{<inapplicable>}.(42, 87, 123);
@@ -178,17 +200,17 @@
   self::expect(null, c6a.{self::Class6::field2}{core::int?});
   self::expect(87, c6a.{self::Class6::field3}{core::int});
   () → Null {
-    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(); // error
        ^" in f6a{<inapplicable>}.();
-    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
     f6a(42); // error
        ^" in f6a{<inapplicable>}.(42);
-    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f6a(42, 87); // error
        ^" in f6a{<inapplicable>}.(42, 87);
-    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(field1: 87, field2: 87); // error
        ^" in f6a{<inapplicable>}.(field1: 87, field2: 87);
   };
@@ -205,12 +227,24 @@
   self::throws(() → dynamic => f6b{dynamic}.call(42), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(42, 87), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(field1: 87, field2: 87));
+  () → self::Class7b f7a = #C8;
+  self::Class7b c7a = f7a(){() → self::Class7b};
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7a);
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7b);
+  <T extends core::Object? = dynamic>() → self::Class8b<T%> f8a = #C9;
+  self::Class8b<dynamic> c8a = f8a<dynamic>(){() → self::Class8b<dynamic>};
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8a<dynamic>);
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8b<dynamic>);
+  self::Class8b<core::int> c8b = f8a<core::int>(){() → self::Class8b<core::int>};
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8a<core::int>);
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8b<core::int>);
+  self::expect(false, c8b is{ForNonNullableByDefault} self::Class8b<core::String>);
 }
 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 = #C8}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C10}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -232,5 +266,7 @@
   #C5 = redirecting-factory-tearoff self::Class4::•
   #C6 = redirecting-factory-tearoff self::Class5::•
   #C7 = redirecting-factory-tearoff self::Class6::•
-  #C8 = false
+  #C8 = redirecting-factory-tearoff self::Class7b::•
+  #C9 = redirecting-factory-tearoff self::Class8b::•
+  #C10 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.strong.transformed.expect
index ccaec42..872ae36 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.strong.transformed.expect
@@ -2,43 +2,43 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f4a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f5a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 // Try removing the extra positional arguments.
 //     f5a(42, 87, 123); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
 //     f6a(42); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f6a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(field1: 87, field2: 87); // error
 //        ^
 //
@@ -55,11 +55,13 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
     : super core::Object::•()
     ;
+  static factory _() → self::Class2
+    return new self::Class2::__();
   static factory named() → self::Class2
-    let Never #redirecting_factory = self::Class2::_ in invalid-expression;
+    let () → self::Class2 #redirecting_factory = self::Class2::_ in invalid-expression;
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -100,6 +102,26 @@
   static factory •(core::int field1, {core::int? field2 = #C1, required core::int field3 = #C1}) → self::Class6
     let Never #redirecting_factory = self::Class6::_ in invalid-expression;
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    : super core::Object::•()
+    ;
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let Never #redirecting_factory = self::Class7a::• in invalid-expression;
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    : super core::Object::•()
+    ;
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let Never #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+}
 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}");
@@ -127,10 +149,10 @@
   self::Class3 c3a = f3a(42){(core::int) → self::Class3};
   self::expect(42, c3a.{self::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
@@ -146,7 +168,7 @@
   self::Class4 c4b = f4a(42){([core::int?]) → self::Class4};
   self::expect(42, c4b.{self::Class4::field}{core::int?});
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f4a(42, 87); // error
        ^" in f4a{<inapplicable>}.(42, 87);
@@ -161,10 +183,10 @@
   self::expect(87, c5b.{self::Class5::field1}{core::int});
   self::expect(42, c5b.{self::Class5::field2}{core::int?});
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
     f5a(); // error
        ^" in f5a{<inapplicable>}.();
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 Try removing the extra positional arguments.
     f5a(42, 87, 123); // error
        ^" in f5a{<inapplicable>}.(42, 87, 123);
@@ -178,17 +200,17 @@
   self::expect(null, c6a.{self::Class6::field2}{core::int?});
   self::expect(87, c6a.{self::Class6::field3}{core::int});
   () → Null {
-    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(); // error
        ^" in f6a{<inapplicable>}.();
-    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
     f6a(42); // error
        ^" in f6a{<inapplicable>}.(42);
-    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f6a(42, 87); // error
        ^" in f6a{<inapplicable>}.(42, 87);
-    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(field1: 87, field2: 87); // error
        ^" in f6a{<inapplicable>}.(field1: 87, field2: 87);
   };
@@ -205,12 +227,24 @@
   self::throws(() → dynamic => f6b{dynamic}.call(42), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(42, 87), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(field1: 87, field2: 87));
+  () → self::Class7b f7a = #C8;
+  self::Class7b c7a = f7a(){() → self::Class7b};
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7a);
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7b);
+  <T extends core::Object? = dynamic>() → self::Class8b<T%> f8a = #C9;
+  self::Class8b<dynamic> c8a = f8a<dynamic>(){() → self::Class8b<dynamic>};
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8a<dynamic>);
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8b<dynamic>);
+  self::Class8b<core::int> c8b = f8a<core::int>(){() → self::Class8b<core::int>};
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8a<core::int>);
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8b<core::int>);
+  self::expect(false, c8b is{ForNonNullableByDefault} self::Class8b<core::String>);
 }
 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 = #C8}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C10}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -232,5 +266,7 @@
   #C5 = redirecting-factory-tearoff self::Class4::•
   #C6 = redirecting-factory-tearoff self::Class5::•
   #C7 = redirecting-factory-tearoff self::Class6::•
-  #C8 = false
+  #C8 = redirecting-factory-tearoff self::Class7b::•
+  #C9 = redirecting-factory-tearoff self::Class8b::•
+  #C10 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.textual_outline.expect
index 999b5d2..2fa25d6 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.textual_outline.expect
@@ -7,7 +7,8 @@
 }
 
 class Class2 {
-  Class2._();
+  Class2.__();
+  factory Class2._() => Class2.__();
   factory Class2.named() = Class2._;
 }
 
@@ -40,6 +41,22 @@
   factory Class6(int field1, {int? field2, required int field3}) = Class6._;
 }
 
+class Class7a implements Class7b {
+  Class7a();
+}
+
+class Class7b {
+  factory Class7b() = Class7a;
+}
+
+class Class8a<T> implements Class8b<T> {
+  Class8a();
+}
+
+class Class8b<T> {
+  factory Class8b() = Class8a<T>;
+}
+
 testArgs() {}
 expect(expected, actual) {}
 throws(Function() f, {bool inSoundModeOnly: false}) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.textual_outline_modelled.expect
index edb4f68..0500e74 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.textual_outline_modelled.expect
@@ -4,7 +4,8 @@
 }
 
 class Class2 {
-  Class2._();
+  Class2.__();
+  factory Class2._() => Class2.__();
   factory Class2.named() = Class2._;
 }
 
@@ -35,6 +36,22 @@
   final int field3;
 }
 
+class Class7a implements Class7b {
+  Class7a();
+}
+
+class Class7b {
+  factory Class7b() = Class7a;
+}
+
+class Class8a<T> implements Class8b<T> {
+  Class8a();
+}
+
+class Class8b<T> {
+  factory Class8b() = Class8a<T>;
+}
+
 expect(expected, actual) {}
 final bool inSoundMode = <int?>[] is! List<int>;
 main() {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.expect
index f2dd642..ce07a22 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.expect
@@ -2,43 +2,43 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f4a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f5a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 // Try removing the extra positional arguments.
 //     f5a(42, 87, 123); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
 //     f6a(42); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f6a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(field1: 87, field2: 87); // error
 //        ^
 //
@@ -55,9 +55,11 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
     : super core::Object::•()
     ;
+  static factory _() → self::Class2
+    return new self::Class2::__();
   static factory named() → self::Class2
     let dynamic #redirecting_factory = self::Class2::_ in invalid-expression;
 }
@@ -100,6 +102,26 @@
   static factory •(core::int field1, {core::int? field2 = #C1, required core::int field3 = #C1}) → self::Class6
     let dynamic #redirecting_factory = self::Class6::_ in invalid-expression;
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    : super core::Object::•()
+    ;
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let dynamic #redirecting_factory = self::Class7a::• in invalid-expression;
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    : super core::Object::•()
+    ;
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let dynamic #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+}
 static final field core::bool inSoundMode = !(<core::int?>[] is{ForNonNullableByDefault} core::List<core::int>);
 static method main() → dynamic {
   core::print("inSoundMode: ${self::inSoundMode}");
@@ -127,10 +149,10 @@
   self::Class3 c3a = f3a(42){(core::int) → self::Class3};
   self::expect(42, c3a.{self::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
@@ -146,7 +168,7 @@
   self::Class4 c4b = f4a(42){([core::int?]) → self::Class4};
   self::expect(42, c4b.{self::Class4::field}{core::int?});
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f4a(42, 87); // error
        ^" in f4a{<inapplicable>}.(42, 87);
@@ -161,10 +183,10 @@
   self::expect(87, c5b.{self::Class5::field1}{core::int});
   self::expect(42, c5b.{self::Class5::field2}{core::int?});
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
     f5a(); // error
        ^" in f5a{<inapplicable>}.();
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 Try removing the extra positional arguments.
     f5a(42, 87, 123); // error
        ^" in f5a{<inapplicable>}.(42, 87, 123);
@@ -178,17 +200,17 @@
   self::expect(null, c6a.{self::Class6::field2}{core::int?});
   self::expect(87, c6a.{self::Class6::field3}{core::int});
   () → Null {
-    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(); // error
        ^" in f6a{<inapplicable>}.();
-    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
     f6a(42); // error
        ^" in f6a{<inapplicable>}.(42);
-    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f6a(42, 87); // error
        ^" in f6a{<inapplicable>}.(42, 87);
-    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(field1: 87, field2: 87); // error
        ^" in f6a{<inapplicable>}.(field1: 87, field2: 87);
   };
@@ -205,12 +227,24 @@
   self::throws(() → dynamic => f6b{dynamic}.call(42), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(42, 87), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(field1: 87, field2: 87));
+  () → self::Class7b f7a = #C8;
+  self::Class7b c7a = f7a(){() → self::Class7b};
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7a);
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7b);
+  <T extends core::Object? = dynamic>() → self::Class8b<T%> f8a = #C9;
+  self::Class8b<dynamic> c8a = f8a<dynamic>(){() → self::Class8b<dynamic>};
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8a<dynamic>);
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8b<dynamic>);
+  self::Class8b<core::int> c8b = f8a<core::int>(){() → self::Class8b<core::int>};
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8a<core::int>);
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8b<core::int>);
+  self::expect(false, c8b is{ForNonNullableByDefault} self::Class8b<core::String>);
 }
 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 = #C8}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C10}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -232,5 +266,7 @@
   #C5 = redirecting-factory-tearoff self::Class4::•
   #C6 = redirecting-factory-tearoff self::Class5::•
   #C7 = redirecting-factory-tearoff self::Class6::•
-  #C8 = false
+  #C8 = redirecting-factory-tearoff self::Class7b::•
+  #C9 = redirecting-factory-tearoff self::Class8b::•
+  #C10 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.outline.expect
index 44ac319..d972b38 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.outline.expect
@@ -11,7 +11,9 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
+    ;
+  static factory _() → self::Class2
     ;
   static factory named() → self::Class2
     let dynamic #redirecting_factory = self::Class2::_ in invalid-expression;
@@ -51,6 +53,24 @@
   static factory •(core::int field1, {core::int? field2, required core::int field3}) → self::Class6
     let dynamic #redirecting_factory = self::Class6::_ in invalid-expression;
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    ;
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let dynamic #redirecting_factory = self::Class7a::• in invalid-expression;
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    ;
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let dynamic #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+}
 static final field core::bool inSoundMode;
 static method main() → dynamic
   ;
diff --git a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.transformed.expect
index ccaec42..872ae36 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart.weak.transformed.expect
@@ -2,43 +2,43 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f3a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f3a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f4a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f5a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 // Try removing the extra positional arguments.
 //     f5a(42, 87, 123); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
 //     f6a(42); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 // Try removing the extra positional arguments.
 //     f6a(42, 87); // error
 //        ^
 //
-// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+// pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
 //     f6a(field1: 87, field2: 87); // error
 //        ^
 //
@@ -55,11 +55,13 @@
 }
 class Class2 extends core::Object {
   static final field dynamic _redirecting# = <dynamic>[self::Class2::named]/*isLegacy*/;
-  constructor _() → self::Class2
+  constructor __() → self::Class2
     : super core::Object::•()
     ;
+  static factory _() → self::Class2
+    return new self::Class2::__();
   static factory named() → self::Class2
-    let Never #redirecting_factory = self::Class2::_ in invalid-expression;
+    let () → self::Class2 #redirecting_factory = self::Class2::_ in invalid-expression;
 }
 class Class3 extends core::Object {
   final field core::int field;
@@ -100,6 +102,26 @@
   static factory •(core::int field1, {core::int? field2 = #C1, required core::int field3 = #C1}) → self::Class6
     let Never #redirecting_factory = self::Class6::_ in invalid-expression;
 }
+class Class7a extends core::Object implements self::Class7b {
+  constructor •() → self::Class7a
+    : super core::Object::•()
+    ;
+}
+class Class7b extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class7b::•]/*isLegacy*/;
+  static factory •() → self::Class7b
+    let Never #redirecting_factory = self::Class7a::• in invalid-expression;
+}
+class Class8a<T extends core::Object? = dynamic> extends core::Object implements self::Class8b<self::Class8a::T%> {
+  constructor •() → self::Class8a<self::Class8a::T%>
+    : super core::Object::•()
+    ;
+}
+class Class8b<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::Class8b::•]/*isLegacy*/;
+  static factory •<T extends core::Object? = dynamic>() → self::Class8b<self::Class8b::•::T%>
+    let Never #redirecting_factory = self::Class8a::• in let self::Class8b::•::T% #typeArg0 = null in invalid-expression;
+}
 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}");
@@ -127,10 +149,10 @@
   self::Class3 c3a = f3a(42){(core::int) → self::Class3};
   self::expect(42, c3a.{self::Class3::field}{core::int});
   () → Null {
-    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:82:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t1 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:99:8: Error: Too few positional arguments: 1 required, 0 given.
     f3a(); // error
        ^" in f3a{<inapplicable>}.();
-    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:83:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t2 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:100:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f3a(42, 87); // error
        ^" in f3a{<inapplicable>}.(42, 87);
@@ -146,7 +168,7 @@
   self::Class4 c4b = f4a(42){([core::int?]) → self::Class4};
   self::expect(42, c4b.{self::Class4::field}{core::int?});
   () → Null {
-    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:98:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t3 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:115:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f4a(42, 87); // error
        ^" in f4a{<inapplicable>}.(42, 87);
@@ -161,10 +183,10 @@
   self::expect(87, c5b.{self::Class5::field1}{core::int});
   self::expect(42, c5b.{self::Class5::field2}{core::int?});
   () → Null {
-    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:112:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t4 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:129:8: Error: Too few positional arguments: 1 required, 0 given.
     f5a(); // error
        ^" in f5a{<inapplicable>}.();
-    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:113:8: Error: Too many positional arguments: 2 allowed, but 3 found.
+    let final Never #t5 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:130:8: Error: Too many positional arguments: 2 allowed, but 3 found.
 Try removing the extra positional arguments.
     f5a(42, 87, 123); // error
        ^" in f5a{<inapplicable>}.(42, 87, 123);
@@ -178,17 +200,17 @@
   self::expect(null, c6a.{self::Class6::field2}{core::int?});
   self::expect(87, c6a.{self::Class6::field3}{core::int});
   () → Null {
-    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:125:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t6 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:142:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(); // error
        ^" in f6a{<inapplicable>}.();
-    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:126:8: Error: Required named parameter 'field3' must be provided.
+    let final Never #t7 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:143:8: Error: Required named parameter 'field3' must be provided.
     f6a(42); // error
        ^" in f6a{<inapplicable>}.(42);
-    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:127:8: Error: Too many positional arguments: 1 allowed, but 2 found.
+    let final Never #t8 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:144:8: Error: Too many positional arguments: 1 allowed, but 2 found.
 Try removing the extra positional arguments.
     f6a(42, 87); // error
        ^" in f6a{<inapplicable>}.(42, 87);
-    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:128:8: Error: Too few positional arguments: 1 required, 0 given.
+    let final Never #t9 = invalid-expression "pkg/front_end/testcases/constructor_tearoffs/redirecting_factory_tear_off.dart:145:8: Error: Too few positional arguments: 1 required, 0 given.
     f6a(field1: 87, field2: 87); // error
        ^" in f6a{<inapplicable>}.(field1: 87, field2: 87);
   };
@@ -205,12 +227,24 @@
   self::throws(() → dynamic => f6b{dynamic}.call(42), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(42, 87), inSoundModeOnly: true);
   self::throws(() → dynamic => f6b{dynamic}.call(field1: 87, field2: 87));
+  () → self::Class7b f7a = #C8;
+  self::Class7b c7a = f7a(){() → self::Class7b};
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7a);
+  self::expect(true, c7a is{ForNonNullableByDefault} self::Class7b);
+  <T extends core::Object? = dynamic>() → self::Class8b<T%> f8a = #C9;
+  self::Class8b<dynamic> c8a = f8a<dynamic>(){() → self::Class8b<dynamic>};
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8a<dynamic>);
+  self::expect(true, c8a is{ForNonNullableByDefault} self::Class8b<dynamic>);
+  self::Class8b<core::int> c8b = f8a<core::int>(){() → self::Class8b<core::int>};
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8a<core::int>);
+  self::expect(true, c8b is{ForNonNullableByDefault} self::Class8b<core::int>);
+  self::expect(false, c8b is{ForNonNullableByDefault} self::Class8b<core::String>);
 }
 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 = #C8}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C10}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -232,5 +266,7 @@
   #C5 = redirecting-factory-tearoff self::Class4::•
   #C6 = redirecting-factory-tearoff self::Class5::•
   #C7 = redirecting-factory-tearoff self::Class6::•
-  #C8 = false
+  #C8 = redirecting-factory-tearoff self::Class7b::•
+  #C9 = redirecting-factory-tearoff self::Class8b::•
+  #C10 = false
 }
diff --git a/tools/VERSION b/tools/VERSION
index 4038a7d..627df9f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 343
+PRERELEASE 344
 PRERELEASE_PATCH 0
\ No newline at end of file