diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 020fe61193..ac40693755 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -26447,7 +26447,14 @@ func (c *Checker) getCrossProductUnionSize(types []*Type) int { for _, t := range types { switch { case t.flags&TypeFlagsUnion != 0: - size *= len(t.Types()) + n := len(t.Types()) + // Cap the result to avoid integer overflow when computing the cross product of many large unions. + // In TypeScript, number overflow produces Infinity which naturally exceeds the limit check; + // in Go, we must guard against int wrapping to zero or negative. + if n > 0 && size > math.MaxInt/n { + return math.MaxInt + } + size *= n case t.flags&TypeFlagsNever != 0: return 0 } diff --git a/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.errors.txt b/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.errors.txt new file mode 100644 index 0000000000..9c3f8ba286 --- /dev/null +++ b/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.errors.txt @@ -0,0 +1,10 @@ +templateLiteralTypeTooComplex.ts(3,10): error TS2590: Expression produces a union type that is too complex to represent. + + +==== templateLiteralTypeTooComplex.ts (1 errors) ==== + // Large template literal types with combinatorial explosion should produce an error, not hang. + type N = 0 | 1 | 2 | 3; + type T = `${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}`; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2590: Expression produces a union type that is too complex to represent. + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.js b/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.js new file mode 100644 index 0000000000..56d95ddb74 --- /dev/null +++ b/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.js @@ -0,0 +1,10 @@ +//// [tests/cases/compiler/templateLiteralTypeTooComplex.ts] //// + +//// [templateLiteralTypeTooComplex.ts] +// Large template literal types with combinatorial explosion should produce an error, not hang. +type N = 0 | 1 | 2 | 3; +type T = `${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}`; + + +//// [templateLiteralTypeTooComplex.js] +"use strict"; diff --git a/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.symbols b/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.symbols new file mode 100644 index 0000000000..6b9ef9e007 --- /dev/null +++ b/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.symbols @@ -0,0 +1,59 @@ +//// [tests/cases/compiler/templateLiteralTypeTooComplex.ts] //// + +=== templateLiteralTypeTooComplex.ts === +// Large template literal types with combinatorial explosion should produce an error, not hang. +type N = 0 | 1 | 2 | 3; +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) + +type T = `${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}`; +>T : Symbol(T, Decl(templateLiteralTypeTooComplex.ts, 1, 23)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) +>N : Symbol(N, Decl(templateLiteralTypeTooComplex.ts, 0, 0)) + diff --git a/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.types b/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.types new file mode 100644 index 0000000000..ef80420132 --- /dev/null +++ b/testdata/baselines/reference/compiler/templateLiteralTypeTooComplex.types @@ -0,0 +1,10 @@ +//// [tests/cases/compiler/templateLiteralTypeTooComplex.ts] //// + +=== templateLiteralTypeTooComplex.ts === +// Large template literal types with combinatorial explosion should produce an error, not hang. +type N = 0 | 1 | 2 | 3; +>N : N + +type T = `${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}`; +>T : any + diff --git a/testdata/tests/cases/compiler/templateLiteralTypeTooComplex.ts b/testdata/tests/cases/compiler/templateLiteralTypeTooComplex.ts new file mode 100644 index 0000000000..e1569f9d4f --- /dev/null +++ b/testdata/tests/cases/compiler/templateLiteralTypeTooComplex.ts @@ -0,0 +1,5 @@ +// @strict: true + +// Large template literal types with combinatorial explosion should produce an error, not hang. +type N = 0 | 1 | 2 | 3; +type T = `${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}${N}`;