[vm/aot/tfa] Whole-program propagation of closure values

This change adds propagation of closure values to TFA.
For now, inferred closure values are used only when they are
constant (tear-off of a static method or a constructor).

Arbitrary closures can now be referenced from unrelated
members via closure-id metadata.

In addition, this change fixes an incorrect stack trace when
an implicit closure (tear-off) was propagated and its call was
inlined. Inlining interval was not recorded because of the missing
source position of a call to a target method within implicit closure.

TEST=pkg/vm/testcases/transformations/type_flow/transformer/closures.dart
Issue: https://github.com/dart-lang/sdk/issues/39692

Change-Id: I3590da91b6057e0b55a8614382dba1bbcc267b39
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/325447
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart
index b74e794..8757b9b 100644
--- a/pkg/dart2wasm/lib/target.dart
+++ b/pkg/dart2wasm/lib/target.dart
@@ -102,6 +102,7 @@
   Class? _wasmImmutableSet;
   Class? _oneByteString;
   Class? _twoByteString;
+  Class? _closure;
   Map<String, Class>? _nativeClasses;
 
   @override
@@ -480,6 +481,11 @@
   }
 
   @override
+  Class concreteClosureClass(CoreTypes coreTypes) {
+    return _closure ??= coreTypes.index.getClass('dart:core', '_Closure');
+  }
+
+  @override
   bool isSupportedPragma(String pragmaName) => pragmaName.startsWith("wasm:");
 
   late final Map<RecordShape, Class> recordClasses;
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 1f1b3d2..c8a598f 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -515,6 +515,7 @@
   Class? concreteConstMapLiteralClass(CoreTypes coreTypes) => null;
   Class? concreteSetLiteralClass(CoreTypes coreTypes) => null;
   Class? concreteConstSetLiteralClass(CoreTypes coreTypes) => null;
+  Class? concreteClosureClass(CoreTypes coreTypes) => null;
   Class getRecordImplementationClass(CoreTypes coreTypes,
           int numPositionalFields, List<String> namedFields) =>
       throw UnsupportedError('Target.getRecordImplementationClass');
diff --git a/pkg/vm/bin/dump_kernel.dart b/pkg/vm/bin/dump_kernel.dart
index 7b131c8..9248413 100644
--- a/pkg/vm/bin/dump_kernel.dart
+++ b/pkg/vm/bin/dump_kernel.dart
@@ -8,6 +8,7 @@
 import 'package:kernel/binary/ast_from_binary.dart'
     show BinaryBuilderWithMetadata;
 
+import 'package:vm/metadata/closure_id.dart' show ClosureIdMetadataRepository;
 import 'package:vm/metadata/direct_call.dart' show DirectCallMetadataRepository;
 import 'package:vm/metadata/inferred_type.dart'
     show InferredTypeMetadataRepository, InferredArgTypeMetadataRepository;
@@ -50,6 +51,7 @@
   component.addMetadataRepository(new UnreachableNodeMetadataRepository());
   component.addMetadataRepository(new CallSiteAttributesMetadataRepository());
   component.addMetadataRepository(new LoadingUnitsMetadataRepository());
+  component.addMetadataRepository(new ClosureIdMetadataRepository());
 
   final List<int> bytes = new File(input).readAsBytesSync();
   new BinaryBuilderWithMetadata(bytes).readComponent(component);
diff --git a/pkg/vm/lib/metadata/closure_id.dart b/pkg/vm/lib/metadata/closure_id.dart
new file mode 100644
index 0000000..8e9f034
--- /dev/null
+++ b/pkg/vm/lib/metadata/closure_id.dart
@@ -0,0 +1,71 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:kernel/ast.dart';
+
+/// Repository for persistent closure IDs.
+class ClosureIdMetadataRepository extends MetadataRepository<int> {
+  static const String repositoryTag = 'vm.closure-id';
+
+  @override
+  String get tag => repositoryTag;
+
+  // For a LocalFunction: id within an enclosing Member,
+  // with 0 reserved for the tear-off of the Member.
+  //
+  // For a Member: number of nested closures.
+  @override
+  final Map<TreeNode, int> mapping = {};
+
+  @override
+  void writeToBinary(int metadata, Node node, BinarySink sink) {
+    sink.writeUInt30(metadata);
+  }
+
+  @override
+  int readFromBinary(Node node, BinarySource source) {
+    return source.readUInt30();
+  }
+
+  /// Return closure ID within the enclosing [Member].
+  ///
+  /// Closures should be indexed within enclosing [Member]
+  /// using [indexClosures].
+  int getClosureId(LocalFunction closure) => mapping[closure]!;
+
+  /// Assign IDs for all closures within [member].
+  void indexClosures(Member member) {
+    if (mapping.containsKey(member)) {
+      return;
+    }
+    _ClosureIndexer indexer = _ClosureIndexer(this, member);
+    member.accept(indexer);
+    mapping[member] = indexer.index - _ClosureIndexer.firstClosureIndex;
+  }
+}
+
+class _ClosureIndexer extends RecursiveVisitor<void> {
+  // Zero is reserved for tear-offs.
+  static int firstClosureIndex = 1;
+
+  final ClosureIdMetadataRepository _repository;
+  final Member member;
+  int index = firstClosureIndex;
+
+  _ClosureIndexer(this._repository, this.member);
+
+  @override
+  void visitFunctionDeclaration(FunctionDeclaration node) =>
+      _visitLocalFunction(node);
+
+  @override
+  void visitFunctionExpression(FunctionExpression node) =>
+      _visitLocalFunction(node);
+
+  void _visitLocalFunction(LocalFunction node) {
+    assert(index > 0);
+    _repository.mapping[node] = index++;
+    node.visitChildren(this);
+  }
+}
diff --git a/pkg/vm/lib/metadata/inferred_type.dart b/pkg/vm/lib/metadata/inferred_type.dart
index 5cb0003..9d7d382 100644
--- a/pkg/vm/lib/metadata/inferred_type.dart
+++ b/pkg/vm/lib/metadata/inferred_type.dart
@@ -11,6 +11,8 @@
 class InferredType {
   final Reference? _concreteClassReference;
   final Constant? _constantValue;
+  final Reference? _closureMemberReference;
+  final int _closureId;
   final int _flags;
 
   static const int flagNullable = 1 << 0;
@@ -19,10 +21,14 @@
   // For invocations: whether to use the unchecked entry-point.
   static const int flagSkipCheck = 1 << 2;
 
+  // Contains inferred constant value.
   static const int flagConstant = 1 << 3;
 
   static const int flagReceiverNotInt = 1 << 4;
 
+  // Contains inferred closure value.
+  static const int flagClosure = 1 << 5;
+
   // Entire list may be null if no type arguments were inferred.
   // Will always be null if `concreteClass` is null.
   //
@@ -33,31 +39,44 @@
   // argument (in the runtime type) is always exactly a particular `DartType`.
   final List<DartType?>? exactTypeArguments;
 
-  InferredType(
-      Class? concreteClass, bool nullable, bool isInt, Constant? constantValue,
+  InferredType(Class? concreteClass, bool nullable, bool isInt,
+      Constant? constantValue, Member? closureMember, int closureId,
       {List<DartType?>? exactTypeArguments,
       bool skipCheck = false,
       bool receiverNotInt = false})
       : this._byReference(
             concreteClass?.reference,
             constantValue,
+            closureMember?.reference,
+            closureId,
             (nullable ? flagNullable : 0) |
                 (isInt ? flagInt : 0) |
                 (skipCheck ? flagSkipCheck : 0) |
                 (constantValue != null ? flagConstant : 0) |
-                (receiverNotInt ? flagReceiverNotInt : 0),
+                (receiverNotInt ? flagReceiverNotInt : 0) |
+                (closureMember != null ? flagClosure : 0),
             exactTypeArguments);
 
-  InferredType._byReference(this._concreteClassReference, this._constantValue,
-      this._flags, this.exactTypeArguments) {
+  InferredType._byReference(
+      this._concreteClassReference,
+      this._constantValue,
+      this._closureMemberReference,
+      this._closureId,
+      this._flags,
+      this.exactTypeArguments) {
     assert(exactTypeArguments == null || _concreteClassReference != null);
     assert(_constantValue == null || _concreteClassReference != null);
+    assert(_closureMemberReference == null || _concreteClassReference != null);
+    assert(_closureId >= 0);
   }
 
   Class? get concreteClass => _concreteClassReference?.asClass;
 
   Constant? get constantValue => _constantValue;
 
+  Member? get closureMember => _closureMemberReference?.asMember;
+  int get closureId => _closureId;
+
   bool get nullable => (_flags & flagNullable) != 0;
   bool get isInt => (_flags & flagInt) != 0;
   bool get skipCheck => (_flags & flagSkipCheck) != 0;
@@ -96,6 +115,10 @@
     if (receiverNotInt) {
       buf.write(' (receiver not int)');
     }
+    if (closureMember != null) {
+      buf.write(
+          ' (closure ${closureId} in ${closureMember!.toText(astTextStrategyForTesting)})');
+    }
     return buf.toString();
   }
 }
@@ -116,10 +139,16 @@
     // them for optimizations.
     sink.writeNullAllowedCanonicalNameReference(
         metadata.concreteClass?.reference);
+    final flags = metadata._flags;
     sink.writeByte(metadata._flags);
-    if (metadata.constantValue != null) {
+    if ((flags & InferredType.flagConstant) != 0) {
       sink.writeConstantReference(metadata.constantValue!);
     }
+    if ((flags & InferredType.flagClosure) != 0) {
+      sink.writeNullAllowedCanonicalNameReference(
+          metadata.closureMember!.reference);
+      sink.writeUInt30(metadata.closureId);
+    }
   }
 
   @override
@@ -132,8 +161,13 @@
     final constantValue = (flags & InferredType.flagConstant) != 0
         ? source.readConstantReference()
         : null;
-    return new InferredType._byReference(
-        concreteClassReference, constantValue, flags, null);
+    final closureMemberReference = (flags & InferredType.flagClosure) != 0
+        ? source.readNullableCanonicalNameReference()!.reference
+        : null;
+    final closureId =
+        (flags & InferredType.flagClosure) != 0 ? source.readUInt30() : 0;
+    return new InferredType._byReference(concreteClassReference, constantValue,
+        closureMemberReference, closureId, flags, null);
   }
 }
 
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index 73e510f..59872c2 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -39,6 +39,7 @@
   Class? _twoByteString;
   Class? _smi;
   Class? _double; // _Double, not double.
+  Class? _closure;
   Class? _syncStarIterable;
 
   VmTarget(this.flags);
@@ -491,6 +492,11 @@
   }
 
   @override
