Skip to content

Commit 78eaf74

Browse files
committed
Replace inner intra expression inference sites with outer ones when possible
1 parent 9d17b34 commit 78eaf74

File tree

1 file changed

+37
-5
lines changed

1 file changed

+37
-5
lines changed

src/compiler/checker.ts

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,6 +2140,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
21402140
var inferenceContexts: (InferenceContext | undefined)[] = [];
21412141
var inferenceContextCount = 0;
21422142

2143+
var intraExpressionInferenceSites: InferenceContext["intraExpressionInferenceSites"][] = [];
2144+
var intraExpressionInferenceSitesLengths: number[] = [];
2145+
var intraExpressionInferenceSitesCount = 0;
2146+
21432147
var emptyStringType = getStringLiteralType("");
21442148
var zeroType = getNumberLiteralType(0);
21452149
var zeroBigIntType = getBigIntLiteralType({ negative: false, base10Value: "0" });
@@ -23956,6 +23960,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2395623960
}
2395723961

2395823962
function addIntraExpressionInferenceSite(context: InferenceContext, node: Expression | MethodDeclaration, type: Type) {
23963+
const stored = intraExpressionInferenceSites[intraExpressionInferenceSitesCount];
23964+
if (stored && stored === context.intraExpressionInferenceSites) {
23965+
// if the `context.intraExpressionInferenceSites` are still the same as when we last stored into `intraExpressionInferenceSites`
23966+
// then we can just shrink the array and push to it since items that were pushed since then are guaranteed to be inside the node
23967+
// and we only need to keep the outermost site until it's pulled from and resetted by `inferFromIntraExpressionSites`
23968+
stored.length = intraExpressionInferenceSitesLengths[intraExpressionInferenceSitesCount];
23969+
stored.push({ node, type });
23970+
return;
23971+
}
2395923972
(context.intraExpressionInferenceSites ??= []).push({ node, type });
2396023973
}
2396123974

@@ -29611,6 +29624,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2961129624
}
2961229625
}
2961329626

29627+
function pushIntraExpressionInferenceSites(node: ObjectLiteralExpression | ArrayLiteralExpression | JsxAttributes, contextualType: Type | undefined, checkMode: CheckMode) {
29628+
const inIntraExpressionInferenceContext = !!(contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive));
29629+
const sites = inIntraExpressionInferenceContext ? getInferenceContext(node)!.intraExpressionInferenceSites : undefined;
29630+
intraExpressionInferenceSites[intraExpressionInferenceSitesCount] = sites;
29631+
intraExpressionInferenceSitesLengths[intraExpressionInferenceSitesCount] = sites?.length || 0;
29632+
intraExpressionInferenceSitesCount++;
29633+
return inIntraExpressionInferenceContext;
29634+
}
29635+
29636+
function popIntraExpressionInferenceSites() {
29637+
intraExpressionInferenceSitesCount--;
29638+
}
29639+
2961429640
function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags: ContextFlags | undefined) {
2961529641
if (isJsxOpeningElement(node) && contextFlags !== ContextFlags.Completions) {
2961629642
const index = findContextualNode(node.parent, /*includeCaches*/ !contextFlags);
@@ -29936,7 +29962,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2993629962
(node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken);
2993729963
}
2993829964

29939-
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined, forceTuple: boolean | undefined): Type {
29965+
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode = CheckMode.Normal, forceTuple: boolean | undefined): Type {
2994029966
const elements = node.elements;
2994129967
const elementCount = elements.length;
2994229968
const elementTypes: Type[] = [];
@@ -29946,6 +29972,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2994629972
const isSpreadIntoCallOrNew = isSpreadElement(node.parent) && isCallOrNewExpression(node.parent.parent);
2994729973
const inConstContext = isSpreadIntoCallOrNew || isConstContext(node);
2994829974
const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined);
29975+
const inIntraExpressionInferenceContext = pushIntraExpressionInferenceSites(node, contextualType, checkMode);
2994929976
const inTupleContext = isSpreadIntoCallOrNew || !!contextualType && someType(contextualType, isTupleLikeType);
2995029977
let hasOmittedExpression = false;
2995129978
for (let i = 0; i < elementCount; i++) {
@@ -29992,14 +30019,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2999230019
const type = checkExpressionForMutableLocation(e, checkMode, forceTuple);
2999330020
elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression));
2999430021
elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required);
29995-
if (inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) {
30022+
if (inTupleContext && inIntraExpressionInferenceContext && isContextSensitive(e)) {
2999630023
const inferenceContext = getInferenceContext(node);
2999730024
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
2999830025
addIntraExpressionInferenceSite(inferenceContext, e, type);
2999930026
}
3000030027
}
3000130028
}
3000230029
popContextualType();
30030+
popIntraExpressionInferenceSites();
3000330031
if (inDestructuringPattern) {
3000430032
return createTupleType(elementTypes, elementFlags);
3000530033
}
@@ -30129,6 +30157,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3012930157
const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined);
3013030158
const contextualTypeHasPattern = contextualType && contextualType.pattern &&
3013130159
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
30160+
const inIntraExpressionInferenceContext = pushIntraExpressionInferenceSites(node, contextualType, checkMode);
3013230161
const inConstContext = isConstContext(node);
3013330162
const checkFlags = inConstContext ? CheckFlags.Readonly : 0;
3013430163
const isInJavascript = isInJSFile(node) && !isInJsonFile(node);
@@ -30217,8 +30246,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3021730246
member = prop;
3021830247
allPropertiesTable?.set(prop.escapedName, prop);
3021930248

30220-
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) &&
30221-
(memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) {
30249+
if (inIntraExpressionInferenceContext && (memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) {
3022230250
const inferenceContext = getInferenceContext(node);
3022330251
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
3022430252
const inferenceNode = memberDecl.kind === SyntaxKind.PropertyAssignment ? memberDecl.initializer : memberDecl;
@@ -30287,6 +30315,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3028730315
propertiesArray.push(member);
3028830316
}
3028930317
popContextualType();
30318+
popIntraExpressionInferenceSites();
3029030319

3029130320
// If object literal is contextually typed by the implied type of a binding pattern, augment the result
3029230321
// type with those properties for which the binding pattern specifies a default value.
@@ -30440,6 +30469,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3044030469
function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, checkMode: CheckMode = CheckMode.Normal) {
3044130470
const attributes = openingLikeElement.attributes;
3044230471
const contextualType = getContextualType(attributes, ContextFlags.None);
30472+
const inIntraExpressionInferenceContext = pushIntraExpressionInferenceSites(attributes, contextualType, checkMode);
3044330473
const allAttributesTable = strictNullChecks ? createSymbolTable() : undefined;
3044430474
let attributesTable = createSymbolTable();
3044530475
let spread: Type = emptyJsxObjectType;
@@ -30474,7 +30504,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3047430504
addDeprecatedSuggestion(attributeDecl.name, prop.declarations, attributeDecl.name.escapedText as string);
3047530505
}
3047630506
}
30477-
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(attributeDecl)) {
30507+
if (inIntraExpressionInferenceContext && isContextSensitive(attributeDecl)) {
3047830508
const inferenceContext = getInferenceContext(attributes);
3047930509
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
3048030510
const inferenceNode = (attributeDecl.initializer as JsxExpression).expression!;
@@ -30543,6 +30573,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3054330573
}
3054430574
}
3054530575

30576+
popIntraExpressionInferenceSites();
30577+
3054630578
if (hasSpreadAnyType) {
3054730579
return anyType;
3054830580
}

0 commit comments

Comments
 (0)