Skip to content

Commit 440ed83

Browse files
authored
Merge pull request #32079 from microsoft/instantiationCountLimiter
Add type instantiation count limiter
2 parents 345777e + a1c6d9e commit 440ed83

File tree

6 files changed

+120
-2
lines changed

6 files changed

+120
-2
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ namespace ts {
6565
let typeCount = 0;
6666
let symbolCount = 0;
6767
let enumCount = 0;
68+
let instantiationCount = 0;
6869
let instantiationDepth = 0;
6970
let constraintDepth = 0;
7071
let currentNode: Node | undefined;
@@ -11425,13 +11426,14 @@ namespace ts {
1142511426
if (!type || !mapper || mapper === identityMapper) {
1142611427
return type;
1142711428
}
11428-
if (instantiationDepth === 50) {
11429+
if (instantiationDepth === 50 || instantiationCount >= 5000000) {
1142911430
// We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing
1143011431
// with a combination of infinite generic types that perpetually generate new type identities. We stop
1143111432
// the recursion here by yielding the error type.
1143211433
error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite);
1143311434
return errorType;
1143411435
}
11436+
instantiationCount++;
1143511437
instantiationDepth++;
1143611438
const result = instantiateTypeWorker(type, mapper);
1143711439
instantiationDepth--;
@@ -24619,6 +24621,7 @@ namespace ts {
2461924621
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type {
2462024622
const saveCurrentNode = currentNode;
2462124623
currentNode = node;
24624+
instantiationCount = 0;
2462224625
const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple);
2462324626
const type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
2462424627
if (isConstEnumObjectType(type)) {
@@ -29340,6 +29343,7 @@ namespace ts {
2934029343
if (node) {
2934129344
const saveCurrentNode = currentNode;
2934229345
currentNode = node;
29346+
instantiationCount = 0;
2934329347
checkSourceElementWorker(node);
2934429348
currentNode = saveCurrentNode;
2934529349
}
@@ -29611,6 +29615,7 @@ namespace ts {
2961129615
function checkDeferredNode(node: Node) {
2961229616
const saveCurrentNode = currentNode;
2961329617
currentNode = node;
29618+
instantiationCount = 0;
2961429619
switch (node.kind) {
2961529620
case SyntaxKind.FunctionExpression:
2961629621
case SyntaxKind.ArrowFunction:

tests/baselines/reference/infiniteConstraints.errors.txt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' ca
22
tests/cases/compiler/infiniteConstraints.ts(31,43): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
33
tests/cases/compiler/infiniteConstraints.ts(31,63): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
44
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
5+
tests/cases/compiler/infiniteConstraints.ts(48,16): error TS2589: Type instantiation is excessively deep and possibly infinite.
56

67

7-
==== tests/cases/compiler/infiniteConstraints.ts (4 errors) ====
8+
==== tests/cases/compiler/infiniteConstraints.ts (5 errors) ====
89
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
910

1011
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
@@ -51,4 +52,18 @@ tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' c
5152
declare function function1<T extends {[K in keyof T]: Cond<T[K]>}>(): T[keyof T]["foo"];
5253
~~~~~~~~~~~~~~~~~
5354
!!! error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
55+
56+
// Repro from #31823
57+
58+
export type Prepend<Elm, T extends unknown[]> =
59+
T extends unknown ?
60+
((arg: Elm, ...rest: T) => void) extends ((...args: infer T2) => void) ? T2 :
61+
never :
62+
never;
63+
export type ExactExtract<T, U> = T extends U ? U extends T ? T : never : never;
64+
65+
type Conv<T, U = T> =
66+
{ 0: [T]; 1: Prepend<T, Conv<ExactExtract<U, T>>>;}[U extends T ? 0 : 1];
67+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
68+
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
5469

tests/baselines/reference/infiniteConstraints.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,24 @@ const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("
3535

3636
type Cond<T> = T extends number ? number : never;
3737
declare function function1<T extends {[K in keyof T]: Cond<T[K]>}>(): T[keyof T]["foo"];
38+
39+
// Repro from #31823
40+
41+
export type Prepend<Elm, T extends unknown[]> =
42+
T extends unknown ?
43+
((arg: Elm, ...rest: T) => void) extends ((...args: infer T2) => void) ? T2 :
44+
never :
45+
never;
46+
export type ExactExtract<T, U> = T extends U ? U extends T ? T : never : never;
47+
48+
type Conv<T, U = T> =
49+
{ 0: [T]; 1: Prepend<T, Conv<ExactExtract<U, T>>>;}[U extends T ? 0 : 1];
3850

3951

4052
//// [infiniteConstraints.js]
4153
"use strict";
4254
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
55+
exports.__esModule = true;
4356
var out = myBug({ obj1: { a: "test" } });
4457
var noError = ensureNoDuplicates({ main: value("test"), alternate: value("test2") });
4558
var shouldBeNoError = ensureNoDuplicates({ main: value("test") });

tests/baselines/reference/infiniteConstraints.symbols

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,53 @@ declare function function1<T extends {[K in keyof T]: Cond<T[K]>}>(): T[keyof T]
138138
>T : Symbol(T, Decl(infiniteConstraints.ts, 35, 27))
139139
>T : Symbol(T, Decl(infiniteConstraints.ts, 35, 27))
140140

141+
// Repro from #31823
142+
143+
export type Prepend<Elm, T extends unknown[]> =
144+
>Prepend : Symbol(Prepend, Decl(infiniteConstraints.ts, 35, 88))
145+
>Elm : Symbol(Elm, Decl(infiniteConstraints.ts, 39, 20))
146+
>T : Symbol(T, Decl(infiniteConstraints.ts, 39, 24))
147+
148+
T extends unknown ?
149+
>T : Symbol(T, Decl(infiniteConstraints.ts, 39, 24))
150+
151+
((arg: Elm, ...rest: T) => void) extends ((...args: infer T2) => void) ? T2 :
152+
>arg : Symbol(arg, Decl(infiniteConstraints.ts, 41, 4))
153+
>Elm : Symbol(Elm, Decl(infiniteConstraints.ts, 39, 20))
154+
>rest : Symbol(rest, Decl(infiniteConstraints.ts, 41, 13))
155+
>T : Symbol(T, Decl(infiniteConstraints.ts, 39, 24))
156+
>args : Symbol(args, Decl(infiniteConstraints.ts, 41, 45))
157+
>T2 : Symbol(T2, Decl(infiniteConstraints.ts, 41, 59))
158+
>T2 : Symbol(T2, Decl(infiniteConstraints.ts, 41, 59))
159+
160+
never :
161+
never;
162+
export type ExactExtract<T, U> = T extends U ? U extends T ? T : never : never;
163+
>ExactExtract : Symbol(ExactExtract, Decl(infiniteConstraints.ts, 43, 8))
164+
>T : Symbol(T, Decl(infiniteConstraints.ts, 44, 25))
165+
>U : Symbol(U, Decl(infiniteConstraints.ts, 44, 27))
166+
>T : Symbol(T, Decl(infiniteConstraints.ts, 44, 25))
167+
>U : Symbol(U, Decl(infiniteConstraints.ts, 44, 27))
168+
>U : Symbol(U, Decl(infiniteConstraints.ts, 44, 27))
169+
>T : Symbol(T, Decl(infiniteConstraints.ts, 44, 25))
170+
>T : Symbol(T, Decl(infiniteConstraints.ts, 44, 25))
171+
172+
type Conv<T, U = T> =
173+
>Conv : Symbol(Conv, Decl(infiniteConstraints.ts, 44, 79))
174+
>T : Symbol(T, Decl(infiniteConstraints.ts, 46, 10))
175+
>U : Symbol(U, Decl(infiniteConstraints.ts, 46, 12))
176+
>T : Symbol(T, Decl(infiniteConstraints.ts, 46, 10))
177+
178+
{ 0: [T]; 1: Prepend<T, Conv<ExactExtract<U, T>>>;}[U extends T ? 0 : 1];
179+
>0 : Symbol(0, Decl(infiniteConstraints.ts, 47, 3))
180+
>T : Symbol(T, Decl(infiniteConstraints.ts, 46, 10))
181+
>1 : Symbol(1, Decl(infiniteConstraints.ts, 47, 11))
182+
>Prepend : Symbol(Prepend, Decl(infiniteConstraints.ts, 35, 88))
183+
>T : Symbol(T, Decl(infiniteConstraints.ts, 46, 10))
184+
>Conv : Symbol(Conv, Decl(infiniteConstraints.ts, 44, 79))
185+
>ExactExtract : Symbol(ExactExtract, Decl(infiniteConstraints.ts, 43, 8))
186+
>U : Symbol(U, Decl(infiniteConstraints.ts, 46, 12))
187+
>T : Symbol(T, Decl(infiniteConstraints.ts, 46, 10))
188+
>U : Symbol(U, Decl(infiniteConstraints.ts, 46, 12))
189+
>T : Symbol(T, Decl(infiniteConstraints.ts, 46, 10))
190+

tests/baselines/reference/infiniteConstraints.types

Lines changed: 23 additions & 0 deletions
Large diffs are not rendered by default.

tests/cases/compiler/infiniteConstraints.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,15 @@ const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("
3636

3737
type Cond<T> = T extends number ? number : never;
3838
declare function function1<T extends {[K in keyof T]: Cond<T[K]>}>(): T[keyof T]["foo"];
39+
40+
// Repro from #31823
41+
42+
export type Prepend<Elm, T extends unknown[]> =
43+
T extends unknown ?
44+
((arg: Elm, ...rest: T) => void) extends ((...args: infer T2) => void) ? T2 :
45+
never :
46+
never;
47+
export type ExactExtract<T, U> = T extends U ? U extends T ? T : never : never;
48+
49+
type Conv<T, U = T> =
50+
{ 0: [T]; 1: Prepend<T, Conv<ExactExtract<U, T>>>;}[U extends T ? 0 : 1];

0 commit comments

Comments
 (0)