+  Class concreteClosureClass(CoreTypes coreTypes) {
+    return _closure ??= coreTypes.index.getClass('dart:core', '_Closure');
+  }
+
+  @override
   Class? concreteAsyncResultClass(CoreTypes coreTypes) =>
       coreTypes.futureImplClass;
 
diff --git a/pkg/vm/lib/transformations/call_site_annotator.dart b/pkg/vm/lib/transformations/call_site_annotator.dart
index e0267ca..9df15ca 100644
--- a/pkg/vm/lib/transformations/call_site_annotator.dart
+++ b/pkg/vm/lib/transformations/call_site_annotator.dart
@@ -84,19 +84,6 @@
   }
 
   @override
-  visitFunctionInvocation(FunctionInvocation node) {
-    super.visitFunctionInvocation(node);
-
-    final DartType receiverType =
-        node.receiver.getStaticType(_staticTypeContext!);
-    if (receiverType is FunctionType &&
-        node.kind == FunctionAccessKind.Function) {
-      throw 'Node ${node.runtimeType}: $node at ${node.location} has receiver'
-          ' static type $receiverType, but kind ${node.kind}';
-    }
-  }
-
-  @override
   visitEqualsCall(EqualsCall node) {
     super.visitEqualsCall(node);
 
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index 360e9fb..341e161 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -315,9 +315,29 @@
       if (selector.callKind == CallKind.PropertyGet) {
         // Tear-off.
         // TODO(alexmarkov): capture receiver type
-        assert((member is Procedure) && !member.isGetter && !member.isSetter);
+        assert((member is Procedure) &&
+            !member.isGetter &&
+            !member.isSetter &&
+            !member.isFactory &&
+            !member.isAbstract);
         typeFlowAnalysis.addRawCall(new DirectSelector(member));
         typeFlowAnalysis._tearOffTaken.add(member);
+        final Class? concreteClass = typeFlowAnalysis.target
+            .concreteClosureClass(typeFlowAnalysis.coreTypes);
+        if (concreteClass != null) {
+          if (!member.isInstanceMember) {
+            return typeFlowAnalysis
+                .addAllocatedClass(concreteClass)
+                .cls
+                .constantConcreteType(
+                    StaticTearOffConstant(member as Procedure));
+          } else {
+            return typeFlowAnalysis
+                .addAllocatedClass(concreteClass)
+                .cls
+                .closureConcreteType(member, null);
+          }
+        }
         return nullableAnyType;
       } else {
         // Call via getter.
diff --git a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
index 6957ff7..721ebc0 100644
--- a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
+++ b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
@@ -182,7 +182,7 @@
     // constant value in every implementation. The constant value inferred does
     // not have to be the same across implementations, as it is specialized in
     // each implementation individually.
-    if (!(type is ConcreteType && type.constant != null ||
+    if (!(type is ConcreteType && type.attributes?.constant != null ||
         type is NullableType && type.baseType is EmptyType)) {
       isConstant = false;
     }
@@ -372,7 +372,7 @@
     if (param.isConstant) {
       Type type = shaker.typeFlowAnalysis.argumentType(member, variable)!;
       if (type is ConcreteType) {
-        value = type.constant!;
+        value = type.attributes!.constant!;
       } else {
         assert(type is NullableType && type.baseType is EmptyType);
         value = NullConstant();
@@ -499,7 +499,8 @@
   void visitVariableGet(VariableGet node) {
     Constant? constantValue = eliminatedParams[node.variable];
     if (constantValue != null) {
-      node.replaceWith(ConstantExpression(constantValue));
+      node.replaceWith(ConstantExpression(
+          constantValue, constantValue.getType(typeContext)));
     }
   }
 
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 4a36c2d..4f0aaeb 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -526,6 +526,7 @@
   // Join which accumulates all return values.
   Join? _returnValue;
 
+  Member? _enclosingMember;
   Parameter? _receiver;
   late ConstantAllocationCollector constantAllocationCollector;
   late RuntimeTypeTranslatorImpl _translator;
@@ -559,6 +560,7 @@
         "${member}${fieldSummaryType == FieldSummaryType.kFieldGuard ? " (guard)" : ""}";
     debugPrint("===== $summaryName =====");
     assert(!member.isAbstract);
+    _enclosingMember = member;
 
     _protobufHandler?.beforeSummaryCreation(member);
 
@@ -769,6 +771,7 @@
     member.enclosingClass?.annotations.forEach(_visit);
     member.enclosingLibrary.annotations.forEach(_visit);
 
+    _enclosingMember = null;
     _staticTypeContext = null;
 
     debugPrint("------------ SUMMARY ------------");
@@ -1370,6 +1373,25 @@
     _returnValue = savedReturn;
   }
 
+  TypeExpr _closureType(LocalFunction node) {
+    final Class? concreteClass =
+        target.concreteClosureClass(_environment.coreTypes);
+    if (concreteClass != null) {
+      return _entryPointsListener
+          .addAllocatedClass(concreteClass)
+          .cls
+          .closureConcreteType(_enclosingMember!, node);
+    }
+    switch (node) {
+      case FunctionExpression():
+        return _staticType(node);
+      case FunctionDeclaration():
+        return _typesBuilder.fromStaticType(node.variable.type, true);
+      default:
+        throw 'Unexpected ${node.runtimeType} $node';
+    }
+  }
+
   // Tests subtypes ignoring any nullabilities.
   bool _isSubtype(DartType subtype, DartType supertype) => _environment
       .isSubtypeOf(subtype, supertype, SubtypeCheckMode.ignoringNullabilities);
@@ -1626,9 +1648,7 @@
   @override
   TypeExpr visitFunctionExpression(FunctionExpression node) {
     _handleNestedFunctionNode(node.function);
-    // TODO(alexmarkov): support function types.
-    // return _concreteType(node.function.functionType);
-    return _staticType(node);
+    return _closureType(node);
   }
 
   @override
@@ -2225,9 +2245,8 @@
 
   @override
   TypeExpr? visitFunctionDeclaration(FunctionDeclaration node) {
-    // TODO(alexmarkov): support function types.
     node.variable.annotations.forEach(_visit);
-    _declareVariableWithStaticType(node.variable);
+    _declareVariable(node.variable, _closureType(node));
     _handleNestedFunctionNode(node.function);
     return null;
   }
@@ -2830,6 +2849,14 @@
           .addAllocatedClass(member.enclosingClass);
     }
     summaryCollector._entryPointsListener.recordTearOff(member);
+    final Class? concreteClass = summaryCollector.target
+        .concreteClosureClass(summaryCollector._environment.coreTypes);
+    if (concreteClass != null) {
+      return summaryCollector._entryPointsListener
+          .addAllocatedClass(concreteClass)
+          .cls
+          .constantConcreteType(constant);
+    }
     return _getStaticType(constant);
   }
 
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 81bc828..38033c8 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -18,6 +18,7 @@
 import 'package:kernel/library_index.dart' show LibraryIndex;
 import 'package:kernel/target/targets.dart';
 import 'package:kernel/type_environment.dart';
+import 'package:vm/metadata/closure_id.dart';
 import 'package:vm/metadata/direct_call.dart';
 import 'package:vm/metadata/inferred_type.dart';
 import 'package:vm/metadata/procedure_attributes.dart';
@@ -310,6 +311,7 @@
   final ProcedureAttributesMetadataRepository _procedureAttributesMetadata;
   final TableSelectorMetadataRepository _tableSelectorMetadata;
   final TableSelectorAssigner _tableSelectorAssigner;
+  final ClosureIdMetadataRepository _closureIdMetadata;
   final UnboxingInfoMetadataRepository _unboxingInfoMetadata;
   final UnboxingInfoManager _unboxingInfo;
   final Class _intClass;
@@ -325,6 +327,7 @@
         _unreachableNodeMetadata = UnreachableNodeMetadataRepository(),
         _procedureAttributesMetadata = ProcedureAttributesMetadataRepository(),
         _tableSelectorMetadata = TableSelectorMetadataRepository(),
+        _closureIdMetadata = ClosureIdMetadataRepository(),
         _unboxingInfoMetadata = UnboxingInfoMetadataRepository(),
         _intClass = _typeFlowAnalysis.environment.coreTypes.intClass {
     component.addMetadataRepository(_inferredTypeMetadata);
@@ -332,6 +335,7 @@
     component.addMetadataRepository(_unreachableNodeMetadata);
     component.addMetadataRepository(_procedureAttributesMetadata);
     component.addMetadataRepository(_tableSelectorMetadata);
+    component.addMetadataRepository(_closureIdMetadata);
     component.addMetadataRepository(_unboxingInfoMetadata);
   }
 
@@ -344,6 +348,8 @@
       {bool skipCheck = false, bool receiverNotInt = false}) {
     Class? concreteClass;
     Constant? constantValue;
+    Member? closureMember;
+    int closureId = 0;
     bool isInt = false;
 
     final nullable = type is NullableType;
@@ -363,7 +369,20 @@
       }
 
       if (type is ConcreteType && !nullable) {
-        constantValue = type.constant;
+        constantValue = type.attributes?.constant;
+
+        final closure = type.attributes?.closure;
+        if (closure != null) {
+          closureMember = closure.member;
+          final function = closure.function;
+          if (function != null) {
+            _closureIdMetadata.indexClosures(closureMember);
+            closureId = _closureIdMetadata.getClosureId(function);
+            assert(closureId > 0);
+          } else {
+            closureId = 0;
+          }
+        }
       }
     }
 
@@ -381,8 +400,10 @@
         isInt ||
         constantValue != null ||
         skipCheck ||
-        receiverNotInt) {
+        receiverNotInt ||
+        closureMember != null) {
       return new InferredType(concreteClass, nullable, isInt, constantValue,
+          closureMember, closureId,
           exactTypeArguments: typeArgs,
           skipCheck: skipCheck,
           receiverNotInt: receiverNotInt);
diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart
index 8cdd58d..a0e1c21 100644
--- a/pkg/vm/lib/transformations/type_flow/types.dart
+++ b/pkg/vm/lib/transformations/type_flow/types.dart
@@ -26,7 +26,7 @@
   final int id;
   final Class classNode;
   final RecordShape? recordShape;
-  final Map<Constant, ConcreteType> _constantConcreteTypes = {};
+  final Map<TypeAttributes, ConcreteType> _concreteTypesWithAttributes = {};
 
   /// TFClass should not be instantiated directly.
   /// Instead, [TypeHierarchy.getTFClass] should be used to obtain [TFClass]
@@ -40,7 +40,21 @@
   /// Returns ConcreteType corresponding to this class and
   /// [constant] value.
   ConcreteType constantConcreteType(Constant constant) =>
-      _constantConcreteTypes[constant] ??= ConcreteType._(this, null, constant);
+      _concreteTypeWithAttributes(
+          TypeAttributes._(constant, _closureForConstant(constant)));
+
+  /// Returns ConcreteType corresponding to this class and
+  /// given [function] in [member].
+  ConcreteType closureConcreteType(Member member, LocalFunction? function) {
+    assert(function != null ||
+        (member is Procedure &&
+            !member.isGetter &&
+            !member.isSetter &&
+            !member.isStatic &&
+            !member.isAbstract));
+    return _concreteTypeWithAttributes(
+        TypeAttributes._(null, Closure._(member, function)));
+  }
 
   /// Returns ConeType corresponding to this class.
   late final ConeType coneType = ConeType._(this);
@@ -55,6 +69,26 @@
 
   @override
   String toString() => nodeToText(classNode);
+
+  ConcreteType _concreteTypeWithAttributes(TypeAttributes attr) =>
+      _concreteTypesWithAttributes[attr] ??= ConcreteType._(this, null, attr);
+
+  Closure? _closureForConstant(Constant c) {
+    if (c is InstantiationConstant) {
+      return _closureForConstant(c.tearOffConstant);
+    } else if (c is TearOffConstant) {
+      final target = c.target;
+      assert(target is Constructor ||
+          (target is Procedure &&
+              target.isStatic &&
+              !target.isGetter &&
+              !target.isSetter));
+      return Closure._(target, null);
+    } else if (c is TypedefTearOffConstant) {
+      throw 'Unexpected TypedefTearOffConstant $c';
+    }
+    return null;
+  }
 }
 
 /// Shape of a record (number of positional fields and a set of named fields).
