Skip to content

Commit 9d17b34

Browse files
authored
Revert collecting only outermost intra expression inference sites (#54613)
1 parent 4c7dfb6 commit 9d17b34

File tree

6 files changed

+1295
-27
lines changed

6 files changed

+1295
-27
lines changed

Diff for: src/compiler/checker.ts

+13-24
Original file line numberDiff line numberDiff line change
@@ -1254,13 +1254,12 @@ export const enum CheckMode {
12541254
Inferential = 1 << 1, // Inferential typing
12551255
SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions
12561256
SkipGenericFunctions = 1 << 3, // Skip single signature generic functions
1257-
SkipAddingIntraExpressionSites = 1 << 4, // Skip adding intra expression sites in nested expressions since only the outermost one has to be added
1258-
IsForSignatureHelp = 1 << 5, // Call resolution for purposes of signature help
1259-
IsForStringLiteralArgumentCompletions = 1 << 6, // Do not infer from the argument currently being typed
1260-
RestBindingElement = 1 << 7, // Checking a type that is going to be used to determine the type of a rest binding element
1257+
IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
1258+
IsForStringLiteralArgumentCompletions = 1 << 5, // Do not infer from the argument currently being typed
1259+
RestBindingElement = 1 << 6, // Checking a type that is going to be used to determine the type of a rest binding element
12611260
// e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`,
12621261
// we need to preserve generic types instead of substituting them for constraints
1263-
TypeOnly = 1 << 8, // Called from getTypeOfExpression, diagnostics may be omitted
1262+
TypeOnly = 1 << 7, // Called from getTypeOfExpression, diagnostics may be omitted
12641263
}
12651264

12661265
/** @internal */
@@ -29990,13 +29989,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2999029989
elementFlags.push(ElementFlags.Optional);
2999129990
}
2999229991
else {
29993-
const shouldAddAsIntraExpressionInferenceSite = inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) && isContextSensitive(e);
29994-
const elementCheckMode = (checkMode || CheckMode.Normal) | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0);
29995-
29996-
const type = checkExpressionForMutableLocation(e, elementCheckMode, forceTuple);
29992+
const type = checkExpressionForMutableLocation(e, checkMode, forceTuple);
2999729993
elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression));
2999829994
elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required);
29999-
if (shouldAddAsIntraExpressionInferenceSite) {
29995+
if (inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) {
3000029996
const inferenceContext = getInferenceContext(node);
3000129997
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
3000229998
addIntraExpressionInferenceSite(inferenceContext, e, type);
@@ -30161,17 +30157,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3016130157
if (memberDecl.kind === SyntaxKind.PropertyAssignment ||
3016230158
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ||
3016330159
isObjectLiteralMethod(memberDecl)) {
30164-
30165-
const shouldAddAsIntraExpressionInferenceSite = contextualType && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) &&
30166-
(memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl);
30167-
const propCheckMode = checkMode | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0);
30168-
30169-
let type = memberDecl.kind === SyntaxKind.PropertyAssignment ? checkPropertyAssignment(memberDecl, propCheckMode) :
30160+
let type = memberDecl.kind === SyntaxKind.PropertyAssignment ? checkPropertyAssignment(memberDecl, checkMode) :
3017030161
// avoid resolving the left side of the ShorthandPropertyAssignment outside of the destructuring
3017130162
// for error recovery purposes. For example, if a user wrote `{ a = 100 }` instead of `{ a: 100 }`.
3017230163
// we don't want to say "could not find 'a'".
30173-
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(!inDestructuringPattern && memberDecl.objectAssignmentInitializer ? memberDecl.objectAssignmentInitializer : memberDecl.name, propCheckMode) :
30174-
checkObjectLiteralMethod(memberDecl, propCheckMode);
30164+
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(!inDestructuringPattern && memberDecl.objectAssignmentInitializer ? memberDecl.objectAssignmentInitializer : memberDecl.name, checkMode) :
30165+
checkObjectLiteralMethod(memberDecl, checkMode);
3017530166
if (isInJavascript) {
3017630167
const jsDocType = getTypeForDeclarationFromJSDocComment(memberDecl);
3017730168
if (jsDocType) {
@@ -30226,7 +30217,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3022630217
member = prop;
3022730218
allPropertiesTable?.set(prop.escapedName, prop);
3022830219

30229-
if (shouldAddAsIntraExpressionInferenceSite) {
30220+
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) &&
30221+
(memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) {
3023030222
const inferenceContext = getInferenceContext(node);
3023130223
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
3023230224
const inferenceNode = memberDecl.kind === SyntaxKind.PropertyAssignment ? memberDecl.initializer : memberDecl;
@@ -30460,10 +30452,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3046030452
for (const attributeDecl of attributes.properties) {
3046130453
const member = attributeDecl.symbol;
3046230454
if (isJsxAttribute(attributeDecl)) {
30463-
const shouldAddAsIntraExpressionInferenceSite = contextualType && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) && isContextSensitive(attributeDecl);
30464-
const attributeCheckMode = checkMode | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0);
30465-
30466-
const exprType = checkJsxAttribute(attributeDecl, attributeCheckMode);
30455+
const exprType = checkJsxAttribute(attributeDecl, checkMode);
3046730456
objectFlags |= getObjectFlags(exprType) & ObjectFlags.PropagatingFlags;
3046830457

3046930458
const attributeSymbol = createSymbol(SymbolFlags.Property | member.flags, member.escapedName);
@@ -30485,7 +30474,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3048530474
addDeprecatedSuggestion(attributeDecl.name, prop.declarations, attributeDecl.name.escapedText as string);
3048630475
}
3048730476
}
30488-
if (shouldAddAsIntraExpressionInferenceSite) {
30477+
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(attributeDecl)) {
3048930478
const inferenceContext = getInferenceContext(attributes);
3049030479
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
3049130480
const inferenceNode = (attributeDecl.initializer as JsxExpression).expression!;

Diff for: tests/baselines/reference/intraExpressionInferences.errors.txt

+118-1
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,121 @@ intraExpressionInferences.ts(133,26): error TS2339: Property 'nonexistent' does
227227
arg.toString();
228228
},
229229
},
230-
});
230+
});
231+
232+
declare function nested<T>(arg: {
233+
prop: {
234+
produce: (arg1: number) => T;
235+
consume: (arg2: T) => void;
236+
};
237+
}): T;
238+
239+
const resNested = nested({
240+
prop: {
241+
produce: (a) => [a],
242+
consume: (arg) => arg.join(","),
243+
},
244+
});
245+
246+
declare function twoConsumers<T>(arg: {
247+
a: (arg: string) => T;
248+
consume1: (arg1: T) => void;
249+
consume2: (arg2: T) => void;
250+
}): T;
251+
252+
const resTwoConsumers = twoConsumers({
253+
a: (arg) => [arg],
254+
consume1: (arg1) => {},
255+
consume2: (arg2) => {},
256+
});
257+
258+
declare function multipleProducersBeforeConsumers<T, T2>(arg: {
259+
a: (arg: string) => T;
260+
b: (arg: string) => T2;
261+
consume1: (arg1: T) => void;
262+
consume2: (arg2: T2) => void;
263+
}): [T, T2];
264+
265+
const resMultipleProducersBeforeConsumers = multipleProducersBeforeConsumers({
266+
a: (arg) => [arg],
267+
b: (arg) => Number(arg),
268+
consume1: (arg1) => {},
269+
consume2: (arg2) => {},
270+
});
271+
272+
declare function withConditionalExpression<T, T2, T3>(arg: {
273+
a: (arg1: string) => T;
274+
b: (arg2: T) => T2;
275+
c: (arg2: T2) => T3;
276+
}): [T, T2, T3];
277+
278+
const resWithConditionalExpression = withConditionalExpression({
279+
a: (arg) => [arg],
280+
b: Math.random() ? (arg) => "first" as const : (arg) => "two" as const,
281+
c: (arg) => Boolean(arg),
282+
});
283+
284+
declare function onion<T, T2, T3>(arg: {
285+
a: (arg1: string) => T;
286+
nested: {
287+
b: (arg2: T) => T2;
288+
nested2: {
289+
c: (arg2: T2) => T3;
290+
};
291+
};
292+
}): [T, T2, T3];
293+
294+
const resOnion = onion({
295+
a: (arg) => [arg],
296+
nested: {
297+
b: (arg) => arg.join(","),
298+
nested2: {
299+
c: (arg) => Boolean(arg),
300+
},
301+
},
302+
});
303+
304+
declare function onion2<T, T2, T3, T4>(arg: {
305+
a: (arg1: string) => T;
306+
nested: {
307+
b: (arg2: T) => T2;
308+
c: (arg3: T) => T3;
309+
nested2: {
310+
d: (arg4: T3) => T4;
311+
};
312+
};
313+
}): [T, T2, T3, T4];
314+
315+
const resOnion2 = onion2({
316+
a: (arg) => [arg],
317+
nested: {
318+
b: (arg) => arg.join(","),
319+
c: (arg) => Number(arg),
320+
nested2: {
321+
d: (arg) => Boolean(arg),
322+
},
323+
},
324+
});
325+
326+
declare function distant<T>(args: {
327+
foo: {
328+
bar: {
329+
baz: {
330+
producer: (arg: string) => T;
331+
};
332+
};
333+
};
334+
consumer: (val: T) => unknown;
335+
}): T;
336+
337+
const distantRes = distant({
338+
foo: {
339+
bar: {
340+
baz: {
341+
producer: (arg) => 1,
342+
},
343+
},
344+
},
345+
consumer: (val) => {},
346+
});
347+

0 commit comments

Comments
 (0)