From 0928d71483ff67f4f25c58086938b162352bec37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 30 Mar 2024 19:29:29 +0100 Subject: [PATCH 1/4] Fixed single signature type parameter leak --- src/compiler/checker.ts | 19 +++++ ...lInferenceWithGenericLocalFunction.symbols | 44 +++++++++++ ...allInferenceWithGenericLocalFunction.types | 76 +++++++++++++++++++ .../reference/genericFunctionInference1.types | 4 +- ...icCallInferenceWithGenericLocalFunction.ts | 14 ++++ 5 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.symbols create mode 100644 tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.types create mode 100644 tests/cases/compiler/genericCallInferenceWithGenericLocalFunction.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0ab8b034175f8..8b3931c296d0a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19867,6 +19867,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } } + if (type.objectFlags & ObjectFlags.SingleSignatureType) { + const singleSignatureType = type as SingleSignatureType; + const isConstructor = !!singleSignatureType.constructSignatures!.length; + const signatureClone = cloneSignature(isConstructor ? singleSignatureType.constructSignatures![0] : singleSignatureType.callSignatures![0]); + signatureClone.mapper = combineTypeMappers(signatureClone.mapper, mapper); + const clone = createObjectType(singleSignatureType.objectFlags, createSymbol(SymbolFlags.Function, InternalSymbolName.Function)) as SingleSignatureType; + if (singleSignatureType.symbol.declarations) { + clone.symbol.declarations = singleSignatureType.symbol.declarations; + clone.symbol.valueDeclaration = singleSignatureType.symbol.valueDeclaration; + } + clone.outerTypeParameters = singleSignatureType.outerTypeParameters; + clone.members = singleSignatureType.members; + clone.properties = singleSignatureType.properties; + clone.callSignatures = isConstructor ? singleSignatureType.callSignatures : [signatureClone]; + clone.constructSignatures = isConstructor ? [signatureClone] : singleSignatureType.constructSignatures; + clone.indexInfos = singleSignatureType.indexInfos; + signatureClone.isolatedSignatureType = clone; + return clone; + } return result; } return type; diff --git a/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.symbols b/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.symbols new file mode 100644 index 0000000000000..dec6af6bac2c5 --- /dev/null +++ b/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.symbols @@ -0,0 +1,44 @@ +//// [tests/cases/compiler/genericCallInferenceWithGenericLocalFunction.ts] //// + +=== genericCallInferenceWithGenericLocalFunction.ts === +// https://github.com/microsoft/TypeScript/issues/43961 + +const createTransform = (tr: (from: I) => O) => tr; +>createTransform : Symbol(createTransform, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 5)) +>I : Symbol(I, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 25)) +>O : Symbol(O, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 27)) +>tr : Symbol(tr, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 31)) +>from : Symbol(from, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 36)) +>I : Symbol(I, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 25)) +>O : Symbol(O, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 27)) +>tr : Symbol(tr, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 31)) + +function withP2

