Skip to content

Commit

Permalink
Revert collecting only outermost intra expression inference sites (#5…
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist authored Jun 12, 2023
1 parent 4c7dfb6 commit 9d17b34
Show file tree
Hide file tree
Showing 6 changed files with 1,295 additions and 27 deletions.
37 changes: 13 additions & 24 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1254,13 +1254,12 @@ export const enum CheckMode {
Inferential = 1 << 1, // Inferential typing
SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions
SkipGenericFunctions = 1 << 3, // Skip single signature generic functions
SkipAddingIntraExpressionSites = 1 << 4, // Skip adding intra expression sites in nested expressions since only the outermost one has to be added
IsForSignatureHelp = 1 << 5, // Call resolution for purposes of signature help
IsForStringLiteralArgumentCompletions = 1 << 6, // Do not infer from the argument currently being typed
RestBindingElement = 1 << 7, // Checking a type that is going to be used to determine the type of a rest binding element
IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
IsForStringLiteralArgumentCompletions = 1 << 5, // Do not infer from the argument currently being typed
RestBindingElement = 1 << 6, // Checking a type that is going to be used to determine the type of a rest binding element
// e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`,
// we need to preserve generic types instead of substituting them for constraints
TypeOnly = 1 << 8, // Called from getTypeOfExpression, diagnostics may be omitted
TypeOnly = 1 << 7, // Called from getTypeOfExpression, diagnostics may be omitted
}

/** @internal */
Expand Down Expand Up @@ -29990,13 +29989,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
elementFlags.push(ElementFlags.Optional);
}
else {
const shouldAddAsIntraExpressionInferenceSite = inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) && isContextSensitive(e);
const elementCheckMode = (checkMode || CheckMode.Normal) | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0);

const type = checkExpressionForMutableLocation(e, elementCheckMode, forceTuple);
const type = checkExpressionForMutableLocation(e, checkMode, forceTuple);
elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression));
elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required);
if (shouldAddAsIntraExpressionInferenceSite) {
if (inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) {
const inferenceContext = getInferenceContext(node);
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
addIntraExpressionInferenceSite(inferenceContext, e, type);
Expand Down Expand Up @@ -30161,17 +30157,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (memberDecl.kind === SyntaxKind.PropertyAssignment ||
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ||
isObjectLiteralMethod(memberDecl)) {

const shouldAddAsIntraExpressionInferenceSite = contextualType && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) &&
(memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl);
const propCheckMode = checkMode | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0);

let type = memberDecl.kind === SyntaxKind.PropertyAssignment ? checkPropertyAssignment(memberDecl, propCheckMode) :
let type = memberDecl.kind === SyntaxKind.PropertyAssignment ? checkPropertyAssignment(memberDecl, checkMode) :
// avoid resolving the left side of the ShorthandPropertyAssignment outside of the destructuring
// for error recovery purposes. For example, if a user wrote `{ a = 100 }` instead of `{ a: 100 }`.
// we don't want to say "could not find 'a'".
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(!inDestructuringPattern && memberDecl.objectAssignmentInitializer ? memberDecl.objectAssignmentInitializer : memberDecl.name, propCheckMode) :
checkObjectLiteralMethod(memberDecl, propCheckMode);
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(!inDestructuringPattern && memberDecl.objectAssignmentInitializer ? memberDecl.objectAssignmentInitializer : memberDecl.name, checkMode) :
checkObjectLiteralMethod(memberDecl, checkMode);
if (isInJavascript) {
const jsDocType = getTypeForDeclarationFromJSDocComment(memberDecl);
if (jsDocType) {
Expand Down Expand Up @@ -30226,7 +30217,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
member = prop;
allPropertiesTable?.set(prop.escapedName, prop);

if (shouldAddAsIntraExpressionInferenceSite) {
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) &&
(memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) {
const inferenceContext = getInferenceContext(node);
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
const inferenceNode = memberDecl.kind === SyntaxKind.PropertyAssignment ? memberDecl.initializer : memberDecl;
Expand Down Expand Up @@ -30460,10 +30452,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
for (const attributeDecl of attributes.properties) {
const member = attributeDecl.symbol;
if (isJsxAttribute(attributeDecl)) {
const shouldAddAsIntraExpressionInferenceSite = contextualType && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) && isContextSensitive(attributeDecl);
const attributeCheckMode = checkMode | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0);

const exprType = checkJsxAttribute(attributeDecl, attributeCheckMode);
const exprType = checkJsxAttribute(attributeDecl, checkMode);
objectFlags |= getObjectFlags(exprType) & ObjectFlags.PropagatingFlags;

const attributeSymbol = createSymbol(SymbolFlags.Property | member.flags, member.escapedName);
Expand All @@ -30485,7 +30474,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
addDeprecatedSuggestion(attributeDecl.name, prop.declarations, attributeDecl.name.escapedText as string);
}
}
if (shouldAddAsIntraExpressionInferenceSite) {
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(attributeDecl)) {
const inferenceContext = getInferenceContext(attributes);
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
const inferenceNode = (attributeDecl.initializer as JsxExpression).expression!;
Expand Down
119 changes: 118 additions & 1 deletion tests/baselines/reference/intraExpressionInferences.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,121 @@ intraExpressionInferences.ts(133,26): error TS2339: Property 'nonexistent' does
arg.toString();
},
},
});
});

