Version 2.14.0-348.0.dev

Merge commit '639665cc4e8a2865a54044eed81445f82dc2a276' into 'dev'
diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart
index 3cb1a0f..9548d6e 100644
--- a/pkg/analyzer/lib/src/summary2/library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/library_builder.dart
@@ -182,6 +182,22 @@
       return false;
     }
 
+    /// Build types for type annotations in new [nodes].
+    void resolveTypeAnnotations(
+      List<ast.AstNode> nodes, {
+      ClassElementImpl? classElement,
+    }) {
+      var nodesToBuildType = NodesToBuildType();
+      var resolver = ReferenceResolver(linker, nodesToBuildType, element);
+      if (classElement != null) {
+        resolver.enterScopeClassElement(classElement);
+      }
+      for (var node in nodes) {
+        node.accept(resolver);
+      }
+      TypesBuilder(linker).build(nodesToBuildType);
+    }
+
     for (var linkingUnit in units) {
       for (var declaration in linkingUnit.node.declarations) {
         if (declaration is ast.ClassDeclarationImpl) {
@@ -206,17 +222,7 @@
             );
             var classElement = declaration.declaredElement as ClassElementImpl;
             elementBuilder.buildMacroClassMembers(classElement, newMembers);
-
-            // TODO(scheglov) extract
-            {
-              var nodesToBuildType = NodesToBuildType();
-              var resolver =
-                  ReferenceResolver(linker, nodesToBuildType, element);
-              for (var newMember in newMembers) {
-                newMember.accept(resolver);
-              }
-              TypesBuilder(linker).build(nodesToBuildType);
-            }
+            resolveTypeAnnotations(newMembers, classElement: classElement);
           }
         }
       }
diff --git a/pkg/analyzer/lib/src/summary2/reference_resolver.dart b/pkg/analyzer/lib/src/summary2/reference_resolver.dart
index 9268b91..1cba245 100644
--- a/pkg/analyzer/lib/src/summary2/reference_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/reference_resolver.dart
@@ -45,6 +45,10 @@
         scope = libraryElement.scope,
         isNNBD = libraryElement.isNonNullableByDefault;
 
+  void enterScopeClassElement(ClassElementImpl element) {
+    scope = TypeParameterScope(scope, element.typeParameters);
+  }
+
   @override
   void visitBlockFunctionBody(BlockFunctionBody node) {}
 
@@ -53,9 +57,6 @@
     var outerScope = scope;
 
     var element = node.declaredElement as ClassElementImpl;
-    element.accessors; // create elements
-    element.constructors; // create elements
-    element.methods; // create elements
 
     scope = TypeParameterScope(scope, element.typeParameters);
 
@@ -102,7 +103,6 @@
     var outerScope = scope;
 
     var element = node.declaredElement as ConstructorElementImpl;
-    element.parameters; // create elements
 
     scope = TypeParameterScope(scope, element.typeParameters);
     LinkingNodeContext(node, scope);
@@ -158,7 +158,6 @@
     var outerScope = scope;
 
     var element = node.declaredElement as FieldFormalParameterElementImpl;
-    element.parameters; // create elements
 
     scope = TypeParameterScope(scope, element.typeParameters);
 
@@ -180,7 +179,6 @@
     var outerScope = scope;
 
     var element = node.declaredElement as ExecutableElementImpl;
-    element.parameters; // create elements
 
     scope = TypeParameterScope(outerScope, element.typeParameters);
     LinkingNodeContext(node, scope);
@@ -208,9 +206,6 @@
 
     node.returnType?.accept(this);
     node.typeParameters?.accept(this);
-
-    var function = element.aliasedElement as GenericFunctionTypeElementImpl;
-    function.parameters; // create elements
     node.parameters.accept(this);
 
     nodesToBuildType.addDeclaration(node);
@@ -223,7 +218,6 @@
     var outerScope = scope;
 
     var element = node.declaredElement as ParameterElementImpl;
-    element.parameters; // create elements
 
     scope = TypeParameterScope(scope, element.typeParameters);
 
@@ -287,7 +281,6 @@
     var outerScope = scope;
 
     var element = node.declaredElement as ExecutableElementImpl;
-    element.parameters; // create elements
 
     scope = TypeParameterScope(scope, element.typeParameters);
     LinkingNodeContext(node, scope);
@@ -305,9 +298,6 @@
     var outerScope = scope;
 
     var element = node.declaredElement as MixinElementImpl;
-    element.accessors; // create elements
-    element.constructors; // create elements
-    element.methods; // create elements
 
     scope = TypeParameterScope(scope, element.typeParameters);
 
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 8c5f16e..814a92b 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -21298,6 +21298,61 @@
 ''');
   }
 
+  test_macro_observable_generic() async {
+    addLibrarySource('/macro_annotations.dart', r'''
+library analyzer.macro.annotations;
+const observable = 0;
+''');
+    var library = await checkLibrary(r'''
+import 'macro_annotations.dart';
+class A<T> {
+  @observable
+  T _f;
+}
+''');
+    checkElementText(library, r'''
+library
+  imports
+    macro_annotations.dart
+  definingUnit
+    classes
+      class A @39
+        typeParameters
+          covariant T @41
+            defaultType: dynamic
+        fields
+          _f @64
+            metadata
+              Annotation
+                atSign: @ @48
+                element: macro_annotations.dart::@getter::observable
+                name: SimpleIdentifier
+                  staticElement: macro_annotations.dart::@getter::observable
+                  staticType: null
+                  token: observable @49
+            type: T
+          synthetic f @-1
+            type: T
+        constructors
+          synthetic @-1
+        accessors
+          synthetic get _f @-1
+            returnType: T
+          synthetic set _f @-1
+            parameters
+              requiredPositional __f @-1
+                type: T
+            returnType: void
+          get f @-1
+            returnType: T
+          set f @-1
+            parameters
+              requiredPositional val @-1
+                type: T
+            returnType: void
+''');
+  }
+
   test_main_class() async {
     var library = await checkLibrary('class main {}');
     checkElementText(library, r'''
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 4646515..2725aac 100644
--- a/pkg/front_end/lib/src/fasta/builder/factory_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/factory_builder.dart
@@ -78,9 +78,8 @@
           ..fileOffset = charOffset
           ..fileEndOffset = charEndOffset
           ..isNonNullableByDefault = libraryBuilder.isNonNullableByDefault,
-        _factoryTearOff = createConstructorTearOffProcedure(
-            name, libraryBuilder, libraryBuilder.fileUri, charOffset,
-            forAbstractClassOrEnum: false),
+        _factoryTearOff = createFactoryTearOffProcedure(
+            name, libraryBuilder, libraryBuilder.fileUri, charOffset),
         super(metadata, modifiers, returnType, name, typeVariables, formals,
             libraryBuilder, charOffset, nativeMethodName) {
     this.asyncModifier = asyncModifier;
diff --git a/pkg/front_end/lib/src/fasta/builder/type_alias_builder.dart b/pkg/front_end/lib/src/fasta/builder/type_alias_builder.dart
index b012042..78e98ed 100644
--- a/pkg/front_end/lib/src/fasta/builder/type_alias_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/type_alias_builder.dart
@@ -5,7 +5,6 @@
 library fasta.function_type_alias_builder;
 
 import 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart';
 import 'package:kernel/src/legacy_erasure.dart';
 
 import 'package:kernel/type_algebra.dart' show substitute, uniteNullabilities;
@@ -20,7 +19,6 @@
 
 import '../problems.dart' show unhandled;
 import '../source/source_library_builder.dart';
-import '../util/helpers.dart';
 
 import 'class_builder.dart';
 import 'library_builder.dart';
@@ -113,10 +111,28 @@
   // as stated in the docs? It is not needed for the implementation.
   List<TypeBuilder>? unaliasTypeArguments(List<TypeBuilder>? typeArguments);
 
-  void buildOutlineExpressions(
-      SourceLibraryBuilder library,
-      CoreTypes coreTypes,
-      List<DelayedActionPerformer> delayedActionPerformers);
+  /// Returns the lowering for the constructor or factory named [name] on the
+  /// effective target class of this typedef.
+  ///
+  /// For instance, if we have
+  ///
+  ///     class A<T> {
+  ///       A();
+  ///     }
+  ///     typedef F = A<int>;
+  ///     typedef G = F;
+  ///     typedef H<X, Y> = A<X>;
+  ///
+  /// the lowering will create
+  ///
+  ///     A<int> _#F#new#tearOff() => new A<int>();
+  ///     A<int> _#G#new#tearOff() => new A<int>();
+  ///     A<int> _#H#new#tearOff<X, Y>() => new A<X>();
+  ///
+  /// which will be return by [findConstructorOrFactory] on `F`, `G`, `H` with
+  /// name 'new' or ''.
+  Procedure? findConstructorOrFactory(
+      String name, int charOffset, Uri uri, LibraryBuilder accessingLibrary);
 }
 
 abstract class TypeAliasBuilderImpl extends TypeDeclarationBuilderImpl
@@ -494,6 +510,17 @@
     }
     return currentTypeArguments;
   }
+
+  Map<Name, Procedure>? get tearOffs;
+
+  Procedure? findConstructorOrFactory(
+      String text, int charOffset, Uri uri, LibraryBuilder accessingLibrary) {
+    if (tearOffs != null) {
+      Name name = new Name(text == 'new' ? '' : text, accessingLibrary.library);
+      return tearOffs![name];
+    }
+    return null;
+  }
 }
 
 /// Used to detect cycles in the declaration of a typedef
diff --git a/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart b/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart
index e52ce9a..4c1afba 100644
--- a/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart
@@ -6,29 +6,7 @@
 
 import 'dart:convert' show jsonDecode;
 
-import 'package:kernel/ast.dart'
-    show
-        Class,
-        ConstantExpression,
-        Constructor,
-        DartType,
-        DynamicType,
-        Expression,
-        Extension,
-        Field,
-        FunctionType,
-        Library,
-        ListLiteral,
-        Member,
-        NamedNode,
-        NeverType,
-        Procedure,
-        ProcedureKind,
-        Reference,
-        StaticGet,
-        StringConstant,
-        StringLiteral,
-        Typedef;
+import 'package:kernel/ast.dart';
 
 import '../builder/builder.dart';
 import '../builder/class_builder.dart';
@@ -122,8 +100,23 @@
     isBuilt = true;
     library.classes.forEach(addClass);
     library.extensions.forEach(addExtension);
+
+    Map<String, Map<Name, Procedure>> tearOffs = {};
+    List<Procedure> nonTearOffs = [];
+    for (Procedure procedure in library.procedures) {
+      List<Object>? names = extractTypedefNameFromTearOff(procedure.name);
+      if (names != null) {
+        Map<Name, Procedure> map = tearOffs[names[0] as String] ??= {};
+        map[names[1] as Name] = procedure;
+      } else {
+        nonTearOffs.add(procedure);
+      }
+    }
+    nonTearOffs.forEach(addMember);
     library.procedures.forEach(addMember);
-    library.typedefs.forEach(addTypedef);
+    for (Typedef typedef in library.typedefs) {
+      addTypedef(typedef, tearOffs[typedef.name]);
+    }
     library.fields.forEach(addMember);
 
     if (isReadyToFinalizeExports) {
@@ -285,12 +278,12 @@
     return declaration;
   }
 
-  void addTypedef(Typedef typedef) {
+  void addTypedef(Typedef typedef, Map<Name, Procedure>? tearOffs) {
     DartType? type = typedef.type;
     if (type is FunctionType && type.typedefType == null) {
       unhandled("null", "addTypedef", typedef.fileOffset, typedef.fileUri);
     }
-    addBuilder(typedef.name, new DillTypeAliasBuilder(typedef, this),
+    addBuilder(typedef.name, new DillTypeAliasBuilder(typedef, tearOffs, this),
         typedef.fileOffset);
   }
 
diff --git a/pkg/front_end/lib/src/fasta/dill/dill_type_alias_builder.dart b/pkg/front_end/lib/src/fasta/dill/dill_type_alias_builder.dart
index f78420c..6dc3d32 100644
--- a/pkg/front_end/lib/src/fasta/dill/dill_type_alias_builder.dart
+++ b/pkg/front_end/lib/src/fasta/dill/dill_type_alias_builder.dart
@@ -4,8 +4,7 @@
 
 library fasta.dill_typedef_builder;
 
-import 'package:kernel/ast.dart' show DartType, InvalidType, NullType, Typedef;
-import 'package:kernel/core_types.dart';
+import 'package:kernel/ast.dart';
 
 import '../builder/library_builder.dart';
 import '../builder/metadata_builder.dart';
@@ -15,20 +14,20 @@
 
 import '../problems.dart' show unimplemented;
 
-import '../util/helpers.dart';
-
 import 'dill_class_builder.dart' show computeTypeVariableBuilders;
 import 'dill_library_builder.dart' show DillLibraryBuilder;
 
 class DillTypeAliasBuilder extends TypeAliasBuilderImpl {
   final Typedef typedef;
 
+  final Map<Name, Procedure>? tearOffs;
+
   List<TypeVariableBuilder>? _typeVariables;
   TypeBuilder? _type;
 
   DartType? thisType;
 
-  DillTypeAliasBuilder(this.typedef, DillLibraryBuilder parent)
+  DillTypeAliasBuilder(this.typedef, this.tearOffs, DillLibraryBuilder parent)
       : super(null, typedef.name, parent, typedef.fileOffset);
 
   List<MetadataBuilder> get metadata {
@@ -91,10 +90,4 @@
 
   @override
   bool get isNullAlias => typedef.type is NullType;
-
-  @override
-  void buildOutlineExpressions(LibraryBuilder library, CoreTypes coreTypes,
-      List<DelayedActionPerformer> delayedActionPerformers) {
-    // 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 2383c40..6cf584a 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
@@ -23,6 +23,18 @@
       library);
 }
 
+/// Creates the synthesized name to use for the lowering of the tear off of a
+/// constructor or factory by the given [constructorName] in [library].
+Name typedefTearOffName(
+    String typedefName, String constructorName, Library library) {
+  return new Name(
+      '$_tearOffNamePrefix'
+      '$typedefName#'
+      '${constructorName.isEmpty ? 'new' : constructorName}'
+      '$_tearOffNameSuffix',
+      library);
+}
+
 /// Returns the name of the corresponding constructor or factory if [name] is
 /// the synthesized name of a lowering of the tear off of a constructor or
 /// factory. Returns `null` otherwise.
@@ -34,11 +46,37 @@
     String text =
         name.text.substring(0, name.text.length - _tearOffNameSuffix.length);
     text = text.substring(_tearOffNamePrefix.length);
+    if (text.contains('#')) {
+      return null;
+    }
     return text == 'new' ? '' : text;
   }
   return null;
 }
 
+/// If [name] is the synthesized name of a lowering of a typedef tear off, a
+/// list containing the [String] name of the typedef and the [Name] name of the
+/// corresponding constructor or factory is returned. Returns `null` otherwise.
+List<Object>? extractTypedefNameFromTearOff(Name name) {
+  if (name.text.startsWith(_tearOffNamePrefix) &&
+      name.text.endsWith(_tearOffNameSuffix) &&
+      name.text.length >
+          _tearOffNamePrefix.length + _tearOffNameSuffix.length) {
+    String text =
+        name.text.substring(0, name.text.length - _tearOffNameSuffix.length);
+    text = text.substring(_tearOffNamePrefix.length);
+    int hashIndex = text.indexOf('#');
+    if (hashIndex == -1) {
+      return null;
+    }
+    String typedefName = text.substring(0, hashIndex);
+    String constructorName = text.substring(hashIndex + 1);
+    constructorName = constructorName == 'new' ? '' : constructorName;
+    return [typedefName, new Name(constructorName, name.library)];
+  }
+  return null;
+}
+
 /// Creates the [Procedure] for the lowering of a generative constructor of
 /// the given [name] in [compilationUnit].
 ///
@@ -58,11 +96,45 @@
   return null;
 }
 
-/// Creates the parameters and body for [tearOff] based on [constructor].
+/// Creates the [Procedure] for the lowering of a non-redirecting factory of
+/// the given [name] in [compilationUnit].
+///
+/// If constructor tear off lowering is not enabled, `null` is returned.
+Procedure? createFactoryTearOffProcedure(String name,
+    SourceLibraryBuilder compilationUnit, Uri fileUri, int fileOffset) {
+  if (compilationUnit
+      .loader.target.backendTarget.isFactoryTearOffLoweringEnabled) {
+    return _createTearOffProcedure(
+        compilationUnit,
+        constructorTearOffName(name, compilationUnit.library),
+        fileUri,
+        fileOffset);
+  }
+  return null;
+}
+
+/// Creates the [Procedure] for the lowering of a typedef tearoff of a
+/// constructor of the given [name] in with the typedef defined in
+/// [libraryBuilder].
+Procedure createTypedefTearOffProcedure(String typedefName, String name,
+    SourceLibraryBuilder libraryBuilder, Uri fileUri, int fileOffset) {
+  return _createTearOffProcedure(
+      libraryBuilder,
+      typedefTearOffName(typedefName, name, libraryBuilder.library),
+      fileUri,
+      fileOffset);
+}
+
+/// Creates the parameters and body for [tearOff] based on [constructor] in
+/// [enclosingClass].
 void buildConstructorTearOffProcedure(Procedure tearOff, Member constructor,
     Class enclosingClass, SourceLibraryBuilder libraryBuilder) {
-  assert(constructor is Constructor ||
-      (constructor is Procedure && constructor.isFactory));
+  assert(
+      constructor is Constructor ||
+          (constructor is Procedure && constructor.isFactory) ||
+          (constructor is Procedure && constructor.isStatic),
+      "Unexpected constructor tear off target $constructor "
+      "(${constructor.runtimeType}).");
 
   int fileOffset = tearOff.fileOffset;
 
@@ -91,6 +163,58 @@
   updatePrivateMemberName(tearOff, libraryBuilder);
 }
 
