Skip to content

Commit 3918e6c

Browse files
Move anonymous type instantiation cache from AST node to root type (microsoft#41084)
* Move anonymous type instantiation cache from AST node to root type * Use "root" type reference as cache location for deferred type references * Add test Co-authored-by: Andrew Branch <[email protected]>
1 parent cdf9c3b commit 3918e6c

File tree

6 files changed

+188
-8
lines changed

6 files changed

+188
-8
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15046,9 +15046,10 @@ namespace ts {
1504615046
}
1504715047

1504815048
function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper) {
15049-
const target = type.objectFlags & ObjectFlags.Instantiated ? type.target! : type;
1505015049
const declaration = type.objectFlags & ObjectFlags.Reference ? (<TypeReference>type).node! : type.symbol.declarations[0];
1505115050
const links = getNodeLinks(declaration);
15051+
const target = type.objectFlags & ObjectFlags.Reference ? <DeferredTypeReference>links.resolvedType! :
15052+
type.objectFlags & ObjectFlags.Instantiated ? type.target! : type;
1505215053
let typeParameters = links.outerTypeParameters;
1505315054
if (!typeParameters) {
1505415055
// The first time an anonymous type is instantiated we compute and store a list of the type
@@ -15065,10 +15066,6 @@ namespace ts {
1506515066
filter(typeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) :
1506615067
typeParameters;
1506715068
links.outerTypeParameters = typeParameters;
15068-
if (typeParameters.length) {
15069-
links.instantiations = new Map<string, Type>();
15070-
links.instantiations.set(getTypeListId(typeParameters), target);
15071-
}
1507215069
}
1507315070
if (typeParameters.length) {
1507415071
// We are instantiating an anonymous type that has one or more type parameters in scope. Apply the
@@ -15077,13 +15074,17 @@ namespace ts {
1507715074
const combinedMapper = combineTypeMappers(type.mapper, mapper);
1507815075
const typeArguments = map(typeParameters, t => getMappedType(t, combinedMapper));
1507915076
const id = getTypeListId(typeArguments);
15080-
let result = links.instantiations!.get(id);
15077+
if (!target.instantiations) {
15078+
target.instantiations = new Map<string, Type>();
15079+
target.instantiations.set(getTypeListId(typeParameters), target);
15080+
}
15081+
let result = target.instantiations.get(id);
1508115082
if (!result) {
1508215083
const newMapper = createTypeMapper(typeParameters, typeArguments);
1508315084
result = target.objectFlags & ObjectFlags.Reference ? createDeferredTypeReference((<DeferredTypeReference>type).target, (<DeferredTypeReference>type).node, newMapper) :
1508415085
target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(<MappedType>target, newMapper) :
1508515086
instantiateAnonymousType(target, newMapper);
15086-
links.instantiations!.set(id, result);
15087+
target.instantiations.set(id, result);
1508715088
}
1508815089
return result;
1508915090
}

src/compiler/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4847,7 +4847,6 @@ namespace ts {
48474847
deferredNodes?: ESMap<NodeId, Node>; // Set of nodes whose checking has been deferred
48484848
capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement
48494849
outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type
4850-
instantiations?: ESMap<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
48514850
isExhaustive?: boolean; // Is node an exhaustive switch statement
48524851
skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type
48534852
declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter.
@@ -5141,6 +5140,8 @@ namespace ts {
51415140
node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode;
51425141
/* @internal */
51435142
mapper?: TypeMapper;
5143+
/* @internal */
5144+
instantiations?: ESMap<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
51445145
}
51455146

51465147
/* @internal */
@@ -5221,6 +5222,7 @@ namespace ts {
52215222
export interface AnonymousType extends ObjectType {
52225223
target?: AnonymousType; // Instantiation target
52235224
mapper?: TypeMapper; // Instantiation mapper
5225+
instantiations?: ESMap<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
52245226
}
52255227

52265228
/* @internal */
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//// [objectInstantiationFromUnionSpread.ts]
2+
// #40995
3+
4+
interface Success {
5+
isSuccess: true;
6+
}
7+
8+
interface Fail {
9+
isSuccess: false;
10+
}
11+
12+
type Item = Success | Fail;
13+
14+
function f1(a: Item[]) {
15+
a.map(item => ({ ...item })).filter(value => {});
16+
}
17+
18+
function f2<T>(a: Item[]) {
19+
a.map(item => ({ ...item })).filter(value => {});
20+
}
21+
22+
23+
//// [objectInstantiationFromUnionSpread.js]
24+
// #40995
25+
var __assign = (this && this.__assign) || function () {
26+
__assign = Object.assign || function(t) {
27+
for (var s, i = 1, n = arguments.length; i < n; i++) {
28+
s = arguments[i];
29+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
30+
t[p] = s[p];
31+
}
32+
return t;
33+
};
34+
return __assign.apply(this, arguments);
35+
};
36+
function f1(a) {
37+
a.map(function (item) { return (__assign({}, item)); }).filter(function (value) { });
38+
}
39+
function f2(a) {
40+
a.map(function (item) { return (__assign({}, item)); }).filter(function (value) { });
41+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
=== tests/cases/compiler/objectInstantiationFromUnionSpread.ts ===
2+
// #40995
3+
4+
interface Success {
5+
>Success : Symbol(Success, Decl(objectInstantiationFromUnionSpread.ts, 0, 0))
6+
7+
isSuccess: true;
8+
>isSuccess : Symbol(Success.isSuccess, Decl(objectInstantiationFromUnionSpread.ts, 2, 19))
9+
}
10+
11+
interface Fail {
12+
>Fail : Symbol(Fail, Decl(objectInstantiationFromUnionSpread.ts, 4, 1))
13+
14+
isSuccess: false;
15+
>isSuccess : Symbol(Fail.isSuccess, Decl(objectInstantiationFromUnionSpread.ts, 6, 16))
16+
}
17+
18+
type Item = Success | Fail;
19+
>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1))
20+
>Success : Symbol(Success, Decl(objectInstantiationFromUnionSpread.ts, 0, 0))
21+
>Fail : Symbol(Fail, Decl(objectInstantiationFromUnionSpread.ts, 4, 1))
22+
23+
function f1(a: Item[]) {
24+
>f1 : Symbol(f1, Decl(objectInstantiationFromUnionSpread.ts, 10, 27))
25+
>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 12, 12))
26+
>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1))
27+
28+
a.map(item => ({ ...item })).filter(value => {});
29+
>a.map(item => ({ ...item })).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
30+
>a.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
31+
>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 12, 12))
32+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
33+
>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 13, 8))
34+
>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 13, 8))
35+
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
36+
>value : Symbol(value, Decl(objectInstantiationFromUnionSpread.ts, 13, 38))
37+
}
38+
39+
function f2<T>(a: Item[]) {
40+
>f2 : Symbol(f2, Decl(objectInstantiationFromUnionSpread.ts, 14, 1))
41+
>T : Symbol(T, Decl(objectInstantiationFromUnionSpread.ts, 16, 12))
42+
>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 16, 15))
43+
>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1))
44+
45+
a.map(item => ({ ...item })).filter(value => {});
46+
>a.map(item => ({ ...item })).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
47+
>a.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
48+
>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 16, 15))
49+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
50+
>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 17, 8))
51+
>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 17, 8))
52+
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
53+
>value : Symbol(value, Decl(objectInstantiationFromUnionSpread.ts, 17, 38))
54+
}
55+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
=== tests/cases/compiler/objectInstantiationFromUnionSpread.ts ===
2+
// #40995
3+
4+
interface Success {
5+
isSuccess: true;
6+
>isSuccess : true
7+
>true : true
8+
}
9+
10+
interface Fail {
11+
isSuccess: false;
12+
>isSuccess : false
13+
>false : false
14+
}
15+
16+
type Item = Success | Fail;
17+
>Item : Item
18+
19+
function f1(a: Item[]) {
20+
>f1 : (a: Item[]) => void
21+
>a : Item[]
22+
23+
a.map(item => ({ ...item })).filter(value => {});
24+
>a.map(item => ({ ...item })).filter(value => {}) : ({ isSuccess: true; } | { isSuccess: false; })[]
25+
>a.map(item => ({ ...item })).filter : { <S extends { isSuccess: true; } | { isSuccess: false; }>(predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => value is S, thisArg?: any): S[]; (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => unknown, thisArg?: any): ({ isSuccess: true; } | { isSuccess: false; })[]; }
26+
>a.map(item => ({ ...item })) : ({ isSuccess: true; } | { isSuccess: false; })[]
27+
>a.map : <U>(callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[]
28+
>a : Item[]
29+
>map : <U>(callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[]
30+
>item => ({ ...item }) : (item: Item) => { isSuccess: true; } | { isSuccess: false; }
31+
>item : Item
32+
>({ ...item }) : { isSuccess: true; } | { isSuccess: false; }
33+
>{ ...item } : { isSuccess: true; } | { isSuccess: false; }
34+
>item : Item
35+
>filter : { <S extends { isSuccess: true; } | { isSuccess: false; }>(predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => value is S, thisArg?: any): S[]; (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => unknown, thisArg?: any): ({ isSuccess: true; } | { isSuccess: false; })[]; }
36+
>value => {} : (value: { isSuccess: true; } | { isSuccess: false; }) => void
37+
>value : { isSuccess: true; } | { isSuccess: false; }
38+
}
39+
40+
function f2<T>(a: Item[]) {
41+
>f2 : <T>(a: Item[]) => void
42+
>a : Item[]
43+
44+
a.map(item => ({ ...item })).filter(value => {});
45+
>a.map(item => ({ ...item })).filter(value => {}) : ({ isSuccess: true; } | { isSuccess: false; })[]
46+
>a.map(item => ({ ...item })).filter : { <S extends { isSuccess: true; } | { isSuccess: false; }>(predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => value is S, thisArg?: any): S[]; (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => unknown, thisArg?: any): ({ isSuccess: true; } | { isSuccess: false; })[]; }
47+
>a.map(item => ({ ...item })) : ({ isSuccess: true; } | { isSuccess: false; })[]
48+
>a.map : <U>(callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[]
49+
>a : Item[]
50+
>map : <U>(callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[]
51+
>item => ({ ...item }) : (item: Item) => { isSuccess: true; } | { isSuccess: false; }
52+
>item : Item
53+
>({ ...item }) : { isSuccess: true; } | { isSuccess: false; }
54+
>{ ...item } : { isSuccess: true; } | { isSuccess: false; }
55+
>item : Item
56+
>filter : { <S extends { isSuccess: true; } | { isSuccess: false; }>(predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => value is S, thisArg?: any): S[]; (predicate: (value: { isSuccess: true; } | { isSuccess: false; }, index: number, array: ({ isSuccess: true; } | { isSuccess: false; })[]) => unknown, thisArg?: any): ({ isSuccess: true; } | { isSuccess: false; })[]; }
57+
>value => {} : (value: { isSuccess: true; } | { isSuccess: false; }) => void
58+
>value : { isSuccess: true; } | { isSuccess: false; }
59+
}
60+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @strictFunctionTypes: true
2+
3+
// #40995
4+
5+
interface Success {
6+
isSuccess: true;
7+
}
8+
9+
interface Fail {
10+
isSuccess: false;
11+
}
12+
13+
type Item = Success | Fail;
14+
15+
function f1(a: Item[]) {
16+
a.map(item => ({ ...item })).filter(value => {});
17+
}
18+
19+
function f2<T>(a: Item[]) {
20+
a.map(item => ({ ...item })).filter(value => {});
21+
}

0 commit comments

Comments
 (0)