Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2272,6 +2272,13 @@ namespace mlirgen
}
else
{
// Tested for truth, a null or undefined member is false, as for an `any`
// (castFromAny). `return t` there cast null to boolean - "can't cast from 'null'
// to 'boolean'" - so a union of null and two or more other types, which is what
// `enabled && handle` makes of a boolean and a `Handle | null`, could not be
// tested in an `if` at all.
auto toBoolean = isa<mlir_ts::BooleanType>(type) || isa<mlir_ts::TypePredicateType>(type);

// default
auto next = false;
for (auto& pair : typeOfs)
Expand All @@ -2281,7 +2288,19 @@ namespace mlirgen
ss << S("if (typeof t == '");
ss << stows(pair.getKey().str());
ss << S("') ");
if (pair.getKey() == "class")
if (toBoolean && (pair.getKey() == "null" || pair.getKey() == "undefined"))
{
ss << S("return false;\n");
}
else if (toBoolean && pair.getKey() == "class" && classInstances.size() == 1)
{
// tested for truth, an instance is true and null is false - and a null
// instance does carry the class's tag, when a `Class | null` became this
// union (`enabled && handle` with handle null). `instanceof` would read
// its vtable through the null pointer.
ss << S("return t;\n");
}
else if (pair.getKey() == "class")
{
ss << S("{ \n");

Expand Down
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ tslang_add_test(NAME test-compile-00-union-bin-ops-2 COMMAND test-runner "${PROJ
tslang_add_test(NAME test-compile-00-union-to-any COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_to_any.ts")
tslang_add_test(NAME test-compile-00-union-errors COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_errors.ts")
tslang_add_test(NAME test-compile-00-union-null-to-string COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_null_to_string.ts")
tslang_add_test(NAME test-compile-00-union-null-truthiness COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_null_truthiness.ts")
tslang_add_test(NAME test-compile-00-union-null-undefined COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_null_undefined.ts")
tslang_add_test(NAME test-compile-00-union-null-undefined-nonstrict COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_null_undefined_nonstrict.ts")
tslang_add_test(NAME test-compile-00-optional-number-compare COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00optional_number_compare.ts")
Expand Down Expand Up @@ -926,6 +927,7 @@ tslang_add_test(NAME test-jit-00-union-bin-ops-2 COMMAND test-runner -jit "${PRO
tslang_add_test(NAME test-jit-00-union-to-any COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_to_any.ts")
tslang_add_test(NAME test-jit-00-union-errors COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_errors.ts")
tslang_add_test(NAME test-jit-00-union-null-to-string COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_null_to_string.ts")
tslang_add_test(NAME test-jit-00-union-null-truthiness COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_null_truthiness.ts")
tslang_add_test(NAME test-jit-00-union-null-undefined COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_null_undefined.ts")
tslang_add_test(NAME test-jit-00-union-null-undefined-nonstrict COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_null_undefined_nonstrict.ts")
tslang_add_test(NAME test-jit-00-optional-number-compare COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00optional_number_compare.ts")
Expand Down Expand Up @@ -2033,6 +2035,7 @@ set(TSLANG_CORPUS
00union_bin_ops2.ts
00union_errors.ts
00union_null_to_string.ts
00union_null_truthiness.ts
00union_ops.ts
00union_to_any.ts
00union_type.ts
Expand Down
69 changes: 69 additions & 0 deletions tslang/test/tester/tests/00union_null_truthiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Testing a union of null and two or more other types for truth failed to compile: "can't
// instantiate function. '___cast' not all generic types can be identified", after "can't cast
// from 'null' to 'boolean'" reported at an unrelated place. `boolean && (Handle | null)` is such
// a union, so `if (enabled && handle)` failed the same way.

class Handle {
value: number;
constructor() {
this.value = 7;
}
}

function use(enabled: boolean, handle: Handle | null): number {
if (enabled && handle) {
return handle.value;
}

return 0;
}

function useReversed(enabled: boolean, handle: Handle | null): number {
if (handle && enabled) {
return 1;
}

return 0;
}

function truthy(x: boolean | string | null): boolean {
if (x) {
return true;
}

return false;
}

function notTruthy(x: boolean | number | null): boolean {
return !x;
}

function andText(e: boolean, s: string | null): number {
return e && s ? 1 : 0;
}

function main() {
const h = new Handle();
assert(use(true, h) == 7, "use(true, h)");
assert(use(false, h) == 0, "use(false, h)");
assert(use(true, null) == 0, "use(true, null)");
assert(useReversed(true, h) == 1, "useReversed(true, h)");
assert(useReversed(true, null) == 0, "useReversed(true, null)");

assert(truthy("a"), "truthy(\"a\")");
assert(!truthy(""), "truthy(\"\")");
assert(truthy(true), "truthy(true)");
assert(!truthy(false), "truthy(false)");
assert(!truthy(null), "truthy(null)");

assert(notTruthy(null), "!null");
assert(notTruthy(0), "!0");
assert(!notTruthy(3), "!3");
assert(!notTruthy(true), "!true");

assert(andText(true, "x") == 1, "true && \"x\"");
assert(andText(true, null) == 0, "true && null");
assert(andText(false, "x") == 0, "false && \"x\"");

print("done.");
}
Loading