(p: P) { +>withP2 : Symbol(withP2, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 57)) +>P : Symbol(P, Decl(genericCallInferenceWithGenericLocalFunction.ts, 4, 16)) +>p : Symbol(p, Decl(genericCallInferenceWithGenericLocalFunction.ts, 4, 19)) +>P : Symbol(P, Decl(genericCallInferenceWithGenericLocalFunction.ts, 4, 16)) + + const m = (from: I) => ({ ...from, ...p }); +>m : Symbol(m, Decl(genericCallInferenceWithGenericLocalFunction.ts, 5, 7)) +>I : Symbol(I, Decl(genericCallInferenceWithGenericLocalFunction.ts, 5, 13)) +>from : Symbol(from, Decl(genericCallInferenceWithGenericLocalFunction.ts, 5, 17)) +>I : Symbol(I, Decl(genericCallInferenceWithGenericLocalFunction.ts, 5, 13)) +>from : Symbol(from, Decl(genericCallInferenceWithGenericLocalFunction.ts, 5, 17)) +>p : Symbol(p, Decl(genericCallInferenceWithGenericLocalFunction.ts, 4, 19)) + + return createTransform(m); +>createTransform : Symbol(createTransform, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 5)) +>m : Symbol(m, Decl(genericCallInferenceWithGenericLocalFunction.ts, 5, 7)) +} + +const addP2 = withP2({ foo: 1 }); +>addP2 : Symbol(addP2, Decl(genericCallInferenceWithGenericLocalFunction.ts, 9, 5)) +>withP2 : Symbol(withP2, Decl(genericCallInferenceWithGenericLocalFunction.ts, 2, 57)) +>foo : Symbol(foo, Decl(genericCallInferenceWithGenericLocalFunction.ts, 9, 22)) + +const added2 = addP2({ bar: 2 }); +>added2 : Symbol(added2, Decl(genericCallInferenceWithGenericLocalFunction.ts, 10, 5)) +>addP2 : Symbol(addP2, Decl(genericCallInferenceWithGenericLocalFunction.ts, 9, 5)) +>bar : Symbol(bar, Decl(genericCallInferenceWithGenericLocalFunction.ts, 10, 22)) + diff --git a/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.types b/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.types new file mode 100644 index 0000000000000..cf159d4993f4c --- /dev/null +++ b/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.types @@ -0,0 +1,76 @@ +//// [tests/cases/compiler/genericCallInferenceWithGenericLocalFunction.ts] //// + +=== genericCallInferenceWithGenericLocalFunction.ts === +// https://github.com/microsoft/TypeScript/issues/43961 + +const createTransform = (tr: (from: I) => O) => tr; +>createTransform : (tr: (from: I) => O) => (from: I) => O +> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^ ^^^^^ +>(tr: (from: I) => O) => tr : (tr: (from: I) => O) => (from: I) => O +> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^ ^^^^^ +>tr : (from: I) => O +> : ^^^^^^^ ^^^^^ +>from : I +> : ^ +>tr : (from: I) => O +> : ^^^^^^^ ^^^^^ + +function withP2

