From e24cc01b08adabf8fcbe010719c348cd525b44dd Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 23 Sep 2024 10:46:57 -0700 Subject: [PATCH] Cache instantiation expression types early enough to prevent reentrancy during printback (#59931) --- src/compiler/checker.ts | 8 +++ src/compiler/types.ts | 1 + ...antiationExpressionErrorNoCrash.errors.txt | 28 +++++++++ .../instantiationExpressionErrorNoCrash.js | 34 ++++++++++ ...nstantiationExpressionErrorNoCrash.symbols | 52 ++++++++++++++++ .../instantiationExpressionErrorNoCrash.types | 62 +++++++++++++++++++ .../instantiationExpressionErrors.types | 4 +- .../instantiationExpressionErrorNoCrash.ts | 17 +++++ 8 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/instantiationExpressionErrorNoCrash.errors.txt create mode 100644 tests/baselines/reference/instantiationExpressionErrorNoCrash.js create mode 100644 tests/baselines/reference/instantiationExpressionErrorNoCrash.symbols create mode 100644 tests/baselines/reference/instantiationExpressionErrorNoCrash.types create mode 100644 tests/cases/compiler/instantiationExpressionErrorNoCrash.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 438e46eca11..0caa7319776 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -37401,9 +37401,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (exprType === silentNeverType || isErrorType(exprType) || !some(typeArguments)) { return exprType; } + const links = getNodeLinks(node); + if (!links.instantiationExpressionTypes) { + links.instantiationExpressionTypes = new Map(); + } + if (links.instantiationExpressionTypes.has(exprType.id)) { + return links.instantiationExpressionTypes.get(exprType.id)!; + } let hasSomeApplicableSignature = false; let nonApplicableType: Type | undefined; const result = getInstantiatedType(exprType); + links.instantiationExpressionTypes.set(exprType.id, result); const errorType = hasSomeApplicableSignature ? nonApplicableType : exprType; if (errorType) { diagnostics.add(createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Type_0_has_no_signatures_for_which_the_type_argument_list_is_applicable, typeToString(errorType))); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f6414327a2b..a0db02d050d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6239,6 +6239,7 @@ export interface NodeLinks { potentialReflectCollisions?: Node[]; potentialUnusedRenamedBindingElementsInTypes?: BindingElement[]; externalHelpersModule?: Symbol; // Resolved symbol for the external helpers module + instantiationExpressionTypes?: Map; // Cache of instantiation expression types for the node } /** @internal */ diff --git a/tests/baselines/reference/instantiationExpressionErrorNoCrash.errors.txt b/tests/baselines/reference/instantiationExpressionErrorNoCrash.errors.txt new file mode 100644 index 00000000000..58ae63d76c5 --- /dev/null +++ b/tests/baselines/reference/instantiationExpressionErrorNoCrash.errors.txt @@ -0,0 +1,28 @@ +instantiationExpressionErrorNoCrash.ts(15,38): error TS2344: Type 'typeof createCacheReducer' does not satisfy the constraint '(...args: any) => any'. + Type 'typeof createCacheReducer' provides no match for the signature '(...args: any): any'. +instantiationExpressionErrorNoCrash.ts(15,64): error TS2635: Type '(queries: { [QK in keyof QR]: any; }) => (state?: { queries: QR; }) => { queries: QR; }' has no signatures for which the type argument list is applicable. + + +==== instantiationExpressionErrorNoCrash.ts (2 errors) ==== + const createCacheReducer = ( + queries: Cache["queries"], + ) => { + const queriesMap = {} as QR; + + const initialState = { + queries: queriesMap, + }; + + return (state = initialState) => state; + }; + + export type Cache = { + queries: { + [QK in keyof QR]: ReturnType>; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2344: Type 'typeof createCacheReducer' does not satisfy the constraint '(...args: any) => any'. +!!! error TS2344: Type 'typeof createCacheReducer' provides no match for the signature '(...args: any): any'. + ~~ +!!! error TS2635: Type '(queries: { [QK in keyof QR]: any; }) => (state?: { queries: QR; }) => { queries: QR; }' has no signatures for which the type argument list is applicable. + }; + }; \ No newline at end of file diff --git a/tests/baselines/reference/instantiationExpressionErrorNoCrash.js b/tests/baselines/reference/instantiationExpressionErrorNoCrash.js new file mode 100644 index 00000000000..fe59052943c --- /dev/null +++ b/tests/baselines/reference/instantiationExpressionErrorNoCrash.js @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/instantiationExpressionErrorNoCrash.ts] //// + +//// [instantiationExpressionErrorNoCrash.ts] +const createCacheReducer = ( + queries: Cache["queries"], +) => { + const queriesMap = {} as QR; + + const initialState = { + queries: queriesMap, + }; + + return (state = initialState) => state; +}; + +export type Cache = { + queries: { + [QK in keyof QR]: ReturnType>; + }; +}; + +//// [instantiationExpressionErrorNoCrash.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var createCacheReducer = function (queries) { + var queriesMap = {}; + var initialState = { + queries: queriesMap, + }; + return function (state) { + if (state === void 0) { state = initialState; } + return state; + }; +}; diff --git a/tests/baselines/reference/instantiationExpressionErrorNoCrash.symbols b/tests/baselines/reference/instantiationExpressionErrorNoCrash.symbols new file mode 100644 index 00000000000..32603b4c638 --- /dev/null +++ b/tests/baselines/reference/instantiationExpressionErrorNoCrash.symbols @@ -0,0 +1,52 @@ +//// [tests/cases/compiler/instantiationExpressionErrorNoCrash.ts] //// + +=== instantiationExpressionErrorNoCrash.ts === +const createCacheReducer = ( +>createCacheReducer : Symbol(createCacheReducer, Decl(instantiationExpressionErrorNoCrash.ts, 0, 5)) +>N : Symbol(N, Decl(instantiationExpressionErrorNoCrash.ts, 0, 28)) +>QR : Symbol(QR, Decl(instantiationExpressionErrorNoCrash.ts, 0, 45)) + + queries: Cache["queries"], +>queries : Symbol(queries, Decl(instantiationExpressionErrorNoCrash.ts, 0, 50)) +>Cache : Symbol(Cache, Decl(instantiationExpressionErrorNoCrash.ts, 10, 2)) +>N : Symbol(N, Decl(instantiationExpressionErrorNoCrash.ts, 0, 28)) +>QR : Symbol(QR, Decl(instantiationExpressionErrorNoCrash.ts, 0, 45)) + +) => { + const queriesMap = {} as QR; +>queriesMap : Symbol(queriesMap, Decl(instantiationExpressionErrorNoCrash.ts, 3, 9)) +>QR : Symbol(QR, Decl(instantiationExpressionErrorNoCrash.ts, 0, 45)) + + const initialState = { +>initialState : Symbol(initialState, Decl(instantiationExpressionErrorNoCrash.ts, 5, 9)) + + queries: queriesMap, +>queries : Symbol(queries, Decl(instantiationExpressionErrorNoCrash.ts, 5, 26)) +>queriesMap : Symbol(queriesMap, Decl(instantiationExpressionErrorNoCrash.ts, 3, 9)) + + }; + + return (state = initialState) => state; +>state : Symbol(state, Decl(instantiationExpressionErrorNoCrash.ts, 9, 12)) +>initialState : Symbol(initialState, Decl(instantiationExpressionErrorNoCrash.ts, 5, 9)) +>state : Symbol(state, Decl(instantiationExpressionErrorNoCrash.ts, 9, 12)) + +}; + +export type Cache = { +>Cache : Symbol(Cache, Decl(instantiationExpressionErrorNoCrash.ts, 10, 2)) +>N : Symbol(N, Decl(instantiationExpressionErrorNoCrash.ts, 12, 18)) +>QR : Symbol(QR, Decl(instantiationExpressionErrorNoCrash.ts, 12, 35)) + + queries: { +>queries : Symbol(queries, Decl(instantiationExpressionErrorNoCrash.ts, 12, 43)) + + [QK in keyof QR]: ReturnType>; +>QK : Symbol(QK, Decl(instantiationExpressionErrorNoCrash.ts, 14, 9)) +>QR : Symbol(QR, Decl(instantiationExpressionErrorNoCrash.ts, 12, 35)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>createCacheReducer : Symbol(createCacheReducer, Decl(instantiationExpressionErrorNoCrash.ts, 0, 5)) +>QR : Symbol(QR, Decl(instantiationExpressionErrorNoCrash.ts, 12, 35)) + + }; +}; diff --git a/tests/baselines/reference/instantiationExpressionErrorNoCrash.types b/tests/baselines/reference/instantiationExpressionErrorNoCrash.types new file mode 100644 index 00000000000..b32eaab9847 --- /dev/null +++ b/tests/baselines/reference/instantiationExpressionErrorNoCrash.types @@ -0,0 +1,62 @@ +//// [tests/cases/compiler/instantiationExpressionErrorNoCrash.ts] //// + +=== instantiationExpressionErrorNoCrash.ts === +const createCacheReducer = ( +>createCacheReducer : (queries: Cache["queries"]) => (state?: { queries: QR; }) => { queries: QR; } +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>( queries: Cache["queries"],) => { const queriesMap = {} as QR; const initialState = { queries: queriesMap, }; return (state = initialState) => state;} : (queries: Cache["queries"]) => (state?: { queries: QR; }) => { queries: QR; } +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + queries: Cache["queries"], +>queries : { [QK in keyof QR]: any; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^ + +) => { + const queriesMap = {} as QR; +>queriesMap : QR +> : ^^ +>{} as QR : QR +> : ^^ +>{} : {} +> : ^^ + + const initialState = { +>initialState : { queries: QR; } +> : ^^^^^^^^^^^^^^^^ +>{ queries: queriesMap, } : { queries: QR; } +> : ^^^^^^^^^^^^^^^^ + + queries: queriesMap, +>queries : QR +> : ^^ +>queriesMap : QR +> : ^^ + + }; + + return (state = initialState) => state; +>(state = initialState) => state : (state?: { queries: QR; }) => { queries: QR; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>state : { queries: QR; } +> : ^^^^^^^^^^^^^^^^ +>initialState : { queries: QR; } +> : ^^^^^^^^^^^^^^^^ +>state : { queries: QR; } +> : ^^^^^^^^^^^^^^^^ + +}; + +export type Cache = { +>Cache : Cache +> : ^^^^^^^^^^^^ + + queries: { +>queries : { [QK in keyof QR]: any; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^ + + [QK in keyof QR]: ReturnType>; +>createCacheReducer : (queries: Cache["queries"]) => (state?: { queries: QR_1; }) => { queries: QR_1; } +> : ^^^^^^^^^^^^^ ^^^^^^^^ ^^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + }; +}; diff --git a/tests/baselines/reference/instantiationExpressionErrors.types b/tests/baselines/reference/instantiationExpressionErrors.types index 4e3f4801143..adb31f243e5 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.types +++ b/tests/baselines/reference/instantiationExpressionErrors.types @@ -179,7 +179,7 @@ const c1 = g || ((x: string) => x); >c1 : (x: string) => string > : ^ ^^^^^^^^^^^^^^^^^^^ >g || ((x: string) => x) : (x: string) => string -> : ^ ^^ ^^^^^^^^^^^ +> : ^ ^^^^^^^^^^^^^^^^^^^ >g : ((x: string) => string) | undefined > : ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >g : ((x: T) => T) | undefined @@ -197,7 +197,7 @@ const c2 = g ?? ((x: string) => x); >c2 : (x: string) => string > : ^ ^^^^^^^^^^^^^^^^^^^ >g ?? ((x: string) => x) : (x: string) => string -> : ^ ^^ ^^^^^^^^^^^ +> : ^ ^^^^^^^^^^^^^^^^^^^ >g : ((x: string) => string) | undefined > : ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >g : ((x: T) => T) | undefined diff --git a/tests/cases/compiler/instantiationExpressionErrorNoCrash.ts b/tests/cases/compiler/instantiationExpressionErrorNoCrash.ts new file mode 100644 index 00000000000..2a87f342d2d --- /dev/null +++ b/tests/cases/compiler/instantiationExpressionErrorNoCrash.ts @@ -0,0 +1,17 @@ +const createCacheReducer = ( + queries: Cache["queries"], +) => { + const queriesMap = {} as QR; + + const initialState = { + queries: queriesMap, + }; + + return (state = initialState) => state; +}; + +export type Cache = { + queries: { + [QK in keyof QR]: ReturnType>; + }; +}; \ No newline at end of file