Skip to content
Open
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
52 changes: 32 additions & 20 deletions tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3177,6 +3177,36 @@ namespace mlirgen

auto toBoolean = isa<mlir_ts::BooleanType>(type) || isa<mlir_ts::TypePredicateType>(type);

// The tag says "class"; `instanceof` tells the classes apart. A @cpp class's objects
// are C++ objects with no type information, so `instanceof` refuses them: when the
// class need not be told - the only class of the union, or tested for truth, as
// `h && n == 3` with h a `Handle | null` is - `typeof` has already narrowed t enough.
// Otherwise only the other classes are tested.
auto writeClassTests = [&]() {
auto hasCppClass = llvm::any_of(classInstances, [&](mlir::Type classType) { return isCppVTableClass(classType); });
if (hasCppClass && (toBoolean || classInstances.size() == 1))
{
ss << S("return t;\n");
return;
}

ss << S("{ \n");

for (auto [index, classType] : enumerate(classInstances))
{
if (isCppVTableClass(classType))
{
continue;
}

ss << S("if (t instanceof TYPE_INST_ALIAS");
ss << index;
ss << S(") return t;\n");
}

ss << S(" }\n");
};

// A union whose actual type has no null member but whose members all hold null in
// their own pointer (`A | B | null`) is null when that pointer is null, whatever its
// tag: asked first, so that no member's test - `instanceof` reads the vtable - meets a
Expand Down Expand Up @@ -3216,16 +3246,7 @@ namespace mlirgen
ss << S("') ");
if (pair.getKey() == "class")
{
ss << S("{ \n");

for (auto [index, _] : enumerate(classInstances))
{
ss << S("if (t instanceof TYPE_INST_ALIAS");
ss << index;
ss << S(") return t;\n");
}

ss << S(" }\n");
writeClassTests();
}
else
{
Expand Down Expand Up @@ -3261,16 +3282,7 @@ namespace mlirgen
}
else if (pair.getKey() == "class")
{
ss << S("{ \n");

for (auto [index, _] : enumerate(classInstances))
{
ss << S("if (t instanceof TYPE_INST_ALIAS");
ss << index;
ss << S(") return t;\n");
}

ss << S(" }\n");
writeClassTests();
}
else
{
Expand Down
5 changes: 5 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,8 @@ tslang_add_test(NAME test-compile-own-00-cpp-slot COMMAND test-runner -mm=own "$
tslang_add_test(NAME test-compile-own-00-cpp-overloads COMMAND test-runner -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_overloads.ts")
tslang_add_test(NAME test-compile-00-cpp-slot COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_slot.ts")
tslang_add_test(NAME test-compile-00-cpp-overloads COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_overloads.ts")
tslang_add_test(NAME test-compile-00-cpp-union-truth COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_union_truth.ts")
tslang_add_test(NAME test-compile-own-00-cpp-union-truth COMMAND test-runner -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_union_truth.ts")
tslang_add_test(NAME test-compile-00-union-value-equality COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_value_equality.ts")
tslang_add_test(NAME test-compile-00-strict-equals-mismatched-types COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00strict_equals_mismatched_types.ts")
tslang_add_test(NAME test-compile-00-any-number-compare COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00any_number_compare.ts")
Expand Down Expand Up @@ -1139,6 +1141,8 @@ tslang_add_test(NAME test-jit-00-cpp-overloads COMMAND test-runner -jit "${PROJE
tslang_add_test(NAME test-jit-own-00-cppvtable-class COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00cppvtable_class.ts")
tslang_add_test(NAME test-jit-own-00-cpp-slot COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_slot.ts")
tslang_add_test(NAME test-jit-own-00-cpp-overloads COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_overloads.ts")
tslang_add_test(NAME test-jit-00-cpp-union-truth COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_union_truth.ts")
tslang_add_test(NAME test-jit-own-00-cpp-union-truth COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00cpp_union_truth.ts")
tslang_add_test(NAME test-jit-00-union-value-equality COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_value_equality.ts")
tslang_add_test(NAME test-jit-00-strict-equals-mismatched-types COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00strict_equals_mismatched_types.ts")
tslang_add_test(NAME test-jit-00-any-number-compare COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00any_number_compare.ts")
Expand Down Expand Up @@ -2747,6 +2751,7 @@ set(TSLANG_CORPUS
00cppvtable_class.ts
00cpp_slot.ts
00cpp_overloads.ts
00cpp_union_truth.ts
)

# the default model runs these ahead of time only, so the other models do too
Expand Down
59 changes: 59 additions & 0 deletions tslang/test/tester/tests/00cpp_union_truth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// A @cpp class that may be null in a union with a boolean (`h && n == 3`) is tested for truth by
// its pointer alone: its objects are C++ objects, and `instanceof` can't ask them what they are.
// The "C++ object" is made by hand, as in tests/00cppvtable_class.ts.

@cpp
class Handle {
value: s32;
@slot(0) first(): s32;
}

@cpp
class Other {
@slot(0) first(): s32;
}

type ObjectData = [vtable: Opaque, value: s32];

function slot0(self: Opaque): s32 {
return 10;
}

function both(h: Handle | null, n: number): boolean {
if (h && n == 3) {
return true;
}

return false;
}

function either(enabled: boolean, h: Handle | null): boolean {
return enabled && h ? true : false;
}

function main() {
let vtable: Opaque[] = [<Opaque>slot0];
let data: ObjectData = [<Opaque>Ref(vtable[0]), 32];
const object = <Opaque>Ref(data);

const handle = <Handle>object;
const none: Handle | null = null;

assert(both(handle, 3), "a handle and true");
assert(!both(handle, 4), "a handle and false");
assert(!both(none, 3), "null and true");

assert(either(true, handle), "true and a handle");
assert(!either(false, handle), "false and a handle");
assert(!either(true, none), "true and null");

// two @cpp classes and a boolean: neither class needs telling apart to be tested for truth
const other = <Other>object;
const flag = true;
let a: Handle | Other | boolean | null = flag && other;
assert(a ? true : false, "a @cpp object of either class");
a = null;
assert(!(a ? true : false), "null");

print("done.");
}
Loading