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
6 changes: 6 additions & 0 deletions tslang/include/TypeScript/Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@
// so would be given a frame's reference it has no owner for. A box's owner is the closure.
#define CAPTURE_BOX_ATTR_NAME "__capture_box"

// On a `ts.Try`: the statement has a catch clause. Its catches region may still hold nothing but
// the terminator - an untyped `catch { }` emits no ops - and without this marker it would be
// indistinguishable from the catch-less TryOps that only wrap a scope for its cleanup, so the
// lowering would make no landing pad and the exception would go uncaught.
#define HAS_CATCH_ATTR_NAME "__has_catch"

// -mm=own only, set by OwnershipInferencePass for the lowering. On a `ts.CreateBoundFunction`: the
// closure does not escape, so its box borrows what it captures - the box's routine frees the box
// and nothing it holds. On a `ts.ReleaseCell`: the cell borrows its value (a captured parameter's,
Expand Down
2 changes: 2 additions & 0 deletions tslang/lib/TypeScript/LowerToAffineLoops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1480,7 +1480,9 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>

auto cleanupHasOps =
llvm::any_of(tryOp.getCleanup(), [](auto &block) { return &block.front() != block.getTerminator(); });
// an empty `catch { }` still catches: HAS_CATCH_ATTR_NAME tells it from no catch clause
auto catchHasOps =
tryOp->hasAttr(HAS_CATCH_ATTR_NAME) ||
llvm::any_of(tryOp.getCatches(), [](auto &block) { return &block.front() != block.getTerminator(); });
auto finallyHasOps =
llvm::any_of(tryOp.getFinally(), [](auto &block) { return &block.front() != block.getTerminator(); });
Expand Down
2 changes: 2 additions & 0 deletions tslang/lib/TypeScript/MLIRGenStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1504,6 +1504,8 @@ namespace mlirgen
builder.setInsertionPointToStart(&tryOp.getCatches().front());
if (catchClause && catchClause->block)
{
tryOp->setAttr(HAS_CATCH_ATTR_NAME, builder.getUnitAttr());

auto location = loc(catchClause->block);
if (!varName.empty())
{
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 @@ -529,6 +529,7 @@ tslang_add_test(NAME test-compile-00-class-new COMMAND test-runner "${PROJECT_SO
tslang_add_test(NAME test-compile-00-array-growth COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_growth.ts")
tslang_add_test(NAME test-compile-00-global-from-global COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_from_global.ts")
tslang_add_test(NAME test-compile-00-try-cleanup-then-catch COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00try_cleanup_then_catch.ts")
tslang_add_test(NAME test-compile-00-try-empty-catch COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00try_empty_catch.ts")
tslang_add_test(NAME test-compile-00-global-union-from-call COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_union_from_call.ts")
tslang_add_test(NAME test-compile-00-capture-in-new-arguments COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00capture_in_new_arguments.ts")
tslang_add_test(NAME test-compile-01-class-new COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01class_new.ts")
Expand Down Expand Up @@ -1073,6 +1074,7 @@ tslang_add_test(NAME test-jit-00-class-new COMMAND test-runner -jit "${PROJECT_S
tslang_add_test(NAME test-jit-00-array-growth COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_growth.ts")
tslang_add_test(NAME test-jit-00-global-from-global COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_from_global.ts")
tslang_add_test(NAME test-jit-00-try-cleanup-then-catch COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00try_cleanup_then_catch.ts")
tslang_add_test(NAME test-jit-00-try-empty-catch COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00try_empty_catch.ts")
tslang_add_test(NAME test-jit-00-global-union-from-call COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_union_from_call.ts")
tslang_add_test(NAME test-jit-00-capture-in-new-arguments COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00capture_in_new_arguments.ts")
tslang_add_test(NAME test-jit-01-class-new COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01class_new.ts")
Expand Down Expand Up @@ -1949,6 +1951,7 @@ set(TSLANG_CORPUS
00in.ts
00global_from_global.ts
00try_cleanup_then_catch.ts
00try_empty_catch.ts
00global_object_literal.ts
00infer.ts
00const_object_literal_field.ts
Expand Down
118 changes: 118 additions & 0 deletions tslang/test/tester/tests/00try_empty_catch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// An empty catch clause catches. An untyped `catch { }` emits no ops into the try's catches region,
// and the lowering took a region holding only its terminator for "no catch clause" - the form of the
// try a scope's cleanup is wrapped in - so it made no landing pad and the exception went uncaught.
class Node {
v = 0;
}

function h() {
throw 1;
}

function emptyCatch() {
try {
h();
} catch {
}
}

function emptyCatchWithVariable() {
try {
h();
} catch (e) {
}
}

function emptyStatementInCatch() {
try {
h();
} catch {
;
}
}

function emptyCatchThenFinally() {
let finallyRan = false;
try {
h();
} catch {
} finally {
finallyRan = true;
}

return finallyRan;
}

function emptyCatchWithOwnedLocal() {
try {
let n = new Node();
n.v = 1;
h();
} catch {
}
}

function nestedEmptyCatches() {
let reached = false;
try {
try {
h();
} catch {
}

reached = true;
h();
} catch {
}

return reached;
}

function emptyCatchInLoop() {
let count = 0;
for (let i = 0; i < 5; i++) {
try {
h();
} catch {
}

count++;
}

return count;
}

function noThrowEmptyCatch() {
let value = 0;
try {
value = 7;
} catch {
}

return value;
}

class C {
run() {
try {
h();
} catch {
}

return true;
}
}

function main() {
emptyCatch();
emptyCatchWithVariable();
emptyStatementInCatch();
assert(emptyCatchThenFinally(), "the finally after an empty catch runs");
emptyCatchWithOwnedLocal();
assert(nestedEmptyCatches(), "an inner empty catch lets the outer try go on");
assert(emptyCatchInLoop() == 5, "an empty catch in a loop");
assert(noThrowEmptyCatch() == 7, "a try that does not throw");
assert(new C().run(), "an empty catch in a method");

print("done.");
}
Loading