+/// Creates the parameters and body for [tearOff] for a typedef tearoff of
+/// [constructor] in [enclosingClass] with [typeParameters] as the typedef
+/// parameters and [typeArguments] as the arguments passed to the
+/// [enclosingClass].
+void buildTypedefTearOffProcedure(
+    Procedure tearOff,
+    Member constructor,
+    Class enclosingClass,
+    List<TypeParameter> typeParameters,
+    List<DartType> typeArguments,
+    SourceLibraryBuilder libraryBuilder) {
+  assert(
+      constructor is Constructor ||
+          (constructor is Procedure && constructor.isFactory) ||
+          (constructor is Procedure && constructor.isStatic),
+      "Unexpected constructor tear off target $constructor "
+      "(${constructor.runtimeType}).");
+
+  int fileOffset = tearOff.fileOffset;
+
+  FunctionNode function = constructor.function!;
+  List<TypeParameter> classTypeParameters;
+  if (constructor is Constructor) {
+    // Generative constructors implicitly have the type parameters of the
+    // enclosing class.
+    classTypeParameters = enclosingClass.typeParameters;
+  } else {
+    // Factory constructors explicitly copy over the type parameters of the
+    // enclosing class.
+    classTypeParameters = function.typeParameters;
+  }
+
+  FreshTypeParameters freshTypeParameters =
+      _createFreshTypeParameters(typeParameters, tearOff.function);
+
+  Substitution substitution = freshTypeParameters.substitution;
+  if (!substitution.isEmpty) {
+    if (typeArguments.isNotEmpty) {
+      // Translate [typeArgument] into the context of the synthesized procedure.
+      typeArguments = new List<DartType>.generate(typeArguments.length,
+          (int index) => substitution.substituteType(typeArguments[index]));
+    }
+  }
+  _createParameters(tearOff, function,
+      Substitution.fromPairs(classTypeParameters, typeArguments));
+  Arguments arguments = _createArguments(tearOff, typeArguments, fileOffset);
+  _createTearOffBody(tearOff, constructor, arguments);
+  tearOff.function.fileOffset = tearOff.fileOffset;
+  tearOff.function.fileEndOffset = tearOff.fileOffset;
+  updatePrivateMemberName(tearOff, libraryBuilder);
+}
+
 /// Copies the parameter types from [constructor] to [tearOff].
 ///
 /// These might have been inferred and therefore not available when the
@@ -125,25 +249,6 @@
   }
 }
 
-void copyTearOffDefaultValues(Procedure tearOff, FunctionNode function) {
-  CloneVisitorNotMembers cloner = new CloneVisitorNotMembers();
-  for (int i = 0; i < function.positionalParameters.length; i++) {
-    VariableDeclaration tearOffParameter =
-        tearOff.function.positionalParameters[i];
-    VariableDeclaration constructorParameter = function.positionalParameters[i];
-    tearOffParameter.initializer =
-        cloner.cloneOptional(constructorParameter.initializer);
-    tearOffParameter.initializer?.parent = tearOffParameter;
-  }
-  for (int i = 0; i < function.namedParameters.length; i++) {
-    VariableDeclaration tearOffParameter = tearOff.function.namedParameters[i];
-    VariableDeclaration constructorParameter = function.namedParameters[i];
-    tearOffParameter.initializer =
-        cloner.cloneOptional(constructorParameter.initializer);
-    tearOffParameter.initializer?.parent = tearOffParameter;
-  }
-}
-
 /// Creates the parameters for the redirecting factory [tearOff] based on the
 /// [redirectingConstructor] declaration.
 FreshTypeParameters buildRedirectingFactoryTearOffProcedureParameters(
@@ -194,79 +299,6 @@
       identicalSignatures: false);
 }
 
-/// Creates the synthesized name to use for the lowering of the tear off of a
-/// typedef in [library] using [index] for a unique name within the library.
-Name typedefTearOffName(int index, Library library) {
-  return new Name(
-      '$_tearOffNamePrefix'
-      '${index}'
-      '$_tearOffNameSuffix',
-      library);
-}
-
-/// Creates a top level procedure to be used as the lowering for the typedef
-/// tear off [node] of a target of type [targetType]. [fileUri] together with
-/// the `fileOffset` of [node] is used as the location for the procedure.
-/// [index] is used to create a unique name for the procedure within
-/// [libraryBuilder].
-Procedure createTypedefTearOffLowering(SourceLibraryBuilder libraryBuilder,
-    TypedefTearOff node, FunctionType targetType, Uri fileUri, int index) {
-  int fileOffset = node.fileOffset;
-  Procedure tearOff = _createTearOffProcedure(
-      libraryBuilder,
-      typedefTearOffName(index, libraryBuilder.library),
-      fileUri,
-      node.fileOffset);
-  FreshTypeParameters freshTypeParameters =
-      _createFreshTypeParameters(node.typeParameters, tearOff.function);
-  Substitution substitution = freshTypeParameters.substitution;
-
-  List<DartType> typeArguments = node.typeArguments;
-  if (typeArguments.isNotEmpty) {
-    if (!substitution.isEmpty) {
-      // Translate [typeArgument] into the context of the synthesized procedure.
-      typeArguments = new List<DartType>.generate(typeArguments.length,
-          (int index) => substitution.substituteType(typeArguments[index]));
-    }
-    // Instantiate [targetType] with [typeArguments].
-    targetType =
-        Substitution.fromPairs(targetType.typeParameters, typeArguments)
-            .substituteType(targetType.withoutTypeParameters) as FunctionType;
-  }
-
-  for (DartType constructorParameter in targetType.positionalParameters) {
-    VariableDeclaration tearOffParameter = new VariableDeclaration(null,
-        type: substitution.substituteType(constructorParameter))
-      ..fileOffset = fileOffset;
-    tearOff.function.positionalParameters.add(tearOffParameter);
-    tearOffParameter.parent = tearOff.function;
-  }
-  for (NamedType constructorParameter in targetType.namedParameters) {
-    VariableDeclaration tearOffParameter = new VariableDeclaration(
-        constructorParameter.name,
-        type: substitution.substituteType(constructorParameter.type),
-        isRequired: constructorParameter.isRequired)
-      ..fileOffset = fileOffset;
-    tearOff.function.namedParameters.add(tearOffParameter);
-    tearOffParameter.parent = tearOff.function;
-  }
-  tearOff.function.returnType =
-      substitution.substituteType(targetType.returnType);
-  tearOff.function.requiredParameterCount = targetType.requiredParameterCount;
-
-  Arguments arguments = _createArguments(tearOff, typeArguments, fileOffset);
-  Expression constructorInvocation = new FunctionInvocation(
-      FunctionAccessKind.FunctionType, node.expression, arguments,
-      functionType: targetType)
-    ..fileOffset = tearOff.fileOffset;
-  tearOff.function.body = new ReturnStatement(constructorInvocation)
-    ..fileOffset = tearOff.fileOffset
-    ..parent = tearOff.function;
-  tearOff.function.fileOffset = tearOff.fileOffset;
-  tearOff.function.fileEndOffset = tearOff.fileOffset;
-  return tearOff;
-}
-
 /// Creates the synthesized [Procedure] node for a tear off lowering by the
 /// given [name].
 Procedure _createTearOffProcedure(SourceLibraryBuilder libraryBuilder,
@@ -351,7 +383,9 @@
 /// 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));
+  assert(target is Constructor ||
+      (target is Procedure && target.isFactory) ||
+      (target is Procedure && target.isStatic));
   Expression constructorInvocation;
   if (target is Constructor) {
     constructorInvocation = new ConstructorInvocation(target, arguments)
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 7a251c4..4a0b5ca 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -3220,6 +3220,13 @@
                 if (_helper.isProperRenameForClass(aliasBuilder!.typedef)) {
                   return tearOffExpression;
                 }
+                Procedure? tearOffLowering = aliasBuilder
+                    .findConstructorOrFactory(
+                        name.text, nameOffset, _uri, _helper.libraryBuilder);
+                if (tearOffLowering != null) {
+                  return _helper.forest
+                      .createStaticTearOff(token.charOffset, tearOffLowering);
+                }
                 FreshTypeParameters freshTypeParameters =
                     getFreshTypeParameters(aliasBuilder.typedef.typeParameters);
                 List<DartType>? substitutedTypeArguments;
@@ -3230,6 +3237,7 @@
                         .add(freshTypeParameters.substitute(builtTypeArgument));
                   }
                 }
+
                 tearOffExpression = _helper.forest.createTypedefTearOff(
                     token.charOffset,
                     freshTypeParameters.freshTypeParameters,
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 01cf218..104b239 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -258,16 +258,8 @@
         typeParameters: freshTypeParameters.freshTypeParameters,
         requiredParameterCount: resultType.requiredParameterCount,
         typedefType: null);
-    Expression replacement = node;
-    if (!inferrer.isTopLevel &&
-        inferrer.library.loader.target.backendTarget
-            .isTypedefTearOffLoweringEnabled) {
-      replacement = inferrer.library.getTypedefTearOffLowering(
-          node, expressionType, inferrer.helper!.uri);
-    }
-
     ExpressionInferenceResult inferredResult =
-        inferrer.instantiateTearOff(resultType, typeContext, replacement);
+        inferrer.instantiateTearOff(resultType, typeContext, node);
     Expression ensuredResultExpression =
         inferrer.ensureAssignableResult(typeContext, inferredResult);
     return new ExpressionInferenceResult(
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 a378dec..00e445c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -326,6 +326,7 @@
       installDefaultSupertypes();
       installSyntheticConstructors(myClasses);
       loader.resolveConstructors();
+      loader.installTypedefTearOffs();
       component =
           link(new List<Library>.from(loader.libraries), nameRoot: nameRoot);
       computeCoreTypes();
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index c4c13ce..cead87b 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -81,8 +81,6 @@
 
 import '../import.dart' show Import;
 
-import '../kernel/constructor_tearoff_lowering.dart';
-
 import '../kernel/internal_ast.dart';
 
 import '../kernel/kernel_builder.dart'
@@ -4237,15 +4235,23 @@
     uncheckedTypedefTypes.clear();
   }
 
-  int _typedefTearOffLoweringIndex = 0;
-
-  Expression getTypedefTearOffLowering(
-      TypedefTearOff node, FunctionType targetType, Uri fileUri) {
-    assert(loader.target.backendTarget.isTypedefTearOffLoweringEnabled);
-    Procedure tearOff = createTypedefTearOffLowering(
-        this, node, targetType, fileUri, _typedefTearOffLoweringIndex++);
-    library.addProcedure(tearOff);
-    return new StaticTearOff(tearOff)..fileOffset = node.fileOffset;
+  void installTypedefTearOffs() {
+    Iterator<Builder> iterator = this.iterator;
+    while (iterator.moveNext()) {
+      Builder? declaration = iterator.current;
+      while (declaration != null) {
+        if (declaration is SourceTypeAliasBuilder) {
+          declaration.buildTypedefTearOffs(this,
+              (Procedure procedure) {
+                procedure.isStatic = true;
+              if (!declaration!.isPatch && !declaration.isDuplicate) {
+                library.addProcedure(procedure);
+              }
+          });
+        }
+        declaration = declaration.next;
+      }
+    }
   }
 }
 
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 db885ca..ed43189 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -124,6 +124,7 @@
 import 'source_class_builder.dart' show SourceClassBuilder;
 
 import 'source_library_builder.dart' show SourceLibraryBuilder;
+import 'source_type_alias_builder.dart';
 
 class SourceLoader extends Loader {
   /// The [FileSystem] which should be used to access files.
@@ -719,6 +720,16 @@
     ticker.logMs("Resolved $count constructors");
   }
 
+  void installTypedefTearOffs() {
+    if (target.backendTarget.isTypedefTearOffLoweringEnabled) {
+      for (LibraryBuilder library in builders.values) {
+        if (library.loader == this && library is SourceLibraryBuilder) {
+          library.installTypedefTearOffs();
+        }
+      }
+    }
+  }
+
   void finishTypeVariables(ClassBuilder object, TypeBuilder dynamicType) {
     int count = 0;
     for (LibraryBuilder library in builders.values) {
@@ -1202,9 +1213,9 @@
           } else if (declaration is MemberBuilder) {
             declaration.buildOutlineExpressions(library, coreTypes,
                 delayedActionPerformers, synthesizedFunctionNodes);
-          } else if (declaration is TypeAliasBuilder) {
-            declaration.buildOutlineExpressions(
-                library, coreTypes, delayedActionPerformers);
+          } else if (declaration is SourceTypeAliasBuilder) {
+            declaration.buildOutlineExpressions(library, coreTypes,
+                delayedActionPerformers, synthesizedFunctionNodes);
           } else {
             assert(
                 declaration is PrefixBuilder ||
diff --git a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
index ae4fbb9..9961217 100644
--- a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
@@ -4,17 +4,7 @@
 
 library fasta.source_type_alias_builder;
 
-import 'package:kernel/ast.dart'
-    show
-        DartType,
-        DynamicType,
-        InvalidType,
-        TypeParameter,
-        TypeParameterType,
-        Typedef,
-        TypedefType,
-        VariableDeclaration,
-        getAsTypeArguments;
+import 'package:kernel/ast.dart';
 
 import 'package:kernel/core_types.dart';
 
@@ -33,6 +23,7 @@
 import '../builder/formal_parameter_builder.dart';
 import '../builder/function_type_builder.dart';
 import '../builder/library_builder.dart';
+import '../builder/member_builder.dart';
 import '../builder/metadata_builder.dart';
 import '../builder/named_type_builder.dart';
 import '../builder/type_builder.dart';
@@ -40,6 +31,9 @@
 import '../builder/type_declaration_builder.dart';
 import '../builder/type_variable_builder.dart';
 
+import '../kernel/constructor_tearoff_lowering.dart';
+import '../kernel/kernel_target.dart';
+
 import '../util/helpers.dart';
 
 import 'source_library_builder.dart' show SourceLibraryBuilder;
@@ -54,6 +48,8 @@
 
   DartType? thisType;
 
+  Map<Name, Procedure>? tearOffs;
+
   SourceTypeAliasBuilder(
       List<MetadataBuilder>? metadata,
       String name,
@@ -253,11 +249,11 @@
         allowSuperBounded: false);
   }
 
-  @override
   void buildOutlineExpressions(
       SourceLibraryBuilder library,
       CoreTypes coreTypes,
-      List<DelayedActionPerformer> delayedActionPerformers) {
+      List<DelayedActionPerformer> delayedActionPerformers,
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     MetadataBuilder.buildAnnotations(
         typedef, metadata, library, null, null, fileUri);
     if (typeVariables != null) {
@@ -266,5 +262,42 @@
             library, null, null, coreTypes, delayedActionPerformers);
       }
     }
+    _tearOffDependencies?.forEach((Procedure tearOff, Member target) {
+      InterfaceType targetType = typedef.type as InterfaceType;
+      buildTypedefTearOffProcedure(tearOff, target, target.enclosingClass!,
+          typedef.typeParameters, targetType.typeArguments, library);
+      synthesizedFunctionNodes.add(new SynthesizedFunctionNode(
+          new Map<TypeParameter, DartType>.fromIterables(
+              target.enclosingClass!.typeParameters, targetType.typeArguments),
+          target.function!,
+          tearOff.function));
+    });
+  }
+
+  Map<Procedure, Member>? _tearOffDependencies;
+
+  void buildTypedefTearOffs(
+      SourceLibraryBuilder library, void Function(Procedure) f) {
+    TypeDeclarationBuilder? declaration = unaliasDeclaration(null);
+    if (declaration is ClassBuilder) {
+      tearOffs = {};
+      _tearOffDependencies = {};
+      declaration
+          .forEachConstructor((String constructorName, MemberBuilder builder) {
+        Member? target = builder.invokeTarget;
+        if (target != null) {
+          if (target is Procedure && target.isRedirectingFactory) {
+            target = builder.readTarget!;
+          }
+          Name targetName =
+              new Name(constructorName, declaration.library.library);
+          Procedure tearOff = tearOffs![targetName] =
+              createTypedefTearOffProcedure(
+                  name, constructorName, library, fileUri, charOffset);
+          _tearOffDependencies![tearOff] = target;
+          f(tearOff);
+        }
+      });
+    }
   }
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/const_tear_off.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/const_tear_off.dart.weak.outline.expect
index 8c8090d..b93adae 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/const_tear_off.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/const_tear_off.dart.weak.outline.expect
@@ -48,10 +48,10 @@
 Evaluated: Instantiation @ org-dartlang-testcase:///const_tear_off.dart:23:11 -> InstantiationConstant(A.fact<int*>)
 Evaluated: RedirectingFactoryTearOff @ org-dartlang-testcase:///const_tear_off.dart:24:11 -> RedirectingFactoryTearOffConstant(A.redirect)
 Evaluated: Instantiation @ org-dartlang-testcase:///const_tear_off.dart:25:11 -> InstantiationConstant(A.redirect<int*>)
-Evaluated: TypedefTearOff @ org-dartlang-testcase:///const_tear_off.dart:26:11 -> TypedefTearOffConstant(A.<T><int>)
+Evaluated: TypedefTearOff @ org-dartlang-testcase:///const_tear_off.dart:26:11 -> TypedefTearOffConstant(<T>A.<int>)
 Evaluated: Instantiation @ org-dartlang-testcase:///const_tear_off.dart:27:11 -> InstantiationConstant(A.<int*>)
-Evaluated: TypedefTearOff @ org-dartlang-testcase:///const_tear_off.dart:28:11 -> TypedefTearOffConstant(A.fact<T><int>)
+Evaluated: TypedefTearOff @ org-dartlang-testcase:///const_tear_off.dart:28:11 -> TypedefTearOffConstant(<T>A.fact<int>)
 Evaluated: Instantiation @ org-dartlang-testcase:///const_tear_off.dart:29:11 -> InstantiationConstant(A.fact<int*>)
