Skip to content

Commit a7a010a

Browse files
authored
Fix getRecursionIdentity function to always return some identity (#43527)
* Fix getRecursionIdentity, undo changes from #43435 (but keep tests) * Remove test that takes excessively long to run * Accept new baselines * Fix formatting * Add regression tests * Reinstate test
1 parent f621d67 commit a7a010a

8 files changed

+280
-503
lines changed

src/compiler/checker.ts

+28-28
Original file line numberDiff line numberDiff line change
@@ -19610,30 +19610,33 @@ namespace ts {
1961019610
function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
1961119611
if (depth >= 5) {
1961219612
const identity = getRecursionIdentity(type);
19613-
if (identity) {
19614-
let count = 0;
19615-
for (let i = 0; i < depth; i++) {
19616-
if (getRecursionIdentity(stack[i]) === identity) {
19617-
count++;
19618-
if (count >= 5) {
19619-
return true;
19620-
}
19613+
let count = 0;
19614+
for (let i = 0; i < depth; i++) {
19615+
if (getRecursionIdentity(stack[i]) === identity) {
19616+
count++;
19617+
if (count >= 5) {
19618+
return true;
1962119619
}
1962219620
}
1962319621
}
1962419622
}
1962519623
return false;
1962619624
}
1962719625

19628-
// Types with constituents that could circularly reference the type have a recursion identity. The recursion
19629-
// identity is some object that is common to instantiations of the type with the same origin.
19630-
function getRecursionIdentity(type: Type): object | undefined {
19626+
// The recursion identity of a type is an object identity that is shared among multiple instantiations of the type.
19627+
// We track recursion identities in order to identify deeply nested and possibly infinite type instantiations with
19628+
// the same origin. For example, when type parameters are in scope in an object type such as { x: T }, all
19629+
// instantiations of that type have the same recursion identity. The default recursion identity is the object
19630+
// identity of the type, meaning that every type is unique. Generally, types with constituents that could circularly
19631+
// reference the type have a recursion identity that differs from the object identity.
19632+
function getRecursionIdentity(type: Type): object {
19633+
// Object and array literals are known not to contain recursive references and don't need a recursion identity.
1963119634
if (type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) {
1963219635
if (getObjectFlags(type) && ObjectFlags.Reference && (type as TypeReference).node) {
1963319636
// Deferred type references are tracked through their associated AST node. This gives us finer
1963419637
// granularity than using their associated target because each manifest type reference has a
1963519638
// unique AST node.
19636-
return (type as TypeReference).node;
19639+
return (type as TypeReference).node!;
1963719640
}
1963819641
if (type.symbol && !(getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol.flags & SymbolFlags.Class)) {
1963919642
// We track all object types that have an associated symbol (representing the origin of the type), but
@@ -19645,6 +19648,9 @@ namespace ts {
1964519648
return type.target;
1964619649
}
1964719650
}
19651+
if (type.flags & TypeFlags.TypeParameter) {
19652+
return type.symbol;
19653+
}
1964819654
if (type.flags & TypeFlags.IndexedAccess) {
1964919655
// Identity is the leftmost object type in a chain of indexed accesses, eg, in A[P][Q] it is A
1965019656
do {
@@ -19656,7 +19662,7 @@ namespace ts {
1965619662
// The root object represents the origin of the conditional type
1965719663
return (type as ConditionalType).root;
1965819664
}
19659-
return undefined;
19665+
return type;
1966019666
}
1966119667

1966219668
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
@@ -19840,13 +19846,7 @@ namespace ts {
1984019846
function isArrayLikeType(type: Type): boolean {
1984119847
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
1984219848
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
19843-
return isArrayType(type) || hasArrayOrReadonlyArrayBaseType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
19844-
}
19845-
19846-
function hasArrayOrReadonlyArrayBaseType(type: Type): boolean {
19847-
return !!(getObjectFlags(type) & ObjectFlags.Reference)
19848-
&& !!(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface)
19849-
&& some(getBaseTypes((type as TypeReference).target as InterfaceType), isArrayType);
19849+
return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
1985019850
}
1985119851

1985219852
function isEmptyArrayLiteralType(type: Type): boolean {
@@ -21083,16 +21083,16 @@ namespace ts {
2108321083
// We stop inferring and report a circularity if we encounter duplicate recursion identities on both
2108421084
// the source side and the target side.
2108521085
const saveExpandingFlags = expandingFlags;
21086-
const sourceIdentity = getRecursionIdentity(source) || source;
21087-
const targetIdentity = getRecursionIdentity(target) || target;
21088-
if (sourceIdentity && contains(sourceStack, sourceIdentity)) expandingFlags |= ExpandingFlags.Source;
21089-
if (targetIdentity && contains(targetStack, targetIdentity)) expandingFlags |= ExpandingFlags.Target;
21086+
const sourceIdentity = getRecursionIdentity(source);
21087+
const targetIdentity = getRecursionIdentity(target);
21088+
if (contains(sourceStack, sourceIdentity)) expandingFlags |= ExpandingFlags.Source;
21089+
if (contains(targetStack, targetIdentity)) expandingFlags |= ExpandingFlags.Target;
2109021090
if (expandingFlags !== ExpandingFlags.Both) {
21091-
if (sourceIdentity) (sourceStack || (sourceStack = [])).push(sourceIdentity);
21092-
if (targetIdentity) (targetStack || (targetStack = [])).push(targetIdentity);
21091+
(sourceStack || (sourceStack = [])).push(sourceIdentity);
21092+
(targetStack || (targetStack = [])).push(targetIdentity);
2109321093
action(source, target);
21094-
if (targetIdentity) targetStack.pop();
21095-
if (sourceIdentity) sourceStack.pop();
21094+
targetStack.pop();
21095+
sourceStack.pop();
2109621096
}
2109721097
else {
2109821098
inferencePriority = InferencePriority.Circularity;

0 commit comments

Comments
 (0)