(p: P) { +>withP2 :

(p: P) => (from: I) => I & P +> : ^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>p : P +> : ^ + + const m = (from: I) => ({ ...from, ...p }); +>m : (from: I) => I & P +> : ^ ^^^^^^^^ ^^^^^^^^^^ +>(from: I) => ({ ...from, ...p }) : (from: I) => I & P +> : ^ ^^^^^^^^ ^^^^^^^^^^ +>from : I +> : ^ +>({ ...from, ...p }) : I & P +> : ^^^^^ +>{ ...from, ...p } : I & P +> : ^^^^^ +>from : I +> : ^ +>p : P +> : ^ + + return createTransform(m); +>createTransform(m) : (from: I) => I & P +> : ^^^^^^^^^^^^^^^^^^^^^ +>createTransform : (tr: (from: I) => O) => (from: I) => O +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>m : (from: I) => I & P +> : ^^^^^^^^^^^^^^^^^^^^^ +} + +const addP2 = withP2({ foo: 1 }); +>addP2 : (from: I) => I & { foo: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>withP2({ foo: 1 }) : (from: I) => I & { foo: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>withP2 :

(p: P) => (from: I) => I & P +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ foo: 1 } : { foo: number; } +> : ^^^^^^^^^^^^^^^^ +>foo : number +> : ^^^^^^ +>1 : 1 +> : ^ + +const added2 = addP2({ bar: 2 }); +>added2 : { bar: number; } & { foo: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>addP2({ bar: 2 }) : { bar: number; } & { foo: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>addP2 : (from: I) => I & { foo: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ bar: 2 } : { bar: number; } +> : ^^^^^^^^^^^^^^^^ +>bar : number +> : ^^^^^^ +>2 : 2 +> : ^ + diff --git a/tests/baselines/reference/genericFunctionInference1.types b/tests/baselines/reference/genericFunctionInference1.types index 803927122062d..968889684bc64 100644 --- a/tests/baselines/reference/genericFunctionInference1.types +++ b/tests/baselines/reference/genericFunctionInference1.types @@ -3,8 +3,8 @@ === Performance Stats === Subtype cache: 100 / 100 (nearest 100) Assignability cache: 200 / 200 (nearest 100) -Type Count: 1,300 / 1,300 (nearest 100) -Instantiation count: 2,000 / 2,500 (nearest 500) +Type Count: 1,200 / 1,300 (nearest 100) +Instantiation count: 2,000 / 2,000 (nearest 500) Symbol count: 32,500 / 33,000 (nearest 500) === genericFunctionInference1.ts === diff --git a/tests/cases/compiler/genericCallInferenceWithGenericLocalFunction.ts b/tests/cases/compiler/genericCallInferenceWithGenericLocalFunction.ts new file mode 100644 index 0000000000000..1aff11c5b3507 --- /dev/null +++ b/tests/cases/compiler/genericCallInferenceWithGenericLocalFunction.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/43961 + +const createTransform = (tr: (from: I) => O) => tr; + +function withP2

(p: P) { + const m = (from: I) => ({ ...from, ...p }); + return createTransform(m); +} + +const addP2 = withP2({ foo: 1 }); +const added2 = addP2({ bar: 2 }); From ff96a84b3af3d6ebf560f38ed64494b5ce375b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 7 Apr 2024 21:06:17 +0200 Subject: [PATCH 2/4] reuse `instantiateAnonymousType` --- src/compiler/checker.ts | 18 +----------------- .../reference/genericFunctionInference1.types | 4 ++-- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e1e75be536dc2..2f887c170d5cb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20014,23 +20014,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } if (type.objectFlags & ObjectFlags.SingleSignatureType) { - const singleSignatureType = type as SingleSignatureType; - const isConstructor = !!singleSignatureType.constructSignatures!.length; - const signatureClone = cloneSignature(isConstructor ? singleSignatureType.constructSignatures![0] : singleSignatureType.callSignatures![0]); - signatureClone.mapper = combineTypeMappers(signatureClone.mapper, mapper); - const clone = createObjectType(singleSignatureType.objectFlags, createSymbol(SymbolFlags.Function, InternalSymbolName.Function)) as SingleSignatureType; - if (singleSignatureType.symbol.declarations) { - clone.symbol.declarations = singleSignatureType.symbol.declarations; - clone.symbol.valueDeclaration = singleSignatureType.symbol.valueDeclaration; - } - clone.outerTypeParameters = singleSignatureType.outerTypeParameters; - clone.members = singleSignatureType.members; - clone.properties = singleSignatureType.properties; - clone.callSignatures = isConstructor ? singleSignatureType.callSignatures : [signatureClone]; - clone.constructSignatures = isConstructor ? [signatureClone] : singleSignatureType.constructSignatures; - clone.indexInfos = singleSignatureType.indexInfos; - signatureClone.isolatedSignatureType = clone; - return clone; + return instantiateAnonymousType(type, mapper); } return result; } diff --git a/tests/baselines/reference/genericFunctionInference1.types b/tests/baselines/reference/genericFunctionInference1.types index 6e7a3a64e5b48..285d4f0bfc4b3 100644 --- a/tests/baselines/reference/genericFunctionInference1.types +++ b/tests/baselines/reference/genericFunctionInference1.types @@ -3,8 +3,8 @@ === Performance Stats === Subtype cache: 100 / 100 (nearest 100) Assignability cache: 200 / 200 (nearest 100) -Type Count: 1,200 / 1,300 (nearest 100) -Instantiation count: 2,000 / 2,000 (nearest 500) +Type Count: 1,300 / 1,300 (nearest 100) +Instantiation count: 2,000 / 2,500 (nearest 500) Symbol count: 32,500 / 33,000 (nearest 500) === genericFunctionInference1.ts === From 205e8d65b5c5181d25fe70cfbfe127cfe7a04a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 7 Apr 2024 21:21:39 +0200 Subject: [PATCH 3/4] cache it --- src/compiler/checker.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2f887c170d5cb..d7d43d4ebdf36 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19984,13 +19984,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const typeArguments = map(typeParameters, t => getMappedType(t, combinedMapper)); const newAliasSymbol = aliasSymbol || type.aliasSymbol; const newAliasTypeArguments = aliasSymbol ? aliasTypeArguments : instantiateTypes(type.aliasTypeArguments, mapper); - const id = getTypeListId(typeArguments) + getAliasId(newAliasSymbol, newAliasTypeArguments); + const id = (type.objectFlags & ObjectFlags.SingleSignatureType ? "S" : "") + getTypeListId(typeArguments) + getAliasId(newAliasSymbol, newAliasTypeArguments); if (!target.instantiations) { target.instantiations = new Map(); target.instantiations.set(getTypeListId(typeParameters) + getAliasId(target.aliasSymbol, target.aliasTypeArguments), target); } let result = target.instantiations.get(id); if (!result) { + if (type.objectFlags & ObjectFlags.SingleSignatureType) { + result = instantiateAnonymousType(type, mapper); + target.instantiations.set(id, result); + return result; + } const newMapper = createTypeMapper(typeParameters, typeArguments); result = target.objectFlags & ObjectFlags.Reference ? createDeferredTypeReference((type as DeferredTypeReference).target, (type as DeferredTypeReference).node, newMapper, newAliasSymbol, newAliasTypeArguments) : target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target as MappedType, newMapper, newAliasSymbol, newAliasTypeArguments) : @@ -20013,9 +20018,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } } - if (type.objectFlags & ObjectFlags.SingleSignatureType) { - return instantiateAnonymousType(type, mapper); - } return result; } return type; From c83d0d58939c0c037fdc28c77ff0680b12bf583c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 19 Apr 2024 09:59:23 -0700 Subject: [PATCH 4/4] Accept new baseline --- ...ericCallInferenceWithGenericLocalFunction.types | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.types b/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.types index ce6abe36a3c58..05b6d0617f3db 100644 --- a/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.types +++ b/tests/baselines/reference/genericCallInferenceWithGenericLocalFunction.types @@ -7,7 +7,7 @@ const createTransform = (tr: (from: I) => O) => tr; >createTransform : (tr: (from: I) => O) => (from: I) => O > : ^ ^^ ^^ ^^ ^^^^^^ ^^ ^^^^^ >(tr: (from: I) => O) => tr : (tr: (from: I) => O) => (from: I) => O -> : +> : ^ ^^ ^^ ^^ ^^^^^^ ^^ ^^^^^ >tr : (from: I) => O > : ^ ^^ ^^^^^ >from : I @@ -25,7 +25,7 @@ function withP2

