diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index b0a120c70..610f523fe 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -3307,6 +3307,16 @@ class MLIRGenImpl else { if (inverse) return mlir::failure(); + + // `typeof x === "function"` (also "class", "interface", "object") names no type, only Opaque. + // The type x already has is more precise: casting would lose it - a function could no longer + // be called in the branch - and for a generic function nobody instantiated it references a + // function that is never emitted. + if (isa(safeType)) + { + return mlir::success(); + } + CAST_A(result, location, safeType, exprValue, genContext); castedValue = V(result); } diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 1f5ae95e3..e6ccc6218 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -171,6 +171,7 @@ add_test(NAME test-compile-00-funcs-nesting-generic COMMAND test-runner "${PROJE add_test(NAME test-compile-00-funcs-nesting-capture COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_nesting_capture.ts") add_test(NAME test-compile-00-funcs-hybrid-null-this COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_hybrid_null_this.ts") add_test(NAME test-compile-00-typeof-static-fold COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_static_fold.ts") +add_test(NAME test-compile-00-typeof-function-narrowing COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_function_narrowing.ts") add_test(NAME test-compile-00-typeof-static-fold-conditions COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_static_fold_conditions.ts") add_test(NAME test-compile-00-array-move-wide-elements COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_move_wide_elements.ts") add_test(NAME test-compile-00-funcs-expression-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_expression_generic.ts") @@ -589,6 +590,7 @@ add_test(NAME test-jit-00-funcs-nesting-generic COMMAND test-runner -jit "${PROJ add_test(NAME test-jit-00-funcs-nesting-capture COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_nesting_capture.ts") add_test(NAME test-jit-00-funcs-hybrid-null-this COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_hybrid_null_this.ts") add_test(NAME test-jit-00-typeof-static-fold COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_static_fold.ts") +add_test(NAME test-jit-00-typeof-function-narrowing COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_function_narrowing.ts") add_test(NAME test-jit-00-typeof-static-fold-conditions COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_static_fold_conditions.ts") add_test(NAME test-jit-00-array-move-wide-elements COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_move_wide_elements.ts") add_test(NAME test-jit-00-funcs-expression-generic COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_expression_generic.ts") @@ -1576,6 +1578,7 @@ set(TSLANG_CORPUS 00type_aliases_in_generics.ts 00type_guard_function.ts 00typed_array.ts + 00typeof_function_narrowing.ts 00typeof_static_fold.ts 00typeof_static_fold_conditions.ts 00types_indexedaccesstype.ts diff --git a/tslang/test/tester/tests/00typeof_function_narrowing.ts b/tslang/test/tester/tests/00typeof_function_narrowing.ts new file mode 100644 index 000000000..d322f0eee --- /dev/null +++ b/tslang/test/tester/tests/00typeof_function_narrowing.ts @@ -0,0 +1,41 @@ +// `typeof f === "function"` names no type of its own, only Opaque. A value whose type is known is +// not narrowed to it: that lost the function type, so the function could not be called in the +// branch, and for a generic function nobody instantiated it referenced a function never emitted. +function one() { + return 1; +} + +function id(x: T) { + return x; +} + +function main() { + let calls = 0; + + if (typeof one === "function") { + assert(one() == 1, "plain function is still callable"); + calls++; + } + + if (typeof id === "function") { + calls++; + } + + if (typeof id === "function") { + assert(id(2) == 2, "generic function is still callable"); + calls++; + } + + const f = () => 3; + assert(typeof f === "function" ? f() == 3 : false, "arrow function in ?:"); + + // `any` is still narrowed at run time + let a: any = one; + if (typeof a === "function") { + calls++; + } + + assert(calls == 4, "every branch ran"); + + print("done."); +}