Skip to content

Commit 2b038ff

Browse files
authored
Fix self tail call return type inference in assigned anonymous functions (#58124)
1 parent 30095a2 commit 2b038ff

9 files changed

+214
-11
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28086,7 +28086,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2808628086
case SyntaxKind.Identifier:
2808728087
if (!isThisInTypeQuery(node)) {
2808828088
const symbol = getResolvedSymbol(node as Identifier);
28089-
return isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol);
28089+
return isConstantVariable(symbol)
28090+
|| isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol)
28091+
|| !!symbol.valueDeclaration && isFunctionExpression(symbol.valueDeclaration);
2809028092
}
2809128093
break;
2809228094
case SyntaxKind.PropertyAccessExpression:
@@ -37796,7 +37798,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3779637798
if (
3779737799
expr.kind === SyntaxKind.CallExpression &&
3779837800
(expr as CallExpression).expression.kind === SyntaxKind.Identifier &&
37799-
checkExpressionCached((expr as CallExpression).expression).symbol === func.symbol
37801+
checkExpressionCached((expr as CallExpression).expression).symbol === getMergedSymbol(func.symbol) &&
37802+
(!isFunctionExpressionOrArrowFunction(func.symbol.valueDeclaration!) || isConstantReference((expr as CallExpression).expression))
3780037803
) {
3780137804
hasReturnOfTypeNever = true;
3780237805
return;

tests/baselines/reference/implicitAnyFromCircularInference.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ implicitAnyFromCircularInference.ts(2,5): error TS2502: 'a' is referenced direct
22
implicitAnyFromCircularInference.ts(5,5): error TS2502: 'b' is referenced directly or indirectly in its own type annotation.
33
implicitAnyFromCircularInference.ts(6,5): error TS2502: 'c' is referenced directly or indirectly in its own type annotation.
44
implicitAnyFromCircularInference.ts(9,5): error TS2502: 'd' is referenced directly or indirectly in its own type annotation.
5+
implicitAnyFromCircularInference.ts(17,5): error TS7023: 'f1' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
56
implicitAnyFromCircularInference.ts(22,5): error TS7023: 'f2' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
67
implicitAnyFromCircularInference.ts(25,10): error TS7023: 'h' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
78
implicitAnyFromCircularInference.ts(27,14): error TS7023: 'foo' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
89
implicitAnyFromCircularInference.ts(44,9): error TS7023: 'x' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
910

1011

11-
==== implicitAnyFromCircularInference.ts (8 errors) ====
12+
==== implicitAnyFromCircularInference.ts (9 errors) ====
1213
// Error expected
1314
var a: typeof a;
1415
~
@@ -34,6 +35,8 @@ implicitAnyFromCircularInference.ts(44,9): error TS7023: 'x' implicitly has retu
3435

3536
// Error expected
3637
var f1 = function () {
38+
~~
39+
!!! error TS7023: 'f1' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
3740
return f1();
3841
};
3942

tests/baselines/reference/implicitAnyFromCircularInference.types

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,16 @@ function g() { return g(); }
4545

4646
// Error expected
4747
var f1 = function () {
48-
>f1 : () => never
49-
> : ^^^^^^^^^^^
50-
>function () { return f1();} : () => never
51-
> :
48+
>f1 : () => any
49+
> : ^^^^^^^^^
50+
>function () { return f1();} : () => any
51+
> :
5252

5353
return f1();
54-
>f1() : never
55-
> : ^^^^^
56-
>f1 : () => never
57-
> : ^^^^^^^^^^^
54+
>f1() : any
55+
> : ^^^
56+
>f1 : () => any
57+
> : ^^^^^^^^^
5858

5959
};
6060

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [tests/cases/compiler/simpleRecursionWithBaseCase3.ts] ////
2+
3+
=== simpleRecursionWithBaseCase3.ts ===
4+
const fn1 = () => {
5+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase3.ts, 0, 5))
6+
7+
if (Math.random() > 0.5) {
8+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
9+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
10+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
11+
12+
return fn1()
13+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase3.ts, 0, 5))
14+
}
15+
return 0
16+
}
17+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [tests/cases/compiler/simpleRecursionWithBaseCase3.ts] ////
2+
3+
=== simpleRecursionWithBaseCase3.ts ===
4+
const fn1 = () => {
5+
>fn1 : () => number
6+
> : ^^^^^^^^^^^^
7+
>() => { if (Math.random() > 0.5) { return fn1() } return 0} : () => number
8+
> :
9+
10+
if (Math.random() > 0.5) {
11+
>Math.random() > 0.5 : boolean
12+
> : ^^^^^^^
13+
>Math.random() : number
14+
> : ^^^^^^
15+
>Math.random : () => number
16+
> : ^^^^^^^^^^^^
17+
>Math : Math
18+
> : ^^^^
19+
>random : () => number
20+
> : ^^^^^^^^^^^^
21+
>0.5 : 0.5
22+
> : ^^^
23+
24+
return fn1()
25+
>fn1() : number
26+
> : ^^^^^^
27+
>fn1 : () => number
28+
> : ^^^^^^^^^^^^
29+
}
30+
return 0
31+
>0 : 0
32+
> : ^
33+
}
34+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [tests/cases/compiler/simpleRecursionWithBaseCase4.ts] ////
2+
3+
=== simpleRecursionWithBaseCase4.ts ===
4+
var fn2 = function(name) {
5+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase4.ts, 0, 3))
6+
>name : Symbol(name, Decl(simpleRecursionWithBaseCase4.ts, 0, 19))
7+
8+
fn2 = compose(this, 0, 1)
9+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase4.ts, 0, 3))
10+
>compose : Symbol(compose, Decl(simpleRecursionWithBaseCase4.ts, 2, 18))
11+
12+
return fn2(name)
13+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase4.ts, 0, 3))
14+
>name : Symbol(name, Decl(simpleRecursionWithBaseCase4.ts, 0, 19))
15+
16+
function compose(child, level, find) {
17+
>compose : Symbol(compose, Decl(simpleRecursionWithBaseCase4.ts, 2, 18))
18+
>child : Symbol(child, Decl(simpleRecursionWithBaseCase4.ts, 4, 19))
19+
>level : Symbol(level, Decl(simpleRecursionWithBaseCase4.ts, 4, 25))
20+
>find : Symbol(find, Decl(simpleRecursionWithBaseCase4.ts, 4, 32))
21+
22+
if (child === find) {
23+
>child : Symbol(child, Decl(simpleRecursionWithBaseCase4.ts, 4, 19))
24+
>find : Symbol(find, Decl(simpleRecursionWithBaseCase4.ts, 4, 32))
25+
26+
return level
27+
>level : Symbol(level, Decl(simpleRecursionWithBaseCase4.ts, 4, 25))
28+
}
29+
return compose(child, level + 1, find)
30+
>compose : Symbol(compose, Decl(simpleRecursionWithBaseCase4.ts, 2, 18))
31+
>child : Symbol(child, Decl(simpleRecursionWithBaseCase4.ts, 4, 19))
32+
>level : Symbol(level, Decl(simpleRecursionWithBaseCase4.ts, 4, 25))
33+
>find : Symbol(find, Decl(simpleRecursionWithBaseCase4.ts, 4, 32))
34+
}
35+
}
36+
37+
var d = fn2(1); // d: any
38+
>d : Symbol(d, Decl(simpleRecursionWithBaseCase4.ts, 12, 3))
39+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase4.ts, 0, 3))
40+
41+
d.redefined();
42+
>d : Symbol(d, Decl(simpleRecursionWithBaseCase4.ts, 12, 3))
43+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//// [tests/cases/compiler/simpleRecursionWithBaseCase4.ts] ////
2+
3+
=== simpleRecursionWithBaseCase4.ts ===
4+
var fn2 = function(name) {
5+
>fn2 : (name: any) => any
6+
> : ^ ^^^^^^^^^^^^^
7+
>function(name) { fn2 = compose(this, 0, 1) return fn2(name) function compose(child, level, find) { if (child === find) { return level } return compose(child, level + 1, find) }} : (name: any) => any
8+
> :
9+
>name : any
10+
11+
fn2 = compose(this, 0, 1)
12+
>fn2 = compose(this, 0, 1) : any
13+
>fn2 : (name: any) => any
14+
> : ^ ^^^^^^^^^^^^^
15+
>compose(this, 0, 1) : any
16+
>compose : (child: any, level: any, find: any) => any
17+
> : ^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^
18+
>this : any
19+
>0 : 0
20+
> : ^
21+
>1 : 1
22+
> : ^
23+
24+
return fn2(name)
25+
>fn2(name) : any
26+
>fn2 : (name: any) => any
27+
> : ^ ^^^^^^^^^^^^^
28+
>name : any
29+
30+
function compose(child, level, find) {
31+
>compose : (child: any, level: any, find: any) => any
32+
> : ^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^
33+
>child : any
34+
>level : any
35+
>find : any
36+
37+
if (child === find) {
38+
>child === find : boolean
39+
> : ^^^^^^^
40+
>child : any
41+
>find : any
42+
43+
return level
44+
>level : any
45+
}
46+
return compose(child, level + 1, find)
47+
>compose(child, level + 1, find) : any
48+
>compose : (child: any, level: any, find: any) => any
49+
> : ^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^
50+
>child : any
51+
>level + 1 : any
52+
>level : any
53+
>1 : 1
54+
> : ^
55+
>find : any
56+
}
57+
}
58+
59+
var d = fn2(1); // d: any
60+
>d : any
61+
>fn2(1) : any
62+
>fn2 : (name: any) => any
63+
> : ^ ^^^^^^^^^^^^^
64+
>1 : 1
65+
> : ^
66+
67+
d.redefined();
68+
>d.redefined() : any
69+
>d.redefined : any
70+
>d : any
71+
> : ^^^
72+
>redefined : any
73+
> : ^^^
74+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @strict: true
2+
// @noImplicitAny: true
3+
// @lib: esnext
4+
// @noEmit: true
5+
6+
const fn1 = () => {
7+
if (Math.random() > 0.5) {
8+
return fn1()
9+
}
10+
return 0
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @checkJs: true
2+
// @noEmit: true
3+
// @strict: false
4+
5+
var fn2 = function(name) {
6+
fn2 = compose(this, 0, 1)
7+
return fn2(name)
8+
9+
function compose(child, level, find) {
10+
if (child === find) {
11+
return level
12+
}
13+
return compose(child, level + 1, find)
14+
}
15+
}
16+
17+
var d = fn2(1); // d: any
18+
d.redefined();

0 commit comments

Comments
 (0)