@@ -894,6 +928,94 @@
   }
 }
 
+/// Object representing a closure function.
+///
+/// Can be used to represent local function (FunctionExpression,
+// FunctionDeclaration) or tear-off of Procedure or Constructor.
+class Closure {
+  final Member member;
+  final LocalFunction? function;
+
+  Closure._(this.member, this.function);
+
+  @override
+  int get hashCode => ((member.hashCode * 31) & kHashMask) + function.hashCode;
+
+  @override
+  bool operator ==(other) {
+    if (identical(this, other)) return true;
+    if (other is! Closure) return false;
+    return (this.member == other.member) && (this.function == other.function);
+  }
+
+  @override
+  String toString() {
+    final function = this.function;
+    if (function == null) {
+      return 'tear-off ${nodeToText(member)}';
+    }
+    switch (function) {
+      case FunctionDeclaration():
+        return function.variable.name!;
+      case FunctionExpression():
+        final location = function.location;
+        return '<anonymous closure' +
+            (location != null
+                ? ' at ${location.file.pathSegments.last}:${location.line}'
+                : '') +
+            '>';
+      default:
+        throw 'Unexpected local function ${function.runtimeType} $function';
+    }
+  }
+}
+
+/// Disjoint (mutually exclusive) attributes of Dart values.
+///
+/// If two sets of values V1 and V2 are known to have
+/// distinct attributes A1 != A2, then Intersection(V1, V2) is empty.
+///
+/// Currently used for
+///  - constant values;
+///  - closures.
+///
+class TypeAttributes {
+  final Constant? constant;
+  final Closure? closure;
+
+  TypeAttributes._(this.constant, this.closure)
+      : hashCode = constant.hashCode ^ closure.hashCode {
+    assert(constant != null || closure != null);
+  }
+
+  @override
+  final int hashCode;
+
+  @override
+  bool operator ==(other) {
+    if (identical(this, other)) return true;
+    if (other is! TypeAttributes) return false;
+    return (this.constant == other.constant) && (this.closure == other.closure);
+  }
+
+  @override
+  String toString() {
+    final buf = StringBuffer();
+    final constant = this.constant;
+    if (constant != null) {
+      buf.write(nodeToText(constant));
+    }
+    final closure = this.closure;
+    if (closure != null) {
+      if (buf.isNotEmpty) {
+        buf.write(', ');
+      }
+      buf.write(closure.toString());
+    }
+    return buf.toString();
+  }
+}
+
 /// Type representing a set of instances of a specific Dart class (no subtypes
 /// or `null` object).
 class ConcreteType extends Type implements Comparable<ConcreteType> {
@@ -920,10 +1042,11 @@
   final int numImmediateTypeArgs;
   final List<Type>? typeArgs;
 
-  // May be null if constant value is not inferred.
-  final Constant? constant;
+  // Additional type attributes such as constant value and
+  // inferred closure.
+  final TypeAttributes? attributes;
 
-  ConcreteType._(this.cls, List<Type>? typeArgs_, this.constant)
+  ConcreteType._(this.cls, List<Type>? typeArgs_, this.attributes)
       : typeArgs = typeArgs_,
         numImmediateTypeArgs =
             typeArgs_ != null ? cls.classNode.typeParameters.length : 0,
@@ -937,7 +1060,7 @@
   ConcreteType(TFClass cls, List<Type> typeArgs) : this._(cls, typeArgs, null);
 
   ConcreteType get raw => cls.concreteType;
-  bool get isRaw => typeArgs == null && constant == null;
+  bool get isRaw => typeArgs == null && attributes == null;
 
   @override
   Class? getConcreteClass(TypeHierarchy typeHierarchy) =>
@@ -1024,7 +1147,7 @@
     for (int i = 0; i < numImmediateTypeArgs; ++i) {
       hash = (((hash * 31) & kHashMask) + typeArgs![i].hashCode) & kHashMask;
     }
-    hash = ((hash * 31) & kHashMask) + constant.hashCode;
+    hash = ((hash * 31) & kHashMask) + attributes.hashCode;
     return hash;
   }
 