declare function nested<T>(arg: {
prop: {
produce: (arg1: number) => T;
consume: (arg2: T) => void;
};
}): T;

const resNested = nested({
prop: {
produce: (a) => [a],
consume: (arg) => arg.join(","),
},
});

declare function twoConsumers<T>(arg: {
a: (arg: string) => T;
consume1: (arg1: T) => void;
consume2: (arg2: T) => void;
}): T;

const resTwoConsumers = twoConsumers({
a: (arg) => [arg],
consume1: (arg1) => {},
consume2: (arg2) => {},
});

declare function multipleProducersBeforeConsumers<T, T2>(arg: {
a: (arg: string) => T;
b: (arg: string) => T2;
consume1: (arg1: T) => void;
consume2: (arg2: T2) => void;
}): [T, T2];

const resMultipleProducersBeforeConsumers = multipleProducersBeforeConsumers({
a: (arg) => [arg],
b: (arg) => Number(arg),
consume1: (arg1) => {},
consume2: (arg2) => {},
});

declare function withConditionalExpression<T, T2, T3>(arg: {
a: (arg1: string) => T;
b: (arg2: T) => T2;
c: (arg2: T2) => T3;
}): [T, T2, T3];

const resWithConditionalExpression = withConditionalExpression({
a: (arg) => [arg],
b: Math.random() ? (arg) => "first" as const : (arg) => "two" as const,
c: (arg) => Boolean(arg),
});

declare function onion<T, T2, T3>(arg: {
a: (arg1: string) => T;
nested: {
b: (arg2: T) => T2;
nested2: {
c: (arg2: T2) => T3;
};
};
}): [T, T2, T3];

const resOnion = onion({
a: (arg) => [arg],
nested: {
b: (arg) => arg.join(","),
nested2: {
c: (arg) => Boolean(arg),
},
},
});

declare function onion2<T, T2, T3, T4>(arg: {
a: (arg1: string) => T;
nested: {
b: (arg2: T) => T2;
c: (arg3: T) => T3;
nested2: {
d: (arg4: T3) => T4;
};
};
}): [T, T2, T3, T4];

const resOnion2 = onion2({
a: (arg) => [arg],
nested: {
b: (arg) => arg.join(","),
c: (arg) => Number(arg),
nested2: {
d: (arg) => Boolean(arg),
},
},
});

declare function distant<T>(args: {
foo: {
bar: {
baz: {
producer: (arg: string) => T;
};
};
};
consumer: (val: T) => unknown;
}): T;

const distantRes = distant({
foo: {
bar: {
baz: {
producer: (arg) => 1,
},
},
},
consumer: (val) => {},
});

Loading

0 comments on commit 9d17b34

Please sign in to comment.