Skip to content

Commit 52a0fe6

Browse files
authored
Fix stack overflow caused by simplification of recursive type (#4260)
1 parent 79fe60a commit 52a0fe6

5 files changed

Lines changed: 150 additions & 6 deletions

File tree

internal/checker/checker.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27716,6 +27716,16 @@ func (c *Checker) getSimplifiedIndexedAccessType(t *Type, writing bool) *Type {
2771627716
return core.IfElse(cached == c.circularConstraintType, t, cached)
2771727717
}
2771827718
c.cachedTypes[key] = t
27719+
result := c.getSimplifiedIndexedAccessTypeWorker(t, writing)
27720+
if result != t {
27721+
// If the simplification is a union type that includes t, remove t from the type.
27722+
result = c.removeType(result, t)
27723+
c.cachedTypes[key] = result
27724+
}
27725+
return result
27726+
}
27727+
27728+
func (c *Checker) getSimplifiedIndexedAccessTypeWorker(t *Type, writing bool) *Type {
2771927729
// We recursively simplify the object type as it may in turn be an indexed access type. For example, with
2772027730
// '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type.
2772127731
objectType := c.getSimplifiedType(t.AsIndexedAccessType().objectType, writing)
@@ -27724,7 +27734,6 @@ func (c *Checker) getSimplifiedIndexedAccessType(t *Type, writing bool) *Type {
2772427734
// T[A | B] -> T[A] & T[B] (writing)
2772527735
distributedOverIndex := c.distributeObjectOverIndexType(objectType, indexType, writing)
2772627736
if distributedOverIndex != nil {
27727-
c.cachedTypes[key] = distributedOverIndex
2772827737
return distributedOverIndex
2772927738
}
2773027739
// Only do the inner distributions if the index can no longer be instantiated to cause index distribution again
@@ -27734,7 +27743,6 @@ func (c *Checker) getSimplifiedIndexedAccessType(t *Type, writing bool) *Type {
2773427743
// (T & U)[K] -> T[K] & U[K]
2773527744
distributedOverObject := c.distributeIndexOverObjectType(objectType, indexType, writing)
2773627745
if distributedOverObject != nil {
27737-
c.cachedTypes[key] = distributedOverObject
2773827746
return distributedOverObject
2773927747
}
2774027748
}
@@ -27746,7 +27754,6 @@ func (c *Checker) getSimplifiedIndexedAccessType(t *Type, writing bool) *Type {
2774627754
if c.isGenericTupleType(objectType) && indexType.flags&TypeFlagsNumberLike != 0 {
2774727755
elementType := c.getElementTypeOfSliceOfTupleType(objectType, core.IfElse(indexType.flags&TypeFlagsNumber != 0, 0, objectType.TargetTupleType().fixedLength), 0 /*endSkipCount*/, writing, false)
2774827756
if elementType != nil {
27749-
c.cachedTypes[key] = elementType
2775027757
return elementType
2775127758
}
2775227759
}
@@ -27755,11 +27762,9 @@ func (c *Checker) getSimplifiedIndexedAccessType(t *Type, writing bool) *Type {
2775527762
// For example, for an index access { [P in K]: Box<T[P]> }[X], we construct the type Box<T[X]>.
2775627763
if c.isGenericMappedType(objectType) {
2775727764
if c.getMappedTypeNameTypeKind(objectType) != MappedTypeNameTypeKindRemapping {
27758-
result := c.mapType(c.substituteIndexedMappedType(objectType, t.AsIndexedAccessType().indexType), func(t *Type) *Type {
27765+
return c.mapType(c.substituteIndexedMappedType(objectType, t.AsIndexedAccessType().indexType), func(t *Type) *Type {
2775927766
return c.getSimplifiedType(t, writing)
2776027767
})
27761-
c.cachedTypes[key] = result
27762-
return result
2776327768
}
2776427769
}
2776527770
return t
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
recursiveIndexedAccessSimplification.ts(5,5): error TS4109: Type arguments for 'Array' circularly reference themselves.
2+
recursiveIndexedAccessSimplification.ts(5,9): error TS2536: Type 'number' cannot be used to index type 'Recur<T>'.
3+
recursiveIndexedAccessSimplification.ts(8,5): error TS2322: Type '[string, ...Recur<T>[]]' is not assignable to type 'Recur<T>'.
4+
Type '[string, ...Recur<T>[]]' is not assignable to type 'Recur<T>[number][]'.
5+
Type 'string | Recur<T>' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
6+
Type 'string' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
7+
Type 'string' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
8+
Type 'string' is not assignable to type '(T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
9+
recursiveIndexedAccessSimplification.ts(8,12): error TS2589: Type instantiation is excessively deep and possibly infinite.
10+
recursiveIndexedAccessSimplification.ts(12,37): error TS2589: Type instantiation is excessively deep and possibly infinite.
11+
12+
13+
==== recursiveIndexedAccessSimplification.ts (5 errors) ====
14+
// https://github.com/microsoft/TypeScript/issues/63270
15+
16+
type Recur<T> =
17+
(T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]>}) |
18+
[...Recur<T>[number][]];
19+
~~~~~~~~~~~~~~~~~~~~~~~
20+
!!! error TS4109: Type arguments for 'Array' circularly reference themselves.
21+
~~~~~~~~~~~~~~~~
22+
!!! error TS2536: Type 'number' cannot be used to index type 'Recur<T>'.
23+
24+
function join<T>(l: Recur<T>[]): Recur<T> {
25+
return ['marker', ...l];
26+
~~~~~~
27+
!!! error TS2322: Type '[string, ...Recur<T>[]]' is not assignable to type 'Recur<T>'.
28+
!!! error TS2322: Type '[string, ...Recur<T>[]]' is not assignable to type 'Recur<T>[number][]'.
29+
!!! error TS2322: Type 'string | Recur<T>' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
30+
!!! error TS2322: Type 'string' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
31+
!!! error TS2322: Type 'string' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
32+
!!! error TS2322: Type 'string' is not assignable to type '(T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
33+
~~~~~~~~~~~~~~~~
34+
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
35+
}
36+
37+
function a<T>(l: Recur<T>[]): void {
38+
const x: Recur<T> | undefined = join(l);
39+
~~~~~~~
40+
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
41+
}
42+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//// [tests/cases/compiler/recursiveIndexedAccessSimplification.ts] ////
2+
3+
=== recursiveIndexedAccessSimplification.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/63270
5+
6+
type Recur<T> =
7+
>Recur : Symbol(Recur, Decl(recursiveIndexedAccessSimplification.ts, 0, 0))
8+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 2, 11))
9+
10+
(T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]>}) |
11+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 2, 11))
12+
>K : Symbol(K, Decl(recursiveIndexedAccessSimplification.ts, 3, 38))
13+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 2, 11))
14+
>Recur : Symbol(Recur, Decl(recursiveIndexedAccessSimplification.ts, 0, 0))
15+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 2, 11))
16+
>K : Symbol(K, Decl(recursiveIndexedAccessSimplification.ts, 3, 38))
17+
18+
[...Recur<T>[number][]];
19+
>Recur : Symbol(Recur, Decl(recursiveIndexedAccessSimplification.ts, 0, 0))
20+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 2, 11))
21+
22+
function join<T>(l: Recur<T>[]): Recur<T> {
23+
>join : Symbol(join, Decl(recursiveIndexedAccessSimplification.ts, 4, 28))
24+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 6, 14))
25+
>l : Symbol(l, Decl(recursiveIndexedAccessSimplification.ts, 6, 17))
26+
>Recur : Symbol(Recur, Decl(recursiveIndexedAccessSimplification.ts, 0, 0))
27+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 6, 14))
28+
>Recur : Symbol(Recur, Decl(recursiveIndexedAccessSimplification.ts, 0, 0))
29+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 6, 14))
30+
31+
return ['marker', ...l];
32+
>l : Symbol(l, Decl(recursiveIndexedAccessSimplification.ts, 6, 17))
33+
}
34+
35+
function a<T>(l: Recur<T>[]): void {
36+
>a : Symbol(a, Decl(recursiveIndexedAccessSimplification.ts, 8, 1))
37+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 10, 11))
38+
>l : Symbol(l, Decl(recursiveIndexedAccessSimplification.ts, 10, 14))
39+
>Recur : Symbol(Recur, Decl(recursiveIndexedAccessSimplification.ts, 0, 0))
40+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 10, 11))
41+
42+
const x: Recur<T> | undefined = join(l);
43+
>x : Symbol(x, Decl(recursiveIndexedAccessSimplification.ts, 11, 9))
44+
>Recur : Symbol(Recur, Decl(recursiveIndexedAccessSimplification.ts, 0, 0))
45+
>T : Symbol(T, Decl(recursiveIndexedAccessSimplification.ts, 10, 11))
46+
>join : Symbol(join, Decl(recursiveIndexedAccessSimplification.ts, 4, 28))
47+
>l : Symbol(l, Decl(recursiveIndexedAccessSimplification.ts, 10, 14))
48+
}
49+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/compiler/recursiveIndexedAccessSimplification.ts] ////
2+
3+
=== recursiveIndexedAccessSimplification.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/63270
5+
6+
type Recur<T> =
7+
>Recur : Recur<T>
8+
9+
(T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]>}) |
10+
[...Recur<T>[number][]];
11+
12+
function join<T>(l: Recur<T>[]): Recur<T> {
13+
>join : <T>(l: Recur<T>[]) => Recur<T>
14+
>l : Recur<T>[]
15+
16+
return ['marker', ...l];
17+
>['marker', ...l] : [string, ...Recur<T>[]]
18+
>'marker' : "marker"
19+
>...l : Recur<T>
20+
>l : Recur<T>[]
21+
}
22+
23+
function a<T>(l: Recur<T>[]): void {
24+
>a : <T>(l: Recur<T>[]) => void
25+
>l : Recur<T>[]
26+
27+
const x: Recur<T> | undefined = join(l);
28+
>x : Recur<T> | undefined
29+
>join(l) : Recur<T>
30+
>join : <T_1>(l: Recur<T_1>[]) => Recur<T_1>
31+
>l : Recur<T>[]
32+
}
33+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @noEmit: true
2+
3+
// https://github.com/microsoft/TypeScript/issues/63270
4+
5+
type Recur<T> =
6+
(T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]>}) |
7+
[...Recur<T>[number][]];
8+
9+
function join<T>(l: Recur<T>[]): Recur<T> {
10+
return ['marker', ...l];
11+
}
12+
13+
function a<T>(l: Recur<T>[]): void {
14+
const x: Recur<T> | undefined = join(l);
15+
}

0 commit comments

Comments
 (0)