Skip to content

Commit 64d3db2

Browse files
committed
fixed checker and fixed results
1 parent 379dce9 commit 64d3db2

14 files changed

+884
-62
lines changed

src/compiler/checker.ts

+83-62
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
14871487
var lastGetCombinedModifierFlagsNode: Declaration | undefined;
14881488
var lastGetCombinedModifierFlagsResult = ModifierFlags.None;
14891489

1490+
var chooseOverloadRecursionLevel = -1; // #56013
1491+
var chooseOverloadFlushNodesSignaturesReq: (Set<Node> | undefined)[] = []; // #56013
1492+
14901493
// for public members that accept a Node or one of its subtypes, we must guard against
14911494
// synthetic nodes created during transformations by calling `getParseTreeNode`.
14921495
// for most of these, we perform the guard only on `checker` to avoid any possible
@@ -34310,87 +34313,95 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3431034313
}
3431134314

3431234315
function chooseOverload(candidates: Signature[], relation: Map<string, RelationComparisonResult>, isSingleNonGenericCandidate: boolean, signatureHelpTrailingComma = false) {
34313-
candidatesForArgumentError = undefined;
34314-
candidateForArgumentArityError = undefined;
34315-
candidateForTypeArgumentError = undefined;
34316-
34317-
if (isSingleNonGenericCandidate) {
34318-
const candidate = candidates[0];
34319-
if (some(typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
34320-
return undefined;
34321-
}
34322-
if (getSignatureApplicabilityError(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) {
34323-
candidatesForArgumentError = [candidate];
34324-
return undefined;
34316+
chooseOverloadRecursionLevel++; // #56013
34317+
chooseOverloadFlushNodesSignaturesReq[chooseOverloadRecursionLevel] = undefined;
34318+
const result = (() => {
34319+
candidatesForArgumentError = undefined;
34320+
candidateForArgumentArityError = undefined;
34321+
candidateForTypeArgumentError = undefined;
34322+
34323+
if (isSingleNonGenericCandidate) {
34324+
const candidate = candidates[0];
34325+
if (some(typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
34326+
return undefined;
34327+
}
34328+
if (getSignatureApplicabilityError(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) {
34329+
candidatesForArgumentError = [candidate];
34330+
return undefined;
34331+
}
34332+
return candidate;
3432534333
}
34326-
return candidate;
34327-
}
3432834334

34329-
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
34330-
const candidate = candidates[candidateIndex];
34331-
if (!hasCorrectTypeArgumentArity(candidate, typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
34332-
continue;
34333-
}
34335+
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
34336+
if (candidateIndex > 0) chooseOverloadFlushNodesSignaturesReq[chooseOverloadRecursionLevel] = new Set(); // #56013
34337+
const candidate = candidates[candidateIndex];
34338+
if (!hasCorrectTypeArgumentArity(candidate, typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
34339+
continue;
34340+
}
3433434341

34335-
let checkCandidate: Signature;
34336-
let inferenceContext: InferenceContext | undefined;
34342+
let checkCandidate: Signature;
34343+
let inferenceContext: InferenceContext | undefined;
3433734344

34338-
if (candidate.typeParameters) {
34339-
let typeArgumentTypes: Type[] | undefined;
34340-
if (some(typeArguments)) {
34341-
typeArgumentTypes = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
34342-
if (!typeArgumentTypes) {
34343-
candidateForTypeArgumentError = candidate;
34344-
continue;
34345+
if (candidate.typeParameters) {
34346+
let typeArgumentTypes: Type[] | undefined;
34347+
if (some(typeArguments)) {
34348+
typeArgumentTypes = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
34349+
if (!typeArgumentTypes) {
34350+
candidateForTypeArgumentError = candidate;
34351+
continue;
34352+
}
3434534353
}
34346-
}
34347-
else {
34348-
inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
34349-
typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext);
34350-
argCheckMode |= inferenceContext.flags & InferenceFlags.SkippedGenericFunction ? CheckMode.SkipGenericFunctions : CheckMode.Normal;
34351-
}
34352-
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters);
34353-
// If the original signature has a generic rest type, instantiation may produce a
34354-
// signature with different arity and we need to perform another arity check.
34355-
if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) {
34356-
candidateForArgumentArityError = checkCandidate;
34357-
continue;
34358-
}
34359-
}
34360-
else {
34361-
checkCandidate = candidate;
34362-
}
34363-
if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) {
34364-
// Give preference to error candidates that have no rest parameters (as they are more specific)
34365-
(candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate);
34366-
continue;
34367-
}
34368-
if (argCheckMode) {
34369-
// If one or more context sensitive arguments were excluded, we start including
34370-
// them now (and keeping do so for any subsequent candidates) and perform a second
34371-
// round of type inference and applicability checking for this particular candidate.
34372-
argCheckMode = CheckMode.Normal;
34373-
if (inferenceContext) {
34374-
const typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode, inferenceContext);
34375-
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext.inferredTypeParameters);
34354+
else {
34355+
inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
34356+
typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext);
34357+
argCheckMode |= inferenceContext.flags & InferenceFlags.SkippedGenericFunction ? CheckMode.SkipGenericFunctions : CheckMode.Normal;
34358+
}
34359+
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters);
3437634360
// If the original signature has a generic rest type, instantiation may produce a
3437734361
// signature with different arity and we need to perform another arity check.
3437834362
if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) {
3437934363
candidateForArgumentArityError = checkCandidate;
3438034364
continue;
3438134365
}
3438234366
}
34367+
else {
34368+
checkCandidate = candidate;
34369+
}
3438334370
if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) {
3438434371
// Give preference to error candidates that have no rest parameters (as they are more specific)
3438534372
(candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate);
3438634373
continue;
3438734374
}
34375+
if (argCheckMode) {
34376+
// If one or more context sensitive arguments were excluded, we start including
34377+
// them now (and keeping do so for any subsequent candidates) and perform a second
34378+
// round of type inference and applicability checking for this particular candidate.
34379+
argCheckMode = CheckMode.Normal;
34380+
if (inferenceContext) {
34381+
const typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode, inferenceContext);
34382+
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext.inferredTypeParameters);
34383+
// If the original signature has a generic rest type, instantiation may produce a
34384+
// signature with different arity and we need to perform another arity check.
34385+
if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) {
34386+
candidateForArgumentArityError = checkCandidate;
34387+
continue;
34388+
}
34389+
}
34390+
if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) {
34391+
// Give preference to error candidates that have no rest parameters (as they are more specific)
34392+
(candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate);
34393+
continue;
34394+
}
34395+
}
34396+
candidates[candidateIndex] = checkCandidate;
34397+
return checkCandidate;
3438834398
}
34389-
candidates[candidateIndex] = checkCandidate;
34390-
return checkCandidate;
34391-
}
3439234399

