Skip to content

Commit

Permalink
Ignore self tail calls when collecting the return type of a function (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
RyanCavanaugh authored May 5, 2023
1 parent 6afe257 commit e9cbebb
Show file tree
Hide file tree
Showing 14 changed files with 377 additions and 46 deletions.
8 changes: 8 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35748,6 +35748,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
forEachReturnStatement(func.body as Block, returnStatement => {
const expr = returnStatement.expression;
if (expr) {
// Bare calls to this same function don't contribute to inference
if (expr.kind === SyntaxKind.CallExpression &&
(expr as CallExpression).expression.kind === SyntaxKind.Identifier &&
checkExpressionCached((expr as CallExpression).expression).symbol === func.symbol) {
hasReturnOfTypeNever = true;
return;
}

let type = checkExpressionCached(expr, checkMode && checkMode & ~CheckMode.SkipGenericFunctions);
if (functionFlags & FunctionFlags.Async) {
// From within an async function you can return either a non-promise value or a promise. Any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ var r2 = foo2(1);
>1 : 1

function foo3() {
>foo3 : () => any
>foo3 : () => never

return foo3();
>foo3() : any
>foo3 : () => any
>foo3() : never
>foo3 : () => never
}
var r3 = foo3();
>r3 : any
>foo3() : any
>foo3 : () => any
>r3 : never
>foo3() : never
>foo3 : () => never

function foo4<T>(x: T) {
>foo4 : <T>(x: T) => T
Expand Down
6 changes: 5 additions & 1 deletion tests/baselines/reference/functionImplementations.errors.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
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'.
tests/cases/conformance/functions/functionImplementations.ts(90,1): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.


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

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

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

Expand Down
18 changes: 9 additions & 9 deletions tests/baselines/reference/functionImplementations.types
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ var a: any = function f() {
};
var a: any = function f() {
>a : any
>function f() { return f();} : () => any
>f : () => any
>function f() { return f();} : () => never
>f : () => never

return f();
>f() : any
>f : () => any
>f() : never
>f : () => never

};

Expand Down Expand Up @@ -205,17 +205,17 @@ var b = function () {
// FunctionExpression with no return type annotation with multiple return statements with one a recursive call
var a = function f() {
>a : any
>function f() { return new Base(); return new Derived(); return f(); // ?} () : any
>function f() { return new Base(); return new Derived(); return f(); // ?} : () => any
>f : () => any
>function f() { return new Base(); return new Derived(); return f(); // ?} () : Base
>function f() { return new Base(); return new Derived(); return f(); // ?} : () => Base
>f : () => Base

return new Base(); return new Derived(); return f(); // ?
>new Base() : Base
>Base : typeof Base
>new Derived() : Derived
>Derived : typeof Derived
>f() : any
>f : () => any
>f() : Base
>f : () => Base

} ();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(2,5): error TS2502: 'a'
tests/cases/compiler/implicitAnyFromCircularInference.ts(5,5): error TS2502: 'b' is referenced directly or indirectly in its own type annotation.
tests/cases/compiler/implicitAnyFromCircularInference.ts(6,5): error TS2502: 'c' is referenced directly or indirectly in its own type annotation.
tests/cases/compiler/implicitAnyFromCircularInference.ts(9,5): error TS2502: 'd' is referenced directly or indirectly in its own type annotation.
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.
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.
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.
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.
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.
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.


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

// Error expected
function g() { return g(); }
~
!!! 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.

// Error expected
var f1 = function () {
~~
!!! 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.
return f1();
};

Expand Down
14 changes: 7 additions & 7 deletions tests/baselines/reference/implicitAnyFromCircularInference.types
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ function f() { return f; }

// Error expected
function g() { return g(); }
>g : () => any
>g() : any
>g : () => any
>g : () => never
>g() : never
>g : () => never

// Error expected
var f1 = function () {
>f1 : () => any
>function () { return f1();} : () => any
>f1 : () => never
>function () { return f1();} : () => never

return f1();
>f1() : any
>f1 : () => any
>f1() : never
>f1 : () => never

};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
=== tests/cases/compiler/recursiveGenericSignatureInstantiation.ts ===
function f6<T>(x: T) {
>f6 : <T>(x: T) => any
>f6 : <T>(x: T) => never
>x : T

return f6(x);
>f6(x) : any
>f6 : <T>(x: T) => any
>f6(x) : never
>f6 : <T>(x: T) => never
>x : T
}

54 changes: 54 additions & 0 deletions tests/baselines/reference/simpleRecursionWithBaseCase.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
tests/cases/compiler/simpleRecursionWithBaseCase.ts(8,21): error TS2554: Expected 1 arguments, but got 0.
tests/cases/compiler/simpleRecursionWithBaseCase.ts(13,20): error TS2554: Expected 1 arguments, but got 0.
tests/cases/compiler/simpleRecursionWithBaseCase.ts(19,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
tests/cases/compiler/simpleRecursionWithBaseCase.ts(27,16): error TS2304: Cannot find name 'notfoundsymbol'.
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.


==== tests/cases/compiler/simpleRecursionWithBaseCase.ts (5 errors) ====
function fn1(n: number) {
if (n === 0) {
return 3;
} else {
return fn1(n - 1);
}
}
const num: number = fn1();
~~~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/compiler/simpleRecursionWithBaseCase.ts:1:14: An argument for 'n' was not provided.

function fn2(n: number) {
return fn2(n);
}
const nev: never = fn2();
~~~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/compiler/simpleRecursionWithBaseCase.ts:10:14: An argument for 'n' was not provided.

function fn3(n: number) {
if (n === 0) {
return 3;
} else {
return fn1("hello world");
~~~~~~~~~~~~~
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
}
}

function fn4(n: number) {
if (n === 0) {
return 3;
} else {
return notfoundsymbol("hello world");
~~~~~~~~~~~~~~
!!! error TS2304: Cannot find name 'notfoundsymbol'.
}
}

function fn5() {
~~~
!!! 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.
return [fn5][0]();
}

70 changes: 70 additions & 0 deletions tests/baselines/reference/simpleRecursionWithBaseCase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//// [simpleRecursionWithBaseCase.ts]
function fn1(n: number) {
if (n === 0) {
return 3;
} else {
return fn1(n - 1);
}
}
const num: number = fn1();

function fn2(n: number) {
return fn2(n);
}
const nev: never = fn2();

function fn3(n: number) {
if (n === 0) {
return 3;
} else {
return fn1("hello world");
}
}

function fn4(n: number) {
if (n === 0) {
return 3;
} else {
return notfoundsymbol("hello world");
}
}

function fn5() {
return [fn5][0]();
}


//// [simpleRecursionWithBaseCase.js]
"use strict";
function fn1(n) {
if (n === 0) {
return 3;
}
else {
return fn1(n - 1);
}
}
var num = fn1();
function fn2(n) {
return fn2(n);
}
var nev = fn2();
function fn3(n) {
if (n === 0) {
return 3;
}
else {
return fn1("hello world");
}
}
function fn4(n) {
if (n === 0) {
return 3;
}
else {
return notfoundsymbol("hello world");
}
}
function fn5() {
return [fn5][0]();
}
65 changes: 65 additions & 0 deletions tests/baselines/reference/simpleRecursionWithBaseCase.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
=== tests/cases/compiler/simpleRecursionWithBaseCase.ts ===
function fn1(n: number) {
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0))
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13))

if (n === 0) {
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13))

return 3;
} else {
return fn1(n - 1);
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0))
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13))
}
}
const num: number = fn1();
>num : Symbol(num, Decl(simpleRecursionWithBaseCase.ts, 7, 5))
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0))

function fn2(n: number) {
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26))
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 9, 13))

return fn2(n);
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26))
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 9, 13))
}
const nev: never = fn2();
>nev : Symbol(nev, Decl(simpleRecursionWithBaseCase.ts, 12, 5))
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26))

function fn3(n: number) {
>fn3 : Symbol(fn3, Decl(simpleRecursionWithBaseCase.ts, 12, 25))
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 14, 13))

if (n === 0) {
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 14, 13))

return 3;
} else {
return fn1("hello world");
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0))
}
}

function fn4(n: number) {
>fn4 : Symbol(fn4, Decl(simpleRecursionWithBaseCase.ts, 20, 1))
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 22, 13))

if (n === 0) {
>n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 22, 13))

return 3;
} else {
return notfoundsymbol("hello world");
}
}

function fn5() {
>fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase.ts, 28, 1))

return [fn5][0]();
>fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase.ts, 28, 1))
}

Loading

0 comments on commit e9cbebb

Please sign in to comment.