Skip to content

Commit

Permalink
Increase type instantiation depth limit (#45025)
Browse files Browse the repository at this point in the history
* Bump instantiation depth limit to 500

* Accept new baselines

* Update tests

* Accept new baselines
  • Loading branch information
ahejlsberg authored Aug 17, 2021
1 parent dc80e6a commit 79474fd
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 241 deletions.
31 changes: 12 additions & 19 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16272,28 +16272,21 @@ namespace ts {
let result = root.instantiations!.get(id);
if (!result) {
const newMapper = createTypeMapper(root.outerTypeParameters, typeArguments);
result = instantiateConditionalType(root, newMapper, aliasSymbol, aliasTypeArguments);
const checkType = root.checkType;
const distributionType = root.isDistributive ? getMappedType(checkType, newMapper) : undefined;
// Distributive conditional types are distributed over union types. For example, when the
// distributive conditional type T extends U ? X : Y is instantiated with A | B for T, the
// result is (A extends U ? X : Y) | (B extends U ? X : Y).
result = distributionType && checkType !== distributionType && distributionType.flags & (TypeFlags.Union | TypeFlags.Never) ?
mapTypeWithAlias(distributionType, t => getConditionalType(root, prependTypeMapping(checkType, t, newMapper)), aliasSymbol, aliasTypeArguments) :
getConditionalType(root, newMapper, aliasSymbol, aliasTypeArguments);
root.instantiations!.set(id, result);
}
return result;
}
return type;
}

function instantiateConditionalType(root: ConditionalRoot, mapper: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type {
// Check if we have a conditional type where the check type is a naked type parameter. If so,
// the conditional type is distributive over union types and when T is instantiated to a union
// type A | B, we produce (A extends U ? X : Y) | (B extends U ? X : Y).
if (root.isDistributive) {
const checkType = root.checkType as TypeParameter;
const instantiatedType = getMappedType(checkType, mapper);
if (checkType !== instantiatedType && instantiatedType.flags & (TypeFlags.Union | TypeFlags.Never)) {
return mapTypeWithAlias(instantiatedType, t => getConditionalType(root, prependTypeMapping(checkType, t, mapper)), aliasSymbol, aliasTypeArguments);
}
}
return getConditionalType(root, mapper, aliasSymbol, aliasTypeArguments);
}

function instantiateType(type: Type, mapper: TypeMapper | undefined): Type;
function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined;
function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined {
Expand All @@ -16304,10 +16297,10 @@ namespace ts {
if (!couldContainTypeVariables(type)) {
return type;
}
if (instantiationDepth === 50 || instantiationCount >= 5000000) {
// We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing
// with a combination of infinite generic types that perpetually generate new type identities. We stop
// the recursion here by yielding the error type.
if (instantiationDepth === 500 || instantiationCount >= 5000000) {
// We have reached 500 recursive type instantiations, or 5M type instantiations caused by the same statement
// or expression. There is a very high likelyhood we're dealing with a combination of infinite generic types
// that perpetually generate new type identities, so we stop the recursion here by yielding the error type.
tracing?.instant(tracing.Phase.CheckTypes, "instantiateType_DepthLimit", { typeId: type.id, instantiationDepth, instantiationCount });
error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite);
return errorType;
Expand Down
13 changes: 7 additions & 6 deletions tests/baselines/reference/recursiveConditionalTypes.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ tests/cases/compiler/recursiveConditionalTypes.ts(21,5): error TS2322: Type 'T'
tests/cases/compiler/recursiveConditionalTypes.ts(22,5): error TS2322: Type 'Awaited<T>' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Awaited<T>'.
tests/cases/compiler/recursiveConditionalTypes.ts(35,11): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/recursiveConditionalTypes.ts(46,12): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/recursiveConditionalTypes.ts(49,5): error TS2322: Type 'TupleOf<number, M>' is not assignable to type 'TupleOf<number, N>'.
tests/cases/compiler/recursiveConditionalTypes.ts(47,12): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/recursiveConditionalTypes.ts(50,5): error TS2322: Type 'TupleOf<number, M>' is not assignable to type 'TupleOf<number, N>'.
Type 'number extends M ? number[] : _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
Type 'number[] | _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
Type 'number[]' is not assignable to type 'TupleOf<number, N>'.
tests/cases/compiler/recursiveConditionalTypes.ts(50,5): error TS2322: Type 'TupleOf<number, N>' is not assignable to type 'TupleOf<number, M>'.
tests/cases/compiler/recursiveConditionalTypes.ts(51,5): error TS2322: Type 'TupleOf<number, N>' is not assignable to type 'TupleOf<number, M>'.
Type 'number extends N ? number[] : _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
Type 'number[] | _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
Type 'number[]' is not assignable to type 'TupleOf<number, M>'.
tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'.
tests/cases/compiler/recursiveConditionalTypes.ts(117,9): error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'.
Type '[] | Grow2<[string], T>' is not assignable to type 'Grow1<[], T>'.
Type '[]' is not assignable to type 'Grow1<[], T>'.
Type 'Grow2<[string], T>' is not assignable to type 'Grow1<[number], T>'.
Expand Down Expand Up @@ -84,8 +84,9 @@ tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument
type TT1 = TupleOf<number, 0 | 2 | 4>;
type TT2 = TupleOf<number, number>;
type TT3 = TupleOf<number, any>;
type TT4 = TupleOf<number, 100>; // Depth error
~~~~~~~~~~~~~~~~~~~~
type TT4 = TupleOf<number, 100>;
type TT5 = TupleOf<number, 1000>; // Depth error
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.

function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) {
Expand Down
4 changes: 3 additions & 1 deletion tests/baselines/reference/recursiveConditionalTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ type TT0 = TupleOf<string, 4>;
type TT1 = TupleOf<number, 0 | 2 | 4>;
type TT2 = TupleOf<number, number>;
type TT3 = TupleOf<number, any>;
type TT4 = TupleOf<number, 100>; // Depth error
type TT4 = TupleOf<number, 100>;
type TT5 = TupleOf<number, 1000>; // Depth error

function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) {
tn = tm;
Expand Down Expand Up @@ -194,6 +195,7 @@ declare type TT1 = TupleOf<number, 0 | 2 | 4>;
declare type TT2 = TupleOf<number, number>;
declare type TT3 = TupleOf<number, any>;
declare type TT4 = TupleOf<number, 100>;
declare type TT5 = TupleOf<number, 1000>;
declare function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>): void;
declare function f23<T>(t: TupleOf<T, 3>): T;
interface Box<T> {
Expand Down
Loading

0 comments on commit 79474fd

Please sign in to comment.