Move anonymous type instantiation cache from AST node to root type (#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 <andrew@wheream.io>
This commit is contained in:
Anders Hejlsberg 2020-10-19 07:26:48 -07:00 committed by GitHub
parent cdf9c3b595
commit 3918e6c535
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 188 additions and 8 deletions

View file

@ -15046,9 +15046,10 @@ namespace ts {
}
function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper) {
const target = type.objectFlags & ObjectFlags.Instantiated ? type.target! : type;
const declaration = type.objectFlags & ObjectFlags.Reference ? (<TypeReference>type).node! : type.symbol.declarations[0];
const links = getNodeLinks(declaration);
const target = type.objectFlags & ObjectFlags.Reference ? <DeferredTypeReference>links.resolvedType! :
type.objectFlags & ObjectFlags.Instantiated ? type.target! : type;
let typeParameters = links.outerTypeParameters;
if (!typeParameters) {
// The first time an anonymous type is instantiated we compute and store a list of the type
@ -15065,10 +15066,6 @@ namespace ts {
filter(typeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) :
typeParameters;
links.outerTypeParameters = typeParameters;
if (typeParameters.length) {
links.instantiations = new Map<string, Type>();
links.instantiations.set(getTypeListId(typeParameters), target);
}
}
if (typeParameters.length) {
// We are instantiating an anonymous type that has one or more type parameters in scope. Apply the
@ -15077,13 +15074,17 @@ namespace ts {
const combinedMapper = combineTypeMappers(type.mapper, mapper);
const typeArguments = map(typeParameters, t => getMappedType(t, combinedMapper));
const id = getTypeListId(typeArguments);
let result = links.instantiations!.get(id);
if (!target.instantiations) {
target.instantiations = new Map<string, Type>();
target.instantiations.set(getTypeListId(typeParameters), target);
}
let result = target.instantiations.get(id);
if (!result) {
const newMapper = createTypeMapper(typeParameters, typeArguments);
result = target.objectFlags & ObjectFlags.Reference ? createDeferredTypeReference((<DeferredTypeReference>type).target, (<DeferredTypeReference>type).node, newMapper) :
target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(<MappedType>target, newMapper) :
instantiateAnonymousType(target, newMapper);
links.instantiations!.set(id, result);
target.instantiations.set(id, result);
}
return result;
}

View file

@ -4847,7 +4847,6 @@ namespace ts {
deferredNodes?: ESMap<NodeId, Node>; // Set of nodes whose checking has been deferred
capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement
outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type
instantiations?: ESMap<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
isExhaustive?: boolean; // Is node an exhaustive switch statement
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
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 {
node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode;
/* @internal */
mapper?: TypeMapper;
/* @internal */
instantiations?: ESMap<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
}
/* @internal */
@ -5221,6 +5222,7 @@ namespace ts {
export interface AnonymousType extends ObjectType {
target?: AnonymousType; // Instantiation target
mapper?: TypeMapper; // Instantiation mapper
instantiations?: ESMap<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
}
/* @internal */

View file

@ -0,0 +1,41 @@
//// [objectInstantiationFromUnionSpread.ts]
// #40995
interface Success {
isSuccess: true;
}
interface Fail {
isSuccess: false;
}
type Item = Success | Fail;
function f1(a: Item[]) {
a.map(item => ({ ...item })).filter(value => {});
}
function f2<T>(a: Item[]) {
a.map(item => ({ ...item })).filter(value => {});
}
//// [objectInstantiationFromUnionSpread.js]
// #40995
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function f1(a) {
a.map(function (item) { return (__assign({}, item)); }).filter(function (value) { });
}
function f2(a) {
a.map(function (item) { return (__assign({}, item)); }).filter(function (value) { });
}

View file

@ -0,0 +1,55 @@
=== tests/cases/compiler/objectInstantiationFromUnionSpread.ts ===
// #40995
interface Success {
>Success : Symbol(Success, Decl(objectInstantiationFromUnionSpread.ts, 0, 0))
isSuccess: true;
>isSuccess : Symbol(Success.isSuccess, Decl(objectInstantiationFromUnionSpread.ts, 2, 19))
}
interface Fail {
>Fail : Symbol(Fail, Decl(objectInstantiationFromUnionSpread.ts, 4, 1))
isSuccess: false;
>isSuccess : Symbol(Fail.isSuccess, Decl(objectInstantiationFromUnionSpread.ts, 6, 16))
}
type Item = Success | Fail;
>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1))
>Success : Symbol(Success, Decl(objectInstantiationFromUnionSpread.ts, 0, 0))
>Fail : Symbol(Fail, Decl(objectInstantiationFromUnionSpread.ts, 4, 1))
function f1(a: Item[]) {
>f1 : Symbol(f1, Decl(objectInstantiationFromUnionSpread.ts, 10, 27))
>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 12, 12))
>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1))
a.map(item => ({ ...item })).filter(value => {});
>a.map(item => ({ ...item })).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>a.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 12, 12))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 13, 8))
>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 13, 8))
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(objectInstantiationFromUnionSpread.ts, 13, 38))
}
function f2<T>(a: Item[]) {
>f2 : Symbol(f2, Decl(objectInstantiationFromUnionSpread.ts, 14, 1))
>T : Symbol(T, Decl(objectInstantiationFromUnionSpread.ts, 16, 12))
>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 16, 15))
>Item : Symbol(Item, Decl(objectInstantiationFromUnionSpread.ts, 8, 1))
a.map(item => ({ ...item })).filter(value => {});
>a.map(item => ({ ...item })).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>a.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>a : Symbol(a, Decl(objectInstantiationFromUnionSpread.ts, 16, 15))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 17, 8))
>item : Symbol(item, Decl(objectInstantiationFromUnionSpread.ts, 17, 8))
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(objectInstantiationFromUnionSpread.ts, 17, 38))
}

