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
This commit is contained in:
Andrew Casey
2020-12-10 13:52:41 -08:00
committed by GitHub
parent 035c7ca905
commit 3e72526600

View File

@@ -16892,7 +16892,6 @@ namespace ts {
return isIdenticalTo(source, target);
}
// We fastpath comparing a type parameter to exactly its constraint, as this is _super_ common,
// and otherwise, for type parameters in large unions, causes us to need to compare the union to itself,
// as we break down the _target_ union first, _then_ get the source constraint - so for every
@@ -16953,6 +16952,8 @@ namespace ts {
return Ternary.False;
}
traceUnionsOrIntersectionsTooLarge(source, target);
let result = Ternary.False;
const saveErrorInfo = captureErrorCalculationState();
@@ -17088,11 +17089,41 @@ namespace ts {
}
}
function traceUnionsOrIntersectionsTooLarge(source: Type, target: Type): void {
if (!tracing.isTracing()) {
return;
}
if ((source.flags & TypeFlags.UnionOrIntersection) && (target.flags & TypeFlags.UnionOrIntersection)) {
const sourceUnionOrIntersection = source as UnionOrIntersectionType;
const targetUnionOrIntersection = target as UnionOrIntersectionType;
if (sourceUnionOrIntersection.objectFlags & targetUnionOrIntersection.objectFlags & ObjectFlags.PrimitiveUnion) {
// There's a fast path for comparing primitive unions
return;
}
const sourceSize = sourceUnionOrIntersection.types.length;
const targetSize = targetUnionOrIntersection.types.length;
if (sourceSize * targetSize > 1E6) {
tracing.instant(tracing.Phase.CheckTypes, "traceUnionsOrIntersectionsTooLarge_DepthLimit", {
sourceId: source.id,
sourceSize,
targetId: target.id,
targetSize,
pos: errorNode?.pos,
end: errorNode?.end
});
}
}
}
function isIdenticalTo(source: Type, target: Type): Ternary {
const flags = source.flags & target.flags;
if (!(flags & TypeFlags.Substructure)) {
return Ternary.False;
}
traceUnionsOrIntersectionsTooLarge(source, target);
if (flags & TypeFlags.UnionOrIntersection) {
let result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target);
if (result) {