[dart2js] Fix record tracing w.r.t to closures.

We were losing track of records (and their contents) when they flowed into a closure. Now we correctly bailout when we encounter this situation.

This matches the behavior we have for tracing other collection objects.

Change-Id: I2b0945f9ab9732e3f460cefee451c7b853316ad5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478780
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Nate Biggs
2026-02-06 16:06:56 -08:00
committed by Commit Queue
parent 352d5bab45
commit 2308a15e09
2 changed files with 89 additions and 0 deletions
@@ -1,6 +1,9 @@
// 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 '../common/names.dart';
import '../elements/entities.dart';
import 'node_tracer.dart';
import 'type_graph_nodes.dart';
@@ -16,4 +19,34 @@ class RecordTracerVisitor extends TracerVisitor {
}
return false;
}
@override
void visitClosureCallSiteTypeInformation(
ClosureCallSiteTypeInformation info,
) {
bailout('Passed to a closure');
}
@override
void visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) {
super.visitStaticCallSiteTypeInformation(info);
MemberEntity called = info.calledElement;
if (inferrer.closedWorld.commonElements.isForeign(called) &&
called.name == Identifiers.js) {
bailout('Used in JS ${info.debugName}');
}
}
@override
void visitDynamicCallSiteTypeInformation(
DynamicCallSiteTypeInformation info,
) {
super.visitDynamicCallSiteTypeInformation(info);
final selector = info.selector!;
if (selector.isCall &&
(info.hasClosureCallTargets || dynamicCallTargetsNonFunction(info))) {
bailout('Passed to a closure');
return;
}
}
}
@@ -0,0 +1,56 @@
// Copyright (c) 2026, 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.
typedef F<T> = void Function(T chunk);
typedef A<T> = ({F<T> sendChunk, int whatever});
class M {
final List<String> content;
M({required this.content});
}
class G {
final M _chunk;
G(this._chunk);
String get text => _chunk.content.join('');
}
void produceIt(A<M> ctx) {
int base(A<M> b) {
b.sendChunk(M(content: ["content-value"]));
return 1;
}
final composeModel = _values.fold(
base,
(next, mw) =>
(c) => mw.f(c, next),
);
composeModel((
whatever: 1,
sendChunk: (c) => ctx.sendChunk(M(content: c.content)),
));
}
void useIt(G g) {
print(g.text);
}
class Wrapper {
int f(A<M> c, int Function(A<M>) next) {
return next(c);
}
}
List<Wrapper> _values = [Wrapper(), Wrapper(), Wrapper()];
void main() {
produceIt((
whatever: 2,
sendChunk: (c) {
useIt(G(c));
},
));
}