Skip to content

Commit 3e72526

Browse files
authored
Detect comparisons between large unions or intersections (#41574)
* Detect comparisons between large unions or intersections If their multiplied size is greater than 1E6 (chosen based on the repro in #41517), then we'll expend a large amount of time and memory comparing them, so record a trace event. Related to #41517 * Make an exception for primitive union comparisons * Address PR feedback * Pick up baseline change from master * Eliminate diagnostic and only trace * Don't check reportErrors
1 parent 035c7ca commit 3e72526

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

src/compiler/checker.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16892,7 +16892,6 @@ namespace ts {
1689216892
return isIdenticalTo(source, target);
1689316893
}
1689416894

16895-
1689616895
// We fastpath comparing a type parameter to exactly its constraint, as this is _super_ common,
1689716896
// and otherwise, for type parameters in large unions, causes us to need to compare the union to itself,
1689816897
// as we break down the _target_ union first, _then_ get the source constraint - so for every
@@ -16953,6 +16952,8 @@ namespace ts {
1695316952
return Ternary.False;
1695416953
}
1695516954

16955+
traceUnionsOrIntersectionsTooLarge(source, target);
16956+
1695616957
let result = Ternary.False;
1695716958
const saveErrorInfo = captureErrorCalculationState();
1695816959

@@ -17088,11 +17089,41 @@ namespace ts {
1708817089
}
1708917090
}
1709017091

17092+
function traceUnionsOrIntersectionsTooLarge(source: Type, target: Type): void {
17093+
if (!tracing.isTracing()) {
17094+
return;
17095+
}
17096+
17097+
if ((source.flags & TypeFlags.UnionOrIntersection) && (target.flags & TypeFlags.UnionOrIntersection)) {
17098+
const sourceUnionOrIntersection = source as UnionOrIntersectionType;
17099+
const targetUnionOrIntersection = target as UnionOrIntersectionType;
17100+
17101+
if (sourceUnionOrIntersection.objectFlags & targetUnionOrIntersection.objectFlags & ObjectFlags.PrimitiveUnion) {
17102+
// There's a fast path for comparing primitive unions
17103+
return;
17104+
}
17105+
17106+
const sourceSize = sourceUnionOrIntersection.types.length;
17107+
const targetSize = targetUnionOrIntersection.types.length;
17108+
if (sourceSize * targetSize > 1E6) {
17109+
tracing.instant(tracing.Phase.CheckTypes, "traceUnionsOrIntersectionsTooLarge_DepthLimit", {
17110+
sourceId: source.id,
17111+
sourceSize,
17112+
targetId: target.id,
17113+
targetSize,
17114+
pos: errorNode?.pos,
17115+
end: errorNode?.end
17116+
});
17117+
}
17118+
}
17119+
}
17120+
1709117121
function isIdenticalTo(source: Type, target: Type): Ternary {
1709217122
const flags = source.flags & target.flags;
1709317123
if (!(flags & TypeFlags.Substructure)) {
1709417124
return Ternary.False;
1709517125
}
17126+
traceUnionsOrIntersectionsTooLarge(source, target);
1709617127
if (flags & TypeFlags.UnionOrIntersection) {
1709717128
let result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target);
1709817129
if (result) {

0 commit comments

Comments
 (0)