Keep track of multiple current inference contexts (#51978)

* Keep track of multiple current inference contexts

* Add regression test
This commit is contained in:
Anders Hejlsberg
2022-12-22 07:52:49 -10:00
committed by GitHub
parent 54a554d8af
commit acf854b636
5 changed files with 220 additions and 9 deletions
+20 -9
View File
@@ -2069,8 +2069,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const contextualTypes: (Type | undefined)[] = [];
let contextualTypeCount = 0;
let currentInferenceNode: Node | undefined;
let currentInferenceContext: InferenceContext | undefined;
const inferenceContextNodes: Node[] = [];
const inferenceContexts: (InferenceContext | undefined)[] = [];
let inferenceContextCount = 0;
const emptyStringType = getStringLiteralType("");
const zeroType = getNumberLiteralType(0);
@@ -28764,8 +28765,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return -1;
}
function pushInferenceContext(node: Node, inferenceContext: InferenceContext | undefined) {
inferenceContextNodes[inferenceContextCount] = node;
inferenceContexts[inferenceContextCount] = inferenceContext;
inferenceContextCount++;
}
function popInferenceContext() {
inferenceContextCount--;
}
function getInferenceContext(node: Node) {
return isNodeDescendantOf(node, currentInferenceNode) ? currentInferenceContext : undefined;
for (let i = inferenceContextCount - 1; i >= 0; i--) {
if (isNodeDescendantOf(node, inferenceContextNodes[i])) {
return inferenceContexts[i];
}
}
}
function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags: ContextFlags | undefined) {
@@ -36022,10 +36037,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function checkExpressionWithContextualType(node: Expression, contextualType: Type, inferenceContext: InferenceContext | undefined, checkMode: CheckMode): Type {
const contextNode = getContextNode(node);
pushContextualType(contextNode, contextualType);
const saveInferenceNode = currentInferenceNode;
const saveInferenceContext = currentInferenceContext;
currentInferenceNode = contextNode;
currentInferenceContext = inferenceContext;
pushInferenceContext(contextNode, inferenceContext);
const type = checkExpression(node, checkMode | CheckMode.Contextual | (inferenceContext ? CheckMode.Inferential : 0));
// In CheckMode.Inferential we collect intra-expression inference sites to process before fixing any type
// parameters. This information is no longer needed after the call to checkExpression.
@@ -36037,8 +36049,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// here would be to not mark contextually typed literals as fresh in the first place.
const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node, /*contextFlags*/ undefined)) ?
getRegularTypeOfLiteralType(type) : type;
currentInferenceNode = saveInferenceNode;
currentInferenceContext = saveInferenceContext;
popInferenceContext();
popContextualType();
return result;
}
@@ -0,0 +1,36 @@
//// [multipleInferenceContexts.ts]
type ConstructorOptions<Data> =
& ComponentOptionsProperties<Data>
& ThisType<Instance<Data>>;
interface ComponentOptionsProperties<Data> {
data: Data;
render(): unknown;
}
interface Instance<Data> {
get<K extends keyof Data>(name: K): unknown;
}
declare var Moon: {
<Data>(options?: ConstructorOptions<Data>): Instance<Data>;
};
const r2 = Moon({
data: { msg: "" },
render() {
const h = (x: unknown) => x;
return h(this.get("msg"));
},
});
//// [multipleInferenceContexts.js]
"use strict";
var r2 = Moon({
data: { msg: "" },
render: function () {
var h = function (x) { return x; };
return h(this.get("msg"));
},
});
@@ -0,0 +1,76 @@
=== tests/cases/compiler/multipleInferenceContexts.ts ===
type ConstructorOptions<Data> =
>ConstructorOptions : Symbol(ConstructorOptions, Decl(multipleInferenceContexts.ts, 0, 0))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 0, 24))
& ComponentOptionsProperties<Data>
>ComponentOptionsProperties : Symbol(ComponentOptionsProperties, Decl(multipleInferenceContexts.ts, 2, 31))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 0, 24))
& ThisType<Instance<Data>>;
>ThisType : Symbol(ThisType, Decl(lib.es5.d.ts, --, --))
>Instance : Symbol(Instance, Decl(multipleInferenceContexts.ts, 7, 1))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 0, 24))
interface ComponentOptionsProperties<Data> {
>ComponentOptionsProperties : Symbol(ComponentOptionsProperties, Decl(multipleInferenceContexts.ts, 2, 31))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 4, 37))
data: Data;
>data : Symbol(ComponentOptionsProperties.data, Decl(multipleInferenceContexts.ts, 4, 44))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 4, 37))
render(): unknown;
>render : Symbol(ComponentOptionsProperties.render, Decl(multipleInferenceContexts.ts, 5, 15))
}
interface Instance<Data> {
>Instance : Symbol(Instance, Decl(multipleInferenceContexts.ts, 7, 1))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 9, 19))
get<K extends keyof Data>(name: K): unknown;
>get : Symbol(Instance.get, Decl(multipleInferenceContexts.ts, 9, 26))
>K : Symbol(K, Decl(multipleInferenceContexts.ts, 10, 8))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 9, 19))
>name : Symbol(name, Decl(multipleInferenceContexts.ts, 10, 30))
>K : Symbol(K, Decl(multipleInferenceContexts.ts, 10, 8))
}
declare var Moon: {
>Moon : Symbol(Moon, Decl(multipleInferenceContexts.ts, 13, 11))
<Data>(options?: ConstructorOptions<Data>): Instance<Data>;
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 14, 5))
>options : Symbol(options, Decl(multipleInferenceContexts.ts, 14, 11))
>ConstructorOptions : Symbol(ConstructorOptions, Decl(multipleInferenceContexts.ts, 0, 0))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 14, 5))
>Instance : Symbol(Instance, Decl(multipleInferenceContexts.ts, 7, 1))
>Data : Symbol(Data, Decl(multipleInferenceContexts.ts, 14, 5))
};
const r2 = Moon({
>r2 : Symbol(r2, Decl(multipleInferenceContexts.ts, 17, 5))
>Moon : Symbol(Moon, Decl(multipleInferenceContexts.ts, 13, 11))
data: { msg: "" },
>data : Symbol(data, Decl(multipleInferenceContexts.ts, 17, 17))
>msg : Symbol(msg, Decl(multipleInferenceContexts.ts, 18, 11))
render() {
>render : Symbol(render, Decl(multipleInferenceContexts.ts, 18, 22))
const h = (x: unknown) => x;
>h : Symbol(h, Decl(multipleInferenceContexts.ts, 20, 13))
>x : Symbol(x, Decl(multipleInferenceContexts.ts, 20, 19))
>x : Symbol(x, Decl(multipleInferenceContexts.ts, 20, 19))
return h(this.get("msg"));
>h : Symbol(h, Decl(multipleInferenceContexts.ts, 20, 13))
>this.get : Symbol(Instance.get, Decl(multipleInferenceContexts.ts, 9, 26))
>this : Symbol(Instance, Decl(multipleInferenceContexts.ts, 7, 1))
>get : Symbol(Instance.get, Decl(multipleInferenceContexts.ts, 9, 26))
},
});
@@ -0,0 +1,62 @@
=== tests/cases/compiler/multipleInferenceContexts.ts ===
type ConstructorOptions<Data> =
>ConstructorOptions : ConstructorOptions<Data>
& ComponentOptionsProperties<Data>
& ThisType<Instance<Data>>;
interface ComponentOptionsProperties<Data> {
data: Data;
>data : Data
render(): unknown;
>render : () => unknown
}
interface Instance<Data> {
get<K extends keyof Data>(name: K): unknown;
>get : <K extends keyof Data>(name: K) => unknown
>name : K
}
declare var Moon: {
>Moon : <Data>(options?: ConstructorOptions<Data> | undefined) => Instance<Data>
<Data>(options?: ConstructorOptions<Data>): Instance<Data>;
>options : ConstructorOptions<Data> | undefined
};
const r2 = Moon({
>r2 : Instance<{ msg: string; }>
>Moon({ data: { msg: "" }, render() { const h = (x: unknown) => x; return h(this.get("msg")); },}) : Instance<{ msg: string; }>
>Moon : <Data>(options?: ConstructorOptions<Data> | undefined) => Instance<Data>
>{ data: { msg: "" }, render() { const h = (x: unknown) => x; return h(this.get("msg")); },} : { data: { msg: string; }; render(): unknown; }
data: { msg: "" },
>data : { msg: string; }
>{ msg: "" } : { msg: string; }
>msg : string
>"" : ""
render() {
>render : () => unknown
const h = (x: unknown) => x;
>h : (x: unknown) => unknown
>(x: unknown) => x : (x: unknown) => unknown
>x : unknown
>x : unknown
return h(this.get("msg"));
>h(this.get("msg")) : unknown
>h : (x: unknown) => unknown
>this.get("msg") : unknown
>this.get : <K extends "msg">(name: K) => unknown
>this : Instance<{ msg: string; }>
>get : <K extends "msg">(name: K) => unknown
>"msg" : "msg"
},
});
@@ -0,0 +1,26 @@
// @strict: true
type ConstructorOptions<Data> =
& ComponentOptionsProperties<Data>
& ThisType<Instance<Data>>;
interface ComponentOptionsProperties<Data> {
data: Data;
render(): unknown;
}
interface Instance<Data> {
get<K extends keyof Data>(name: K): unknown;
}
declare var Moon: {
<Data>(options?: ConstructorOptions<Data>): Instance<Data>;
};
const r2 = Moon({
data: { msg: "" },
render() {
const h = (x: unknown) => x;
return h(this.get("msg"));
},
});