Reuse cached resolved signatures early (#60208)

This commit is contained in:
Mateusz Burzyński 2024-11-06 02:16:37 +01:00 коммит произвёл GitHub
Родитель 8d95ac5968
Коммит 5e2e32120b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 132 добавлений и 11 удалений

Просмотреть файл

@ -35846,10 +35846,28 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (!result) {
result = chooseOverload(candidates, assignableRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma);
}
const links = getNodeLinks(node);
if (links.resolvedSignature !== resolvingSignature && !candidatesOutArray) {
// There are 2 situations in which it's good to preemptively return the cached result here:
//
// 1. if the signature resolution originated on a node that itself depends on the contextual type
// then it's possible that the resolved signature might not be the same as the one that would be computed in source order
// since resolving such signature leads to resolving the potential outer signature, its arguments and thus the very same signature
// it's possible that this inner resolution sets the resolvedSignature first.
// In such a case we ignore the local result and reuse the correct one that was cached.
//
// 2. In certain circular-like situations it's possible that the compiler reentries this function for the same node.
// It's possible to resolve the inner call against preemptively set empty members (for example in `resolveAnonymousTypeMembers`) of some type.
// When that happens the compiler might report an error for that inner call but at the same time it might end up resolving the actual members of the other type.
// This in turn creates a situation in which the outer call fails in `getSignatureApplicabilityError` due to a cached `RelationComparisonResult.Failed`
// but when the compiler tries to report that error (in the code below) it also tries to elaborate it and that can succeed as types would be related against the *resolved* members of the other type.
// This can hit `No error for last overload signature` assert but since that error was already reported when the inner call failed we can skip this step altogether here by returning the cached signature early.
Debug.assert(links.resolvedSignature);
return links.resolvedSignature;
}
if (result) {
return result;
}
result = getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray, checkMode);
// Preemptively cache the result; getResolvedSignature will do this after we return, but
// we need to ensure that the result is present for the error checks below so that if
@ -35858,7 +35876,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// don't hit this issue because they only observe this result after it's had a chance to
// be cached, but the error reporting code below executes before getResolvedSignature sets
// resolvedSignature.
getNodeLinks(node).resolvedSignature = result;
links.resolvedSignature = result;
// No signatures were applicable. Now report errors based on the last applicable signature with
// no arguments excluded from assignability checks.
@ -36871,19 +36889,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
resolutionStart = resolutionTargets.length;
}
links.resolvedSignature = resolvingSignature;
let result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal);
const result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal);
resolutionStart = saveResolutionStart;
// When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call
// resolution should be deferred.
if (result !== resolvingSignature) {
// if the signature resolution originated on a node that itself depends on the contextual type
// then it's possible that the resolved signature might not be the same as the one that would be computed in source order
// since resolving such signature leads to resolving the potential outer signature, its arguments and thus the very same signature
// it's possible that this inner resolution sets the resolvedSignature first.
// In such a case we ignore the local result and reuse the correct one that was cached.
if (links.resolvedSignature !== resolvingSignature) {
result = links.resolvedSignature;
}
// If signature resolution originated in control flow type analysis (for example to compute the
// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
// types from the control flow analysis.

Просмотреть файл

@ -0,0 +1,22 @@
mixinWithBaseDependingOnSelfNoCrash1.ts(11,48): error TS2345: Argument of type 'typeof BaseItem' is not assignable to parameter of type 'new (...args: any[]) => any'.
Type 'typeof BaseItem' provides no match for the signature 'new (...args: any[]): any'.
==== mixinWithBaseDependingOnSelfNoCrash1.ts (1 errors) ====
// https://github.com/microsoft/TypeScript/issues/60202
declare class Document<Parent> {}
declare class BaseItem extends Document<typeof Item> {}
declare function ClientDocumentMixin<
BaseClass extends new (...args: any[]) => any,
>(Base: BaseClass): any;
declare class Item extends ClientDocumentMixin(BaseItem) {}
~~~~~~~~
!!! error TS2345: Argument of type 'typeof BaseItem' is not assignable to parameter of type 'new (...args: any[]) => any'.
!!! error TS2345: Type 'typeof BaseItem' provides no match for the signature 'new (...args: any[]): any'.
export {};

Просмотреть файл

@ -0,0 +1,32 @@
//// [tests/cases/conformance/classes/mixinWithBaseDependingOnSelfNoCrash1.ts] ////
=== mixinWithBaseDependingOnSelfNoCrash1.ts ===
// https://github.com/microsoft/TypeScript/issues/60202
declare class Document<Parent> {}
>Document : Symbol(Document, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 0, 0))
>Parent : Symbol(Parent, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 2, 23))
declare class BaseItem extends Document<typeof Item> {}
>BaseItem : Symbol(BaseItem, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 2, 33))
>Document : Symbol(Document, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 0, 0))
>Item : Symbol(Item, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 8, 24))
declare function ClientDocumentMixin<
>ClientDocumentMixin : Symbol(ClientDocumentMixin, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 4, 55))
BaseClass extends new (...args: any[]) => any,
>BaseClass : Symbol(BaseClass, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 6, 37))
>args : Symbol(args, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 7, 25))
>(Base: BaseClass): any;
>Base : Symbol(Base, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 8, 2))
>BaseClass : Symbol(BaseClass, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 6, 37))
declare class Item extends ClientDocumentMixin(BaseItem) {}
>Item : Symbol(Item, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 8, 24))
>ClientDocumentMixin : Symbol(ClientDocumentMixin, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 4, 55))
>BaseItem : Symbol(BaseItem, Decl(mixinWithBaseDependingOnSelfNoCrash1.ts, 2, 33))
export {};

Просмотреть файл

@ -0,0 +1,41 @@
//// [tests/cases/conformance/classes/mixinWithBaseDependingOnSelfNoCrash1.ts] ////
=== mixinWithBaseDependingOnSelfNoCrash1.ts ===
// https://github.com/microsoft/TypeScript/issues/60202
declare class Document<Parent> {}
>Document : Document<Parent>
> : ^^^^^^^^^^^^^^^^
declare class BaseItem extends Document<typeof Item> {}
>BaseItem : BaseItem
> : ^^^^^^^^
>Document : Document<typeof Item>
> : ^^^^^^^^^^^^^^^^^^^^^
>Item : typeof Item
> : ^^^^^^^^^^^
declare function ClientDocumentMixin<
>ClientDocumentMixin : <BaseClass extends new (...args: any[]) => any>(Base: BaseClass) => any
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
BaseClass extends new (...args: any[]) => any,
>args : any[]
> : ^^^^^
>(Base: BaseClass): any;
>Base : BaseClass
> : ^^^^^^^^^
declare class Item extends ClientDocumentMixin(BaseItem) {}
>Item : Item
> : ^^^^
>ClientDocumentMixin(BaseItem) : any
> : ^^^
>ClientDocumentMixin : <BaseClass extends new (...args: any[]) => any>(Base: BaseClass) => any
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>BaseItem : typeof BaseItem
> : ^^^^^^^^^^^^^^^
export {};

Просмотреть файл

@ -0,0 +1,16 @@
// @strict: true
// @noEmit: true
// https://github.com/microsoft/TypeScript/issues/60202
declare class Document<Parent> {}
declare class BaseItem extends Document<typeof Item> {}
declare function ClientDocumentMixin<
BaseClass extends new (...args: any[]) => any,
>(Base: BaseClass): any;
declare class Item extends ClientDocumentMixin(BaseItem) {}
export {};