Skip to content

Commit

Permalink
Still generate signatures in SkipContextSensitive mode just to match …
Browse files Browse the repository at this point in the history
…on return types (#25937)

* Still generate signatures in SkipContextSensitive mode just to match on return types

* Add cache for context-free type of a signature node

* Accept post-merge baseline
  • Loading branch information
weswigham authored Aug 9, 2018
1 parent 01f6093 commit f6af618
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10369,7 +10369,10 @@ namespace ts {
return true;
}
}
return hasContextSensitiveReturnExpression(node);
}

function hasContextSensitiveReturnExpression(node: FunctionLikeDeclaration) {
// TODO(anhans): A block should be context-sensitive if it has a context-sensitive return value.
const body = node.body!;
return body.kind === SyntaxKind.Block ? false : isContextSensitive(body);
Expand Down Expand Up @@ -20704,6 +20707,16 @@ namespace ts {

// The identityMapper object is used to indicate that function expressions are wildcards
if (checkMode === CheckMode.SkipContextSensitive && isContextSensitive(node)) {
// Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage
if (!getEffectiveReturnTypeNode(node) && hasContextSensitiveReturnExpression(node)) {
const links = getNodeLinks(node);
if (links.contextFreeType) {
return links.contextFreeType;
}
const returnType = getReturnTypeFromBody(node, checkMode);
const singleReturnSignature = createSignature(undefined, undefined, undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
return links.contextFreeType = createAnonymousType(node.symbol, emptySymbols, [singleReturnSignature], emptyArray, undefined, undefined);
}
return anyFunctionType;
}

Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3667,6 +3667,7 @@ namespace ts {
superCall?: SuperCall; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
switchTypes?: Type[]; // Cached array of switch case expression types
jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node
contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive
}

export const enum TypeFlags {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//// [badInferenceLowerPriorityThanGoodInference.ts]
interface Foo<A> {
a: A;
b: (x: A) => void;
}

declare function canYouInferThis<A>(fn: () => Foo<A>): A;

const result = canYouInferThis(() => ({
a: { BLAH: 33 },
b: x => { }
}))

result.BLAH;

//// [badInferenceLowerPriorityThanGoodInference.js]
var result = canYouInferThis(function () { return ({
a: { BLAH: 33 },
b: function (x) { }
}); });
result.BLAH;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
=== tests/cases/compiler/badInferenceLowerPriorityThanGoodInference.ts ===
interface Foo<A> {
>Foo : Symbol(Foo, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 0))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))

a: A;
>a : Symbol(Foo.a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 18))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))

b: (x: A) => void;
>b : Symbol(Foo.b, Decl(badInferenceLowerPriorityThanGoodInference.ts, 1, 9))
>x : Symbol(x, Decl(badInferenceLowerPriorityThanGoodInference.ts, 2, 8))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))
}

declare function canYouInferThis<A>(fn: () => Foo<A>): A;
>canYouInferThis : Symbol(canYouInferThis, Decl(badInferenceLowerPriorityThanGoodInference.ts, 3, 1))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))
>fn : Symbol(fn, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 36))
>Foo : Symbol(Foo, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 0))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))

const result = canYouInferThis(() => ({
>result : Symbol(result, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 5))
>canYouInferThis : Symbol(canYouInferThis, Decl(badInferenceLowerPriorityThanGoodInference.ts, 3, 1))

a: { BLAH: 33 },
>a : Symbol(a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 39))
>BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))

b: x => { }
>b : Symbol(b, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 20))
>x : Symbol(x, Decl(badInferenceLowerPriorityThanGoodInference.ts, 9, 6))

}))

result.BLAH;
>result.BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))
>result : Symbol(result, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 5))
>BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
=== tests/cases/compiler/badInferenceLowerPriorityThanGoodInference.ts ===
interface Foo<A> {
a: A;
>a : A

b: (x: A) => void;
>b : (x: A) => void
>x : A
}

declare function canYouInferThis<A>(fn: () => Foo<A>): A;
>canYouInferThis : <A>(fn: () => Foo<A>) => A
>fn : () => Foo<A>

const result = canYouInferThis(() => ({
>result : { BLAH: number; }
>canYouInferThis(() => ({ a: { BLAH: 33 }, b: x => { }})) : { BLAH: number; }
>canYouInferThis : <A>(fn: () => Foo<A>) => A
>() => ({ a: { BLAH: 33 }, b: x => { }}) : () => { a: { BLAH: number; }; b: (x: { BLAH: number; }) => void; }
>({ a: { BLAH: 33 }, b: x => { }}) : { a: { BLAH: number; }; b: (x: { BLAH: number; }) => void; }
>{ a: { BLAH: 33 }, b: x => { }} : { a: { BLAH: number; }; b: (x: { BLAH: number; }) => void; }

a: { BLAH: 33 },
>a : { BLAH: number; }
>{ BLAH: 33 } : { BLAH: number; }
>BLAH : number
>33 : 33

b: x => { }
>b : (x: { BLAH: number; }) => void
>x => { } : (x: { BLAH: number; }) => void
>x : { BLAH: number; }

}))

result.BLAH;
>result.BLAH : number
>result : { BLAH: number; }
>BLAH : number

13 changes: 13 additions & 0 deletions tests/cases/compiler/badInferenceLowerPriorityThanGoodInference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
interface Foo<A> {
a: A;
b: (x: A) => void;
}

declare function canYouInferThis<A>(fn: () => Foo<A>): A;

const result = canYouInferThis(() => ({
a: { BLAH: 33 },
b: x => { }
}))

result.BLAH;

0 comments on commit f6af618

Please sign in to comment.