View file

@ -0,0 +1,60 @@
=== tests/cases/compiler/objectInstantiationFromUnionSpread.ts ===
// #40995
interface Success {
isSuccess: true;
>isSuccess : true
>true : true
}
interface Fail {
isSuccess: false;
>isSuccess : false
>false : false
}
type Item = Success | Fail;
>Item : Item
function f1(a: Item[]) {
>f1 : (a: Item[]) => void
>a : Item[]
a.map(item => ({ ...item })).filter(value => {});
>a.map(item => ({ ...item })).filter(value => {}) : ({ isSuccess: true; } | { isSuccess: false; })[]
>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; })[]; }
>a.map(item => ({ ...item })) : ({ isSuccess: true; } | { isSuccess: false; })[]
>a.map : <U>(callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[]
>a : Item[]
>map : <U>(callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[]
>item => ({ ...item }) : (item: Item) => { isSuccess: true; } | { isSuccess: false; }
>item : Item
>({ ...item }) : { isSuccess: true; } | { isSuccess: false; }
>{ ...item } : { isSuccess: true; } | { isSuccess: false; }
>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; })[]; }
>value => {} : (value: { isSuccess: true; } | { isSuccess: false; }) => void
>value : { isSuccess: true; } | { isSuccess: false; }
}
function f2<T>(a: Item[]) {
>f2 : <T>(a: Item[]) => void
>a : Item[]
a.map(item => ({ ...item })).filter(value => {});
>a.map(item => ({ ...item })).filter(value => {}) : ({ isSuccess: true; } | { isSuccess: false; })[]
>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; })[]; }
>a.map(item => ({ ...item })) : ({ isSuccess: true; } | { isSuccess: false; })[]
>a.map : <U>(callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[]
>a : Item[]
>map : <U>(callbackfn: (value: Item, index: number, array: Item[]) => U, thisArg?: any) => U[]
>item => ({ ...item }) : (item: Item) => { isSuccess: true; } | { isSuccess: false; }
>item : Item
>({ ...item }) : { isSuccess: true; } | { isSuccess: false; }
>{ ...item } : { isSuccess: true; } | { isSuccess: false; }
>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; })[]; }
>value => {} : (value: { isSuccess: true; } | { isSuccess: false; }) => void
>value : { isSuccess: true; } | { isSuccess: false; }
}

View file

@ -0,0 +1,21 @@
// @strictFunctionTypes: true
// #40995
interface Success {
isSuccess: true;
}
interface Fail {
isSuccess: false;
}
type Item = Success | Fail;
function f1(a: Item[]) {
a.map(item => ({ ...item })).filter(value => {});
}
function f2<T>(a: Item[]) {
a.map(item => ({ ...item })).filter(value => {});
}