@@ -1034,7 +1157,7 @@
     if (other is ConcreteType) {
       if (!identical(this.cls, other.cls) ||
           this.numImmediateTypeArgs != other.numImmediateTypeArgs ||
-          !identical(this.constant, other.constant)) {
+          !identical(this.attributes, other.attributes)) {
         return false;
       }
       if (this.typeArgs != null) {
@@ -1057,7 +1180,7 @@
 
   @override
   String toString() {
-    if (typeArgs == null && constant == null) {
+    if (typeArgs == null && attributes == null) {
       return "_T (${cls})";
     }
     final StringBuffer buf = new StringBuffer();
@@ -1065,8 +1188,8 @@
     if (typeArgs != null) {
       buf.write("<${typeArgs!.take(numImmediateTypeArgs).join(', ')}>");
     }
-    if (constant != null) {
-      buf.write(", ${nodeToText(constant!)}");
+    if (attributes != null) {
+      buf.write(", $attributes");
     }
     buf.write(")");
     return buf.toString();
@@ -1090,9 +1213,9 @@
         return SetType(types);
       } else {
         assert(typeArgs != null ||
-            constant != null ||
+            attributes != null ||
             other.typeArgs != null ||
-            other.constant != null);
+            other.attributes != null);
         return raw;
       }
     } else {
@@ -1112,13 +1235,13 @@
       if (!identical(this.cls, other.cls)) {
         return emptyType;
       }
-      if (constant != null) {
-        if (other.constant == null) {
+      if (attributes != null) {
+        if (other.attributes == null) {
           return this;
         }
-        assert(constant != other.constant);
+        assert(attributes != other.attributes);
         return emptyType;
-      } else if (other.constant != null) {
+      } else if (other.attributes != null) {
         return other;
       }
 
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_async_star.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_async_star.dart.aot.expect
index 0e782ed..c511f9b 100644
--- a/pkg/vm/testcases/transformations/ffi/finalizable_async_star.dart.aot.expect
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_async_star.dart.aot.expect
@@ -14,7 +14,7 @@
 }
 [@vm.unboxing-info.metadata=()->i]static method doSomething() → core::int
   return 3;
-static method useFinalizableAsyncStar([@vm.inferred-arg-type.metadata=#lib::MyFinalizable] ffi::Finalizable finalizable) → asy::Stream<core::int> async* {
+[@vm.closure-id=1]static method useFinalizableAsyncStar([@vm.inferred-arg-type.metadata=#lib::MyFinalizable] ffi::Finalizable finalizable) → asy::Stream<core::int> async* {
   final self::MyFinalizable finalizable2 = new self::MyFinalizable::•();
   yield block {
     final synthesized core::int :expressionValueWrappedFinalizable = self::doSomething();
@@ -23,7 +23,7 @@
   } =>:expressionValueWrappedFinalizable;
   final self::MyFinalizable finalizable3 = new self::MyFinalizable::•();
   await block {
-    final synthesized asy::Future<core::int> :expressionValueWrappedFinalizable = asy::Future::sync<core::int>(() → core::int => 3);
+    final synthesized asy::Future<core::int> :expressionValueWrappedFinalizable = asy::Future::sync<core::int>([@vm.closure-id=1]() → core::int => 3);
     _in::reachabilityFence(finalizable);
     _in::reachabilityFence(finalizable2);
     _in::reachabilityFence(finalizable3);
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.aot.expect
index 90bb19e..e64a891 100644
--- a/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.aot.expect
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.aot.expect
@@ -27,10 +27,10 @@
 }
 [@vm.inferred-type.metadata=dart.ffi::Pointer]static final field ffi::Pointer<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>> free = [@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] [@vm.inferred-type.metadata=dart.ffi::DynamicLibrary] ffi::DynamicLibrary::process().{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>>("free"){(core::String) → ffi::Pointer<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>>};
 [@vm.inferred-type.metadata=dart.ffi::_NativeFinalizer]static final field ffi::NativeFinalizer _nativeFinalizer = new ffi::_NativeFinalizer::•([@vm.inferred-type.metadata=dart.ffi::Pointer] self::free);
-static method main() → asy::Future<void> async /* futureValueType= void */ {
+[@vm.closure-id=1]static method main() → asy::Future<void> async /* futureValueType= void */ {
   final self::B b = new self::B::•(new self::A::•());
   [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] final core::List<core::int> l = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::•<core::int>(0);
-  asy::Future::doWhile(() → core::bool {
+  asy::Future::doWhile([@vm.closure-id=1]() → core::bool {
     [@vm.call-site-attributes.metadata=receiverType:dart.core::List<dart.core::int>] [@vm.direct-call.metadata=dart.core::_GrowableList.add] [@vm.inferred-type.metadata=!? (skip check)] l.{core::List::add}(1){(core::int) → void};
     return true;
   });
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_sync2.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_sync2.dart.aot.expect
index 78727a8..5da7e8f 100644
--- a/pkg/vm/testcases/transformations/ffi/finalizable_sync2.dart.aot.expect
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_sync2.dart.aot.expect
@@ -11,7 +11,7 @@
     : super core::Object::•()
     ;
 }
-static method main() → void {
+[@vm.closure-id=1]static method main() → void {
   final self::MyFinalizable finalizable = new self::MyFinalizable::•();
   {
     final self::MyFinalizable finalizable2 = new self::MyFinalizable::•();
@@ -210,7 +210,7 @@
     _in::reachabilityFence(finalizable16);
   }
   {
-    synthesized core::Iterator<ffi::Finalizable> :sync-for-iterator = [@vm.inferred-type.metadata=!] [@vm.inferred-type.metadata=!] core::Iterable::generate<ffi::Finalizable>((core::int index) → self::MyFinalizable => new self::MyFinalizable::•()).{core::Iterable::iterator}{core::Iterator<ffi::Finalizable>};
+    synthesized core::Iterator<ffi::Finalizable> :sync-for-iterator = [@vm.inferred-type.metadata=!] [@vm.inferred-type.metadata=!] core::Iterable::generate<ffi::Finalizable>([@vm.closure-id=1](core::int index) → self::MyFinalizable => new self::MyFinalizable::•()).{core::Iterable::iterator}{core::Iterator<ffi::Finalizable>};
     for (; [@vm.inferred-type.metadata=dart.core::bool] :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
       final ffi::Finalizable finalizable17 = [@vm.inferred-type.metadata=#lib::MyFinalizable] :sync-for-iterator.{core::Iterator::current}{ffi::Finalizable};
       {
diff --git a/pkg/vm/testcases/transformations/ffi/native_callable.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/native_callable.dart.aot.expect
index 166b87c..cbc30fc 100644
--- a/pkg/vm/testcases/transformations/ffi/native_callable.dart.aot.expect
+++ b/pkg/vm/testcases/transformations/ffi/native_callable.dart.aot.expect
@@ -28,12 +28,12 @@
   core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
   [@vm.direct-call.metadata=dart.ffi::_NativeCallableListener.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
 }
-static method testNativeCallableListenerClosure() → void {
+[@vm.closure-id=2]static method testNativeCallableListenerClosure() → void {
   [@vm.inferred-type.metadata=dart.core::_Smi (value: 123)] core::int j = 123;
-  function closure(core::int i) → void
+[@vm.closure-id=1]  function closure(core::int i) → void
     return core::print([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(j){(core::num) → core::int});
   final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = block {
-    final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t2 = new ffi::_NativeCallableListener::•<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
+    final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t2 = new ffi::_NativeCallableListener::•<(ffi::Int32) → ffi::Void>([@vm.closure-id=2](final core::List<dynamic> args) → void
       closure(args.{core::List::[]}(0){(core::int) → dynamic}){(ffi::Int32) → ffi::Void};
 , "NativeCallable(VariableGetImpl(closure))");
     [@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] [@vm.direct-call.metadata=dart.ffi::_NativeCallableBase._pointer] #t2.{ffi::_NativeCallableBase::_pointer} = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableListener<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), [@vm.direct-call.metadata=dart.ffi::_NativeCallableListener._port] [@vm.inferred-type.metadata=dart.isolate::_RawReceivePort] #t2.{ffi::_NativeCallableListener::_port}{iso::RawReceivePort});
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect
index 528987f..145d2d2 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect
@@ -287,7 +287,7 @@
 t2 = read x
 t3 = _Call direct [#lib::foo] (t2)
 t4 = _Call direct [#lib::bar] (t2)
-t5 = _Call direct [#lib::foo] (_T (dart.core::Function)+?)
+t5 = _Call direct [#lib::foo] (_T (dart.core::_Closure, <anonymous closure at control_flow.dart:268>))
 t6* = _Call direct [#lib::C2.] (_T (#lib::C2))
 write x = t6
 RESULT: _T {}?
@@ -298,7 +298,7 @@
 t3 = _Call direct [#lib::foo] (t2)
 t4* = _Call direct [#lib::C2.] (_T (#lib::C2))
 write x = t4
-t6 = _Call direct [#lib::foo] (_T (dart.core::Function)+?)
+t6 = _Call direct [#lib::foo] (_T (dart.core::_Closure, <anonymous closure at control_flow.dart:277>))
 RESULT: t2
 ------------ switch1 ------------
 %selector = _Parameter #0 [_T (dart.core::int)+?]
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/closures.dart b/pkg/vm/testcases/transformations/type_flow/transformer/closures.dart
new file mode 100644
index 0000000..eee3b0c
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/closures.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2023, 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 C {
+  C(arg) {
+    print(arg);
+  }
+  void instanceMethod(arg) {
+    print(arg);
+  }
+
+  static void staticMethod(arg) {
+    print(arg);
+  }
+}
+
+dynamic createClosure1() => (arg) => print(arg);
+
+dynamic createClosure2() {
+  void inner(arg) {
+    print(arg);
+  }
+
+  return inner;
+}
+
+dynamic createClosure3(obj) => obj.instanceMethod;
+
+dynamic createClosure4() => C.staticMethod;
+
+dynamic createClosure5() => C.new;
+
+void useClosure11(void Function(dynamic) func) {
+  func(42);
+}
+
+void useClosure12(void Function(dynamic) func) {
+  func(42);
+}
+
+void useClosure13(void Function(dynamic) func) {
+  func(42);
+}
+
+void useClosure14(void Function(dynamic) func) {
+  func(42);
+}
+
+void useClosure15(void Function(dynamic) func) {
+  func(42);
+}
+
+void useClosure21(func) {
+  func(42);
+}
+
+void useClosure22(func) {
+  func(42);
+}
+
+void useClosure23(func) {
+  func(42);
+}
+
+void useClosure24(func) {
+  func(42);
+}
+
+void useClosure25(func) {
+  func(42);
+}
+
+class UseClosure31 {
+  final void Function(dynamic) func;
+  UseClosure31(this.func);
+  void use() {
+    func(42);
+  }
+}
+
+class UseClosure32 {
+  final void Function(dynamic) func;
+  UseClosure32(this.func);
+  void use() {
+    func(42);
+  }
+}
+
+class UseClosure33 {
+  final void Function(dynamic) func;
+  UseClosure33(this.func);
+  void use() {
+    func(42);
+  }
+}
+
+class UseClosure34 {
+  final void Function(dynamic) func;
+  UseClosure34(this.func);
+  void use() {
+    func(42);
+  }
+}
+
+class UseClosure35 {
+  final void Function(dynamic) func;
+  UseClosure35(this.func);
+  void use() {
+    func(42);
+  }
+}
+
+void main() {
+  useClosure11(createClosure1());
+  useClosure12(createClosure2());
+  useClosure13(createClosure3(C(3)));
+  useClosure14(createClosure4());
+  useClosure15(createClosure5());
+
+  useClosure21(createClosure1());
+  useClosure22(createClosure2());
+  useClosure23(createClosure3(C(3)));
+  useClosure24(createClosure4());
+  useClosure25(createClosure5());
+
+  UseClosure31(createClosure1()).use();
+  UseClosure32(createClosure2()).use();
+  UseClosure33(createClosure3(C(3))).use();
+  UseClosure34(createClosure4()).use();
+  UseClosure35(createClosure5()).use();
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/closures.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/closures.dart.expect
new file mode 100644
index 0000000..ff0026d
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/closures.dart.expect
@@ -0,0 +1,126 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+
+class C extends core::Object {
+  constructor •(dynamic arg) → self::C
+    : super core::Object::•() {
+    core::print(arg);
+  }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,methodOrSetterSelectorId:1,getterSelectorId:2]  method instanceMethod(dynamic arg) → void {
+    core::print(arg);
+  }
+  static method staticMethod(dynamic arg) → void {
+    core::print(arg);
+  }
+}
+class UseClosure31 extends core::Object {
+[@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure1)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3]  final field (dynamic) → void func;
+  constructor •([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure1)] (dynamic) → void func) → self::UseClosure31
+    : self::UseClosure31::func = func, super core::Object::•()
+    ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:4,getterSelectorId:5]  method use() → void {
+    let final core::int #t1 = 42 in [@vm.direct-call.metadata=#lib::UseClosure31.func] this.{self::UseClosure31::func}{(dynamic) → void}(#t1){(dynamic) → void};
+  }
+}
+class UseClosure32 extends core::Object {
+[@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure2)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:6]  final field (dynamic) → void func;
+  constructor •([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure2)] (dynamic) → void func) → self::UseClosure32
+    : self::UseClosure32::func = func, super core::Object::•()
+    ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8]  method use() → void {
+    let final core::int #t2 = 42 in [@vm.direct-call.metadata=#lib::UseClosure32.func] this.{self::UseClosure32::func}{(dynamic) → void}(#t2){(dynamic) → void};
+  }
+}
+class UseClosure33 extends core::Object {
+[@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::C.instanceMethod)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:9]  final field (dynamic) → void func;
+  constructor •([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 0 in #lib::C.instanceMethod)] (dynamic) → void func) → self::UseClosure33
+    : self::UseClosure33::func = func, super core::Object::•()
+    ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:10,getterSelectorId:11]  method use() → void {
+    let final core::int #t3 = 42 in [@vm.direct-call.metadata=#lib::UseClosure33.func] this.{self::UseClosure33::func}{(dynamic) → void}(#t3){(dynamic) → void};
+  }
+}
+class UseClosure34 extends core::Object {
+[@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::C.staticMethod) (closure 0 in #lib::C.staticMethod)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:12]  final field (dynamic) → void func;
+  constructor •() → self::UseClosure34
+    : self::UseClosure34::func = #C1, super core::Object::•()
+    ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:13,getterSelectorId:14]  method use() → void {
+    let final core::int #t4 = 42 in [@vm.direct-call.metadata=#lib::UseClosure34.func] this.{self::UseClosure34::func}{(dynamic) → void}(#t4){(dynamic) → void};
+  }
+}
+class UseClosure35 extends core::Object {
+[@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::C.) (closure 0 in #lib::C.)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:15]  final field (dynamic) → void func;
+  constructor •() → self::UseClosure35
+    : self::UseClosure35::func = #C2, super core::Object::•()
+    ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:16,getterSelectorId:17]  method use() → void {
+    let final core::int #t5 = 42 in [@vm.direct-call.metadata=#lib::UseClosure35.func] this.{self::UseClosure35::func}{(dynamic) → void}(#t5){(dynamic) → void};
+  }
+}
+[@vm.closure-id=1]static method createClosure1() → dynamic
+  return [@vm.closure-id=1](dynamic arg) → void => core::print(arg);
+[@vm.closure-id=1]static method createClosure2() → dynamic {
+[@vm.closure-id=1]  function inner(dynamic arg) → void {
+    core::print(arg);
+  }
+  return inner;
+}
+static method createClosure3([@vm.inferred-arg-type.metadata=#lib::C] dynamic obj) → dynamic
+  return [@vm.inferred-type.metadata=dart.core::_Closure (receiver not int) (closure 0 in #lib::C.instanceMethod)] obj{dynamic}.instanceMethod;
+static method createClosure4() → dynamic
+  return #C1;
+static method createClosure5() → dynamic
+  return #C2;
+static method useClosure11([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure1)] (dynamic) → void func) → void {
+  func(42){(dynamic) → void};
+}
+static method useClosure12([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure2)] (dynamic) → void func) → void {
+  func(42){(dynamic) → void};
+}
+static method useClosure13([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 0 in #lib::C.instanceMethod)] (dynamic) → void func) → void {
+  func(42){(dynamic) → void};
+}
+static method useClosure14() → void {
+  #C1(42){(dynamic) → void};
+}
+static method useClosure15() → void {
+  #C2(42){(dynamic) → void};
+}
+static method useClosure21([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure1)] dynamic func) → void {
+  [@vm.inferred-type.metadata=!? (receiver not int)] func{dynamic}.call(42);
+}
+static method useClosure22([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure2)] dynamic func) → void {
+  [@vm.inferred-type.metadata=!? (receiver not int)] func{dynamic}.call(42);
+}
+static method useClosure23([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 0 in #lib::C.instanceMethod)] dynamic func) → void {
+  [@vm.inferred-type.metadata=!? (receiver not int)] func{dynamic}.call(42);
+}
+static method useClosure24() → void {
+  [@vm.inferred-type.metadata=!? (receiver not int)] #C1{dynamic}.call(42);
+}
+static method useClosure25() → void {
+  [@vm.inferred-type.metadata=!? (receiver not int)] #C2{dynamic}.call(42);
+}
+static method main() → void {
+  self::useClosure11([@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure1)] self::createClosure1() as{TypeError,ForDynamic} (dynamic) → void);
+  self::useClosure12([@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure2)] self::createClosure2() as{TypeError,ForDynamic} (dynamic) → void);
+  self::useClosure13([@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::C.instanceMethod)] self::createClosure3(new self::C::•(3)) as{TypeError,ForDynamic} (dynamic) → void);
+  let final (dynamic) → void #t6 = [@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::C.staticMethod) (closure 0 in #lib::C.staticMethod)] self::createClosure4() as{TypeError,ForDynamic} (dynamic) → void in self::useClosure14();
+  let final (dynamic) → void #t7 = [@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::C.) (closure 0 in #lib::C.)] self::createClosure5() as{TypeError,ForDynamic} (dynamic) → void in self::useClosure15();
+  self::useClosure21([@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure1)] self::createClosure1());
+  self::useClosure22([@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure2)] self::createClosure2());
+  self::useClosure23([@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::C.instanceMethod)] self::createClosure3(new self::C::•(3)));
+  let final dynamic #t8 = [@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::C.staticMethod) (closure 0 in #lib::C.staticMethod)] self::createClosure4() in self::useClosure24();
+  let final dynamic #t9 = [@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::C.) (closure 0 in #lib::C.)] self::createClosure5() in self::useClosure25();
+  [@vm.direct-call.metadata=#lib::UseClosure31.use] [@vm.inferred-type.metadata=!? (skip check)] new self::UseClosure31::•([@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure1)] self::createClosure1() as{TypeError,ForDynamic} (dynamic) → void).{self::UseClosure31::use}(){() → void};
+  [@vm.direct-call.metadata=#lib::UseClosure32.use] [@vm.inferred-type.metadata=!? (skip check)] new self::UseClosure32::•([@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::createClosure2)] self::createClosure2() as{TypeError,ForDynamic} (dynamic) → void).{self::UseClosure32::use}(){() → void};
+  [@vm.direct-call.metadata=#lib::UseClosure33.use] [@vm.inferred-type.metadata=!? (skip check)] new self::UseClosure33::•([@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::C.instanceMethod)] self::createClosure3(new self::C::•(3)) as{TypeError,ForDynamic} (dynamic) → void).{self::UseClosure33::use}(){() → void};
+  [@vm.direct-call.metadata=#lib::UseClosure34.use] [@vm.inferred-type.metadata=!? (skip check)](let final (dynamic) → void #t10 = [@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::C.staticMethod) (closure 0 in #lib::C.staticMethod)] self::createClosure4() as{TypeError,ForDynamic} (dynamic) → void in new self::UseClosure34::•()).{self::UseClosure34::use}(){() → void};
+  [@vm.direct-call.metadata=#lib::UseClosure35.use] [@vm.inferred-type.metadata=!? (skip check)](let final (dynamic) → void #t11 = [@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::C.) (closure 0 in #lib::C.)] self::createClosure5() as{TypeError,ForDynamic} (dynamic) → void in new self::UseClosure35::•()).{self::UseClosure35::use}(){() → void};
+}
+constants  {
+  #C1 = static-tearoff self::C::staticMethod
+  #C2 = constructor-tearoff self::C::•
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/lists.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/lists.dart.expect
index 0988e52..f7daf42 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/lists.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/lists.dart.expect
@@ -25,8 +25,8 @@
 [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::List<dart.core::int>>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:19]  final field core::List<core::List<core::int>> generateFactory5;
 [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:20]  final field core::List<core::int> generateFactory6;
 [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:21]  final field core::List<core::int> generateFactory7;
-  synthetic constructor •() → self::A
-    : self::A::literal1 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::•<core::int>(0), self::A::literal2 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::_literal3<core::int>(1, 2, 3), self::A::filledFactory1 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] core::_List::filled<core::int>(2, 0), self::A::filledFactory2 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::filled<core::int>(2, 0), self::A::filledFactory3 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] core::_List::filled<core::int>(2, 0), self::A::filledFactory4 = let final core::bool #t1 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::filled<core::int>(2, 0, #t1), self::A::filledFactory5 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int?>] core::_List::•<core::int?>(2), self::A::filledFactory6 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int?>] core::_GrowableList::•<core::int?>(2), self::A::filledFactory7 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int?>] core::_List::•<core::int?>(2), self::A::filledFactory8 = let final core::bool #t2 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::filled<core::int?>(2, null, #t2), self::A::filledFactory9 = let final core::int #t3 = 2 in let final core::bool #t4 = true in [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int?>] core::_GrowableList::•<core::int?>(#t3), self::A::filledFactory10 = let final core::int #t5 = 2 in let final core::bool #t6 = false in [@vm.inferred-type.metadata=dart.core::_List<dart.core::int?>] core::_List::•<core::int?>(#t5), self::A::generateFactory1 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::generate<core::int>(2, (core::int i) → core::int => i), self::A::generateFactory2 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::generate<core::int>(2, (core::int i) → core::int => i), self::A::generateFactory3 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] core::_List::generate<core::int>((core::int i) → core::int => i), self::A::generateFactory4 = let final (core::int) → core::int #t7 = (core::int i) → core::int => i in let final core::bool #t8 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::generate<core::int>(#t7, #t8), self::A::generateFactory5 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::List<dart.core::int>>] core::_GrowableList::generate<core::List<core::int>>(2, (core::int _) → core::List<core::int> => core::_GrowableList::•<core::int>(0)), self::A::generateFactory6 = let final core::int #t9 = 2 in let final core::bool #t10 = true in [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::generate<core::int>(#t9, (core::int i) → core::int => i), self::A::generateFactory7 = let final core::int #t11 = 2 in let final core::bool #t12 = false in [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] core::_List::generate<core::int>((core::int i) → core::int => i), super core::Object::•()
+[@vm.closure-id=7]  synthetic constructor •() → self::A
+    : self::A::literal1 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::•<core::int>(0), self::A::literal2 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::_literal3<core::int>(1, 2, 3), self::A::filledFactory1 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] core::_List::filled<core::int>(2, 0), self::A::filledFactory2 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::filled<core::int>(2, 0), self::A::filledFactory3 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] core::_List::filled<core::int>(2, 0), self::A::filledFactory4 = let final core::bool #t1 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::filled<core::int>(2, 0, #t1), self::A::filledFactory5 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int?>] core::_List::•<core::int?>(2), self::A::filledFactory6 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int?>] core::_GrowableList::•<core::int?>(2), self::A::filledFactory7 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int?>] core::_List::•<core::int?>(2), self::A::filledFactory8 = let final core::bool #t2 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::filled<core::int?>(2, null, #t2), self::A::filledFactory9 = let final core::int #t3 = 2 in let final core::bool #t4 = true in [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int?>] core::_GrowableList::•<core::int?>(#t3), self::A::filledFactory10 = let final core::int #t5 = 2 in let final core::bool #t6 = false in [@vm.inferred-type.metadata=dart.core::_List<dart.core::int?>] core::_List::•<core::int?>(#t5), self::A::generateFactory1 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::generate<core::int>(2, [@vm.closure-id=1](core::int i) → core::int => i), self::A::generateFactory2 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::generate<core::int>(2, [@vm.closure-id=2](core::int i) → core::int => i), self::A::generateFactory3 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] core::_List::generate<core::int>([@vm.closure-id=3](core::int i) → core::int => i), self::A::generateFactory4 = let final (core::int) → core::int #t7 = [@vm.closure-id=4](core::int i) → core::int => i in let final core::bool #t8 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::generate<core::int>(#t7, #t8), self::A::generateFactory5 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::List<dart.core::int>>] core::_GrowableList::generate<core::List<core::int>>(2, [@vm.closure-id=5](core::int _) → core::List<core::int> => core::_GrowableList::•<core::int>(0)), self::A::generateFactory6 = let final core::int #t9 = 2 in let final core::bool #t10 = true in [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::generate<core::int>(#t9, [@vm.closure-id=6](core::int i) → core::int => i), self::A::generateFactory7 = let final core::int #t11 = 2 in let final core::bool #t12 = false in [@vm.inferred-type.metadata=dart.core::_List<dart.core::int>] core::_List::generate<core::int>([@vm.closure-id=7](core::int i) → core::int => i), super core::Object::•()
     ;
 }
 static method nonConstant() → dynamic
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/null_test_elimination2.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/null_test_elimination2.dart.expect
index 73ea026..fb6ac8e 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/null_test_elimination2.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/null_test_elimination2.dart.expect
@@ -5,13 +5,13 @@
 
 static method _defaultCheck([dynamic _ = #C1]) → core::bool
   return true;
-static method testStaticTypeOfConditional<T extends core::Object? = dynamic>([@vm.inferred-arg-type.metadata=dart.core::_Closure] (self::testStaticTypeOfConditional::T%) →? core::bool check) → void {
+static method testStaticTypeOfConditional<T extends core::Object? = dynamic>([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 1 in #lib::main)] (self::testStaticTypeOfConditional::T%) →? core::bool check) → void {
   if(#C2 is self::testStaticTypeOfConditional::T% && (let final (self::testStaticTypeOfConditional::T%) →? core::bool #t1 = check in _in::unsafeCast<core::Function>(#t1{(self::testStaticTypeOfConditional::T%) → core::bool}))(#C2) as{TypeError,ForDynamic} core::bool) {
     core::print("ok");
   }
 }
-static method main() → void {
-  self::testStaticTypeOfConditional<core::String>((core::String _) → core::bool => true);
+[@vm.closure-id=1]static method main() → void {
+  self::testStaticTypeOfConditional<core::String>([@vm.closure-id=1](core::String _) → core::bool => true);
 }
 constants  {
   #C1 = null
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
index bfd7bb7..7bd31a1 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
@@ -50,12 +50,12 @@
 }
 static method getDynamic() → dynamic
   return self::unknown();
-static method use(dynamic x) → dynamic
+static method use([@vm.inferred-arg-type.metadata=dart.core::_Closure] dynamic x) → dynamic
   return self::unknown(x);
 static method main(core::List<core::String> args) → dynamic {
   self::func1(self::getDynamic() as{TypeError,ForDynamic} self::T0);
   self::use(#C1);
-  self::use(new self::A::•().{self::A::method1}{(self::T0) → void});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::A.method1)] new self::A::•().{self::A::method1}{(self::T0) → void});
   self::B bb = self::getDynamic() as{TypeError,ForDynamic} self::B;
   [@vm.direct-call.metadata=#lib::C.method2] [@vm.inferred-type.metadata=!? (skip check)] bb.{self::B::method2}(self::getDynamic()){(dynamic) → void};
   self::getDynamic(){dynamic}.method3(self::getDynamic());
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart.expect
index ee20971..e3843a2 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/create_test.dart.expect
@@ -17,7 +17,7 @@
 import "package:matcher/src/type_matcher.dart";
 import "package:matcher/src/util.dart";
 
-static method main() → dynamic {
+[@vm.closure-id=1]static method main() → dynamic {
   [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep foo = let final pb::FooKeep #t1 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep::•() in block {
     [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::barKeep} = let final pb::BarKeep #t2 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] pb::BarKeep::•() in block {
       [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pb::BarKeep::aKeep} = 5;
@@ -27,7 +27,7 @@
     } =>#t3){(core::String, pb::BarKeep) → void};
     [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::aKeep} = 43;
   } =>#t1;
-  sca::test(() → Null {
+  sca::test([@vm.closure-id=1]() → Null {
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=int] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] foo.{pb::FooKeep::barKeep}{pb::BarKeep}.{pb::BarKeep::aKeep}{core::int}, 5);
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=int] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] foo.{pb::FooKeep::mapKeep}{core::Map<core::String, pb::BarKeep>}.{core::Map::[]}("foo"){(core::Object?) → pb::BarKeep?}!.{pb::BarKeep::aKeep}{core::int}, 2);
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.hasHasKeep] [@vm.inferred-type.metadata=dart.core::bool (skip check)] foo.{pb::FooKeep::hasHasKeep}(){() → core::bool}, false);
@@ -47,7 +47,7 @@
 [@vm.inferred-type.metadata=library package:protobuf/protobuf.dart::BuilderInfo]  static final field pro::BuilderInfo _i = let final pro::BuilderInfo #t1 = new pro::BuilderInfo::•(#C1 ?{core::String} "" : "FooKeep") in block {
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::BarKeep>(1, #C1 ?{core::String} "" : "barKeep", #C2){(core::int, core::String, {protoName: core::String?, required subBuilder: () → self::BarKeep}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<Null>(0, null, null, null, null){(core::int, core::String, core::int?, dynamic, () →? pro::GeneratedMessage, (core::int) →? pro::ProtobufEnum?, core::List<pro::ProtobufEnum>?, {protoName: core::String?}) → void};
-    let final core::String #t2 = #C1 ?{core::String} "" : "mapKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String, self::BarKeep>(#C2){(core::int, core::String, {defaultEnumValue: pro::ProtobufEnum?, entryClassName: core::String?, enumValues: core::List<pro::ProtobufEnum>?, required keyFieldType: core::int, packageName: pro::PackageName, protoName: core::String?, valueCreator: () →? pro::GeneratedMessage, valueDefaultOrMaker: dynamic, required valueFieldType: core::int, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
+    let final core::String #t2 = #C1 ?{core::String} "" : "mapKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String, self::BarKeep>(){(core::int, core::String, {defaultEnumValue: pro::ProtobufEnum?, entryClassName: core::String?, enumValues: core::List<pro::ProtobufEnum>?, required keyFieldType: core::int, packageName: pro::PackageName, protoName: core::String?, valueCreator: () →? pro::GeneratedMessage, valueDefaultOrMaker: dynamic, required valueFieldType: core::int, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<Null>(0, null, null, null, null){(core::int, core::String, core::int?, dynamic, () →? pro::GeneratedMessage, (core::int) →? pro::ProtobufEnum?, core::List<pro::ProtobufEnum>?, {protoName: core::String?}) → void};
     let final core::String #t3 = #C1 ?{core::String} "" : "aKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::a}<core::int>(5){(core::int, core::String, core::int, {defaultOrMaker: dynamic, enumValues: core::List<pro::ProtobufEnum>?, protoName: core::String?, subBuilder: () →? pro::GeneratedMessage, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::HasKeep>(6, #C1 ?{core::String} "" : "hasKeep", #C3){(core::int, core::String, {protoName: core::String?, required subBuilder: () → self::HasKeep}) → void};
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart.expect
index ea6bed4..123d7b1 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/decode_test.dart.expect
@@ -18,9 +18,9 @@
 import "package:matcher/src/util.dart";
 
 [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>]static field core::List<core::int> buffer = <core::int>[10, 4, 8, 5, 16, 4, 26, 9, 10, 3, 102, 111, 111, 18, 2, 8, 42, 34, 9, 10, 3, 122, 111, 112, 18, 2, 8, 3, 40, 43, 50, 0, 58, 0];
-static method main() → dynamic {
+[@vm.closure-id=1]static method main() → dynamic {
   [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep foo = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep::fromBuffer([@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] self::buffer);
-  sca::test(() → Null {
+  sca::test([@vm.closure-id=1]() → Null {
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=int] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] foo.{pb::FooKeep::mapKeep}{core::Map<core::String, pb::BarKeep>}.{core::Map::[]}("foo"){(core::Object?) → pb::BarKeep?}!.{pb::BarKeep::aKeep}{core::int}, 42);
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=int] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] foo.{pb::FooKeep::barKeep}{pb::BarKeep}.{pb::BarKeep::aKeep}{core::int}, 5);
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=int] foo.{pb::FooKeep::aKeep}{core::int}, 43);
@@ -40,7 +40,7 @@
 [@vm.inferred-type.metadata=library package:protobuf/protobuf.dart::BuilderInfo]  static final field pro::BuilderInfo _i = let final pro::BuilderInfo #t1 = new pro::BuilderInfo::•(#C1 ?{core::String} "" : "FooKeep", createEmptyInstance: #C2) in block {
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::BarKeep>(1, #C1 ?{core::String} "" : "barKeep", #C3){(core::int, core::String, {protoName: core::String?, required subBuilder: () → self::BarKeep}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<Null>(0, null, null, null, null){(core::int, core::String, core::int?, dynamic, () →? pro::GeneratedMessage, (core::int) →? pro::ProtobufEnum?, core::List<pro::ProtobufEnum>?, {protoName: core::String?}) → void};
-    let final core::String #t2 = #C1 ?{core::String} "" : "mapKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String, self::BarKeep>(#C3){(core::int, core::String, {defaultEnumValue: pro::ProtobufEnum?, entryClassName: core::String?, enumValues: core::List<pro::ProtobufEnum>?, required keyFieldType: core::int, packageName: pro::PackageName, protoName: core::String?, valueCreator: () →? pro::GeneratedMessage, valueDefaultOrMaker: dynamic, required valueFieldType: core::int, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
+    let final core::String #t2 = #C1 ?{core::String} "" : "mapKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String, self::BarKeep>(){(core::int, core::String, {defaultEnumValue: pro::ProtobufEnum?, entryClassName: core::String?, enumValues: core::List<pro::ProtobufEnum>?, required keyFieldType: core::int, packageName: pro::PackageName, protoName: core::String?, valueCreator: () →? pro::GeneratedMessage, valueDefaultOrMaker: dynamic, required valueFieldType: core::int, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<Null>(0, null, null, null, null){(core::int, core::String, core::int?, dynamic, () →? pro::GeneratedMessage, (core::int) →? pro::ProtobufEnum?, core::List<pro::ProtobufEnum>?, {protoName: core::String?}) → void};
     let final core::String #t3 = #C1 ?{core::String} "" : "aKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::a}<core::int>(5){(core::int, core::String, core::int, {defaultOrMaker: dynamic, enumValues: core::List<pro::ProtobufEnum>?, protoName: core::String?, subBuilder: () →? pro::GeneratedMessage, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::HasKeep>(6, #C1 ?{core::String} "" : "hasKeep", #C4){(core::int, core::String, {protoName: core::String?, required subBuilder: () → self::HasKeep}) → void};
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart.expect
index 6929208..cfe746a 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/freeze_test.dart.expect
@@ -20,7 +20,7 @@
 import "package:matcher/src/type_matcher.dart";
 import "package:matcher/src/util.dart";
 
-static method main() → dynamic {
+[@vm.closure-id=2]static method main() → dynamic {
   [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep foo = let final pb::FooKeep #t1 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep] pb::FooKeep::•() in block {
     [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::barKeep} = let final pb::BarKeep #t2 = [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] pb::BarKeep::•() in block {
       [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pb::BarKeep::aKeep} = 5;
@@ -30,13 +30,13 @@
     } =>#t3){(core::String, pb::BarKeep) → void};
     [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pb::FooKeep::aKeep} = 43;
   } =>#t1;
-  sca::test(() → Null {
+  sca::test([@vm.closure-id=1]() → Null {
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::GeneratedMessage.freeze] [@vm.inferred-type.metadata=!? (skip check)] foo.{pro::GeneratedMessage::freeze}(){() → pro::GeneratedMessage};
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=int] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.barKeep] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep] foo.{pb::FooKeep::barKeep}{pb::BarKeep}.{pb::BarKeep::aKeep}{core::int}, 5);
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep.aKeep] [@vm.inferred-type.metadata=int] [@vm.inferred-type.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::BarKeep?] [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.mapKeep] [@vm.inferred-type.metadata=!] foo.{pb::FooKeep::mapKeep}{core::Map<core::String, pb::BarKeep>}.{core::Map::[]}("foo"){(core::Object?) → pb::BarKeep?}!.{pb::BarKeep::aKeep}{core::int}, 2);
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.hasHasKeep] [@vm.inferred-type.metadata=dart.core::bool (skip check)] foo.{pb::FooKeep::hasHasKeep}(){() → core::bool}, false);
     exp::expect([@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.aKeep] [@vm.inferred-type.metadata=int] foo.{pb::FooKeep::aKeep}{core::int}, 43);
-    exp::expect(() → void => [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.clearClearKeep] [@vm.inferred-type.metadata=!? (skip check)] foo.{pb::FooKeep::clearClearKeep}(){() → void}, [@vm.inferred-type.metadata=library package:matcher/src/expect/throws_matcher.dart::Throws] thr::throwsA());
+    exp::expect([@vm.closure-id=2]() → void => [@vm.direct-call.metadata=library file:pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/generated/foo.pb.dart::FooKeep.clearClearKeep] [@vm.inferred-type.metadata=!? (skip check)] foo.{pb::FooKeep::clearClearKeep}(){() → void}, [@vm.inferred-type.metadata=library package:matcher/src/expect/throws_matcher.dart::Throws] thr::throwsA());
   });
 }
 library foo.pb.dart;
@@ -51,7 +51,7 @@
 [@vm.inferred-type.metadata=library package:protobuf/protobuf.dart::BuilderInfo]  static final field pro::BuilderInfo _i = let final pro::BuilderInfo #t1 = new pro::BuilderInfo::•(#C1 ?{core::String} "" : "FooKeep") in block {
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::BarKeep>(1, #C1 ?{core::String} "" : "barKeep", #C2){(core::int, core::String, {protoName: core::String?, required subBuilder: () → self::BarKeep}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<Null>(0, null, null, null, null){(core::int, core::String, core::int?, dynamic, () →? pro::GeneratedMessage, (core::int) →? pro::ProtobufEnum?, core::List<pro::ProtobufEnum>?, {protoName: core::String?}) → void};
-    let final core::String #t2 = #C1 ?{core::String} "" : "mapKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String, self::BarKeep>(#C2){(core::int, core::String, {defaultEnumValue: pro::ProtobufEnum?, entryClassName: core::String?, enumValues: core::List<pro::ProtobufEnum>?, required keyFieldType: core::int, packageName: pro::PackageName, protoName: core::String?, valueCreator: () →? pro::GeneratedMessage, valueDefaultOrMaker: dynamic, required valueFieldType: core::int, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
+    let final core::String #t2 = #C1 ?{core::String} "" : "mapKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.m] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::m}<core::String, self::BarKeep>(){(core::int, core::String, {defaultEnumValue: pro::ProtobufEnum?, entryClassName: core::String?, enumValues: core::List<pro::ProtobufEnum>?, required keyFieldType: core::int, packageName: pro::PackageName, protoName: core::String?, valueCreator: () →? pro::GeneratedMessage, valueDefaultOrMaker: dynamic, required valueFieldType: core::int, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.add] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::add}<Null>(0, null, null, null, null){(core::int, core::String, core::int?, dynamic, () →? pro::GeneratedMessage, (core::int) →? pro::ProtobufEnum?, core::List<pro::ProtobufEnum>?, {protoName: core::String?}) → void};
     let final core::String #t3 = #C1 ?{core::String} "" : "aKeep" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.a] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::a}<core::int>(5){(core::int, core::String, core::int, {defaultOrMaker: dynamic, enumValues: core::List<pro::ProtobufEnum>?, protoName: core::String?, subBuilder: () →? pro::GeneratedMessage, valueOf: (core::int) →? pro::ProtobufEnum?}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t1.{pro::BuilderInfo::aOM}<self::HasKeep>(6, #C1 ?{core::String} "" : "hasKeep", #C3){(core::int, core::String, {protoName: core::String?, required subBuilder: () → self::HasKeep}) → void};
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart.expect
index c28c3cd..2efe6aa 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/name_mangling_test.dart.expect
@@ -34,7 +34,7 @@
 }
 class NameManglingKeep extends pro::GeneratedMessage {
 [@vm.inferred-type.metadata=library package:protobuf/protobuf.dart::BuilderInfo]  static final field pro::BuilderInfo _i = let final pro::BuilderInfo #t2 = new pro::BuilderInfo::•(#C1 ?{core::String} "" : "NameManglingKeep", #C6) in block {
-    let final core::String #t3 = #C1 ?{core::String} "" : "clone" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::aOM}<self::AKeep>(#C2){(core::int, core::String, {protoName: core::String?, required subBuilder: () → self::AKeep}) → void};
+    let final core::String #t3 = #C1 ?{core::String} "" : "clone" in [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.aOM] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::aOM}<self::AKeep>(){(core::int, core::String, {protoName: core::String?, required subBuilder: () → self::AKeep}) → void};
     [@vm.direct-call.metadata=library package:protobuf/protobuf.dart::BuilderInfo.hasRequiredFields] [@vm.inferred-type.metadata=!? (skip check)] #t2.{pro::BuilderInfo::hasRequiredFields} = false;
   } =>#t2;
   constructor _() → self::NameManglingKeep
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
index bae6c58..07dffe4 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
@@ -112,9 +112,9 @@
 }
 [@vm.inferred-type.metadata=dart.core::bool?]static field core::bool? ok;
 [@vm.inferred-type.metadata=#lib::B3?]static field dynamic bb3 = new self::B3::•();
-[@vm.inferred-type.metadata=dart.core::_Closure]static field core::Function unknown3 = () → dynamic => self::bb3;
+[@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::unknown3)] [@vm.closure-id=1]static field core::Function unknown3 = [@vm.closure-id=1]() → dynamic => self::bb3;
 [@vm.inferred-type.metadata=#lib::B4?]static field dynamic bb4 = new self::B4::•();
-[@vm.inferred-type.metadata=dart.core::_Closure]static field core::Function unknown4 = () → dynamic => self::bb4;
+[@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::unknown4)] [@vm.closure-id=1]static field core::Function unknown4 = [@vm.closure-id=1]() → dynamic => self::bb4;
 static method test1() → void {
   self::B1 bb = new self::B1::•();
   let final self::B1 #t1 = bb in let final core::int #t2 = 1 in let final core::int #t3 = 2 in let final core::int #t4 = 3 in let final core::int #t5 = 4 in let final self::T1 #t6 = new self::T1::•() in [@vm.direct-call.metadata=#lib::A1.call] [@vm.inferred-type.metadata=!? (skip check)] [@vm.direct-call.metadata=#lib::B1.aa1] [@vm.inferred-type.metadata=#lib::A1] #t1.{self::B1::aa1}{self::A1}.{self::A1::call}(#t2, #t3, #t4, #t5, #t6){([dynamic, dynamic, dynamic, dynamic, dynamic]) → void};
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart.expect
index 95580c3..0b8a3d1 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter81068.dart.expect
@@ -17,11 +17,11 @@
     ;
 [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2]  method noSuchMethod(core::Invocation i) → dynamic
     return throw "Not implemented";
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4]  no-such-method-forwarder method then<R extends core::Object? = dynamic>((self::B::T%) → FutureOr<self::B::then::R%>onValue, {core::Function? onError = #C1}) → asy::Future<self::B::then::R%>
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4]  no-such-method-forwarder method then<R extends core::Object? = dynamic>([@vm.inferred-arg-type.metadata=dart.core::_Closure] (self::B::T%) → FutureOr<self::B::then::R%>onValue, {[@vm.inferred-arg-type.metadata=dart.core::_Closure?] core::Function? onError = #C1}) → asy::Future<self::B::then::R%>
     return _in::unsafeCast<asy::Future<self::B::then::R%>>([@vm.direct-call.metadata=#lib::B.noSuchMethod] [@vm.inferred-type.metadata=! (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#C2, 0, [@vm.inferred-type.metadata=dart.core::_ImmutableList] core::List::unmodifiable<core::Type*>([@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::Type*>] core::_GrowableList::_literal1<core::Type*>(self::B::then::R%)), [@vm.inferred-type.metadata=dart.core::_ImmutableList] core::List::unmodifiable<dynamic>([@vm.inferred-type.metadata=dart.core::_GrowableList<dynamic>] core::_GrowableList::_literal1<dynamic>(onValue)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol*, dynamic>] core::Map::unmodifiable<core::Symbol*, dynamic>(<core::Symbol*, dynamic>{#C3: onError}))){(core::Invocation) → dynamic});
 [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6]  no-such-method-forwarder method catchError([@vm.inferred-arg-type.metadata=dart.core::_Closure] core::Function onError) → asy::Future<self::B::T%>
     return _in::unsafeCast<asy::Future<self::B::T%>>([@vm.direct-call.metadata=#lib::B.noSuchMethod] [@vm.inferred-type.metadata=! (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#C4, 0, #C5, [@vm.inferred-type.metadata=dart.core::_ImmutableList] core::List::unmodifiable<dynamic>([@vm.inferred-type.metadata=dart.core::_GrowableList<dynamic>] core::_GrowableList::_literal1<dynamic>(onError)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol*, dynamic>] core::Map::unmodifiable<core::Symbol*, dynamic>(<core::Symbol*, dynamic>{#C6: #C1}))){(core::Invocation) → dynamic});
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8]  no-such-method-forwarder method whenComplete(() → FutureOr<void>action) → asy::Future<self::B::T%>
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8]  no-such-method-forwarder method whenComplete([@vm.inferred-arg-type.metadata=dart.core::_Closure] () → FutureOr<void>action) → asy::Future<self::B::T%>
     return _in::unsafeCast<asy::Future<self::B::T%>>([@vm.direct-call.metadata=#lib::B.noSuchMethod] [@vm.inferred-type.metadata=! (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#C7, 0, #C5, [@vm.inferred-type.metadata=dart.core::_ImmutableList] core::List::unmodifiable<dynamic>([@vm.inferred-type.metadata=dart.core::_GrowableList<dynamic>] core::_GrowableList::_literal1<dynamic>(action)), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol*, dynamic>] core::Map::unmodifiable<core::Symbol*, dynamic>(#C8))){(core::Invocation) → dynamic});
 }
 static method createB<T extends core::Object? = dynamic>() → self::B<dynamic>
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
index 2d569fc..d1c2fe2 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
@@ -15,10 +15,10 @@
     return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=! (skip check)] 1.{core::num::+}([@vm.direct-call.metadata=#lib::B.foo] [@vm.inferred-type.metadata=!? (receiver not int)] [@vm.inferred-type.metadata=#lib::B] self::knownResult(){dynamic}.foo() as{TypeError,ForDynamic} core::num){(core::num) → core::num} as core::int;
 }
 class TearOffDynamicMethod extends core::Object {
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4]  field dynamic bazz;
+[@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::B.foo)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4]  field dynamic bazz;
   constructor •(dynamic arg) → self::TearOffDynamicMethod
-    : self::TearOffDynamicMethod::bazz = arg{dynamic}.foo, super core::Object::•() {
-    [@vm.direct-call.metadata=#lib::TearOffDynamicMethod.bazz] this.{self::TearOffDynamicMethod::bazz}{dynamic}{dynamic}.call();
+    : self::TearOffDynamicMethod::bazz = [@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::B.foo)] arg{dynamic}.foo, super core::Object::•() {
+    [@vm.inferred-type.metadata=!? (receiver not int)] [@vm.direct-call.metadata=#lib::TearOffDynamicMethod.bazz] [@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::B.foo)] this.{self::TearOffDynamicMethod::bazz}{dynamic}{dynamic}.call();
   }
 }
 static method knownResult() → dynamic
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
index c35c81d..cd8b2e5 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
@@ -19,13 +19,13 @@
     return 3;
 }
 class TearOffInterfaceMethod extends core::Object {
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6]  field dynamic bazz;
+[@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::B.foo)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6]  field dynamic bazz;
   constructor •([@vm.inferred-arg-type.metadata=#lib::B] self::A arg) → self::TearOffInterfaceMethod
-    : self::TearOffInterfaceMethod::bazz = arg.{self::A::foo}{() → core::int}, super core::Object::•()
+    : self::TearOffInterfaceMethod::bazz = [@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::B.foo)] arg.{self::A::foo}{() → core::int}, super core::Object::•()
     ;
 }
 static method knownResult() → dynamic
   return new self::B::•();
 static method main(core::List<core::String> args) → dynamic {
-  [@vm.direct-call.metadata=#lib::TearOffInterfaceMethod.bazz] new self::TearOffInterfaceMethod::•(new self::B::•()).{self::TearOffInterfaceMethod::bazz}{dynamic}{dynamic}.call();
+  [@vm.inferred-type.metadata=!? (receiver not int)] [@vm.direct-call.metadata=#lib::TearOffInterfaceMethod.bazz] [@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::B.foo)] new self::TearOffInterfaceMethod::•(new self::B::•()).{self::TearOffInterfaceMethod::bazz}{dynamic}{dynamic}.call();
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
index c226a22..b079063 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
@@ -24,15 +24,15 @@
     ;
 [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] [@vm.unboxing-info.metadata=()->i]  method foo() → core::int
     return _in::unsafeCast<core::int>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] 3.{core::num::+}(_in::unsafeCast<core::num>([@vm.direct-call.metadata=#lib::B.foo] [@vm.inferred-type.metadata=int (receiver not int)] [@vm.inferred-type.metadata=#lib::B] self::knownResult(){dynamic}.foo())){(core::num) → core::num});
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6]  method doCall(dynamic x) → core::int?
-    return x{dynamic}.call() as{TypeError,ForDynamic} core::int?;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6]  method doCall([@vm.inferred-arg-type.metadata=dart.core::_Closure (closure 0 in #lib::Base.foo)] dynamic x) → core::int?
+    return [@vm.inferred-type.metadata=!? (receiver not int)] x{dynamic}.call() as{TypeError,ForDynamic} core::int?;
 }
 class TearOffSuperMethod extends self::Base {
   synthetic constructor •() → self::TearOffSuperMethod
     : super self::Base::•()
     ;
 [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8]  method bar() → core::int?
-    return [@vm.direct-call.metadata=#lib::Base.doCall] [@vm.inferred-type.metadata=int? (skip check)] this.{self::Base::doCall}(super.{self::Base::foo}){(dynamic) → core::int?};
+    return [@vm.direct-call.metadata=#lib::Base.doCall] [@vm.inferred-type.metadata=int? (skip check)] this.{self::Base::doCall}([@vm.inferred-type.metadata=dart.core::_Closure (closure 0 in #lib::Base.foo)] super.{self::Base::foo}){(dynamic) → core::int?};
 }
 [@vm.inferred-type.metadata=#lib::B]static field self::A aa = new self::B::•();
 static method knownResult() → dynamic
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/unboxed_instance_method_tearoff.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/unboxed_instance_method_tearoff.dart.expect
index e53c68e..2333532 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/unboxed_instance_method_tearoff.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/unboxed_instance_method_tearoff.dart.expect
@@ -170,14 +170,14 @@
   self::use([@vm.inferred-type.metadata=dart.core::_Double?] d.{self::Interface::returnBoxedNullableIntOrDouble}(null){(self::X?) → dynamic});
   self::use([@vm.inferred-type.metadata=#lib::X?] d.{self::Interface::returnBoxedNullableX}(null){(self::X?) → dynamic});
   self::use([@vm.inferred-type.metadata=#lib::X] d.{self::Interface::returnBoxedX}(null){(self::X?) → dynamic});
-  self::use(d.{self::Interface::takePositional}{(core::int?, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic) → void});
-  self::use(d.{self::Interface::returnUnboxedSmi}{(self::X?) → dynamic});
-  self::use(d.{self::Interface::returnUnboxedInt}{(self::X?) → dynamic});
-  self::use(d.{self::Interface::returnUnboxedDouble}{(self::X?) → dynamic});
-  self::use(d.{self::Interface::returnBoxedNullableInt}{(self::X?) → dynamic});
-  self::use(d.{self::Interface::returnBoxedNullableDouble}{(self::X?) → dynamic});
-  self::use(d.{self::Interface::returnBoxedIntOrDouble}{(self::X?) → dynamic});
-  self::use(d.{self::Interface::returnBoxedNullableIntOrDouble}{(self::X?) → dynamic});
-  self::use(d.{self::Interface::returnBoxedNullableX}{(self::X?) → dynamic});
-  self::use(d.{self::Interface::returnBoxedX}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::takePositional}{(core::int?, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic) → void});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnUnboxedSmi}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnUnboxedInt}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnUnboxedDouble}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnBoxedNullableInt}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnBoxedNullableDouble}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnBoxedIntOrDouble}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnBoxedNullableIntOrDouble}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnBoxedNullableX}{(self::X?) → dynamic});
+  self::use([@vm.inferred-type.metadata=dart.core::_Closure] d.{self::Interface::returnBoxedX}{(self::X?) → dynamic});
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/weak.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/weak.dart.expect
index 9ba6795..624fcc0 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/weak.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/weak.dart.expect
@@ -4,7 +4,7 @@
 
 class A extends core::Object {
 [@vm.inferred-type.metadata=dart.core::_Smi (value: 1)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=(i)->i]  field core::int x;
-[@vm.inferred-type.metadata=dart.core::_Closure] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4]  field core::Function y = #C1;
+[@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::used2) (closure 0 in #lib::used2)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4]  field core::Function y = #C1;
   synthetic constructor •() → self::A
     : self::A::x = [@vm.inferred-type.metadata=dart.core::_Smi (value: 1)] self::used1(), super core::Object::•()
     ;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect
index 8c4e04c..b2dc6a9 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect
@@ -52,11 +52,11 @@
   }
 }
 static method foo() → dynamic {}
-static method main() → void {
+[@vm.closure-id=1]static method main() → void {
   new self::A::•();
   new self::B::•();
   [@vm.inferred-type.metadata=#lib::D] self::C<core::num> c = new self::D::•();
-  exp::Expect::throws<core::Object>(() → void {
+  exp::Expect::throws<core::Object>([@vm.closure-id=1]() → void {
     [@vm.call-site-attributes.metadata=receiverType:#lib::C<dart.core::num>] [@vm.direct-call.metadata=#lib::D.bar] c.{self::C::bar} = 3.14;
   });
   self::E e = new self::F::•();
diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc
index 7e5a605..6674771 100644
--- a/runtime/vm/code_descriptors.cc
+++ b/runtime/vm/code_descriptors.cc
@@ -594,11 +594,9 @@
     const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg);
     switch (opcode) {
       case CodeSourceMapOps::kChangePosition: {
-        const TokenPosition& old_token =
-            (*token_positions)[token_positions->length() - 1];
-        (*token_positions)[token_positions->length() - 1] =
-            TokenPosition::Deserialize(
-                Utils::AddWithWrapAround(arg, old_token.Serialize()));
+        const TokenPosition& old_token = token_positions->Last();
+        token_positions->Last() = TokenPosition::Deserialize(
+            Utils::AddWithWrapAround(arg, old_token.Serialize()));
         break;
       }
       case CodeSourceMapOps::kAdvancePC: {
@@ -692,12 +690,14 @@
 
 void CodeSourceMapReader::DumpInlineIntervals(uword start) {
   GrowableArray<const Function*> function_stack;
+  GrowableArray<TokenPosition> token_positions;
   LogBlock lb;
   NoSafepointScope no_safepoint;
   ReadStream stream(map_.Data(), map_.Length());
 
   int32_t current_pc_offset = 0;
   function_stack.Add(&root_);
+  token_positions.Add(InitialPosition());
 
   THR_Print("Inline intervals for function '%s' {\n",
             root_.ToFullyQualifiedCString());
@@ -706,13 +706,19 @@
     const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg);
     switch (opcode) {
       case CodeSourceMapOps::kChangePosition: {
+        const TokenPosition& old_token = token_positions.Last();
+        token_positions.Last() = TokenPosition::Deserialize(
+            Utils::AddWithWrapAround(arg, old_token.Serialize()));
         break;
       }
       case CodeSourceMapOps::kAdvancePC: {
         THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
                   start + current_pc_offset + arg - 1);
         for (intptr_t i = 0; i < function_stack.length(); i++) {
-          THR_Print("%s ", function_stack[i]->ToCString());
+          THR_Print("%s", function_stack[i]->ToCString());
+          if (token_positions[i].IsReal()) {
+            THR_Print(" @%" Pd, token_positions[i].Pos());
+          }
         }
         THR_Print("\n");
         current_pc_offset += arg;
@@ -721,12 +727,15 @@
       case CodeSourceMapOps::kPushFunction: {
         function_stack.Add(
             &Function::Handle(Function::RawCast(functions_.At(arg))));
+        token_positions.Add(InitialPosition());
         break;
       }
       case CodeSourceMapOps::kPopFunction: {
         // We never pop the root function.
         ASSERT(function_stack.length() > 1);
+        ASSERT(token_positions.length() > 1);
         function_stack.RemoveLast();
+        token_positions.RemoveLast();
         break;
       }
       case CodeSourceMapOps::kNullCheck: {
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 8b8f7f4..35655e6 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -3912,7 +3912,7 @@
   Array& argument_names =
       Array::ZoneHandle(Z, GetOptionalParameterNames(function));
 
-  closure += StaticCall(TokenPosition::kNoSource, target, argument_count,
+  closure += StaticCall(function.token_pos(), target, argument_count,
                         argument_names, ICData::kNoRebind,
                         /* result_type = */ nullptr, type_args_len);