Skip to content

Commit 17cbcca

Browse files
committed
Fix borked test, re-add type parameter copying
1 parent 0822f33 commit 17cbcca

6 files changed

+36
-17
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15632,6 +15632,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1563215632
);
1563315633
}
1563415634

15635+
function getImplementationSignature(signature: Signature) {
15636+
return signature.typeParameters ?
15637+
signature.implementationSignatureCache ||= createImplementationSignature(signature) :
15638+
signature;
15639+
}
15640+
15641+
function createImplementationSignature(signature: Signature) {
15642+
return signature.typeParameters ? instantiateSignature(signature, createTypeMapper([], [])) : signature;
15643+
}
15644+
1563515645
function getBaseSignature(signature: Signature) {
1563615646
const typeParameters = signature.typeParameters;
1563715647
if (typeParameters) {
@@ -26364,9 +26374,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2636426374
// inference error only occurs when there are *conflicting* candidates, i.e.
2636526375
// candidates with no common supertype.
2636626376
const defaultType = getDefaultFromTypeParameter(inference.typeParameter);
26367-
// Instantiate the default type. Any forward reference to a type
26368-
// parameter should be instantiated to the empty object type.
26369-
inferredType = instantiateType(defaultType, mergeTypeMappers(createBackreferenceMapper(context, index), context.nonFixingMapper));
26377+
if (defaultType) {
26378+
// Instantiate the default type. Any forward reference to a type
26379+
// parameter should be instantiated to the empty object type.
26380+
inferredType = instantiateType(defaultType, mergeTypeMappers(createBackreferenceMapper(context, index), context.nonFixingMapper));
26381+
}
2637026382
}
2637126383
}
2637226384
else {
@@ -34867,7 +34879,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3486734879
}
3486834880

3486934881
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
34870-
const candidate = candidates[candidateIndex];
34882+
let candidate = candidates[candidateIndex];
3487134883
if (!hasCorrectTypeArgumentArity(candidate, typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
3487234884
continue;
3487334885
}
@@ -34876,6 +34888,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3487634888
let inferenceContext: InferenceContext | undefined;
3487734889

3487834890
if (candidate.typeParameters) {
34891+
// If we are *inside the body of candidate*, we need to create a clone of `candidate` with differing type parameter identities,
34892+
// so our inference results for this call doesn't pollute expression types referencing the outer type parameter!
34893+
if (candidate.declaration && findAncestor(node, a => a === candidate.declaration)) {
34894+
candidate = getImplementationSignature(candidate);
34895+
}
3487934896
let typeArgumentTypes: readonly Type[] | undefined;
3488034897
if (some(typeArguments)) {
3488134898
typeArgumentTypes = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
@@ -34885,7 +34902,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3488534902
}
3488634903
}
3488734904
else {
34888-
inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
34905+
inferenceContext = createInferenceContext(candidate.typeParameters!, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
3488934906
// The resulting type arguments are instantiated with the inference context mapper, as the inferred types may still contain references to the inference context's
3489034907
// type variables via contextual projection. These are kept generic until all inferences are locked in, so the dependencies expressed can pass constraint checks.
3489134908
typeArgumentTypes = instantiateTypes(inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext), inferenceContext.nonFixingMapper);

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6819,6 +6819,8 @@ export interface Signature {
68196819
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
68206820
/** @internal */
68216821
instantiations?: Map<string, Signature>; // Generic signature instantiation cache
6822+
/** @internal */
6823+
implementationSignatureCache?: Signature; // Copy of the signature with fresh type parameters to use in checking the body of a potentially self-referential generic function (deferred)
68226824
}
68236825

68246826
export const enum IndexKind {

tests/baselines/reference/genericCallWithinOwnBodyCastTypeParameterIdentity.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenab
2222
const result = fn(input)
2323
return {
2424
then(onFulfilled) {
25-
return toThenable(onFulfilled)(result as Result)
25+
return toThenableInferred(onFulfilled)(result as Result)
2626
}
2727
};
2828
}
@@ -45,7 +45,7 @@ var toThenableInferred = function (fn) {
4545
var result = fn(input);
4646
return {
4747
then: function (onFulfilled) {
48-
return toThenable(onFulfilled)(result);
48+
return toThenableInferred(onFulfilled)(result);
4949
}
5050
};
5151
};

tests/baselines/reference/genericCallWithinOwnBodyCastTypeParameterIdentity.symbols

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenab
9393
>then : Symbol(then, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 19, 16))
9494
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 20, 17))
9595

96-
return toThenable(onFulfilled)(result as Result)
97-
>toThenable : Symbol(toThenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 5))
96+
return toThenableInferred(onFulfilled)(result as Result)
97+
>toThenableInferred : Symbol(toThenableInferred, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 5))
9898
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 20, 17))
9999
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 18, 13))
100100
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))

tests/baselines/reference/genericCallWithinOwnBodyCastTypeParameterIdentity.types

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Resul
4949

5050
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
5151
>toThenableInferred : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
52-
><Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input): Thenable<Result> => { const result = fn(input) return { then(onFulfilled) { return toThenable(onFulfilled)(result as Result) } }; } : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
52+
><Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input): Thenable<Result> => { const result = fn(input) return { then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } }; } : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
5353
>fn : (input: Input) => Result | Thenable<Result>
5454
>input : Input
5555

5656
(input: Input): Thenable<Result> => {
57-
>(input: Input): Thenable<Result> => { const result = fn(input) return { then(onFulfilled) { return toThenable(onFulfilled)(result as Result) } }; } : (input: Input) => Thenable<Result>
57+
>(input: Input): Thenable<Result> => { const result = fn(input) return { then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } }; } : (input: Input) => Thenable<Result>
5858
>input : Input
5959

6060
const result = fn(input)
@@ -64,16 +64,16 @@ const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenab
6464
>input : Input
6565

6666
return {
67-
>{ then(onFulfilled) { return toThenable(onFulfilled)(result as Result) } } : { then<V>(onFulfilled: (value: Result) => V | Thenable<V>): Thenable<V>; }
67+
>{ then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } } : { then<V>(onFulfilled: (value: Result) => V | Thenable<V>): Thenable<V>; }
6868

6969
then(onFulfilled) {
7070
>then : <V>(onFulfilled: (value: Result) => V | Thenable<V>) => Thenable<V>
7171
>onFulfilled : (value: Result) => V | Thenable<V>
7272

73-
return toThenable(onFulfilled)(result as Result)
74-
>toThenable(onFulfilled)(result as Result) : Thenable<V>
75-
>toThenable(onFulfilled) : (input: Result) => Thenable<V>
76-
>toThenable : <Result_1, Input_1>(fn: (input: Input_1) => Result_1 | Thenable<Result_1>) => (input: Input_1) => Thenable<Result_1>
73+
return toThenableInferred(onFulfilled)(result as Result)
74+
>toThenableInferred(onFulfilled)(result as Result) : Thenable<V>
75+
>toThenableInferred(onFulfilled) : (input: Result) => Thenable<V>
76+
>toThenableInferred : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
7777
>onFulfilled : (value: Result) => V | Thenable<V>
7878
>result as Result : Result
7979
>result : Result | Thenable<Result>

tests/cases/compiler/genericCallWithinOwnBodyCastTypeParameterIdentity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenab
2020
const result = fn(input)
2121
return {
2222
then(onFulfilled) {
23-
return toThenable(onFulfilled)(result as Result)
23+
return toThenableInferred(onFulfilled)(result as Result)
2424
}
2525
};
2626
}

0 commit comments

Comments
 (0)