Skip to content

Commit e5416b5

Browse files
authored
Merge pull request #15075 from Microsoft/fix-exponential-expanding-array-assignment
Evolving array element type ignores contextual type
2 parents af64ef8 + 1671a06 commit e5416b5

File tree

5 files changed

+428
-5
lines changed

5 files changed

+428
-5
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10934,7 +10934,7 @@ namespace ts {
1093410934
// we defer subtype reduction until the evolving array type is finalized into a manifest
1093510935
// array type.
1093610936
function addEvolvingArrayElementType(evolvingArrayType: EvolvingArrayType, node: Expression): EvolvingArrayType {
10937-
const elementType = getBaseTypeOfLiteralType(getTypeOfExpression(node));
10937+
const elementType = getBaseTypeOfLiteralType(getContextFreeTypeOfExpression(node));
1093810938
return isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : getEvolvingArrayType(getUnionType([evolvingArrayType.elementType, elementType]));
1093910939
}
1094010940

@@ -17105,10 +17105,12 @@ namespace ts {
1710517105
return type;
1710617106
}
1710717107

17108-
// Returns the type of an expression. Unlike checkExpression, this function is simply concerned
17109-
// with computing the type and may not fully check all contained sub-expressions for errors.
17110-
// A cache argument of true indicates that if the function performs a full type check, it is ok
17111-
// to cache the result.
17108+
/**
17109+
* Returns the type of an expression. Unlike checkExpression, this function is simply concerned
17110+
* with computing the type and may not fully check all contained sub-expressions for errors.
17111+
* A cache argument of true indicates that if the function performs a full type check, it is ok
17112+
* to cache the result.
17113+
*/
1711217114
function getTypeOfExpression(node: Expression, cache?: boolean) {
1711317115
// Optimize for the common case of a call to a function with a single non-generic call
1711417116
// signature where we can just fetch the return type without checking the arguments.
@@ -17125,6 +17127,21 @@ namespace ts {
1712517127
return cache ? checkExpressionCached(node) : checkExpression(node);
1712617128
}
1712717129

17130+
/**
17131+
* Returns the type of an expression. Unlike checkExpression, this function is simply concerned
17132+
* with computing the type and may not fully check all contained sub-expressions for errors.
17133+
* It is intended for uses where you know there is no contextual type,
17134+
* and requesting the contextual type might cause a circularity or other bad behaviour.
17135+
* It sets the contextual type of the node to any before calling getTypeOfExpression.
17136+
*/
17137+
function getContextFreeTypeOfExpression(node: Expression) {
17138+
const saveContextualType = node.contextualType;
17139+
node.contextualType = anyType;
17140+
const type = getTypeOfExpression(node);
17141+
node.contextualType = saveContextualType;
17142+
return type;
17143+
}
17144+
1712817145
// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
1712917146
// contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the
1713017147
// expression is being inferentially typed (section 4.15.2 in spec) and provides the type mapper to use in
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//// [assignmentToExpandingArrayType.ts]
2+
// Fixes exponential time/space in #14628
3+
let x = []
4+
x[0] = { foo: 'hi' }
5+
x[0] = { foo: 'hi' }
6+
x[0] = { foo: 'hi' }
7+
x[0] = { foo: 'hi' }
8+
x[0] = { foo: 'hi' }
9+
x[0] = { foo: 'hi' }
10+
x[0] = { foo: 'hi' }
11+
x[0] = { foo: 'hi' }
12+
x[0] = { foo: 'hi' }
13+
x[0] = { foo: 'hi' }
14+
x[0] = { foo: 'hi' }
15+
x[0] = { foo: 'hi' }
16+
x[0] = { foo: 'hi' } // previously ran out of memory here
17+
x[0] = { foo: 'hi' }
18+
x[0] = { foo: 'hi' }
19+
x[0] = { foo: 'hi' }
20+
x[0] = { foo: 'hi' }
21+
x[0] = { foo: 'hi' }
22+
x[0] = { foo: 'hi' }
23+
x[0] = { foo: 'hi' }
24+
x[0] = { foo: 'hi' }
25+
x[0] = { foo: 'hi' }
26+
x[0] = { foo: 'hi' }
27+
x[0] = { foo: 'hi' }
28+
29+
30+
//// [assignmentToExpandingArrayType.js]
31+
// Fixes exponential time/space in #14628
32+
var x = [];
33+
x[0] = { foo: 'hi' };
34+
x[0] = { foo: 'hi' };
35+
x[0] = { foo: 'hi' };
36+
x[0] = { foo: 'hi' };
37+
x[0] = { foo: 'hi' };
38+
x[0] = { foo: 'hi' };
39+
x[0] = { foo: 'hi' };
40+
x[0] = { foo: 'hi' };
41+
x[0] = { foo: 'hi' };
42+
x[0] = { foo: 'hi' };
43+
x[0] = { foo: 'hi' };
44+
x[0] = { foo: 'hi' };
45+
x[0] = { foo: 'hi' }; // previously ran out of memory here
46+
x[0] = { foo: 'hi' };
47+
x[0] = { foo: 'hi' };
48+
x[0] = { foo: 'hi' };
49+
x[0] = { foo: 'hi' };
50+
x[0] = { foo: 'hi' };
51+
x[0] = { foo: 'hi' };
52+
x[0] = { foo: 'hi' };
53+
x[0] = { foo: 'hi' };
54+
x[0] = { foo: 'hi' };
55+
x[0] = { foo: 'hi' };
56+
x[0] = { foo: 'hi' };
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
=== tests/cases/compiler/assignmentToExpandingArrayType.ts ===
2+
// Fixes exponential time/space in #14628
3+
let x = []
4+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
5+
6+
x[0] = { foo: 'hi' }
7+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
8+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 2, 8))
9+
10+
x[0] = { foo: 'hi' }
11+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
12+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 3, 8))
13+
14+
x[0] = { foo: 'hi' }
15+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
16+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 4, 8))
17+
18+
x[0] = { foo: 'hi' }
19+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
20+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 5, 8))
21+
22+
x[0] = { foo: 'hi' }
23+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
24+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 6, 8))
25+
26+
x[0] = { foo: 'hi' }
27+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
28+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 7, 8))
29+
30+
x[0] = { foo: 'hi' }
31+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
32+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 8, 8))
33+
34+
x[0] = { foo: 'hi' }
35+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
36+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 9, 8))
37+
38+
x[0] = { foo: 'hi' }
39+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
40+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 10, 8))
41+
42+
x[0] = { foo: 'hi' }
43+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
44+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 11, 8))
45+
46+
x[0] = { foo: 'hi' }
47+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
48+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 12, 8))
49+
50+
x[0] = { foo: 'hi' }
51+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
52+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 13, 8))
53+
54+
x[0] = { foo: 'hi' } // previously ran out of memory here
55+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
56+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 14, 8))
57+
58+
x[0] = { foo: 'hi' }
59+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
60+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 15, 8))
61+
62+
x[0] = { foo: 'hi' }
63+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
64+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 16, 8))
65+
66+
x[0] = { foo: 'hi' }
67+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
68+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 17, 8))
69+
70+
x[0] = { foo: 'hi' }
71+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
72+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 18, 8))
73+
74+
x[0] = { foo: 'hi' }
75+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
76+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 19, 8))
77+
78+
x[0] = { foo: 'hi' }
79+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
80+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 20, 8))
81+
82+
x[0] = { foo: 'hi' }
83+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
84+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 21, 8))
85+
86+
x[0] = { foo: 'hi' }
87+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
88+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 22, 8))
89+
90+
x[0] = { foo: 'hi' }
91+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
92+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 23, 8))
93+
94+
x[0] = { foo: 'hi' }
95+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
96+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 24, 8))
97+
98+
x[0] = { foo: 'hi' }
99+
>x : Symbol(x, Decl(assignmentToExpandingArrayType.ts, 1, 3))
100+
>foo : Symbol(foo, Decl(assignmentToExpandingArrayType.ts, 25, 8))
101+

0 commit comments

Comments
 (0)