From 53a34ce71630fce7b1ebc89f3451a2e70d58a4dd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 29 Sep 2019 08:08:42 -0700 Subject: [PATCH] Optimize checking for signatures with control flow effects --- src/compiler/checker.ts | 25 ++++++++++--------------- src/compiler/types.ts | 3 ++- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0731ccbf04c..600f286c520 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18580,10 +18580,9 @@ namespace ts { } } - function getEffectsSignature(node: CallExpression) { + function hasEffectsSignature(node: CallExpression) { const links = getNodeLinks(node); - let signature = links.effectsSignature; - if (signature === undefined) { + if (!(links.flags & NodeCheckFlags.EffectsSignatureChecked)) { // A call expression parented by an expression statement is a potential assertion. Other call // expressions are potential type predicate function calls. In order to avoid triggering // circularities in control flow analysis, we use getTypeOfDottedName when resolving the call @@ -18592,12 +18591,9 @@ namespace ts { node.expression.kind !== SyntaxKind.SuperKeyword ? checkNonNullExpression(node.expression) : undefined; const signatures = getSignaturesOfType(funcType && getApparentType(funcType) || unknownType, SignatureKind.Call); - const candidate = signatures.length === 1 && !signatures[0].typeParameters ? signatures[0] : - some(signatures, hasTypePredicateOrNeverReturnType) ? getResolvedSignature(node) : - undefined; - signature = links.effectsSignature = candidate && hasTypePredicateOrNeverReturnType(candidate) ? candidate : unknownSignature; + links.flags |= NodeCheckFlags.EffectsSignatureChecked | (some(signatures, hasTypePredicateOrNeverReturnType) ? NodeCheckFlags.HasEffectsSignature : 0); } - return signature === unknownSignature ? undefined : signature; + return !!(links.flags & NodeCheckFlags.HasEffectsSignature); } function hasTypePredicateOrNeverReturnType(signature: Signature) { @@ -18641,8 +18637,8 @@ namespace ts { flow = (flow).antecedent; } else if (flags & FlowFlags.Call) { - const signature = getEffectsSignature((flow).node); - if (signature && getReturnTypeOfSignature(signature).flags & TypeFlags.Never) { + const node = (flow).node; + if (hasEffectsSignature(node) && getReturnTypeOfSignature(getResolvedSignature(node)).flags & TypeFlags.Never) { return false; } flow = (flow).antecedent; @@ -18894,8 +18890,8 @@ namespace ts { } function getTypeAtFlowCall(flow: FlowCall): FlowType | undefined { - const signature = getEffectsSignature(flow.node); - if (signature) { + if (hasEffectsSignature(flow.node)) { + const signature = getResolvedSignature(flow.node); const predicate = getTypePredicateOfSignature(signature); if (predicate && (predicate.kind === TypePredicateKind.AssertsThis || predicate.kind === TypePredicateKind.AssertsIdentifier)) { const flowType = getTypeAtFlowNode(flow.antecedent); @@ -19506,9 +19502,8 @@ namespace ts { } function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { - if (hasMatchingArgument(callExpression, reference)) { - const signature = getEffectsSignature(callExpression); - const predicate = signature && getTypePredicateOfSignature(signature); + if (hasMatchingArgument(callExpression, reference) && hasEffectsSignature(callExpression)) { + const predicate = getTypePredicateOfSignature(getResolvedSignature(callExpression)); if (predicate && (predicate.kind === TypePredicateKind.This || predicate.kind === TypePredicateKind.Identifier)) { return narrowTypeByTypePredicate(type, predicate, callExpression, assumeTrue); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4ac846fd5e1..a33cfb680a9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4028,6 +4028,8 @@ namespace ts { AssignmentsMarked = 0x00800000, // Parameter assignments have been marked ClassWithConstructorReference = 0x01000000, // Class that contains a binding to its constructor inside of the class body. ConstructorReferenceInClass = 0x02000000, // Binding to a class constructor inside of the class's body. + EffectsSignatureChecked = 0x04000000, // Node checked for assertion or never-returning signature + HasEffectsSignature = 0x08000000, // Node has assertion or never-returning signature } /* @internal */ @@ -4038,7 +4040,6 @@ namespace ts { resolvedSignature?: Signature; // Cached signature of signature node or call expression resolvedSymbol?: Symbol; // Cached name resolution result resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result - effectsSignature?: Signature; // Signature with possible control flow effects enumMemberValue?: string | number; // Constant value of enum member isVisible?: boolean; // Is this node visible containsArgumentsReference?: boolean; // Whether a function-like declaration contains an 'arguments' reference