-Evaluated: TypedefTearOff @ org-dartlang-testcase:///const_tear_off.dart:30:11 -> TypedefTearOffConstant(A.redirect<T><int>)
+Evaluated: TypedefTearOff @ org-dartlang-testcase:///const_tear_off.dart:30:11 -> TypedefTearOffConstant(<T>A.redirect<int>)
 Evaluated: Instantiation @ org-dartlang-testcase:///const_tear_off.dart:31:11 -> InstantiationConstant(A.redirect<int*>)
 Extra constant evaluation: evaluated: 23, effectively constant: 18
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart
new file mode 100644
index 0000000..aa3ec2f
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart
@@ -0,0 +1,78 @@
+// 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 'main_lib.dart';
+
+typedef H<X, Y> = A<Y>;
+
+// TODO(johnniwinther): Use 'var' here when dependency on inferred parameter
+// types is handled.
+dynamic H_new = H.new;
+dynamic H_named = H.named;
+dynamic H_fact = H.fact;
+dynamic H_redirect = H.redirect;
+
+dynamic F_new = F.new;
+dynamic F_named = F.named;
+dynamic F_fact = F.fact;
+dynamic F_redirect = F.redirect;
+
+main() {
+  expect(true, identical(F_new, F_new_lib));
+  expect(false, identical(F_new, F_named_lib));
+  expect(false, identical(F_new, F_fact_lib));
+  expect(false, identical(F_new, F_redirect_lib));
+  expect(false, identical(F_new, G_new_lib));
+  expect(false, identical(F_new, G_named_lib));
+  expect(false, identical(F_new, G_fact_lib));
+  expect(false, identical(F_new, G_redirect_lib));
+  expect(false, identical(F_new, H_new));
+  expect(false, identical(F_new, H_named));
+  expect(false, identical(F_new, H_fact));
+  expect(false, identical(F_new, H_redirect));
+
+  expect(false, identical(F_named, F_new_lib));
+  expect(true, identical(F_named, F_named_lib));
+  expect(false, identical(F_named, F_fact_lib));
+  expect(false, identical(F_named, F_redirect_lib));
+  expect(false, identical(F_named, G_new_lib));
+  expect(false, identical(F_named, G_named_lib));
+  expect(false, identical(F_named, G_fact_lib));
+  expect(false, identical(F_named, G_redirect_lib));
+  expect(false, identical(F_named, H_new));
+  expect(false, identical(F_named, H_named));
+  expect(false, identical(F_named, H_fact));
+  expect(false, identical(F_named, H_redirect));
+
+  expect(false, identical(F_fact, F_new_lib));
+  expect(false, identical(F_fact, F_named_lib));
+  expect(true, identical(F_fact, F_fact_lib));
+  expect(false, identical(F_fact, F_redirect_lib));
+  expect(false, identical(F_fact, G_new_lib));
+  expect(false, identical(F_fact, G_named_lib));
+  expect(false, identical(F_fact, G_fact_lib));
+  expect(false, identical(F_fact, G_redirect_lib));
+  expect(false, identical(F_fact, H_new));
+  expect(false, identical(F_fact, H_named));
+  expect(false, identical(F_fact, H_fact));
+  expect(false, identical(F_fact, H_redirect));
+
+  expect(false, identical(F_redirect, F_new_lib));
+  expect(false, identical(F_redirect, F_named_lib));
+  expect(false, identical(F_redirect, F_fact_lib));
+  expect(true, identical(F_redirect, F_redirect_lib));
+  expect(false, identical(F_redirect, G_new_lib));
+  expect(false, identical(F_redirect, G_named_lib));
+  expect(false, identical(F_redirect, G_fact_lib));
+  expect(false, identical(F_redirect, G_redirect_lib));
+  expect(false, identical(F_redirect, H_new));
+  expect(false, identical(F_redirect, H_named));
+  expect(false, identical(F_redirect, H_fact));
+  expect(false, identical(F_redirect, H_redirect));
+}
+
+
+expect(expected, actual) {
+  if (expected != actual) throw 'Expected $expected, actual $actual';
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.strong.expect
new file mode 100644
index 0000000..2dc1f69
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.strong.expect
@@ -0,0 +1,147 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+static field dynamic H_new = #C1;
+static field dynamic H_named = #C2;
+static field dynamic H_fact = #C3;
+static field dynamic H_redirect = #C4;
+static field dynamic F_new = #C5;
+static field dynamic F_named = #C6;
+static field dynamic F_fact = #C7;
+static field dynamic F_redirect = #C8;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::F_new, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_new, self::H_new));
+  self::expect(false, core::identical(self::F_new, self::H_named));
+  self::expect(false, core::identical(self::F_new, self::H_fact));
+  self::expect(false, core::identical(self::F_new, self::H_redirect));
+  self::expect(false, core::identical(self::F_named, mai::F_new_lib));
+  self::expect(true, core::identical(self::F_named, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_named, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_named, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_named, self::H_new));
+  self::expect(false, core::identical(self::F_named, self::H_named));
+  self::expect(false, core::identical(self::F_named, self::H_fact));
+  self::expect(false, core::identical(self::F_named, self::H_redirect));
+  self::expect(false, core::identical(self::F_fact, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_fact, mai::F_named_lib));
+  self::expect(true, core::identical(self::F_fact, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_fact, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, self::H_new));
+  self::expect(false, core::identical(self::F_fact, self::H_named));
+  self::expect(false, core::identical(self::F_fact, self::H_fact));
+  self::expect(false, core::identical(self::F_fact, self::H_redirect));
+  self::expect(false, core::identical(self::F_redirect, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::F_fact_lib));
+  self::expect(true, core::identical(self::F_redirect, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, self::H_new));
+  self::expect(false, core::identical(self::F_redirect, self::H_named));
+  self::expect(false, core::identical(self::F_redirect, self::H_fact));
+  self::expect(false, core::identical(self::F_redirect, self::H_redirect));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<self::_#H#fact#tearOff::Y%>
+  return mai::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#new#tearOff::Y%>
+  return new mai::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<self::_#H#named#tearOff::Y%>
+  return new mai::A::named<self::_#H#named#tearOff::Y%>(a, b);
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::A::redirect]/*isLegacy*/;
+  constructor •() → mai::A<mai::A::T%>
+    : super core::Object::•()
+    ;
+  constructor named(mai::A::T% a, [core::int? b = #C9]) → mai::A<mai::A::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#new#tearOff::T%>
+    return new mai::A::•<mai::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(mai::A::_#named#tearOff::T% a, [core::int? b = #C9]) → mai::A<mai::A::_#named#tearOff::T%>
+    return new mai::A::named<mai::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(mai::A::fact::T% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::A::fact::T%>
+    return new mai::A::•<mai::A::fact::T%>();
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(mai::A::_#fact#tearOff::T% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::A::_#fact#tearOff::T%>
+    return mai::A::fact<mai::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → mai::A<mai::A::redirect::T%>
+    let dynamic #redirecting_factory = mai::A::• in let mai::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#redirect#tearOff::T%>
+    return new mai::A::•<mai::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib = #C5;
+static field dynamic F_named_lib = #C6;
+static field dynamic F_fact_lib = #C7;
+static field dynamic F_redirect_lib = #C8;
+static field dynamic G_new_lib = #C11;
+static field dynamic G_named_lib = #C12;
+static field dynamic G_fact_lib = #C13;
+static field dynamic G_redirect_lib = #C14;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#new#tearOff::Y%>
+  return new mai::A::•<mai::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<mai::_#F#named#tearOff::Y%>
+  return new mai::A::named<mai::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::_#F#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#new#tearOff::Y%>
+  return new mai::A::•<mai::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<mai::_#G#named#tearOff::Y%>
+  return new mai::A::named<mai::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::_#G#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#G#redirect#tearOff::Y%>();
+
+constants  {
+  #C1 = static-tearoff self::_#H#new#tearOff
+  #C2 = static-tearoff self::_#H#named#tearOff
+  #C3 = static-tearoff self::_#H#fact#tearOff
+  #C4 = static-tearoff self::_#H#redirect#tearOff
+  #C5 = static-tearoff mai::_#F#new#tearOff
+  #C6 = static-tearoff mai::_#F#named#tearOff
+  #C7 = static-tearoff mai::_#F#fact#tearOff
+  #C8 = static-tearoff mai::_#F#redirect#tearOff
+  #C9 = null
+  #C10 = 42
+  #C11 = static-tearoff mai::_#G#new#tearOff
+  #C12 = static-tearoff mai::_#G#named#tearOff
+  #C13 = static-tearoff mai::_#G#fact#tearOff
+  #C14 = static-tearoff mai::_#G#redirect#tearOff
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.strong.transformed.expect
new file mode 100644
index 0000000..a5b6962
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.strong.transformed.expect
@@ -0,0 +1,147 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+static field dynamic H_new = #C1;
+static field dynamic H_named = #C2;
+static field dynamic H_fact = #C3;
+static field dynamic H_redirect = #C4;
+static field dynamic F_new = #C5;
+static field dynamic F_named = #C6;
+static field dynamic F_fact = #C7;
+static field dynamic F_redirect = #C8;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::F_new, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_new, self::H_new));
+  self::expect(false, core::identical(self::F_new, self::H_named));
+  self::expect(false, core::identical(self::F_new, self::H_fact));
+  self::expect(false, core::identical(self::F_new, self::H_redirect));
+  self::expect(false, core::identical(self::F_named, mai::F_new_lib));
+  self::expect(true, core::identical(self::F_named, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_named, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_named, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_named, self::H_new));
+  self::expect(false, core::identical(self::F_named, self::H_named));
+  self::expect(false, core::identical(self::F_named, self::H_fact));
+  self::expect(false, core::identical(self::F_named, self::H_redirect));
+  self::expect(false, core::identical(self::F_fact, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_fact, mai::F_named_lib));
+  self::expect(true, core::identical(self::F_fact, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_fact, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, self::H_new));
+  self::expect(false, core::identical(self::F_fact, self::H_named));
+  self::expect(false, core::identical(self::F_fact, self::H_fact));
+  self::expect(false, core::identical(self::F_fact, self::H_redirect));
+  self::expect(false, core::identical(self::F_redirect, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::F_fact_lib));
+  self::expect(true, core::identical(self::F_redirect, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, self::H_new));
+  self::expect(false, core::identical(self::F_redirect, self::H_named));
+  self::expect(false, core::identical(self::F_redirect, self::H_fact));
+  self::expect(false, core::identical(self::F_redirect, self::H_redirect));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<self::_#H#fact#tearOff::Y%>
+  return mai::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#new#tearOff::Y%>
+  return new mai::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<self::_#H#named#tearOff::Y%>
+  return new mai::A::named<self::_#H#named#tearOff::Y%>(a, b);
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::A::redirect]/*isLegacy*/;
+  constructor •() → mai::A<mai::A::T%>
+    : super core::Object::•()
+    ;
+  constructor named(mai::A::T% a, [core::int? b = #C9]) → mai::A<mai::A::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#new#tearOff::T%>
+    return new mai::A::•<mai::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(mai::A::_#named#tearOff::T% a, [core::int? b = #C9]) → mai::A<mai::A::_#named#tearOff::T%>
+    return new mai::A::named<mai::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(mai::A::fact::T% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::A::fact::T%>
+    return new mai::A::•<mai::A::fact::T%>();
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(mai::A::_#fact#tearOff::T% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::A::_#fact#tearOff::T%>
+    return mai::A::fact<mai::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → mai::A<mai::A::redirect::T%>
+    let Never #redirecting_factory = mai::A::• in let mai::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#redirect#tearOff::T%>
+    return new mai::A::•<mai::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib = #C5;
+static field dynamic F_named_lib = #C6;
+static field dynamic F_fact_lib = #C7;
+static field dynamic F_redirect_lib = #C8;
+static field dynamic G_new_lib = #C11;
+static field dynamic G_named_lib = #C12;
+static field dynamic G_fact_lib = #C13;
+static field dynamic G_redirect_lib = #C14;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#new#tearOff::Y%>
+  return new mai::A::•<mai::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<mai::_#F#named#tearOff::Y%>
+  return new mai::A::named<mai::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::_#F#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#new#tearOff::Y%>
+  return new mai::A::•<mai::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<mai::_#G#named#tearOff::Y%>
+  return new mai::A::named<mai::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::_#G#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#G#redirect#tearOff::Y%>();
+
+constants  {
+  #C1 = static-tearoff self::_#H#new#tearOff
+  #C2 = static-tearoff self::_#H#named#tearOff
+  #C3 = static-tearoff self::_#H#fact#tearOff
+  #C4 = static-tearoff self::_#H#redirect#tearOff
+  #C5 = static-tearoff mai::_#F#new#tearOff
+  #C6 = static-tearoff mai::_#F#named#tearOff
+  #C7 = static-tearoff mai::_#F#fact#tearOff
+  #C8 = static-tearoff mai::_#F#redirect#tearOff
+  #C9 = null
+  #C10 = 42
+  #C11 = static-tearoff mai::_#G#new#tearOff
+  #C12 = static-tearoff mai::_#G#named#tearOff
+  #C13 = static-tearoff mai::_#G#fact#tearOff
+  #C14 = static-tearoff mai::_#G#redirect#tearOff
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.textual_outline.expect
new file mode 100644
index 0000000..4d2dfc1
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.textual_outline.expect
@@ -0,0 +1,12 @@
+import 'main_lib.dart';
+typedef H<X, Y> = A<Y>;
+dynamic H_new = H.new;
+dynamic H_named = H.named;
+dynamic H_fact = H.fact;
+dynamic H_redirect = H.redirect;
+dynamic F_new = F.new;
+dynamic F_named = F.named;
+dynamic F_fact = F.fact;
+dynamic F_redirect = F.redirect;
+main() {}
+expect(expected, actual) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.expect
new file mode 100644
index 0000000..2dc1f69
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.expect
@@ -0,0 +1,147 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+static field dynamic H_new = #C1;
+static field dynamic H_named = #C2;
+static field dynamic H_fact = #C3;
+static field dynamic H_redirect = #C4;
+static field dynamic F_new = #C5;
+static field dynamic F_named = #C6;
+static field dynamic F_fact = #C7;
+static field dynamic F_redirect = #C8;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::F_new, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_new, self::H_new));
+  self::expect(false, core::identical(self::F_new, self::H_named));
+  self::expect(false, core::identical(self::F_new, self::H_fact));
+  self::expect(false, core::identical(self::F_new, self::H_redirect));
+  self::expect(false, core::identical(self::F_named, mai::F_new_lib));
+  self::expect(true, core::identical(self::F_named, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_named, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_named, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_named, self::H_new));
+  self::expect(false, core::identical(self::F_named, self::H_named));
+  self::expect(false, core::identical(self::F_named, self::H_fact));
+  self::expect(false, core::identical(self::F_named, self::H_redirect));
+  self::expect(false, core::identical(self::F_fact, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_fact, mai::F_named_lib));
+  self::expect(true, core::identical(self::F_fact, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_fact, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, self::H_new));
+  self::expect(false, core::identical(self::F_fact, self::H_named));
+  self::expect(false, core::identical(self::F_fact, self::H_fact));
+  self::expect(false, core::identical(self::F_fact, self::H_redirect));
+  self::expect(false, core::identical(self::F_redirect, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::F_fact_lib));
+  self::expect(true, core::identical(self::F_redirect, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, self::H_new));
+  self::expect(false, core::identical(self::F_redirect, self::H_named));
+  self::expect(false, core::identical(self::F_redirect, self::H_fact));
+  self::expect(false, core::identical(self::F_redirect, self::H_redirect));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<self::_#H#fact#tearOff::Y%>
+  return mai::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#new#tearOff::Y%>
+  return new mai::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<self::_#H#named#tearOff::Y%>
+  return new mai::A::named<self::_#H#named#tearOff::Y%>(a, b);
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::A::redirect]/*isLegacy*/;
+  constructor •() → mai::A<mai::A::T%>
+    : super core::Object::•()
+    ;
+  constructor named(mai::A::T% a, [core::int? b = #C9]) → mai::A<mai::A::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#new#tearOff::T%>
+    return new mai::A::•<mai::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(mai::A::_#named#tearOff::T% a, [core::int? b = #C9]) → mai::A<mai::A::_#named#tearOff::T%>
+    return new mai::A::named<mai::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(mai::A::fact::T% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::A::fact::T%>
+    return new mai::A::•<mai::A::fact::T%>();
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(mai::A::_#fact#tearOff::T% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::A::_#fact#tearOff::T%>
+    return mai::A::fact<mai::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → mai::A<mai::A::redirect::T%>
+    let dynamic #redirecting_factory = mai::A::• in let mai::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#redirect#tearOff::T%>
+    return new mai::A::•<mai::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib = #C5;
+static field dynamic F_named_lib = #C6;
+static field dynamic F_fact_lib = #C7;
+static field dynamic F_redirect_lib = #C8;
+static field dynamic G_new_lib = #C11;
+static field dynamic G_named_lib = #C12;
+static field dynamic G_fact_lib = #C13;
+static field dynamic G_redirect_lib = #C14;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#new#tearOff::Y%>
+  return new mai::A::•<mai::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<mai::_#F#named#tearOff::Y%>
+  return new mai::A::named<mai::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::_#F#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#new#tearOff::Y%>
+  return new mai::A::•<mai::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<mai::_#G#named#tearOff::Y%>
+  return new mai::A::named<mai::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::_#G#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#G#redirect#tearOff::Y%>();
+
+constants  {
+  #C1 = static-tearoff self::_#H#new#tearOff
+  #C2 = static-tearoff self::_#H#named#tearOff
+  #C3 = static-tearoff self::_#H#fact#tearOff
+  #C4 = static-tearoff self::_#H#redirect#tearOff
+  #C5 = static-tearoff mai::_#F#new#tearOff
+  #C6 = static-tearoff mai::_#F#named#tearOff
+  #C7 = static-tearoff mai::_#F#fact#tearOff
+  #C8 = static-tearoff mai::_#F#redirect#tearOff
+  #C9 = null
+  #C10 = 42
+  #C11 = static-tearoff mai::_#G#new#tearOff
+  #C12 = static-tearoff mai::_#G#named#tearOff
+  #C13 = static-tearoff mai::_#G#fact#tearOff
+  #C14 = static-tearoff mai::_#G#redirect#tearOff
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.outline.expect
new file mode 100644
index 0000000..f1af048
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.outline.expect
@@ -0,0 +1,78 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+static field dynamic H_new;
+static field dynamic H_named;
+static field dynamic H_fact;
+static field dynamic H_redirect;
+static field dynamic F_new;
+static field dynamic F_named;
+static field dynamic F_fact;
+static field dynamic F_redirect;
+static method main() → dynamic
+  ;
+static method expect(dynamic expected, dynamic actual) → dynamic
+  ;
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b, core::int c}) → mai::A<self::_#H#fact#tearOff::Y%>
+  return mai::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#new#tearOff::Y%>
+  return new mai::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b]) → mai::A<self::_#H#named#tearOff::Y%>
+  return new mai::A::named<self::_#H#named#tearOff::Y%>(a, b);
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::A::redirect]/*isLegacy*/;
+  constructor •() → mai::A<mai::A::T%>
+    ;
+  constructor named(mai::A::T% a, [core::int? b]) → mai::A<mai::A::T%>
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#new#tearOff::T%>
+    return new mai::A::•<mai::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(mai::A::_#named#tearOff::T% a, [core::int? b]) → mai::A<mai::A::_#named#tearOff::T%>
+    return new mai::A::named<mai::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(mai::A::fact::T% a, {core::int? b, core::int c}) → mai::A<mai::A::fact::T%>
+    ;
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(mai::A::_#fact#tearOff::T% a, {core::int? b, core::int c}) → mai::A<mai::A::_#fact#tearOff::T%>
+    return mai::A::fact<mai::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → mai::A<mai::A::redirect::T%>
+    let dynamic #redirecting_factory = mai::A::• in let mai::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#redirect#tearOff::T%>
+    return new mai::A::•<mai::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib;
+static field dynamic F_named_lib;
+static field dynamic F_fact_lib;
+static field dynamic F_redirect_lib;
+static field dynamic G_new_lib;
+static field dynamic G_named_lib;
+static field dynamic G_fact_lib;
+static field dynamic G_redirect_lib;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#new#tearOff::Y%>
+  return new mai::A::•<mai::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#named#tearOff::Y% a, [core::int? b]) → mai::A<mai::_#F#named#tearOff::Y%>
+  return new mai::A::named<mai::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#fact#tearOff::Y% a, {core::int? b, core::int c}) → mai::A<mai::_#F#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#new#tearOff::Y%>
+  return new mai::A::•<mai::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#named#tearOff::Y% a, [core::int? b]) → mai::A<mai::_#G#named#tearOff::Y%>
+  return new mai::A::named<mai::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#fact#tearOff::Y% a, {core::int? b, core::int c}) → mai::A<mai::_#G#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#G#redirect#tearOff::Y%>();
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.transformed.expect
new file mode 100644
index 0000000..a5b6962
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main.dart.weak.transformed.expect
@@ -0,0 +1,147 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+static field dynamic H_new = #C1;
+static field dynamic H_named = #C2;
+static field dynamic H_fact = #C3;
+static field dynamic H_redirect = #C4;
+static field dynamic F_new = #C5;
+static field dynamic F_named = #C6;
+static field dynamic F_fact = #C7;
+static field dynamic F_redirect = #C8;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::F_new, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_new, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_new, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_new, self::H_new));
+  self::expect(false, core::identical(self::F_new, self::H_named));
+  self::expect(false, core::identical(self::F_new, self::H_fact));
+  self::expect(false, core::identical(self::F_new, self::H_redirect));
+  self::expect(false, core::identical(self::F_named, mai::F_new_lib));
+  self::expect(true, core::identical(self::F_named, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_named, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_named, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_named, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_named, self::H_new));
+  self::expect(false, core::identical(self::F_named, self::H_named));
+  self::expect(false, core::identical(self::F_named, self::H_fact));
+  self::expect(false, core::identical(self::F_named, self::H_redirect));
+  self::expect(false, core::identical(self::F_fact, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_fact, mai::F_named_lib));
+  self::expect(true, core::identical(self::F_fact, mai::F_fact_lib));
+  self::expect(false, core::identical(self::F_fact, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_fact, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, self::H_new));
+  self::expect(false, core::identical(self::F_fact, self::H_named));
+  self::expect(false, core::identical(self::F_fact, self::H_fact));
+  self::expect(false, core::identical(self::F_fact, self::H_redirect));
+  self::expect(false, core::identical(self::F_redirect, mai::F_new_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::F_named_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::F_fact_lib));
+  self::expect(true, core::identical(self::F_redirect, mai::F_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_new_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_named_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_fact_lib));
+  self::expect(false, core::identical(self::F_redirect, mai::G_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, self::H_new));
+  self::expect(false, core::identical(self::F_redirect, self::H_named));
+  self::expect(false, core::identical(self::F_redirect, self::H_fact));
+  self::expect(false, core::identical(self::F_redirect, self::H_redirect));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<self::_#H#fact#tearOff::Y%>
+  return mai::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<self::_#H#new#tearOff::Y%>
+  return new mai::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<self::_#H#named#tearOff::Y%>
+  return new mai::A::named<self::_#H#named#tearOff::Y%>(a, b);
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = mai::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[mai::A::redirect]/*isLegacy*/;
+  constructor •() → mai::A<mai::A::T%>
+    : super core::Object::•()
+    ;
+  constructor named(mai::A::T% a, [core::int? b = #C9]) → mai::A<mai::A::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#new#tearOff::T%>
+    return new mai::A::•<mai::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(mai::A::_#named#tearOff::T% a, [core::int? b = #C9]) → mai::A<mai::A::_#named#tearOff::T%>
+    return new mai::A::named<mai::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(mai::A::fact::T% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::A::fact::T%>
+    return new mai::A::•<mai::A::fact::T%>();
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(mai::A::_#fact#tearOff::T% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::A::_#fact#tearOff::T%>
+    return mai::A::fact<mai::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → mai::A<mai::A::redirect::T%>
+    let Never #redirecting_factory = mai::A::• in let mai::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → mai::A<mai::A::_#redirect#tearOff::T%>
+    return new mai::A::•<mai::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib = #C5;
+static field dynamic F_named_lib = #C6;
+static field dynamic F_fact_lib = #C7;
+static field dynamic F_redirect_lib = #C8;
+static field dynamic G_new_lib = #C11;
+static field dynamic G_named_lib = #C12;
+static field dynamic G_fact_lib = #C13;
+static field dynamic G_redirect_lib = #C14;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#new#tearOff::Y%>
+  return new mai::A::•<mai::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<mai::_#F#named#tearOff::Y%>
+  return new mai::A::named<mai::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#F#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::_#F#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#F#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#new#tearOff::Y%>
+  return new mai::A::•<mai::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#named#tearOff::Y% a, [core::int? b = #C9]) → mai::A<mai::_#G#named#tearOff::Y%>
+  return new mai::A::named<mai::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(mai::_#G#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → mai::A<mai::_#G#fact#tearOff::Y%>
+  return mai::A::fact<mai::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → mai::A<mai::_#G#redirect#tearOff::Y%>
+  return mai::A::_#redirect#tearOff<mai::_#G#redirect#tearOff::Y%>();
+
+constants  {
+  #C1 = static-tearoff self::_#H#new#tearOff
+  #C2 = static-tearoff self::_#H#named#tearOff
+  #C3 = static-tearoff self::_#H#fact#tearOff
+  #C4 = static-tearoff self::_#H#redirect#tearOff
+  #C5 = static-tearoff mai::_#F#new#tearOff
+  #C6 = static-tearoff mai::_#F#named#tearOff
+  #C7 = static-tearoff mai::_#F#fact#tearOff
+  #C8 = static-tearoff mai::_#F#redirect#tearOff
+  #C9 = null
+  #C10 = 42
+  #C11 = static-tearoff mai::_#G#new#tearOff
+  #C12 = static-tearoff mai::_#G#named#tearOff
+  #C13 = static-tearoff mai::_#G#fact#tearOff
+  #C14 = static-tearoff mai::_#G#redirect#tearOff
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main_lib.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main_lib.dart
new file mode 100644
index 0000000..a57b86b
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/main_lib.dart
@@ -0,0 +1,23 @@
+// 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 A<T> {
+  A();
+  A.named(T a, [int? b]);
+  factory A.fact(T a, {int? b, int c = 42}) => new A();
+  factory A.redirect() = A;
+}
+
+typedef F<X, Y> = A<Y>;
+typedef G<X, Y> = A<Y>;
+
+dynamic F_new_lib = F.new;
+dynamic F_named_lib = F.named;
+dynamic F_fact_lib = F.fact;
+dynamic F_redirect_lib = F.redirect;
+
+dynamic G_new_lib = G.new;
+dynamic G_named_lib = G.named;
+dynamic G_fact_lib = G.fact;
+dynamic G_redirect_lib = G.redirect;
\ No newline at end of file
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/test.options b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/test.options
new file mode 100644
index 0000000..bfe6dc8
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_from_dill/test.options
@@ -0,0 +1 @@
+main_lib.dart
\ No newline at end of file
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart
new file mode 100644
index 0000000..8bd470e
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart
@@ -0,0 +1,78 @@
+// 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 'typedef_identical_lib.dart';
+
+typedef H<X, Y> = A<Y>;
+
+// TODO(johnniwinther): Use 'var' here when dependency on inferred parameter
+// types is handled.
+dynamic H_new = H.new;
+dynamic H_named = H.named;
+dynamic H_fact = H.fact;
+dynamic H_redirect = H.redirect;
+
+dynamic F_new = F.new;
+dynamic F_named = F.named;
+dynamic F_fact = F.fact;
+dynamic F_redirect = F.redirect;
+
+main() {
+  expect(true, identical(F_new, F_new_lib));
+  expect(false, identical(F_new, F_named_lib));
+  expect(false, identical(F_new, F_fact_lib));
+  expect(false, identical(F_new, F_redirect_lib));
+  expect(false, identical(F_new, G_new_lib));
+  expect(false, identical(F_new, G_named_lib));
+  expect(false, identical(F_new, G_fact_lib));
+  expect(false, identical(F_new, G_redirect_lib));
+  expect(false, identical(F_new, H_new));
+  expect(false, identical(F_new, H_named));
+  expect(false, identical(F_new, H_fact));
+  expect(false, identical(F_new, H_redirect));
+
+  expect(false, identical(F_named, F_new_lib));
+  expect(true, identical(F_named, F_named_lib));
+  expect(false, identical(F_named, F_fact_lib));
+  expect(false, identical(F_named, F_redirect_lib));
+  expect(false, identical(F_named, G_new_lib));
+  expect(false, identical(F_named, G_named_lib));
+  expect(false, identical(F_named, G_fact_lib));
+  expect(false, identical(F_named, G_redirect_lib));
+  expect(false, identical(F_named, H_new));
+  expect(false, identical(F_named, H_named));
+  expect(false, identical(F_named, H_fact));
+  expect(false, identical(F_named, H_redirect));
+
+  expect(false, identical(F_fact, F_new_lib));
+  expect(false, identical(F_fact, F_named_lib));
+  expect(true, identical(F_fact, F_fact_lib));
+  expect(false, identical(F_fact, F_redirect_lib));
+  expect(false, identical(F_fact, G_new_lib));
+  expect(false, identical(F_fact, G_named_lib));
+  expect(false, identical(F_fact, G_fact_lib));
+  expect(false, identical(F_fact, G_redirect_lib));
+  expect(false, identical(F_fact, H_new));
+  expect(false, identical(F_fact, H_named));
+  expect(false, identical(F_fact, H_fact));
+  expect(false, identical(F_fact, H_redirect));
+
+  expect(false, identical(F_redirect, F_new_lib));
+  expect(false, identical(F_redirect, F_named_lib));
+  expect(false, identical(F_redirect, F_fact_lib));
+  expect(true, identical(F_redirect, F_redirect_lib));
+  expect(false, identical(F_redirect, G_new_lib));
+  expect(false, identical(F_redirect, G_named_lib));
+  expect(false, identical(F_redirect, G_fact_lib));
+  expect(false, identical(F_redirect, G_redirect_lib));
+  expect(false, identical(F_redirect, H_new));
+  expect(false, identical(F_redirect, H_named));
+  expect(false, identical(F_redirect, H_fact));
+  expect(false, identical(F_redirect, H_redirect));
+}
+
+
+expect(expected, actual) {
+  if (expected != actual) throw 'Expected $expected, actual $actual';
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.strong.expect
new file mode 100644
index 0000000..98b8997
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.strong.expect
@@ -0,0 +1,147 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "typedef_identical_lib.dart" as typ;
+
+import "org-dartlang-testcase:///typedef_identical_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+static field dynamic H_new = #C1;
+static field dynamic H_named = #C2;
+static field dynamic H_fact = #C3;
+static field dynamic H_redirect = #C4;
+static field dynamic F_new = #C5;
+static field dynamic F_named = #C6;
+static field dynamic F_fact = #C7;
+static field dynamic F_redirect = #C8;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::F_new, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_new, self::H_new));
+  self::expect(false, core::identical(self::F_new, self::H_named));
+  self::expect(false, core::identical(self::F_new, self::H_fact));
+  self::expect(false, core::identical(self::F_new, self::H_redirect));
+  self::expect(false, core::identical(self::F_named, typ::F_new_lib));
+  self::expect(true, core::identical(self::F_named, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_named, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_named, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_named, self::H_new));
+  self::expect(false, core::identical(self::F_named, self::H_named));
+  self::expect(false, core::identical(self::F_named, self::H_fact));
+  self::expect(false, core::identical(self::F_named, self::H_redirect));
+  self::expect(false, core::identical(self::F_fact, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_fact, typ::F_named_lib));
+  self::expect(true, core::identical(self::F_fact, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_fact, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, self::H_new));
+  self::expect(false, core::identical(self::F_fact, self::H_named));
+  self::expect(false, core::identical(self::F_fact, self::H_fact));
+  self::expect(false, core::identical(self::F_fact, self::H_redirect));
+  self::expect(false, core::identical(self::F_redirect, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::F_fact_lib));
+  self::expect(true, core::identical(self::F_redirect, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, self::H_new));
+  self::expect(false, core::identical(self::F_redirect, self::H_named));
+  self::expect(false, core::identical(self::F_redirect, self::H_fact));
+  self::expect(false, core::identical(self::F_redirect, self::H_redirect));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#new#tearOff::Y%>
+  return new typ::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<self::_#H#named#tearOff::Y%>
+  return new typ::A::named<self::_#H#named#tearOff::Y%>(a, b);
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<self::_#H#fact#tearOff::Y%>
+  return typ::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+
+library /*isNonNullableByDefault*/;
+import self as typ;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[typ::A::redirect]/*isLegacy*/;
+  constructor •() → typ::A<typ::A::T%>
+    : super core::Object::•()
+    ;
+  constructor named(typ::A::T% a, [core::int? b = #C9]) → typ::A<typ::A::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#new#tearOff::T%>
+    return new typ::A::•<typ::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(typ::A::_#named#tearOff::T% a, [core::int? b = #C9]) → typ::A<typ::A::_#named#tearOff::T%>
+    return new typ::A::named<typ::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(typ::A::fact::T% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::A::fact::T%>
+    return new typ::A::•<typ::A::fact::T%>();
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(typ::A::_#fact#tearOff::T% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::A::_#fact#tearOff::T%>
+    return typ::A::fact<typ::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → typ::A<typ::A::redirect::T%>
+    let dynamic #redirecting_factory = typ::A::• in let typ::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#redirect#tearOff::T%>
+    return new typ::A::•<typ::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib = #C5;
+static field dynamic F_named_lib = #C6;
+static field dynamic F_fact_lib = #C7;
+static field dynamic F_redirect_lib = #C8;
+static field dynamic G_new_lib = #C11;
+static field dynamic G_named_lib = #C12;
+static field dynamic G_fact_lib = #C13;
+static field dynamic G_redirect_lib = #C14;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#new#tearOff::Y%>
+  return new typ::A::•<typ::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<typ::_#F#named#tearOff::Y%>
+  return new typ::A::named<typ::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::_#F#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#new#tearOff::Y%>
+  return new typ::A::•<typ::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<typ::_#G#named#tearOff::Y%>
+  return new typ::A::named<typ::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::_#G#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#G#redirect#tearOff::Y%>();
+
+constants  {
+  #C1 = static-tearoff self::_#H#new#tearOff
+  #C2 = static-tearoff self::_#H#named#tearOff
+  #C3 = static-tearoff self::_#H#fact#tearOff
+  #C4 = static-tearoff self::_#H#redirect#tearOff
+  #C5 = static-tearoff typ::_#F#new#tearOff
+  #C6 = static-tearoff typ::_#F#named#tearOff
+  #C7 = static-tearoff typ::_#F#fact#tearOff
+  #C8 = static-tearoff typ::_#F#redirect#tearOff
+  #C9 = null
+  #C10 = 42
+  #C11 = static-tearoff typ::_#G#new#tearOff
+  #C12 = static-tearoff typ::_#G#named#tearOff
+  #C13 = static-tearoff typ::_#G#fact#tearOff
+  #C14 = static-tearoff typ::_#G#redirect#tearOff
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.strong.transformed.expect
new file mode 100644
index 0000000..b956c9c
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.strong.transformed.expect
@@ -0,0 +1,147 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "typedef_identical_lib.dart" as typ;
+
+import "org-dartlang-testcase:///typedef_identical_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+static field dynamic H_new = #C1;
+static field dynamic H_named = #C2;
+static field dynamic H_fact = #C3;
+static field dynamic H_redirect = #C4;
+static field dynamic F_new = #C5;
+static field dynamic F_named = #C6;
+static field dynamic F_fact = #C7;
+static field dynamic F_redirect = #C8;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::F_new, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_new, self::H_new));
+  self::expect(false, core::identical(self::F_new, self::H_named));
+  self::expect(false, core::identical(self::F_new, self::H_fact));
+  self::expect(false, core::identical(self::F_new, self::H_redirect));
+  self::expect(false, core::identical(self::F_named, typ::F_new_lib));
+  self::expect(true, core::identical(self::F_named, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_named, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_named, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_named, self::H_new));
+  self::expect(false, core::identical(self::F_named, self::H_named));
+  self::expect(false, core::identical(self::F_named, self::H_fact));
+  self::expect(false, core::identical(self::F_named, self::H_redirect));
+  self::expect(false, core::identical(self::F_fact, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_fact, typ::F_named_lib));
+  self::expect(true, core::identical(self::F_fact, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_fact, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, self::H_new));
+  self::expect(false, core::identical(self::F_fact, self::H_named));
+  self::expect(false, core::identical(self::F_fact, self::H_fact));
+  self::expect(false, core::identical(self::F_fact, self::H_redirect));
+  self::expect(false, core::identical(self::F_redirect, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::F_fact_lib));
+  self::expect(true, core::identical(self::F_redirect, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, self::H_new));
+  self::expect(false, core::identical(self::F_redirect, self::H_named));
+  self::expect(false, core::identical(self::F_redirect, self::H_fact));
+  self::expect(false, core::identical(self::F_redirect, self::H_redirect));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#new#tearOff::Y%>
+  return new typ::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<self::_#H#named#tearOff::Y%>
+  return new typ::A::named<self::_#H#named#tearOff::Y%>(a, b);
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<self::_#H#fact#tearOff::Y%>
+  return typ::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+
+library /*isNonNullableByDefault*/;
+import self as typ;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[typ::A::redirect]/*isLegacy*/;
+  constructor •() → typ::A<typ::A::T%>
+    : super core::Object::•()
+    ;
+  constructor named(typ::A::T% a, [core::int? b = #C9]) → typ::A<typ::A::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#new#tearOff::T%>
+    return new typ::A::•<typ::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(typ::A::_#named#tearOff::T% a, [core::int? b = #C9]) → typ::A<typ::A::_#named#tearOff::T%>
+    return new typ::A::named<typ::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(typ::A::fact::T% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::A::fact::T%>
+    return new typ::A::•<typ::A::fact::T%>();
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(typ::A::_#fact#tearOff::T% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::A::_#fact#tearOff::T%>
+    return typ::A::fact<typ::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → typ::A<typ::A::redirect::T%>
+    let Never #redirecting_factory = typ::A::• in let typ::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#redirect#tearOff::T%>
+    return new typ::A::•<typ::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib = #C5;
+static field dynamic F_named_lib = #C6;
+static field dynamic F_fact_lib = #C7;
+static field dynamic F_redirect_lib = #C8;
+static field dynamic G_new_lib = #C11;
+static field dynamic G_named_lib = #C12;
+static field dynamic G_fact_lib = #C13;
+static field dynamic G_redirect_lib = #C14;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#new#tearOff::Y%>
+  return new typ::A::•<typ::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<typ::_#F#named#tearOff::Y%>
+  return new typ::A::named<typ::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::_#F#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#new#tearOff::Y%>
+  return new typ::A::•<typ::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<typ::_#G#named#tearOff::Y%>
+  return new typ::A::named<typ::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::_#G#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#G#redirect#tearOff::Y%>();
+
+constants  {
+  #C1 = static-tearoff self::_#H#new#tearOff
+  #C2 = static-tearoff self::_#H#named#tearOff
+  #C3 = static-tearoff self::_#H#fact#tearOff
+  #C4 = static-tearoff self::_#H#redirect#tearOff
+  #C5 = static-tearoff typ::_#F#new#tearOff
+  #C6 = static-tearoff typ::_#F#named#tearOff
+  #C7 = static-tearoff typ::_#F#fact#tearOff
+  #C8 = static-tearoff typ::_#F#redirect#tearOff
+  #C9 = null
+  #C10 = 42
+  #C11 = static-tearoff typ::_#G#new#tearOff
+  #C12 = static-tearoff typ::_#G#named#tearOff
+  #C13 = static-tearoff typ::_#G#fact#tearOff
+  #C14 = static-tearoff typ::_#G#redirect#tearOff
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.textual_outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.textual_outline.expect
new file mode 100644
index 0000000..0f1bd66
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.textual_outline.expect
@@ -0,0 +1,12 @@
+import 'typedef_identical_lib.dart';
+typedef H<X, Y> = A<Y>;
+dynamic H_new = H.new;
+dynamic H_named = H.named;
+dynamic H_fact = H.fact;
+dynamic H_redirect = H.redirect;
+dynamic F_new = F.new;
+dynamic F_named = F.named;
+dynamic F_fact = F.fact;
+dynamic F_redirect = F.redirect;
+main() {}
+expect(expected, actual) {}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.expect
new file mode 100644
index 0000000..98b8997
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.expect
@@ -0,0 +1,147 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "typedef_identical_lib.dart" as typ;
+
+import "org-dartlang-testcase:///typedef_identical_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+static field dynamic H_new = #C1;
+static field dynamic H_named = #C2;
+static field dynamic H_fact = #C3;
+static field dynamic H_redirect = #C4;
+static field dynamic F_new = #C5;
+static field dynamic F_named = #C6;
+static field dynamic F_fact = #C7;
+static field dynamic F_redirect = #C8;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::F_new, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_new, self::H_new));
+  self::expect(false, core::identical(self::F_new, self::H_named));
+  self::expect(false, core::identical(self::F_new, self::H_fact));
+  self::expect(false, core::identical(self::F_new, self::H_redirect));
+  self::expect(false, core::identical(self::F_named, typ::F_new_lib));
+  self::expect(true, core::identical(self::F_named, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_named, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_named, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_named, self::H_new));
+  self::expect(false, core::identical(self::F_named, self::H_named));
+  self::expect(false, core::identical(self::F_named, self::H_fact));
+  self::expect(false, core::identical(self::F_named, self::H_redirect));
+  self::expect(false, core::identical(self::F_fact, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_fact, typ::F_named_lib));
+  self::expect(true, core::identical(self::F_fact, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_fact, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, self::H_new));
+  self::expect(false, core::identical(self::F_fact, self::H_named));
+  self::expect(false, core::identical(self::F_fact, self::H_fact));
+  self::expect(false, core::identical(self::F_fact, self::H_redirect));
+  self::expect(false, core::identical(self::F_redirect, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::F_fact_lib));
+  self::expect(true, core::identical(self::F_redirect, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, self::H_new));
+  self::expect(false, core::identical(self::F_redirect, self::H_named));
+  self::expect(false, core::identical(self::F_redirect, self::H_fact));
+  self::expect(false, core::identical(self::F_redirect, self::H_redirect));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#new#tearOff::Y%>
+  return new typ::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<self::_#H#named#tearOff::Y%>
+  return new typ::A::named<self::_#H#named#tearOff::Y%>(a, b);
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<self::_#H#fact#tearOff::Y%>
+  return typ::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+
+library /*isNonNullableByDefault*/;
+import self as typ;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[typ::A::redirect]/*isLegacy*/;
+  constructor •() → typ::A<typ::A::T%>
+    : super core::Object::•()
+    ;
+  constructor named(typ::A::T% a, [core::int? b = #C9]) → typ::A<typ::A::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#new#tearOff::T%>
+    return new typ::A::•<typ::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(typ::A::_#named#tearOff::T% a, [core::int? b = #C9]) → typ::A<typ::A::_#named#tearOff::T%>
+    return new typ::A::named<typ::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(typ::A::fact::T% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::A::fact::T%>
+    return new typ::A::•<typ::A::fact::T%>();
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(typ::A::_#fact#tearOff::T% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::A::_#fact#tearOff::T%>
+    return typ::A::fact<typ::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → typ::A<typ::A::redirect::T%>
+    let dynamic #redirecting_factory = typ::A::• in let typ::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#redirect#tearOff::T%>
+    return new typ::A::•<typ::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib = #C5;
+static field dynamic F_named_lib = #C6;
+static field dynamic F_fact_lib = #C7;
+static field dynamic F_redirect_lib = #C8;
+static field dynamic G_new_lib = #C11;
+static field dynamic G_named_lib = #C12;
+static field dynamic G_fact_lib = #C13;
+static field dynamic G_redirect_lib = #C14;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#new#tearOff::Y%>
+  return new typ::A::•<typ::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<typ::_#F#named#tearOff::Y%>
+  return new typ::A::named<typ::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::_#F#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#new#tearOff::Y%>
+  return new typ::A::•<typ::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<typ::_#G#named#tearOff::Y%>
+  return new typ::A::named<typ::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::_#G#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#G#redirect#tearOff::Y%>();
+
+constants  {
+  #C1 = static-tearoff self::_#H#new#tearOff
+  #C2 = static-tearoff self::_#H#named#tearOff
+  #C3 = static-tearoff self::_#H#fact#tearOff
+  #C4 = static-tearoff self::_#H#redirect#tearOff
+  #C5 = static-tearoff typ::_#F#new#tearOff
+  #C6 = static-tearoff typ::_#F#named#tearOff
+  #C7 = static-tearoff typ::_#F#fact#tearOff
+  #C8 = static-tearoff typ::_#F#redirect#tearOff
+  #C9 = null
+  #C10 = 42
+  #C11 = static-tearoff typ::_#G#new#tearOff
+  #C12 = static-tearoff typ::_#G#named#tearOff
+  #C13 = static-tearoff typ::_#G#fact#tearOff
+  #C14 = static-tearoff typ::_#G#redirect#tearOff
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.outline.expect
new file mode 100644
index 0000000..7f142cf
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.outline.expect
@@ -0,0 +1,78 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "typedef_identical_lib.dart" as typ;
+
+import "org-dartlang-testcase:///typedef_identical_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+static field dynamic H_new;
+static field dynamic H_named;
+static field dynamic H_fact;
+static field dynamic H_redirect;
+static field dynamic F_new;
+static field dynamic F_named;
+static field dynamic F_fact;
+static field dynamic F_redirect;
+static method main() → dynamic
+  ;
+static method expect(dynamic expected, dynamic actual) → dynamic
+  ;
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#new#tearOff::Y%>
+  return new typ::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b]) → typ::A<self::_#H#named#tearOff::Y%>
+  return new typ::A::named<self::_#H#named#tearOff::Y%>(a, b);
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b, core::int c}) → typ::A<self::_#H#fact#tearOff::Y%>
+  return typ::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+
+library /*isNonNullableByDefault*/;
+import self as typ;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[typ::A::redirect]/*isLegacy*/;
+  constructor •() → typ::A<typ::A::T%>
+    ;
+  constructor named(typ::A::T% a, [core::int? b]) → typ::A<typ::A::T%>
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#new#tearOff::T%>
+    return new typ::A::•<typ::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(typ::A::_#named#tearOff::T% a, [core::int? b]) → typ::A<typ::A::_#named#tearOff::T%>
+    return new typ::A::named<typ::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(typ::A::fact::T% a, {core::int? b, core::int c}) → typ::A<typ::A::fact::T%>
+    ;
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(typ::A::_#fact#tearOff::T% a, {core::int? b, core::int c}) → typ::A<typ::A::_#fact#tearOff::T%>
+    return typ::A::fact<typ::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → typ::A<typ::A::redirect::T%>
+    let dynamic #redirecting_factory = typ::A::• in let typ::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#redirect#tearOff::T%>
+    return new typ::A::•<typ::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib;
+static field dynamic F_named_lib;
+static field dynamic F_fact_lib;
+static field dynamic F_redirect_lib;
+static field dynamic G_new_lib;
+static field dynamic G_named_lib;
+static field dynamic G_fact_lib;
+static field dynamic G_redirect_lib;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#new#tearOff::Y%>
+  return new typ::A::•<typ::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#named#tearOff::Y% a, [core::int? b]) → typ::A<typ::_#F#named#tearOff::Y%>
+  return new typ::A::named<typ::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#fact#tearOff::Y% a, {core::int? b, core::int c}) → typ::A<typ::_#F#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#new#tearOff::Y%>
+  return new typ::A::•<typ::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#named#tearOff::Y% a, [core::int? b]) → typ::A<typ::_#G#named#tearOff::Y%>
+  return new typ::A::named<typ::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#fact#tearOff::Y% a, {core::int? b, core::int c}) → typ::A<typ::_#G#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#G#redirect#tearOff::Y%>();
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.transformed.expect
new file mode 100644
index 0000000..b956c9c
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical.dart.weak.transformed.expect
@@ -0,0 +1,147 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "typedef_identical_lib.dart" as typ;
+
+import "org-dartlang-testcase:///typedef_identical_lib.dart";
+
+typedef H<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+static field dynamic H_new = #C1;
+static field dynamic H_named = #C2;
+static field dynamic H_fact = #C3;
+static field dynamic H_redirect = #C4;
+static field dynamic F_new = #C5;
+static field dynamic F_named = #C6;
+static field dynamic F_fact = #C7;
+static field dynamic F_redirect = #C8;
+static method main() → dynamic {
+  self::expect(true, core::identical(self::F_new, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_new, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_new, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_new, self::H_new));
+  self::expect(false, core::identical(self::F_new, self::H_named));
+  self::expect(false, core::identical(self::F_new, self::H_fact));
+  self::expect(false, core::identical(self::F_new, self::H_redirect));
+  self::expect(false, core::identical(self::F_named, typ::F_new_lib));
+  self::expect(true, core::identical(self::F_named, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_named, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_named, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_named, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_named, self::H_new));
+  self::expect(false, core::identical(self::F_named, self::H_named));
+  self::expect(false, core::identical(self::F_named, self::H_fact));
+  self::expect(false, core::identical(self::F_named, self::H_redirect));
+  self::expect(false, core::identical(self::F_fact, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_fact, typ::F_named_lib));
+  self::expect(true, core::identical(self::F_fact, typ::F_fact_lib));
+  self::expect(false, core::identical(self::F_fact, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_fact, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_fact, self::H_new));
+  self::expect(false, core::identical(self::F_fact, self::H_named));
+  self::expect(false, core::identical(self::F_fact, self::H_fact));
+  self::expect(false, core::identical(self::F_fact, self::H_redirect));
+  self::expect(false, core::identical(self::F_redirect, typ::F_new_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::F_named_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::F_fact_lib));
+  self::expect(true, core::identical(self::F_redirect, typ::F_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_new_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_named_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_fact_lib));
+  self::expect(false, core::identical(self::F_redirect, typ::G_redirect_lib));
+  self::expect(false, core::identical(self::F_redirect, self::H_new));
+  self::expect(false, core::identical(self::F_redirect, self::H_named));
+  self::expect(false, core::identical(self::F_redirect, self::H_fact));
+  self::expect(false, core::identical(self::F_redirect, self::H_redirect));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
+    throw "Expected ${expected}, actual ${actual}";
+}
+static method _#H#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#new#tearOff::Y%>
+  return new typ::A::•<self::_#H#new#tearOff::Y%>();
+static method _#H#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<self::_#H#named#tearOff::Y%>
+  return new typ::A::named<self::_#H#named#tearOff::Y%>(a, b);
+static method _#H#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::_#H#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<self::_#H#fact#tearOff::Y%>
+  return typ::A::fact<self::_#H#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#H#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<self::_#H#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<self::_#H#redirect#tearOff::Y%>();
+
+library /*isNonNullableByDefault*/;
+import self as typ;
+import "dart:core" as core;
+
+typedef F<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+typedef G<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic> = typ::A<Y%>;
+class A<T extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[typ::A::redirect]/*isLegacy*/;
+  constructor •() → typ::A<typ::A::T%>
+    : super core::Object::•()
+    ;
+  constructor named(typ::A::T% a, [core::int? b = #C9]) → typ::A<typ::A::T%>
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#new#tearOff::T%>
+    return new typ::A::•<typ::A::_#new#tearOff::T%>();
+  static method _#named#tearOff<T extends core::Object? = dynamic>(typ::A::_#named#tearOff::T% a, [core::int? b = #C9]) → typ::A<typ::A::_#named#tearOff::T%>
+    return new typ::A::named<typ::A::_#named#tearOff::T%>(a, b);
+  static factory fact<T extends core::Object? = dynamic>(typ::A::fact::T% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::A::fact::T%>
+    return new typ::A::•<typ::A::fact::T%>();
+  static method _#fact#tearOff<T extends core::Object? = dynamic>(typ::A::_#fact#tearOff::T% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::A::_#fact#tearOff::T%>
+    return typ::A::fact<typ::A::_#fact#tearOff::T%>(a, b: b, c: c);
+  static factory redirect<T extends core::Object? = dynamic>() → typ::A<typ::A::redirect::T%>
+    let Never #redirecting_factory = typ::A::• in let typ::A::redirect::T% #typeArg0 = null in invalid-expression;
+  static method _#redirect#tearOff<T extends core::Object? = dynamic>() → typ::A<typ::A::_#redirect#tearOff::T%>
+    return new typ::A::•<typ::A::_#redirect#tearOff::T%>();
+}
+static field dynamic F_new_lib = #C5;
+static field dynamic F_named_lib = #C6;
+static field dynamic F_fact_lib = #C7;
+static field dynamic F_redirect_lib = #C8;
+static field dynamic G_new_lib = #C11;
+static field dynamic G_named_lib = #C12;
+static field dynamic G_fact_lib = #C13;
+static field dynamic G_redirect_lib = #C14;
+static method _#F#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#new#tearOff::Y%>
+  return new typ::A::•<typ::_#F#new#tearOff::Y%>();
+static method _#F#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<typ::_#F#named#tearOff::Y%>
+  return new typ::A::named<typ::_#F#named#tearOff::Y%>(a, b);
+static method _#F#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#F#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::_#F#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#F#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#F#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#F#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#F#redirect#tearOff::Y%>();
+static method _#G#new#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#new#tearOff::Y%>
+  return new typ::A::•<typ::_#G#new#tearOff::Y%>();
+static method _#G#named#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#named#tearOff::Y% a, [core::int? b = #C9]) → typ::A<typ::_#G#named#tearOff::Y%>
+  return new typ::A::named<typ::_#G#named#tearOff::Y%>(a, b);
+static method _#G#fact#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(typ::_#G#fact#tearOff::Y% a, {core::int? b = #C9, core::int c = #C10}) → typ::A<typ::_#G#fact#tearOff::Y%>
+  return typ::A::fact<typ::_#G#fact#tearOff::Y%>(a, b: b, c: c);
+static method _#G#redirect#tearOff<unrelated X extends core::Object? = dynamic, Y extends core::Object? = dynamic>() → typ::A<typ::_#G#redirect#tearOff::Y%>
+  return typ::A::_#redirect#tearOff<typ::_#G#redirect#tearOff::Y%>();
+
+constants  {
+  #C1 = static-tearoff self::_#H#new#tearOff
+  #C2 = static-tearoff self::_#H#named#tearOff
+  #C3 = static-tearoff self::_#H#fact#tearOff
+  #C4 = static-tearoff self::_#H#redirect#tearOff
+  #C5 = static-tearoff typ::_#F#new#tearOff
+  #C6 = static-tearoff typ::_#F#named#tearOff
+  #C7 = static-tearoff typ::_#F#fact#tearOff
+  #C8 = static-tearoff typ::_#F#redirect#tearOff
+  #C9 = null
+  #C10 = 42
+  #C11 = static-tearoff typ::_#G#new#tearOff
+  #C12 = static-tearoff typ::_#G#named#tearOff
+  #C13 = static-tearoff typ::_#G#fact#tearOff
+  #C14 = static-tearoff typ::_#G#redirect#tearOff
+}
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical_lib.dart b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical_lib.dart
new file mode 100644
index 0000000..a57b86b
--- /dev/null
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_identical_lib.dart
@@ -0,0 +1,23 @@
+// 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 A<T> {
+  A();
+  A.named(T a, [int? b]);
+  factory A.fact(T a, {int? b, int c = 42}) => new A();
+  factory A.redirect() = A;
+}
+
+typedef F<X, Y> = A<Y>;
+typedef G<X, Y> = A<Y>;
+
+dynamic F_new_lib = F.new;
+dynamic F_named_lib = F.named;
+dynamic F_fact_lib = F.fact;
+dynamic F_redirect_lib = F.redirect;
+
+dynamic G_new_lib = G.new;
+dynamic G_named_lib = G.named;
+dynamic G_fact_lib = G.fact;
+dynamic G_redirect_lib = G.redirect;
\ No newline at end of file
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.strong.expect
index 08a9470..f68f95f 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.strong.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.strong.expect
@@ -143,14 +143,14 @@
        ^" in f2a{<inapplicable>}.<core::num>(0);
     f2a<core::String>(){() → self::A};
   };
-  dynamic f2b = #C3;
+  dynamic f2b = #C2;
   dynamic c2b = f2b{dynamic}.call();
   self::expect(true, c2b is{ForNonNullableByDefault} self::A);
   dynamic c2c = f2b{dynamic}.call<core::int>();
   self::expect(true, c2c is{ForNonNullableByDefault} self::A);
   self::throws(() → dynamic => f2b{dynamic}.call(0));
   self::throws(() → dynamic => f2b{dynamic}.call<core::String>());
-  () → self::B<core::String> f3a = #C5;
+  () → self::B<core::String> f3a = #C4;
   self::B<core::String> c3a = f3a(){() → self::B<core::String>};
   self::expect(true, c3a is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3a is{ForNonNullableByDefault} self::B<core::int>);
@@ -165,7 +165,7 @@
     f3a<String>(); // error
        ^" in f3a{<inapplicable>}.<core::String>();
   };
-  dynamic f3b = #C5;
+  dynamic f3b = #C4;
   dynamic c3b = f3b{dynamic}.call();
   self::expect(true, c3b is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3b is{ForNonNullableByDefault} self::B<core::int>);
@@ -173,7 +173,7 @@
   self::expect("", c3a.{self::B::field2}{core::String});
   self::throws(() → dynamic => f3b{dynamic}.call(0));
   self::throws(() → dynamic => f3b{dynamic}.call<core::String>());
-  (core::int) → self::B<core::String> f3c = #C7;
+  (core::int) → self::B<core::String> f3c = #C6;
   self::B<core::String> c3c = f3c(42){(core::int) → self::B<core::String>};
   self::expect(true, c3c is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3c is{ForNonNullableByDefault} self::B<core::int>);
@@ -191,7 +191,7 @@
     f3c<String>(0); // error
        ^" in f3c{<inapplicable>}.<core::String>(0);
   };
-  dynamic f3d = #C7;
+  dynamic f3d = #C6;
   dynamic c3d = f3d{dynamic}.call(42);
   self::expect(true, c3d is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3d is{ForNonNullableByDefault} self::B<core::int>);
@@ -200,7 +200,7 @@
   self::throws(() → dynamic => f3d{dynamic}.call());
   self::throws(() → dynamic => f3d{dynamic}.call(0, 0));
   self::throws(() → dynamic => f3d{dynamic}.call<core::String>(0));
-  (core::int, core::String) → self::B<core::String> f3e = #C9;
+  (core::int, core::String) → self::B<core::String> f3e = #C8;
   self::B<core::String> c3e = f3e(42, "foo"){(core::int, core::String) → self::B<core::String>};
   self::expect(true, c3e is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3e is{ForNonNullableByDefault} self::B<core::int>);
@@ -217,7 +217,7 @@
     f3e<String>(0, ''); // error
        ^" in f3e{<inapplicable>}.<core::String>(0, "");
   };
-  dynamic f3f = #C9;
+  dynamic f3f = #C8;
   dynamic c3f = f3f{dynamic}.call(42, "foo");
   self::expect(true, c3f is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3f is{ForNonNullableByDefault} self::B<core::int>);
@@ -226,7 +226,7 @@
   self::throws(() → dynamic => c3f{dynamic}.call());
   self::throws(() → dynamic => c3f{dynamic}.call(0));
   self::throws(() → dynamic => c3f{dynamic}.call<core::String>(0));
-  <X extends core::num>() → self::B<X> f4a = #C10;
+  <X extends core::num>() → self::B<X> f4a = #C9;
   self::B<core::num> c4a = f4a<core::num>(){() → self::B<core::num>};
   self::expect(true, c4a is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c4a is{ForNonNullableByDefault} self::B<core::int>);
@@ -240,7 +240,7 @@
        ^" in f4a{<inapplicable>}.<core::num>(0);
     f4a<core::String>(){() → self::B<core::String>};
   };
-  dynamic f4b = #C11;
+  dynamic f4b = #C9;
   dynamic c4c = f4b{dynamic}.call();
   self::expect(true, c4c is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c4c is{ForNonNullableByDefault} self::B<core::int>);
@@ -249,7 +249,7 @@
   self::expect(false, c4d is{ForNonNullableByDefault} self::B<core::double>);
   self::throws(() → dynamic => f4b{dynamic}.call(0));
   self::throws(() → dynamic => f4b{dynamic}.call<core::String>());
-  <X extends core::num, unrelated Y extends core::String>() → self::B<X> f5a = #C12;
+  <X extends core::num, unrelated Y extends core::String>() → self::B<X> f5a = #C10;
   self::B<core::num> c5a = f5a<core::num, core::String>(){() → self::B<core::num>};
   self::expect(true, c5a is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c5a is{ForNonNullableByDefault} self::B<core::int>);
@@ -267,7 +267,7 @@
     f5a<core::String, core::String>(){() → self::B<core::String>};
     f5a<core::num, core::num>(){() → self::B<core::num>};
   };
-  dynamic f5b = #C13;
+  dynamic f5b = #C10;
   dynamic c5c = f5b{dynamic}.call();
   self::expect(true, c5c is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c5c is{ForNonNullableByDefault} self::B<core::int>);
@@ -283,7 +283,7 @@
   if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
     throw "Expected ${expected}, actual ${actual}";
 }
-static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C14}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C11}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -296,32 +296,45 @@
   }
   throw "Expected exception";
 }
-static method _#0#tearOff<unrelated X extends core::num>() → self::A
-  return (#C1)(){() → self::A};
-static method _#1#tearOff<unrelated X extends core::num>() → self::A
-  return (#C1)(){() → self::A};
-static method _#2#tearOff<X extends core::num>() → self::B<self::_#2#tearOff::X>
-  return (#C4)<self::_#2#tearOff::X>(){() → self::B<self::_#2#tearOff::X>};
-static method _#3#tearOff<X extends core::num>() → self::B<self::_#3#tearOff::X>
-  return (#C4)<self::_#3#tearOff::X>(){() → self::B<self::_#3#tearOff::X>};
-static method _#4#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#4#tearOff::X>
-  return (#C4)<self::_#4#tearOff::X>(){() → self::B<self::_#4#tearOff::X>};
-static method _#5#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#5#tearOff::X>
-  return (#C4)<self::_#5#tearOff::X>(){() → self::B<self::_#5#tearOff::X>};
+static method _#DA1#new#tearOff() → self::A
+  return new self::A::•();
+static method _#DA2#new#tearOff<unrelated X extends core::num>() → self::A
+  return new self::A::•();
+static method _#DB1#_#tearOff(core::int field1, core::String field2) → self::B<core::String>
+  return new self::B::_<core::String>(field1, field2);
+static method _#DB1#new#tearOff() → self::B<core::String>
+  return new self::B::•<core::String>();
+static method _#DB1#foo#tearOff(core::int field1) → self::B<core::String>
+  return new self::B::foo<core::String>(field1);
+static method _#DB1#bar#tearOff(core::int i, core::String j) → self::B<core::String>
+  return self::B::bar<core::String>(i, j);
+static method _#DB2#_#tearOff<X extends core::num>(core::int field1, core::String field2) → self::B<self::_#DB2#_#tearOff::X>
+  return new self::B::_<self::_#DB2#_#tearOff::X>(field1, field2);
+static method _#DB2#new#tearOff<X extends core::num>() → self::B<self::_#DB2#new#tearOff::X>
+  return new self::B::•<self::_#DB2#new#tearOff::X>();
+static method _#DB2#foo#tearOff<X extends core::num>(core::int field1) → self::B<self::_#DB2#foo#tearOff::X>
+  return new self::B::foo<self::_#DB2#foo#tearOff::X>(field1);
+static method _#DB2#bar#tearOff<X extends core::num>(core::int i, core::String j) → self::B<self::_#DB2#bar#tearOff::X>
+  return self::B::bar<self::_#DB2#bar#tearOff::X>(i, j);
+static method _#DB3#_#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1, core::String field2) → self::B<self::_#DB3#_#tearOff::X>
+  return new self::B::_<self::_#DB3#_#tearOff::X>(field1, field2);
+static method _#DB3#new#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#DB3#new#tearOff::X>
+  return new self::B::•<self::_#DB3#new#tearOff::X>();
+static method _#DB3#foo#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1) → self::B<self::_#DB3#foo#tearOff::X>
+  return new self::B::foo<self::_#DB3#foo#tearOff::X>(field1);
+static method _#DB3#bar#tearOff<X extends core::num, unrelated Y extends core::String>(core::int i, core::String j) → self::B<self::_#DB3#bar#tearOff::X>
+  return self::B::bar<self::_#DB3#bar#tearOff::X>(i, j);
 
 constants  {
   #C1 = static-tearoff self::A::_#new#tearOff
-  #C2 = static-tearoff self::_#0#tearOff
-  #C3 = static-tearoff self::_#1#tearOff
-  #C4 = static-tearoff self::B::_#new#tearOff
-  #C5 = instantiation self::B::_#new#tearOff <core::String>
-  #C6 = static-tearoff self::B::_#foo#tearOff
-  #C7 = instantiation self::B::_#foo#tearOff <core::String>
-  #C8 = static-tearoff self::B::_#bar#tearOff
-  #C9 = instantiation self::B::_#bar#tearOff <core::String>
-  #C10 = static-tearoff self::_#2#tearOff
-  #C11 = static-tearoff self::_#3#tearOff
-  #C12 = static-tearoff self::_#4#tearOff
-  #C13 = static-tearoff self::_#5#tearOff
-  #C14 = false
+  #C2 = static-tearoff self::_#DA2#new#tearOff
+  #C3 = static-tearoff self::B::_#new#tearOff
+  #C4 = instantiation self::B::_#new#tearOff <core::String>
+  #C5 = static-tearoff self::B::_#foo#tearOff
+  #C6 = instantiation self::B::_#foo#tearOff <core::String>
+  #C7 = static-tearoff self::B::_#bar#tearOff
+  #C8 = instantiation self::B::_#bar#tearOff <core::String>
+  #C9 = static-tearoff self::_#DB2#new#tearOff
+  #C10 = static-tearoff self::_#DB3#new#tearOff
+  #C11 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.strong.transformed.expect
index 151872a..f5bd2bb 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.strong.transformed.expect
@@ -143,14 +143,14 @@
        ^" in f2a{<inapplicable>}.<core::num>(0);
     f2a<core::String>(){() → self::A};
   };
-  dynamic f2b = #C3;
+  dynamic f2b = #C2;
   dynamic c2b = f2b{dynamic}.call();
   self::expect(true, c2b is{ForNonNullableByDefault} self::A);
   dynamic c2c = f2b{dynamic}.call<core::int>();
   self::expect(true, c2c is{ForNonNullableByDefault} self::A);
   self::throws(() → dynamic => f2b{dynamic}.call(0));
   self::throws(() → dynamic => f2b{dynamic}.call<core::String>());
-  () → self::B<core::String> f3a = #C5;
+  () → self::B<core::String> f3a = #C4;
   self::B<core::String> c3a = f3a(){() → self::B<core::String>};
   self::expect(true, c3a is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3a is{ForNonNullableByDefault} self::B<core::int>);
@@ -165,7 +165,7 @@
     f3a<String>(); // error
        ^" in f3a{<inapplicable>}.<core::String>();
   };
-  dynamic f3b = #C5;
+  dynamic f3b = #C4;
   dynamic c3b = f3b{dynamic}.call();
   self::expect(true, c3b is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3b is{ForNonNullableByDefault} self::B<core::int>);
@@ -173,7 +173,7 @@
   self::expect("", c3a.{self::B::field2}{core::String});
   self::throws(() → dynamic => f3b{dynamic}.call(0));
   self::throws(() → dynamic => f3b{dynamic}.call<core::String>());
-  (core::int) → self::B<core::String> f3c = #C7;
+  (core::int) → self::B<core::String> f3c = #C6;
   self::B<core::String> c3c = f3c(42){(core::int) → self::B<core::String>};
   self::expect(true, c3c is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3c is{ForNonNullableByDefault} self::B<core::int>);
@@ -191,7 +191,7 @@
     f3c<String>(0); // error
        ^" in f3c{<inapplicable>}.<core::String>(0);
   };
-  dynamic f3d = #C7;
+  dynamic f3d = #C6;
   dynamic c3d = f3d{dynamic}.call(42);
   self::expect(true, c3d is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3d is{ForNonNullableByDefault} self::B<core::int>);
@@ -200,7 +200,7 @@
   self::throws(() → dynamic => f3d{dynamic}.call());
   self::throws(() → dynamic => f3d{dynamic}.call(0, 0));
   self::throws(() → dynamic => f3d{dynamic}.call<core::String>(0));
-  (core::int, core::String) → self::B<core::String> f3e = #C9;
+  (core::int, core::String) → self::B<core::String> f3e = #C8;
   self::B<core::String> c3e = f3e(42, "foo"){(core::int, core::String) → self::B<core::String>};
   self::expect(true, c3e is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3e is{ForNonNullableByDefault} self::B<core::int>);
@@ -217,7 +217,7 @@
     f3e<String>(0, ''); // error
        ^" in f3e{<inapplicable>}.<core::String>(0, "");
   };
-  dynamic f3f = #C9;
+  dynamic f3f = #C8;
   dynamic c3f = f3f{dynamic}.call(42, "foo");
   self::expect(true, c3f is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3f is{ForNonNullableByDefault} self::B<core::int>);
@@ -226,7 +226,7 @@
   self::throws(() → dynamic => c3f{dynamic}.call());
   self::throws(() → dynamic => c3f{dynamic}.call(0));
   self::throws(() → dynamic => c3f{dynamic}.call<core::String>(0));
-  <X extends core::num>() → self::B<X> f4a = #C10;
+  <X extends core::num>() → self::B<X> f4a = #C9;
   self::B<core::num> c4a = f4a<core::num>(){() → self::B<core::num>};
   self::expect(true, c4a is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c4a is{ForNonNullableByDefault} self::B<core::int>);
@@ -240,7 +240,7 @@
        ^" in f4a{<inapplicable>}.<core::num>(0);
     f4a<core::String>(){() → self::B<core::String>};
   };
-  dynamic f4b = #C11;
+  dynamic f4b = #C9;
   dynamic c4c = f4b{dynamic}.call();
   self::expect(true, c4c is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c4c is{ForNonNullableByDefault} self::B<core::int>);
@@ -249,7 +249,7 @@
   self::expect(false, c4d is{ForNonNullableByDefault} self::B<core::double>);
   self::throws(() → dynamic => f4b{dynamic}.call(0));
   self::throws(() → dynamic => f4b{dynamic}.call<core::String>());
-  <X extends core::num, unrelated Y extends core::String>() → self::B<X> f5a = #C12;
+  <X extends core::num, unrelated Y extends core::String>() → self::B<X> f5a = #C10;
   self::B<core::num> c5a = f5a<core::num, core::String>(){() → self::B<core::num>};
   self::expect(true, c5a is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c5a is{ForNonNullableByDefault} self::B<core::int>);
@@ -267,7 +267,7 @@
     f5a<core::String, core::String>(){() → self::B<core::String>};
     f5a<core::num, core::num>(){() → self::B<core::num>};
   };
-  dynamic f5b = #C13;
+  dynamic f5b = #C10;
   dynamic c5c = f5b{dynamic}.call();
   self::expect(true, c5c is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c5c is{ForNonNullableByDefault} self::B<core::int>);
@@ -283,7 +283,7 @@
   if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
     throw "Expected ${expected}, actual ${actual}";
 }
-static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C14}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C11}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -296,32 +296,45 @@
   }
   throw "Expected exception";
 }
-static method _#0#tearOff<unrelated X extends core::num>() → self::A
-  return (#C1)(){() → self::A};
-static method _#1#tearOff<unrelated X extends core::num>() → self::A
-  return (#C1)(){() → self::A};
-static method _#2#tearOff<X extends core::num>() → self::B<self::_#2#tearOff::X>
-  return (#C4)<self::_#2#tearOff::X>(){() → self::B<self::_#2#tearOff::X>};
-static method _#3#tearOff<X extends core::num>() → self::B<self::_#3#tearOff::X>
-  return (#C4)<self::_#3#tearOff::X>(){() → self::B<self::_#3#tearOff::X>};
-static method _#4#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#4#tearOff::X>
-  return (#C4)<self::_#4#tearOff::X>(){() → self::B<self::_#4#tearOff::X>};
-static method _#5#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#5#tearOff::X>
-  return (#C4)<self::_#5#tearOff::X>(){() → self::B<self::_#5#tearOff::X>};
+static method _#DA1#new#tearOff() → self::A
+  return new self::A::•();
+static method _#DA2#new#tearOff<unrelated X extends core::num>() → self::A
+  return new self::A::•();
+static method _#DB1#_#tearOff(core::int field1, core::String field2) → self::B<core::String>
+  return new self::B::_<core::String>(field1, field2);
+static method _#DB1#new#tearOff() → self::B<core::String>
+  return new self::B::•<core::String>();
+static method _#DB1#foo#tearOff(core::int field1) → self::B<core::String>
+  return new self::B::foo<core::String>(field1);
+static method _#DB1#bar#tearOff(core::int i, core::String j) → self::B<core::String>
+  return self::B::bar<core::String>(i, j);
+static method _#DB2#_#tearOff<X extends core::num>(core::int field1, core::String field2) → self::B<self::_#DB2#_#tearOff::X>
+  return new self::B::_<self::_#DB2#_#tearOff::X>(field1, field2);
+static method _#DB2#new#tearOff<X extends core::num>() → self::B<self::_#DB2#new#tearOff::X>
+  return new self::B::•<self::_#DB2#new#tearOff::X>();
+static method _#DB2#foo#tearOff<X extends core::num>(core::int field1) → self::B<self::_#DB2#foo#tearOff::X>
+  return new self::B::foo<self::_#DB2#foo#tearOff::X>(field1);
+static method _#DB2#bar#tearOff<X extends core::num>(core::int i, core::String j) → self::B<self::_#DB2#bar#tearOff::X>
+  return self::B::bar<self::_#DB2#bar#tearOff::X>(i, j);
+static method _#DB3#_#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1, core::String field2) → self::B<self::_#DB3#_#tearOff::X>
+  return new self::B::_<self::_#DB3#_#tearOff::X>(field1, field2);
+static method _#DB3#new#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#DB3#new#tearOff::X>
+  return new self::B::•<self::_#DB3#new#tearOff::X>();
+static method _#DB3#foo#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1) → self::B<self::_#DB3#foo#tearOff::X>
+  return new self::B::foo<self::_#DB3#foo#tearOff::X>(field1);
+static method _#DB3#bar#tearOff<X extends core::num, unrelated Y extends core::String>(core::int i, core::String j) → self::B<self::_#DB3#bar#tearOff::X>
+  return self::B::bar<self::_#DB3#bar#tearOff::X>(i, j);
 
 constants  {
   #C1 = static-tearoff self::A::_#new#tearOff
-  #C2 = static-tearoff self::_#0#tearOff
-  #C3 = static-tearoff self::_#1#tearOff
-  #C4 = static-tearoff self::B::_#new#tearOff
-  #C5 = instantiation self::B::_#new#tearOff <core::String>
-  #C6 = static-tearoff self::B::_#foo#tearOff
-  #C7 = instantiation self::B::_#foo#tearOff <core::String>
-  #C8 = static-tearoff self::B::_#bar#tearOff
-  #C9 = instantiation self::B::_#bar#tearOff <core::String>
-  #C10 = static-tearoff self::_#2#tearOff
-  #C11 = static-tearoff self::_#3#tearOff
-  #C12 = static-tearoff self::_#4#tearOff
-  #C13 = static-tearoff self::_#5#tearOff
-  #C14 = false
+  #C2 = static-tearoff self::_#DA2#new#tearOff
+  #C3 = static-tearoff self::B::_#new#tearOff
+  #C4 = instantiation self::B::_#new#tearOff <core::String>
+  #C5 = static-tearoff self::B::_#foo#tearOff
+  #C6 = instantiation self::B::_#foo#tearOff <core::String>
+  #C7 = static-tearoff self::B::_#bar#tearOff
+  #C8 = instantiation self::B::_#bar#tearOff <core::String>
+  #C9 = static-tearoff self::_#DB2#new#tearOff
+  #C10 = static-tearoff self::_#DB3#new#tearOff
+  #C11 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.expect
index 630a7de..d485819 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.expect
@@ -143,14 +143,14 @@
        ^" in f2a{<inapplicable>}.<core::num>(0);
     f2a<core::String>(){() → self::A};
   };
-  dynamic f2b = #C3;
+  dynamic f2b = #C2;
   dynamic c2b = f2b{dynamic}.call();
   self::expect(true, c2b is{ForNonNullableByDefault} self::A);
   dynamic c2c = f2b{dynamic}.call<core::int>();
   self::expect(true, c2c is{ForNonNullableByDefault} self::A);
   self::throws(() → dynamic => f2b{dynamic}.call(0));
   self::throws(() → dynamic => f2b{dynamic}.call<core::String>());
-  () → self::B<core::String> f3a = #C5;
+  () → self::B<core::String> f3a = #C4;
   self::B<core::String> c3a = f3a(){() → self::B<core::String>};
   self::expect(true, c3a is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3a is{ForNonNullableByDefault} self::B<core::int>);
@@ -165,7 +165,7 @@
     f3a<String>(); // error
        ^" in f3a{<inapplicable>}.<core::String>();
   };
-  dynamic f3b = #C5;
+  dynamic f3b = #C4;
   dynamic c3b = f3b{dynamic}.call();
   self::expect(true, c3b is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3b is{ForNonNullableByDefault} self::B<core::int>);
@@ -173,7 +173,7 @@
   self::expect("", c3a.{self::B::field2}{core::String});
   self::throws(() → dynamic => f3b{dynamic}.call(0));
   self::throws(() → dynamic => f3b{dynamic}.call<core::String>());
-  (core::int) → self::B<core::String> f3c = #C7;
+  (core::int) → self::B<core::String> f3c = #C6;
   self::B<core::String> c3c = f3c(42){(core::int) → self::B<core::String>};
   self::expect(true, c3c is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3c is{ForNonNullableByDefault} self::B<core::int>);
@@ -191,7 +191,7 @@
     f3c<String>(0); // error
        ^" in f3c{<inapplicable>}.<core::String>(0);
   };
-  dynamic f3d = #C7;
+  dynamic f3d = #C6;
   dynamic c3d = f3d{dynamic}.call(42);
   self::expect(true, c3d is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3d is{ForNonNullableByDefault} self::B<core::int>);
@@ -200,7 +200,7 @@
   self::throws(() → dynamic => f3d{dynamic}.call());
   self::throws(() → dynamic => f3d{dynamic}.call(0, 0));
   self::throws(() → dynamic => f3d{dynamic}.call<core::String>(0));
-  (core::int, core::String) → self::B<core::String> f3e = #C9;
+  (core::int, core::String) → self::B<core::String> f3e = #C8;
   self::B<core::String> c3e = f3e(42, "foo"){(core::int, core::String) → self::B<core::String>};
   self::expect(true, c3e is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3e is{ForNonNullableByDefault} self::B<core::int>);
@@ -217,7 +217,7 @@
     f3e<String>(0, ''); // error
        ^" in f3e{<inapplicable>}.<core::String>(0, "");
   };
-  dynamic f3f = #C9;
+  dynamic f3f = #C8;
   dynamic c3f = f3f{dynamic}.call(42, "foo");
   self::expect(true, c3f is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3f is{ForNonNullableByDefault} self::B<core::int>);
@@ -226,7 +226,7 @@
   self::throws(() → dynamic => c3f{dynamic}.call());
   self::throws(() → dynamic => c3f{dynamic}.call(0));
   self::throws(() → dynamic => c3f{dynamic}.call<core::String>(0));
-  <X extends core::num>() → self::B<X> f4a = #C10;
+  <X extends core::num>() → self::B<X> f4a = #C9;
   self::B<core::num> c4a = f4a<core::num>(){() → self::B<core::num>};
   self::expect(true, c4a is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c4a is{ForNonNullableByDefault} self::B<core::int>);
@@ -240,7 +240,7 @@
        ^" in f4a{<inapplicable>}.<core::num>(0);
     f4a<core::String>(){() → self::B<core::String>};
   };
-  dynamic f4b = #C11;
+  dynamic f4b = #C9;
   dynamic c4c = f4b{dynamic}.call();
   self::expect(true, c4c is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c4c is{ForNonNullableByDefault} self::B<core::int>);
@@ -249,7 +249,7 @@
   self::expect(false, c4d is{ForNonNullableByDefault} self::B<core::double>);
   self::throws(() → dynamic => f4b{dynamic}.call(0));
   self::throws(() → dynamic => f4b{dynamic}.call<core::String>());
-  <X extends core::num, unrelated Y extends core::String>() → self::B<X> f5a = #C12;
+  <X extends core::num, unrelated Y extends core::String>() → self::B<X> f5a = #C10;
   self::B<core::num> c5a = f5a<core::num, core::String>(){() → self::B<core::num>};
   self::expect(true, c5a is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c5a is{ForNonNullableByDefault} self::B<core::int>);
@@ -267,7 +267,7 @@
     f5a<core::String, core::String>(){() → self::B<core::String>};
     f5a<core::num, core::num>(){() → self::B<core::num>};
   };
-  dynamic f5b = #C13;
+  dynamic f5b = #C10;
   dynamic c5c = f5b{dynamic}.call();
   self::expect(true, c5c is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c5c is{ForNonNullableByDefault} self::B<core::int>);
@@ -283,7 +283,7 @@
   if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
     throw "Expected ${expected}, actual ${actual}";
 }
-static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C14}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C11}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -296,32 +296,45 @@
   }
   throw "Expected exception";
 }
-static method _#0#tearOff<unrelated X extends core::num>() → self::A
-  return (#C1)(){() → self::A};
-static method _#1#tearOff<unrelated X extends core::num>() → self::A
-  return (#C1)(){() → self::A};
-static method _#2#tearOff<X extends core::num>() → self::B<self::_#2#tearOff::X>
-  return (#C4)<self::_#2#tearOff::X>(){() → self::B<self::_#2#tearOff::X>};
-static method _#3#tearOff<X extends core::num>() → self::B<self::_#3#tearOff::X>
-  return (#C4)<self::_#3#tearOff::X>(){() → self::B<self::_#3#tearOff::X>};
-static method _#4#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#4#tearOff::X>
-  return (#C4)<self::_#4#tearOff::X>(){() → self::B<self::_#4#tearOff::X>};
-static method _#5#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#5#tearOff::X>
-  return (#C4)<self::_#5#tearOff::X>(){() → self::B<self::_#5#tearOff::X>};
+static method _#DA1#new#tearOff() → self::A
+  return new self::A::•();
+static method _#DA2#new#tearOff<unrelated X extends core::num>() → self::A
+  return new self::A::•();
+static method _#DB1#_#tearOff(core::int field1, core::String field2) → self::B<core::String>
+  return new self::B::_<core::String>(field1, field2);
+static method _#DB1#new#tearOff() → self::B<core::String>
+  return new self::B::•<core::String>();
+static method _#DB1#foo#tearOff(core::int field1) → self::B<core::String>
+  return new self::B::foo<core::String>(field1);
+static method _#DB1#bar#tearOff(core::int i, core::String j) → self::B<core::String>
+  return self::B::bar<core::String>(i, j);
+static method _#DB2#_#tearOff<X extends core::num>(core::int field1, core::String field2) → self::B<self::_#DB2#_#tearOff::X>
+  return new self::B::_<self::_#DB2#_#tearOff::X>(field1, field2);
+static method _#DB2#new#tearOff<X extends core::num>() → self::B<self::_#DB2#new#tearOff::X>
+  return new self::B::•<self::_#DB2#new#tearOff::X>();
+static method _#DB2#foo#tearOff<X extends core::num>(core::int field1) → self::B<self::_#DB2#foo#tearOff::X>
+  return new self::B::foo<self::_#DB2#foo#tearOff::X>(field1);
+static method _#DB2#bar#tearOff<X extends core::num>(core::int i, core::String j) → self::B<self::_#DB2#bar#tearOff::X>
+  return self::B::bar<self::_#DB2#bar#tearOff::X>(i, j);
+static method _#DB3#_#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1, core::String field2) → self::B<self::_#DB3#_#tearOff::X>
+  return new self::B::_<self::_#DB3#_#tearOff::X>(field1, field2);
+static method _#DB3#new#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#DB3#new#tearOff::X>
+  return new self::B::•<self::_#DB3#new#tearOff::X>();
+static method _#DB3#foo#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1) → self::B<self::_#DB3#foo#tearOff::X>
+  return new self::B::foo<self::_#DB3#foo#tearOff::X>(field1);
+static method _#DB3#bar#tearOff<X extends core::num, unrelated Y extends core::String>(core::int i, core::String j) → self::B<self::_#DB3#bar#tearOff::X>
+  return self::B::bar<self::_#DB3#bar#tearOff::X>(i, j);
 
 constants  {
   #C1 = static-tearoff self::A::_#new#tearOff
-  #C2 = static-tearoff self::_#0#tearOff
-  #C3 = static-tearoff self::_#1#tearOff
-  #C4 = static-tearoff self::B::_#new#tearOff
-  #C5 = instantiation self::B::_#new#tearOff <core::String*>
-  #C6 = static-tearoff self::B::_#foo#tearOff
-  #C7 = instantiation self::B::_#foo#tearOff <core::String*>
-  #C8 = static-tearoff self::B::_#bar#tearOff
-  #C9 = instantiation self::B::_#bar#tearOff <core::String*>
-  #C10 = static-tearoff self::_#2#tearOff
-  #C11 = static-tearoff self::_#3#tearOff
-  #C12 = static-tearoff self::_#4#tearOff
-  #C13 = static-tearoff self::_#5#tearOff
-  #C14 = false
+  #C2 = static-tearoff self::_#DA2#new#tearOff
+  #C3 = static-tearoff self::B::_#new#tearOff
+  #C4 = instantiation self::B::_#new#tearOff <core::String*>
+  #C5 = static-tearoff self::B::_#foo#tearOff
+  #C6 = instantiation self::B::_#foo#tearOff <core::String*>
+  #C7 = static-tearoff self::B::_#bar#tearOff
+  #C8 = instantiation self::B::_#bar#tearOff <core::String*>
+  #C9 = static-tearoff self::_#DB2#new#tearOff
+  #C10 = static-tearoff self::_#DB3#new#tearOff
+  #C11 = false
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.outline.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.outline.expect
index be18978..a3bed2d 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.outline.expect
@@ -40,3 +40,31 @@
   ;
 static method throws(() → dynamic f, {core::bool inSoundModeOnly}) → dynamic
   ;
+static method _#DA1#new#tearOff() → self::A
+  return new self::A::•();
+static method _#DA2#new#tearOff<unrelated X extends core::num>() → self::A
+  return new self::A::•();
+static method _#DB1#_#tearOff(core::int field1, core::String field2) → self::B<core::String>
+  return new self::B::_<core::String>(field1, field2);
+static method _#DB1#new#tearOff() → self::B<core::String>
+  return new self::B::•<core::String>();
+static method _#DB1#foo#tearOff(core::int field1) → self::B<core::String>
+  return new self::B::foo<core::String>(field1);
+static method _#DB1#bar#tearOff(core::int i, core::String j) → self::B<core::String>
+  return self::B::bar<core::String>(i, j);
+static method _#DB2#_#tearOff<X extends core::num>(core::int field1, core::String field2) → self::B<self::_#DB2#_#tearOff::X>
+  return new self::B::_<self::_#DB2#_#tearOff::X>(field1, field2);
+static method _#DB2#new#tearOff<X extends core::num>() → self::B<self::_#DB2#new#tearOff::X>
+  return new self::B::•<self::_#DB2#new#tearOff::X>();
+static method _#DB2#foo#tearOff<X extends core::num>(core::int field1) → self::B<self::_#DB2#foo#tearOff::X>
+  return new self::B::foo<self::_#DB2#foo#tearOff::X>(field1);
+static method _#DB2#bar#tearOff<X extends core::num>(core::int i, core::String j) → self::B<self::_#DB2#bar#tearOff::X>
+  return self::B::bar<self::_#DB2#bar#tearOff::X>(i, j);
+static method _#DB3#_#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1, core::String field2) → self::B<self::_#DB3#_#tearOff::X>
+  return new self::B::_<self::_#DB3#_#tearOff::X>(field1, field2);
+static method _#DB3#new#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#DB3#new#tearOff::X>
+  return new self::B::•<self::_#DB3#new#tearOff::X>();
+static method _#DB3#foo#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1) → self::B<self::_#DB3#foo#tearOff::X>
+  return new self::B::foo<self::_#DB3#foo#tearOff::X>(field1);
+static method _#DB3#bar#tearOff<X extends core::num, unrelated Y extends core::String>(core::int i, core::String j) → self::B<self::_#DB3#bar#tearOff::X>
+  return self::B::bar<self::_#DB3#bar#tearOff::X>(i, j);
diff --git a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.transformed.expect
index 3190b63..e905181 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/lowering/typedef_tear_off.dart.weak.transformed.expect
@@ -143,14 +143,14 @@
        ^" in f2a{<inapplicable>}.<core::num>(0);
     f2a<core::String>(){() → self::A};
   };
-  dynamic f2b = #C3;
+  dynamic f2b = #C2;
   dynamic c2b = f2b{dynamic}.call();
   self::expect(true, c2b is{ForNonNullableByDefault} self::A);
   dynamic c2c = f2b{dynamic}.call<core::int>();
   self::expect(true, c2c is{ForNonNullableByDefault} self::A);
   self::throws(() → dynamic => f2b{dynamic}.call(0));
   self::throws(() → dynamic => f2b{dynamic}.call<core::String>());
-  () → self::B<core::String> f3a = #C5;
+  () → self::B<core::String> f3a = #C4;
   self::B<core::String> c3a = f3a(){() → self::B<core::String>};
   self::expect(true, c3a is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3a is{ForNonNullableByDefault} self::B<core::int>);
@@ -165,7 +165,7 @@
     f3a<String>(); // error
        ^" in f3a{<inapplicable>}.<core::String>();
   };
-  dynamic f3b = #C5;
+  dynamic f3b = #C4;
   dynamic c3b = f3b{dynamic}.call();
   self::expect(true, c3b is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3b is{ForNonNullableByDefault} self::B<core::int>);
@@ -173,7 +173,7 @@
   self::expect("", c3a.{self::B::field2}{core::String});
   self::throws(() → dynamic => f3b{dynamic}.call(0));
   self::throws(() → dynamic => f3b{dynamic}.call<core::String>());
-  (core::int) → self::B<core::String> f3c = #C7;
+  (core::int) → self::B<core::String> f3c = #C6;
   self::B<core::String> c3c = f3c(42){(core::int) → self::B<core::String>};
   self::expect(true, c3c is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3c is{ForNonNullableByDefault} self::B<core::int>);
@@ -191,7 +191,7 @@
     f3c<String>(0); // error
        ^" in f3c{<inapplicable>}.<core::String>(0);
   };
-  dynamic f3d = #C7;
+  dynamic f3d = #C6;
   dynamic c3d = f3d{dynamic}.call(42);
   self::expect(true, c3d is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3d is{ForNonNullableByDefault} self::B<core::int>);
@@ -200,7 +200,7 @@
   self::throws(() → dynamic => f3d{dynamic}.call());
   self::throws(() → dynamic => f3d{dynamic}.call(0, 0));
   self::throws(() → dynamic => f3d{dynamic}.call<core::String>(0));
-  (core::int, core::String) → self::B<core::String> f3e = #C9;
+  (core::int, core::String) → self::B<core::String> f3e = #C8;
   self::B<core::String> c3e = f3e(42, "foo"){(core::int, core::String) → self::B<core::String>};
   self::expect(true, c3e is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3e is{ForNonNullableByDefault} self::B<core::int>);
@@ -217,7 +217,7 @@
     f3e<String>(0, ''); // error
        ^" in f3e{<inapplicable>}.<core::String>(0, "");
   };
-  dynamic f3f = #C9;
+  dynamic f3f = #C8;
   dynamic c3f = f3f{dynamic}.call(42, "foo");
   self::expect(true, c3f is{ForNonNullableByDefault} self::B<core::String>);
   self::expect(false, c3f is{ForNonNullableByDefault} self::B<core::int>);
@@ -226,7 +226,7 @@
   self::throws(() → dynamic => c3f{dynamic}.call());
   self::throws(() → dynamic => c3f{dynamic}.call(0));
   self::throws(() → dynamic => c3f{dynamic}.call<core::String>(0));
-  <X extends core::num>() → self::B<X> f4a = #C10;
+  <X extends core::num>() → self::B<X> f4a = #C9;
   self::B<core::num> c4a = f4a<core::num>(){() → self::B<core::num>};
   self::expect(true, c4a is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c4a is{ForNonNullableByDefault} self::B<core::int>);
@@ -240,7 +240,7 @@
        ^" in f4a{<inapplicable>}.<core::num>(0);
     f4a<core::String>(){() → self::B<core::String>};
   };
-  dynamic f4b = #C11;
+  dynamic f4b = #C9;
   dynamic c4c = f4b{dynamic}.call();
   self::expect(true, c4c is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c4c is{ForNonNullableByDefault} self::B<core::int>);
@@ -249,7 +249,7 @@
   self::expect(false, c4d is{ForNonNullableByDefault} self::B<core::double>);
   self::throws(() → dynamic => f4b{dynamic}.call(0));
   self::throws(() → dynamic => f4b{dynamic}.call<core::String>());
-  <X extends core::num, unrelated Y extends core::String>() → self::B<X> f5a = #C12;
+  <X extends core::num, unrelated Y extends core::String>() → self::B<X> f5a = #C10;
   self::B<core::num> c5a = f5a<core::num, core::String>(){() → self::B<core::num>};
   self::expect(true, c5a is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c5a is{ForNonNullableByDefault} self::B<core::int>);
@@ -267,7 +267,7 @@
     f5a<core::String, core::String>(){() → self::B<core::String>};
     f5a<core::num, core::num>(){() → self::B<core::num>};
   };
-  dynamic f5b = #C13;
+  dynamic f5b = #C10;
   dynamic c5c = f5b{dynamic}.call();
   self::expect(true, c5c is{ForNonNullableByDefault} self::B<core::num>);
   self::expect(false, c5c is{ForNonNullableByDefault} self::B<core::int>);
@@ -283,7 +283,7 @@
   if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
     throw "Expected ${expected}, actual ${actual}";
 }
-static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C14}) → dynamic {
+static method throws(() → dynamic f, {core::bool inSoundModeOnly = #C11}) → dynamic {
   try {
     f(){() → dynamic};
   }
@@ -296,32 +296,45 @@
   }
   throw "Expected exception";
 }
-static method _#0#tearOff<unrelated X extends core::num>() → self::A
-  return (#C1)(){() → self::A};
-static method _#1#tearOff<unrelated X extends core::num>() → self::A
-  return (#C1)(){() → self::A};
-static method _#2#tearOff<X extends core::num>() → self::B<self::_#2#tearOff::X>
-  return (#C4)<self::_#2#tearOff::X>(){() → self::B<self::_#2#tearOff::X>};
-static method _#3#tearOff<X extends core::num>() → self::B<self::_#3#tearOff::X>
-  return (#C4)<self::_#3#tearOff::X>(){() → self::B<self::_#3#tearOff::X>};
-static method _#4#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#4#tearOff::X>
-  return (#C4)<self::_#4#tearOff::X>(){() → self::B<self::_#4#tearOff::X>};
-static method _#5#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#5#tearOff::X>
-  return (#C4)<self::_#5#tearOff::X>(){() → self::B<self::_#5#tearOff::X>};
+static method _#DA1#new#tearOff() → self::A
+  return new self::A::•();
+static method _#DA2#new#tearOff<unrelated X extends core::num>() → self::A
+  return new self::A::•();
+static method _#DB1#_#tearOff(core::int field1, core::String field2) → self::B<core::String>
+  return new self::B::_<core::String>(field1, field2);
+static method _#DB1#new#tearOff() → self::B<core::String>
+  return new self::B::•<core::String>();
+static method _#DB1#foo#tearOff(core::int field1) → self::B<core::String>
+  return new self::B::foo<core::String>(field1);
+static method _#DB1#bar#tearOff(core::int i, core::String j) → self::B<core::String>
+  return self::B::bar<core::String>(i, j);
+static method _#DB2#_#tearOff<X extends core::num>(core::int field1, core::String field2) → self::B<self::_#DB2#_#tearOff::X>
+  return new self::B::_<self::_#DB2#_#tearOff::X>(field1, field2);
+static method _#DB2#new#tearOff<X extends core::num>() → self::B<self::_#DB2#new#tearOff::X>
+  return new self::B::•<self::_#DB2#new#tearOff::X>();
+static method _#DB2#foo#tearOff<X extends core::num>(core::int field1) → self::B<self::_#DB2#foo#tearOff::X>
+  return new self::B::foo<self::_#DB2#foo#tearOff::X>(field1);
+static method _#DB2#bar#tearOff<X extends core::num>(core::int i, core::String j) → self::B<self::_#DB2#bar#tearOff::X>
+  return self::B::bar<self::_#DB2#bar#tearOff::X>(i, j);
+static method _#DB3#_#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1, core::String field2) → self::B<self::_#DB3#_#tearOff::X>
+  return new self::B::_<self::_#DB3#_#tearOff::X>(field1, field2);
+static method _#DB3#new#tearOff<X extends core::num, unrelated Y extends core::String>() → self::B<self::_#DB3#new#tearOff::X>
+  return new self::B::•<self::_#DB3#new#tearOff::X>();
+static method _#DB3#foo#tearOff<X extends core::num, unrelated Y extends core::String>(core::int field1) → self::B<self::_#DB3#foo#tearOff::X>
+  return new self::B::foo<self::_#DB3#foo#tearOff::X>(field1);
+static method _#DB3#bar#tearOff<X extends core::num, unrelated Y extends core::String>(core::int i, core::String j) → self::B<self::_#DB3#bar#tearOff::X>
+  return self::B::bar<self::_#DB3#bar#tearOff::X>(i, j);
 
 constants  {
   #C1 = static-tearoff self::A::_#new#tearOff
-  #C2 = static-tearoff self::_#0#tearOff
-  #C3 = static-tearoff self::_#1#tearOff
-  #C4 = static-tearoff self::B::_#new#tearOff
-  #C5 = instantiation self::B::_#new#tearOff <core::String*>
-  #C6 = static-tearoff self::B::_#foo#tearOff
-  #C7 = instantiation self::B::_#foo#tearOff <core::String*>
-  #C8 = static-tearoff self::B::_#bar#tearOff
-  #C9 = instantiation self::B::_#bar#tearOff <core::String*>
-  #C10 = static-tearoff self::_#2#tearOff
-  #C11 = static-tearoff self::_#3#tearOff
-  #C12 = static-tearoff self::_#4#tearOff
-  #C13 = static-tearoff self::_#5#tearOff
-  #C14 = false
+  #C2 = static-tearoff self::_#DA2#new#tearOff
+  #C3 = static-tearoff self::B::_#new#tearOff
+  #C4 = instantiation self::B::_#new#tearOff <core::String*>
+  #C5 = static-tearoff self::B::_#foo#tearOff
+  #C6 = instantiation self::B::_#foo#tearOff <core::String*>
+  #C7 = static-tearoff self::B::_#bar#tearOff
+  #C8 = instantiation self::B::_#bar#tearOff <core::String*>
+  #C9 = static-tearoff self::_#DB2#new#tearOff
+  #C10 = static-tearoff self::_#DB3#new#tearOff
+  #C11 = false
 }
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index b961189..8082cff 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -32,6 +32,8 @@
 constructor_tearoffs/inferred_constructor_tear_off: FormatterCrash
 constructor_tearoffs/instantiation: FormatterCrash
 constructor_tearoffs/lowering/inferred_constructor_tear_off: FormatterCrash
+constructor_tearoffs/lowering/typedef_from_dill/main: FormatterCrash
+constructor_tearoffs/lowering/typedef_identical: FormatterCrash
 constructor_tearoffs/nongeneric_tearoff_with_context: FormatterCrash
 constructor_tearoffs/nongeneric_tearoff_without_context: FormatterCrash
 constructor_tearoffs/redirecting_constructors: FormatterCrash
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 2dd4cf9..0009adc 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -13116,8 +13116,8 @@
 
   @override
   void toTextInternal(AstPrinter printer) {
-    printer.writeConstant(tearOffConstant);
     printer.writeTypeParameters(parameters);
+    printer.writeConstant(tearOffConstant);
     printer.writeTypeArguments(types);
   }
 
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index bb2aace..6885c8a 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -354,7 +354,7 @@
   /// synthesized top level functions.
   int get enabledConstructorTearOffLowerings;
 
-  /// Returns `true` if lowering of constructor tear offs is enabled.
+  /// Returns `true` if lowering of generative constructor tear offs is enabled.
   ///
   /// This is determined by the [enabledConstructorTearOffLowerings] mask.
   bool get isConstructorTearOffLoweringEnabled =>
@@ -362,6 +362,15 @@
           ConstructorTearOffLowering.constructors) !=
       0;
 
+  /// Returns `true` if lowering of non-redirecting factory tear offs is
+  /// enabled.
+  ///
+  /// This is determined by the [enabledConstructorTearOffLowerings] mask.
+  bool get isFactoryTearOffLoweringEnabled =>
+      (enabledConstructorTearOffLowerings &
+          ConstructorTearOffLowering.factories) !=
+      0;
+
   /// Returns `true` if lowering of redirecting factory tear offs is enabled.
   ///
   /// This is determined by the [enabledConstructorTearOffLowerings] mask.
@@ -643,16 +652,19 @@
 }
 
 class ConstructorTearOffLowering {
-  /// Create static functions to use as tear offs of constructors and.
+  /// Create static functions to use as tear offs of generative constructors.
   static const int constructors = 1 << 0;
 
+  /// Create static functions to use as tear offs of non-redirecting factories.
+  static const int factories = 1 << 1;
+
   /// Create static functions to use as tear offs of redirecting factories.
-  static const int redirectingFactories = 1 << 1;
+  static const int redirectingFactories = 1 << 2;
 
   /// Create top level functions to use as tear offs of typedefs that are not
   /// proper renames.
-  static const int typedefs = 1 << 2;
+  static const int typedefs = 1 << 3;
 
   static const int none = 0;
-  static const int all = (1 << 3) - 1;
+  static const int all = (1 << 4) - 1;
 }
diff --git a/runtime/include/dart_tools_api.h b/runtime/include/dart_tools_api.h
index 65d35ce..7d40798 100644
--- a/runtime/include/dart_tools_api.h
+++ b/runtime/include/dart_tools_api.h
@@ -371,6 +371,20 @@
  */
 DART_EXPORT int64_t Dart_TimelineGetMicros();
 
+/**
+ * Returns a raw timestamp in from the monotonic clock.
+ *
+ * \return A raw timestamp from the monotonic clock.
+ */
+DART_EXPORT int64_t Dart_TimelineGetTicks();
+
+/**
+ * Returns the frequency of the monotonic clock.
+ *
+ * \return The frequency of the monotonic clock.
+ */
+DART_EXPORT int64_t Dart_TimelineGetTicksFrequency();
+
 /** Timeline stream for Dart API calls */
 #define DART_TIMELINE_STREAM_API (1 << 0)
 /** Timeline stream for compiler events */
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index ebe4a4b..283841a 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6163,10 +6163,6 @@
   return ServiceIsolate::IsServiceIsolate(iso);
 }
 
-DART_EXPORT int64_t Dart_TimelineGetMicros() {
-  return OS::GetCurrentMonotonicMicros();
-}
-
 DART_EXPORT void Dart_RegisterIsolateServiceRequestCallback(
     const char* name,
     Dart_ServiceRequestCallback callback,
@@ -6308,6 +6304,18 @@
 #endif
 }
 
+DART_EXPORT int64_t Dart_TimelineGetMicros() {
+  return OS::GetCurrentMonotonicMicros();
+}
+
+DART_EXPORT int64_t Dart_TimelineGetTicks() {
+  return OS::GetCurrentMonotonicTicks();
+}
+
+DART_EXPORT int64_t Dart_TimelineGetTicksFrequency() {
+  return OS::GetCurrentMonotonicFrequency();
+}
+
 DART_EXPORT void Dart_GlobalTimelineSetRecordedStreams(int64_t stream_mask) {
 #if defined(SUPPORT_TIMELINE)
   const bool api_enabled = (stream_mask & DART_TIMELINE_STREAM_API) != 0;
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 2be540b..50b0ab9 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -9082,6 +9082,19 @@
   EXPECT_SUBSTRING("testAsyncEvent", js.ToCString());
 }
 
+TEST_CASE(DartAPI_TimelineClock) {
+  int64_t micros1 = Dart_TimelineGetMicros();
+  int64_t ticks1 = Dart_TimelineGetTicks();
+  int64_t frequency1 = Dart_TimelineGetTicksFrequency();
+  OS::Sleep(1);
+  int64_t micros2 = Dart_TimelineGetMicros();
+  int64_t ticks2 = Dart_TimelineGetTicks();
+  int64_t frequency2 = Dart_TimelineGetTicksFrequency();
+  EXPECT_NE(micros1, micros2);
+  EXPECT_NE(ticks1, ticks2);
+  EXPECT_EQ(frequency1, frequency2);
+}
+
 static void HintFreedNative(Dart_NativeArguments args) {
   int64_t size = 0;
   EXPECT_VALID(Dart_GetNativeIntegerArgument(args, 0, &size));
diff --git a/tools/VERSION b/tools/VERSION
index c32c3d3..778bf10 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 347
+PRERELEASE 348
 PRERELEASE_PATCH 0
\ No newline at end of file