Skip to content

Commit e9cbebb

Browse files
Ignore self tail calls when collecting the return type of a function (microsoft#53995)
1 parent 6afe257 commit e9cbebb

14 files changed

+377
-46
lines changed

src/compiler/checker.ts

+8
Original file line numberDiff line numberDiff line change
@@ -35748,6 +35748,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3574835748
forEachReturnStatement(func.body as Block, returnStatement => {
3574935749
const expr = returnStatement.expression;
3575035750
if (expr) {
35751+
// Bare calls to this same function don't contribute to inference
35752+
if (expr.kind === SyntaxKind.CallExpression &&
35753+
(expr as CallExpression).expression.kind === SyntaxKind.Identifier &&
35754+
checkExpressionCached((expr as CallExpression).expression).symbol === func.symbol) {
35755+
hasReturnOfTypeNever = true;
35756+
return;
35757+
}
35758+
3575135759
let type = checkExpressionCached(expr, checkMode && checkMode & ~CheckMode.SkipGenericFunctions);
3575235760
if (functionFlags & FunctionFlags.Async) {
3575335761
// From within an async function you can return either a non-promise value or a promise. Any

tests/baselines/reference/callSignatureWithoutReturnTypeAnnotationInference.types

+6-6
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ var r2 = foo2(1);
3131
>1 : 1
3232

3333
function foo3() {
34-
>foo3 : () => any
34+
>foo3 : () => never
3535

3636
return foo3();
37-
>foo3() : any
38-
>foo3 : () => any
37+
>foo3() : never
38+
>foo3 : () => never
3939
}
4040
var r3 = foo3();
41-
>r3 : any
42-
>foo3() : any
43-
>foo3 : () => any
41+
>r3 : never
42+
>foo3() : never
43+
>foo3 : () => never
4444

4545
function foo4<T>(x: T) {
4646
>foo4 : <T>(x: T) => T

tests/baselines/reference/functionImplementations.errors.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
tests/cases/conformance/functions/functionImplementations.ts(85,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'any', but here has type 'Base'.
12
tests/cases/conformance/functions/functionImplementations.ts(90,1): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
23

34

4-
==== tests/cases/conformance/functions/functionImplementations.ts (1 errors) ====
5+
==== tests/cases/conformance/functions/functionImplementations.ts (2 errors) ====
56
// FunctionExpression with no return type annotation and no return statement returns void
67
var v: void = function () { } ();
78

@@ -87,6 +88,9 @@ tests/cases/conformance/functions/functionImplementations.ts(90,1): error TS2839
8788

8889
// FunctionExpression with no return type annotation with multiple return statements with one a recursive call
8990
var a = function f() {
91+
~
92+
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'any', but here has type 'Base'.
93+
!!! related TS6203 tests/cases/conformance/functions/functionImplementations.ts:5:5: 'a' was also declared here.
9094
return new Base(); return new Derived(); return f(); // ?
9195
} ();
9296

tests/baselines/reference/functionImplementations.types

+9-9
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ var a: any = function f() {
1717
};
1818
var a: any = function f() {
1919
>a : any
20-
>function f() { return f();} : () => any
21-
>f : () => any
20+
>function f() { return f();} : () => never
21+
>f : () => never
2222

2323
return f();
24-
>f() : any
25-
>f : () => any
24+
>f() : never
25+
>f : () => never
2626

2727
};
2828

@@ -205,17 +205,17 @@ var b = function () {
205205
// FunctionExpression with no return type annotation with multiple return statements with one a recursive call
206206
var a = function f() {
207207
>a : any
208-
>function f() { return new Base(); return new Derived(); return f(); // ?} () : any
209-
>function f() { return new Base(); return new Derived(); return f(); // ?} : () => any
210-
>f : () => any
208+
>function f() { return new Base(); return new Derived(); return f(); // ?} () : Base
209+
>function f() { return new Base(); return new Derived(); return f(); // ?} : () => Base
210+
>f : () => Base
211211

212212
return new Base(); return new Derived(); return f(); // ?
213213
>new Base() : Base
214214
>Base : typeof Base
215215
>new Derived() : Derived
216216
>Derived : typeof Derived
217-
>f() : any
218-
>f : () => any
217+
>f() : Base
218+
>f : () => Base
219219

220220
} ();
221221

tests/baselines/reference/implicitAnyFromCircularInference.errors.txt

+1-7
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(2,5): error TS2502: 'a'
22
tests/cases/compiler/implicitAnyFromCircularInference.ts(5,5): error TS2502: 'b' is referenced directly or indirectly in its own type annotation.
33
tests/cases/compiler/implicitAnyFromCircularInference.ts(6,5): error TS2502: 'c' is referenced directly or indirectly in its own type annotation.
44
tests/cases/compiler/implicitAnyFromCircularInference.ts(9,5): error TS2502: 'd' is referenced directly or indirectly in its own type annotation.
5-
tests/cases/compiler/implicitAnyFromCircularInference.ts(14,10): error TS7023: 'g' 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.
6-
tests/cases/compiler/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.
75
tests/cases/compiler/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.
86
tests/cases/compiler/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.
97
tests/cases/compiler/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.
108
tests/cases/compiler/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.
119

1210

13-
==== tests/cases/compiler/implicitAnyFromCircularInference.ts (10 errors) ====
11+
==== tests/cases/compiler/implicitAnyFromCircularInference.ts (8 errors) ====
1412
// Error expected
1513
var a: typeof a;
1614
~
@@ -33,13 +31,9 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(44,9): error TS7023: 'x
3331

3432
// Error expected
3533
function g() { return g(); }
36-
~
37-
!!! error TS7023: 'g' 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.
3834

3935
// Error expected
4036
var f1 = function () {
41-
~~
42-
!!! 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.
4337
return f1();
4438
};
4539

tests/baselines/reference/implicitAnyFromCircularInference.types

+7-7
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ function f() { return f; }
2424

2525
// Error expected
2626
function g() { return g(); }
27-
>g : () => any
28-
>g() : any
29-
>g : () => any
27+
>g : () => never
28+
>g() : never
29+
>g : () => never
3030

3131
// Error expected
3232
var f1 = function () {
33-
>f1 : () => any
34-
>function () { return f1();} : () => any
33+
>f1 : () => never
34+
>function () { return f1();} : () => never
3535

3636
return f1();
37-
>f1() : any
38-
>f1 : () => any
37+
>f1() : never
38+
>f1 : () => never
3939

4040
};
4141

Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
=== tests/cases/compiler/recursiveGenericSignatureInstantiation.ts ===
22
function f6<T>(x: T) {
3-
>f6 : <T>(x: T) => any
3+
>f6 : <T>(x: T) => never
44
>x : T
55

66
return f6(x);
7-
>f6(x) : any
8-
>f6 : <T>(x: T) => any
7+
>f6(x) : never
8+
>f6 : <T>(x: T) => never
99
>x : T
1010
}
1111

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
tests/cases/compiler/simpleRecursionWithBaseCase.ts(8,21): error TS2554: Expected 1 arguments, but got 0.
2+
tests/cases/compiler/simpleRecursionWithBaseCase.ts(13,20): error TS2554: Expected 1 arguments, but got 0.
3+
tests/cases/compiler/simpleRecursionWithBaseCase.ts(19,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
4+
tests/cases/compiler/simpleRecursionWithBaseCase.ts(27,16): error TS2304: Cannot find name 'notfoundsymbol'.
5+
tests/cases/compiler/simpleRecursionWithBaseCase.ts(31,10): error TS7023: 'fn5' 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.
6+
7+
8+
==== tests/cases/compiler/simpleRecursionWithBaseCase.ts (5 errors) ====
9+
function fn1(n: number) {
10+
if (n === 0) {
11+
return 3;
12+
} else {
13+
return fn1(n - 1);
14+
}
15+
}
16+
const num: number = fn1();
17+
~~~~~
18+
!!! error TS2554: Expected 1 arguments, but got 0.
19+
!!! related TS6210 tests/cases/compiler/simpleRecursionWithBaseCase.ts:1:14: An argument for 'n' was not provided.
20+
21+
function fn2(n: number) {
22+
return fn2(n);
23+
}
24+
const nev: never = fn2();
25+
~~~~~
26+
!!! error TS2554: Expected 1 arguments, but got 0.
27+
!!! related TS6210 tests/cases/compiler/simpleRecursionWithBaseCase.ts:10:14: An argument for 'n' was not provided.
28+
29+
function fn3(n: number) {
30+
if (n === 0) {
31+
return 3;
32+
} else {
33+
return fn1("hello world");
34+
~~~~~~~~~~~~~
35+
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
36+
}
37+
}
38+
39+
function fn4(n: number) {
40+
if (n === 0) {
41+
return 3;
42+
} else {
43+
return notfoundsymbol("hello world");
44+
~~~~~~~~~~~~~~
45+
!!! error TS2304: Cannot find name 'notfoundsymbol'.
46+
}
47+
}
48+
49+
function fn5() {
50+
~~~
51+
!!! error TS7023: 'fn5' 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.
52+
return [fn5][0]();
53+
}
54+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//// [simpleRecursionWithBaseCase.ts]
2+
function fn1(n: number) {
3+
if (n === 0) {
4+
return 3;
5+
} else {
6+
return fn1(n - 1);
7+
}
8+
}
9+
const num: number = fn1();
10+
11+
function fn2(n: number) {
12+
return fn2(n);
13+
}
14+
const nev: never = fn2();
15+
16+
function fn3(n: number) {
17+
if (n === 0) {
18+
return 3;
19+
} else {
20+
return fn1("hello world");
21+
}
22+
}
23+
24+
function fn4(n: number) {
25+
if (n === 0) {
26+
return 3;
27+
} else {
28+
return notfoundsymbol("hello world");
29+
}
30+
}
31+
32+
function fn5() {
33+
return [fn5][0]();
34+
}
35+
36+
37+
//// [simpleRecursionWithBaseCase.js]
38+
"use strict";
39+
function fn1(n) {
40+
if (n === 0) {
41+
return 3;
42+
}
43+
else {
44+
return fn1(n - 1);
45+
}
46+
}
47+
var num = fn1();
48+
function fn2(n) {
49+
return fn2(n);
50+
}
51+
var nev = fn2();
52+
function fn3(n) {
53+
if (n === 0) {
54+
return 3;
55+
}
56+
else {
57+
return fn1("hello world");
58+
}
59+
}
60+
function fn4(n) {
61+
if (n === 0) {
62+
return 3;
63+
}
64+
else {
65+
return notfoundsymbol("hello world");
66+
}
67+
}
68+
function fn5() {
69+
return [fn5][0]();
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
=== tests/cases/compiler/simpleRecursionWithBaseCase.ts ===
2+
function fn1(n: number) {
3+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0))
4+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13))
5+
6+
if (n === 0) {
7+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13))
8+
9+
return 3;
10+
} else {
11+
return fn1(n - 1);
12+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0))
13+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13))
14+
}
15+
}
16+
const num: number = fn1();
17+
>num : Symbol(num, Decl(simpleRecursionWithBaseCase.ts, 7, 5))
18+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0))
19+
20+
function fn2(n: number) {
21+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26))
22+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 9, 13))
23+
24+
return fn2(n);
25+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26))
26+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 9, 13))
27+
}
28+
const nev: never = fn2();
29+
>nev : Symbol(nev, Decl(simpleRecursionWithBaseCase.ts, 12, 5))
30+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26))
31+
32+
function fn3(n: number) {
33+
>fn3 : Symbol(fn3, Decl(simpleRecursionWithBaseCase.ts, 12, 25))
34+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 14, 13))
35+
36+
if (n === 0) {
37+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 14, 13))
38+
39+
return 3;
40+
} else {
41+
return fn1("hello world");
42+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0))
43+
}
44+
}
45+
46+
function fn4(n: number) {
47+
>fn4 : Symbol(fn4, Decl(simpleRecursionWithBaseCase.ts, 20, 1))
48+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 22, 13))
49+
50+
if (n === 0) {
51+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 22, 13))
52+
53+
return 3;
54+
} else {
55+
return notfoundsymbol("hello world");
56+
}
57+
}
58+
59+
function fn5() {
60+
>fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase.ts, 28, 1))
61+
62+
return [fn5][0]();
63+
>fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase.ts, 28, 1))
64+
}
65+

0 commit comments

Comments
 (0)