(p: P) { >m : (from: I) => I & P > : ^ ^^ ^^ ^^^^^^^^^^ >(from: I) => ({ ...from, ...p }) : (from: I) => I & P -> : +> : ^ ^^ ^^ ^^^^^^^^^^ >from : I > : ^ >({ ...from, ...p }) : I & P @@ -41,18 +41,18 @@ function withP2

(p: P) { >createTransform(m) : (from: I) => I & P > : ^^^^ ^^^^^^^^^^^^^ >createTransform : (tr: (from: I) => O) => (from: I) => O -> : ^^^^^^^ ^^^ ^^^^^^^^^^^^^^ +> : ^ ^^ ^^ ^^ ^^^^^^ ^^ ^^^^^^ >m : (from: I) => I & P -> : ^^^^ ^^^^^^^^^^^^^ +> : ^ ^^ ^^ ^^^^^^^^^^ } const addP2 = withP2({ foo: 1 }); >addP2 : (from: I) => I & { foo: number; } > : ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >withP2({ foo: 1 }) : (from: I) => I & { foo: number; } -> : +> : ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >withP2 :

(p: P) => (from: I) => I & P -> : ^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^ +> : ^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^^^^^^ >{ foo: 1 } : { foo: number; } > : ^^^^^^^^^^^^^^^^ >foo : number @@ -64,7 +64,7 @@ const added2 = addP2({ bar: 2 }); >added2 : { bar: number; } & { foo: number; } > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >addP2({ bar: 2 }) : { bar: number; } & { foo: number; } -> : ^^^ +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >addP2 : (from: I) => I & { foo: number; } > : ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >{ bar: 2 } : { bar: number; }