34393-
return undefined;
34400+
return undefined;
34401+
})();
34402+
chooseOverloadFlushNodesSignaturesReq[chooseOverloadRecursionLevel] = undefined;
34403+
chooseOverloadRecursionLevel--;
34404+
return result;
3439434405
}
3439534406
}
3439634407

@@ -38932,6 +38943,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3893238943
const saveCurrentNode = currentNode;
3893338944
currentNode = node;
3893438945
instantiationCount = 0;
38946+
// #56013
38947+
if (node.kind === SyntaxKind.CallExpression && chooseOverloadRecursionLevel >= 0) {
38948+
let setOfNode: Set<Node> | undefined;
38949+
if (chooseOverloadRecursionLevel >= 0 && (setOfNode = chooseOverloadFlushNodesSignaturesReq[chooseOverloadRecursionLevel])) {
38950+
if (!setOfNode.has(node)) {
38951+
getNodeLinks(node).resolvedSignature = undefined;
38952+
setOfNode.add(node);
38953+
}
38954+
}
38955+
}
3893538956
const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple);
3893638957
const type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
3893738958
if (isConstEnumObjectType(type)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts] ////
2+
3+
//// [arrayFilterBooleanExternalOverload1.ts]
4+
// #56013
5+
6+
// For reference, thise cases work as expected (no errors) when no external BooleanConstrudtor like overload is present
7+
declare const maybe: boolean;
8+
{
9+
const id = <T>() => (t: T) => !!t;
10+
11+
const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id());
12+
13+
result1;
14+
15+
const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean
16+
17+
result2;
18+
}
19+
20+
21+
//// [arrayFilterBooleanExternalOverload1.js]
22+
"use strict";
23+
// #56013
24+
{
25+
const id = () => (t) => !!t;
26+
const result1 = (maybe ? ['foo', 'bar', undefined] : [1]).filter(id());
27+
result1;
28+
const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean
29+
result2;
30+
}
31+
32+
33+
//// [arrayFilterBooleanExternalOverload1.d.ts]
34+
declare const maybe: boolean;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//// [tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts] ////
2+
3+
=== arrayFilterBooleanExternalOverload1.ts ===
4+
// #56013
5+
6+
// For reference, thise cases work as expected (no errors) when no external BooleanConstrudtor like overload is present
7+
declare const maybe: boolean;
8+
>maybe : Symbol(maybe, Decl(arrayFilterBooleanExternalOverload1.ts, 3, 13))
9+
{
10+
const id = <T>() => (t: T) => !!t;
11+
>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 9))
12+
>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 16))
13+
>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 25))
14+
>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 16))
15+
>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 25))
16+
17+
const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id());
18+
>result1 : Symbol(result1, Decl(arrayFilterBooleanExternalOverload1.ts, 7, 9))
19+
>(maybe ? ['foo', 'bar', undefined] : [1] ).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
20+
>maybe : Symbol(maybe, Decl(arrayFilterBooleanExternalOverload1.ts, 3, 13))
21+
>undefined : Symbol(undefined)
22+
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
23+
>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 9))
24+
25+
result1;
26+
>result1 : Symbol(result1, Decl(arrayFilterBooleanExternalOverload1.ts, 7, 9))
27+
28+
const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean
29+
>result2 : Symbol(result2, Decl(arrayFilterBooleanExternalOverload1.ts, 11, 9))
30+
>['foo', 'bar', undefined].filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
31+
>undefined : Symbol(undefined)
32+
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
33+
>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 9))
34+
35+
result2;
36+
>result2 : Symbol(result2, Decl(arrayFilterBooleanExternalOverload1.ts, 11, 9))
37+
}
38+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//// [tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts] ////
2+
3+
=== arrayFilterBooleanExternalOverload1.ts ===
4+
// #56013
5+
6+
// For reference, thise cases work as expected (no errors) when no external BooleanConstrudtor like overload is present
7+
declare const maybe: boolean;
8+
>maybe : boolean
9+
{
10+
const id = <T>() => (t: T) => !!t;
11+
>id : <T>() => (t: T) => boolean
12+
><T>() => (t: T) => !!t : <T>() => (t: T) => boolean
13+
>(t: T) => !!t : (t: T) => boolean
14+
>t : T
15+
>!!t : boolean
16+
>!t : boolean
17+
>t : T
18+
19+
const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id());
20+
>result1 : (string | number | undefined)[]
21+
>(maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()) : (string | number | undefined)[]
22+
>(maybe ? ['foo', 'bar', undefined] : [1] ).filter : { <S extends string | undefined>(predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; } | { <S_1 extends number>(predicate: (value: number, index: number, array: number[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }
23+
>(maybe ? ['foo', 'bar', undefined] : [1] ) : (string | undefined)[] | number[]
24+
>maybe ? ['foo', 'bar', undefined] : [1] : (string | undefined)[] | number[]
25+
>maybe : boolean
26+
>['foo', 'bar', undefined] : (string | undefined)[]
27+
>'foo' : "foo"
28+
>'bar' : "bar"
29+
>undefined : undefined
30+
>[1] : number[]
31+
>1 : 1
32+
>filter : { <S extends string | undefined>(predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; } | { <S_1 extends number>(predicate: (value: number, index: number, array: number[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; }
33+
>id() : (t: string | number | undefined) => boolean
34+
>id : <T>() => (t: T) => boolean
35+
36+
result1;
37+
>result1 : (string | number | undefined)[]
38+
39+
const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean
40+
>result2 : (string | undefined)[]
41+
>['foo', 'bar', undefined].filter(id()) : (string | undefined)[]
42+
>['foo', 'bar', undefined].filter : { <S extends string | undefined>(predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; }
43+
>['foo', 'bar', undefined] : (string | undefined)[]
44+
>'foo' : "foo"
45+
>'bar' : "bar"
46+
>undefined : undefined
47+
>filter : { <S extends string | undefined>(predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; }
48+
>id() : (t: string | undefined) => boolean
49+
>id : <T>() => (t: T) => boolean
50+
51+
result2;
52+
>result2 : (string | undefined)[]
53+
}
54+

0 commit comments

Comments
 (0)