summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeejin Ahn <aheejin@gmail.com>2021-02-18 04:36:56 +0900
committerGitHub <noreply@github.com>2021-02-18 04:36:56 +0900
commit1254b564b2d36cbf96ef8b8fe0c17fa2fa668ae3 (patch)
tree3cac90d2bffe8eed22ee8a857c9b1123b1235566
parent1d3f578d5dc26fc8ea83ea851ac0f1100a3cbad6 (diff)
downloadbinaryen-1254b564b2d36cbf96ef8b8fe0c17fa2fa668ae3.tar.gz
binaryen-1254b564b2d36cbf96ef8b8fe0c17fa2fa668ae3.tar.bz2
binaryen-1254b564b2d36cbf96ef8b8fe0c17fa2fa668ae3.zip
[EH] Make rethrow's target a try label (#3568)
I was previously mistaken about `rethrow`'s argument rule and thought it only counted `catch`'s depth. But it turns out it follows the same rule `delegate`'s label: the immediate argument follows the same rule as when computing branch labels, but it only can target `try` labels (semantically it targets that `try`'s corresponding `catch`); otherwise it will be a validation failure. Unlike `delegate`, `rethrow`'s label denotes not where to rethrow, but which exception to rethrow. For example, ```wasm try $l0 catch ($l0) try $l1 catch ($l1) rethrow $l0 ;; rethrow the exception caught by 'catch ($l0)' end end ``` Refer to this comment for the more detailed informal semantics: https://github.com/WebAssembly/exception-handling/issues/146#issuecomment-777714491 --- This also reverts some of `delegateTarget` -> `exceptionTarget` changes done in #3562 in the validator. Label validation rules apply differently for `delegate` and `rethrow` for try-catch. For example, this is valid: ```wasm try $l0 try delegate $l0 catch ($l0) end ``` But this is NOT valid: ```wasm try $l0 catch ($l0) try delegate $l0 end ``` So `try`'s label should be used within try-catch range (not catch-end range) for `delegate`s. But for the `rethrow` the rule is different. For example, this is valid: ```wasm try $l0 catch ($l0) rethrow $l0 end ``` But this is NOT valid: ```wasm try $l0 rethrow $l0 catch ($l0) end ``` So the `try`'s label should be used within catch-end range instead.
-rw-r--r--src/binaryen-c.cpp13
-rw-r--r--src/binaryen-c.h12
-rw-r--r--src/ir/branch-utils.h6
-rw-r--r--src/js/binaryen.js-post.js15
-rw-r--r--src/passes/Print.cpp2
-rw-r--r--src/wasm-builder.h4
-rw-r--r--src/wasm-delegations-fields.h2
-rw-r--r--src/wasm-interpreter.h12
-rw-r--r--src/wasm.h2
-rw-r--r--src/wasm/wasm-binary.cpp23
-rw-r--r--src/wasm/wasm-s-parser.cpp29
-rw-r--r--src/wasm/wasm-stack.cpp2
-rw-r--r--src/wasm/wasm-validator.cpp36
-rw-r--r--test/binaryen.js/exception-handling.js10
-rw-r--r--test/binaryen.js/exception-handling.js.txt8
-rw-r--r--test/binaryen.js/expressions.js10
-rw-r--r--test/binaryen.js/expressions.js.txt2
-rw-r--r--test/exception-handling.wast119
-rw-r--r--test/exception-handling.wast.from-wast145
-rw-r--r--test/exception-handling.wast.fromBinary119
-rw-r--r--test/exception-handling.wast.fromBinary.noDebugInfo119
-rw-r--r--test/passes/code-pushing_all-features.txt4
-rw-r--r--test/passes/code-pushing_all-features.wast4
-rw-r--r--test/passes/dce_all-features.txt12
-rw-r--r--test/passes/dce_all-features.wast18
-rw-r--r--test/passes/dwarf_with_exceptions.bin.txt4
-rw-r--r--test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt20
-rw-r--r--test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast4
-rw-r--r--test/passes/rse_all-features.txt12
-rw-r--r--test/passes/rse_all-features.wast12
-rw-r--r--test/spec/exception-handling.wast64
31 files changed, 626 insertions, 218 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index f4d93afa1..26a9c5801 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -1248,8 +1248,9 @@ BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module,
}
BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module,
- BinaryenIndex depth) {
- return static_cast<Expression*>(Builder(*(Module*)module).makeRethrow(depth));
+ const char* target) {
+ return static_cast<Expression*>(
+ Builder(*(Module*)module).makeRethrow(target));
}
BinaryenExpressionRef BinaryenI31New(BinaryenModuleRef module,
@@ -2965,15 +2966,15 @@ BinaryenExpressionRef BinaryenThrowRemoveOperandAt(BinaryenExpressionRef expr,
return static_cast<Throw*>(expression)->operands.removeAt(index);
}
// Rethrow
-BinaryenIndex BinaryenRethrowGetDepth(BinaryenExpressionRef expr) {
+const char* BinaryenRethrowGetTarget(BinaryenExpressionRef expr) {
auto* expression = (Expression*)expr;
assert(expression->is<Rethrow>());
- return static_cast<Rethrow*>(expression)->depth;
+ return static_cast<Rethrow*>(expression)->target.c_str();
}
-void BinaryenRethrowSetDepth(BinaryenExpressionRef expr, BinaryenIndex depth) {
+void BinaryenRethrowSetTarget(BinaryenExpressionRef expr, const char* target) {
auto* expression = (Expression*)expr;
assert(expression->is<Rethrow>());
- static_cast<Rethrow*>(expression)->depth = depth;
+ static_cast<Rethrow*>(expression)->target = target;
}
// TupleMake
BinaryenIndex BinaryenTupleMakeGetNumOperands(BinaryenExpressionRef expr) {
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index 967bb547f..23bd5fdcc 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -818,7 +818,7 @@ BinaryenThrow(BinaryenModuleRef module,
BinaryenExpressionRef* operands,
BinaryenIndex numOperands);
BINARYEN_API BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module,
- BinaryenIndex depth);
+ const char* target);
BINARYEN_API BinaryenExpressionRef
BinaryenTupleMake(BinaryenModuleRef module,
BinaryenExpressionRef* operands,
@@ -1829,11 +1829,11 @@ BinaryenThrowRemoveOperandAt(BinaryenExpressionRef expr, BinaryenIndex index);
// Rethrow
-// Gets the depth of a `rethrow` expression.
-BINARYEN_API BinaryenIndex BinaryenRethrowGetDepth(BinaryenExpressionRef expr);
-// Sets the exception reference expression of a `rethrow` expression.
-BINARYEN_API void BinaryenRethrowSetDepth(BinaryenExpressionRef expr,
- BinaryenIndex depth);
+// Gets the target catch's corresponding try label of a `rethrow` expression.
+BINARYEN_API const char* BinaryenRethrowGetTarget(BinaryenExpressionRef expr);
+// Sets the target catch's corresponding try label of a `rethrow` expression.
+BINARYEN_API void BinaryenRethrowSetTarget(BinaryenExpressionRef expr,
+ const char* target);
// TupleMake
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h
index 826483b09..f6c55c563 100644
--- a/src/ir/branch-utils.h
+++ b/src/ir/branch-utils.h
@@ -83,7 +83,7 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) {
} else if (auto* br = expr->dynCast<BrOn>()) {
func(name, br->getCastType());
} else {
- assert(expr->is<Try>()); // delegate
+ assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
}
});
}
@@ -135,14 +135,14 @@ inline bool replacePossibleTarget(Expression* branch, Name from, Name to) {
return worked;
}
-// Replace all delegate targets within the given AST.
+// Replace all delegate/rethrow targets within the given AST.
inline void replaceExceptionTargets(Expression* ast, Name from, Name to) {
struct Replacer
: public PostWalker<Replacer, UnifiedExpressionVisitor<Replacer>> {
Name from, to;
Replacer(Name from, Name to) : from(from), to(to) {}
void visitExpression(Expression* curr) {
- if (curr->is<Try>()) {
+ if (curr->is<Try>() || curr->is<Rethrow>()) {
operateOnScopeNameUses(curr, [&](Name& name) {
if (name == from) {
name = to;
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index 63be4d446..7c53298ad 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -2154,8 +2154,8 @@ function wrapModule(module, self = {}) {
self['throw'] = function(event_, operands) {
return preserveStack(() => Module['_BinaryenThrow'](module, strToStack(event_), i32sToStack(operands), operands.length));
};
- self['rethrow'] = function(depth) {
- return Module['_BinaryenRethrow'](module, depth);
+ self['rethrow'] = function(target) {
+ return Module['_BinaryenRethrow'](module, strToStack(target));
};
self['tuple'] = {
@@ -2916,7 +2916,7 @@ Module['getExpressionInfo'] = function(expr) {
return {
'id': id,
'type': type,
- 'depth': Module['_BinaryenRethrowGetDepth'](expr)
+ 'target': UTF8ToString(Module['_BinaryenRethrowGetTarget'](expr))
};
case Module['TupleMakeId']:
return {
@@ -4287,11 +4287,12 @@ Module['Throw'] = makeExpressionWrapper({
});
Module['Rethrow'] = makeExpressionWrapper({
- 'getDepth'(expr) {
- return Module['_BinaryenRethrowGetDepth'](expr);
+ 'getTarget'(expr) {
+ const target = Module['_BinaryenRethrowGetTarget'](expr);
+ return target ? UTF8ToString(target) : null;
},
- 'setDepth'(expr, depthExpr) {
- Module['_BinaryenRethrowSetDepth'](expr, depthExpr);
+ 'setTarget'(expr, target) {
+ preserveStack(() => { Module['_BinaryenRethrowSetTarget'](expr, strToStack(target)) });
}
});
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index cc4318ba9..b067e43d5 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1778,7 +1778,7 @@ struct PrintExpressionContents
}
void visitRethrow(Rethrow* curr) {
printMedium(o, "rethrow ");
- o << curr->depth;
+ printName(curr->target, o);
}
void visitNop(Nop* curr) { printMinor(o, "nop"); }
void visitUnreachable(Unreachable* curr) { printMinor(o, "unreachable"); }
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index fb67597b9..2277fed93 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -708,9 +708,9 @@ public:
ret->finalize();
return ret;
}
- Rethrow* makeRethrow(Index depth) {
+ Rethrow* makeRethrow(Name target) {
auto* ret = wasm.allocator.alloc<Rethrow>();
- ret->depth = depth;
+ ret->target = target;
ret->finalize();
return ret;
}
diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h
index 9677aa4de..63ff235d7 100644
--- a/src/wasm-delegations-fields.h
+++ b/src/wasm-delegations-fields.h
@@ -532,7 +532,7 @@ switch (DELEGATE_ID) {
}
case Expression::Id::RethrowId: {
DELEGATE_START(Rethrow);
- DELEGATE_FIELD_INT(Rethrow, depth);
+ DELEGATE_FIELD_SCOPE_NAME_USE(Rethrow, target);
DELEGATE_END(Rethrow);
break;
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 05135bfb3..f880e5c5a 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -2402,7 +2402,8 @@ private:
: public ExpressionRunner<RuntimeExpressionRunner> {
ModuleInstanceBase& instance;
FunctionScope& scope;
- SmallVector<WasmException, 4> exceptionStack;
+ // Stack of <caught exception, caught catch's try label>
+ SmallVector<std::pair<WasmException, Name>, 4> exceptionStack;
public:
RuntimeExpressionRunner(ModuleInstanceBase& instance,
@@ -3048,7 +3049,7 @@ private:
auto processCatchBody = [&](Expression* catchBody) {
// Push the current exception onto the exceptionStack in case
// 'rethrow's use it
- exceptionStack.push_back(e);
+ exceptionStack.push_back(std::make_pair(e, curr->name));
// We need to pop exceptionStack in either case: when the catch body
// exits normally or when a new exception is thrown
Flow ret;
@@ -3076,8 +3077,11 @@ private:
}
}
Flow visitRethrow(Rethrow* curr) {
- assert(exceptionStack.size() > curr->depth);
- throwException(exceptionStack[exceptionStack.size() - 1 - curr->depth]);
+ for (int i = exceptionStack.size() - 1; i >= 0; i--) {
+ if (exceptionStack[i].second == curr->target) {
+ throwException(exceptionStack[i].first);
+ }
+ }
WASM_UNREACHABLE("rethrow");
}
Flow visitPop(Pop* curr) {
diff --git a/src/wasm.h b/src/wasm.h
index c2df1616e..199257925 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1335,7 +1335,7 @@ class Rethrow : public SpecificExpression<Expression::RethrowId> {
public:
Rethrow(MixedArena& allocator) {}
- Index depth;
+ Name target;
void finalize();
};
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index c6ed1dcfb..b843aad03 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -3475,12 +3475,12 @@ Name WasmBinaryBuilder::getExceptionTargetName(int32_t offset) {
}
size_t index = breakStack.size() - 1 - offset;
if (index > breakStack.size()) {
- throwError("bad delegate index (high)");
+ throwError("bad try index (high)");
}
- BYN_TRACE("delegate target " << breakStack[index].name << std::endl);
+ BYN_TRACE("exception target " << breakStack[index].name << std::endl);
auto& ret = breakStack[index];
- // if the delegate is in literally unreachable code, then we will not emit it
- // anyhow, so do not note that the target has delegate to it
+ // if the delegate/rethrow is in literally unreachable code, then we will not
+ // emit it anyhow, so do not note that the target has a reference to it
if (!willBeIgnored) {
exceptionTargetNames.insert(ret.name);
}
@@ -5835,11 +5835,12 @@ void WasmBinaryBuilder::visitTryOrTryInBlock(Expression*& out) {
curr->delegateTarget = getExceptionTargetName(getU32LEB());
}
- // For simplicity, we make try's labels only can be targeted by delegates, and
- // delegates can only target try's labels. (If they target blocks or loops, it
- // is a validation failure.) Because we create an inner block within each try
- // and catch body, if any delegate targets those inner blocks, we should make
- // them target the try's label instead.
+ // For simplicity, we ensure that try's labels can only be targeted by
+ // delegates and rethrows, and delegates/rethrows can only target try's
+ // labels. (If they target blocks or loops, it is a validation failure.)
+ // Because we create an inner block within each try and catch body, if any
+ // delegate/rethrow targets those inner blocks, we should make them target the
+ // try's label instead.
curr->name = getNextLabel();
if (auto* block = curr->body->dynCast<Block>()) {
if (block->name.is()) {
@@ -5936,7 +5937,9 @@ void WasmBinaryBuilder::visitThrow(Throw* curr) {
void WasmBinaryBuilder::visitRethrow(Rethrow* curr) {
BYN_TRACE("zz node: Rethrow\n");
- curr->depth = getU32LEB();
+ curr->target = getExceptionTargetName(getU32LEB());
+ // This special target is valid only for delegates
+ assert(curr->target != DELEGATE_CALLER_TARGET);
curr->finalize();
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 0ff4bfb9f..13b76801b 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2067,8 +2067,8 @@ Expression* SExpressionWasmBuilder::makeTry(Element& s) {
// We create a different name for the wrapping block, because try's name can
// be used by internal delegates
block->name = nameMapper.pushLabelName(sName);
- // For simplicity, try's name canonly be targeted by delegates. Make the
- // branches target the new wrapping block instead.
+ // For simplicity, try's name can only be targeted by delegates and
+ // rethrows. Make the branches target the new wrapping block instead.
BranchUtils::replaceBranchTargets(ret, ret->name, block->name);
block->list.push_back(ret);
nameMapper.popLabelName(block->name);
@@ -2078,29 +2078,6 @@ Expression* SExpressionWasmBuilder::makeTry(Element& s) {
return ret;
}
-Expression*
-SExpressionWasmBuilder::makeTryOrCatchBody(Element& s, Type type, bool isTry) {
- if (isTry && !elementStartsWith(s, "do")) {
- throw ParseException("invalid try do clause", s.line, s.col);
- }
- if (!isTry && !elementStartsWith(s, "catch") &&
- !elementStartsWith(s, "catch_all")) {
- throw ParseException("invalid catch clause", s.line, s.col);
- }
- if (s.size() == 1) { // (do) / (catch) / (catch_all) without instructions
- return makeNop();
- }
- auto ret = allocator.alloc<Block>();
- for (size_t i = 1; i < s.size(); i++) {
- ret->list.push_back(parseExpression(s[i]));
- }
- if (ret->list.size() == 1) {
- return ret->list[0];
- }
- ret->finalize(type);
- return ret;
-}
-
Expression* SExpressionWasmBuilder::makeThrow(Element& s) {
auto ret = allocator.alloc<Throw>();
Index i = 1;
@@ -2118,7 +2095,7 @@ Expression* SExpressionWasmBuilder::makeThrow(Element& s) {
Expression* SExpressionWasmBuilder::makeRethrow(Element& s) {
auto ret = allocator.alloc<Rethrow>();
- ret->depth = atoi(s[1]->str().c_str());
+ ret->target = getLabel(*s[1], LabelType::Exception);
ret->finalize();
return ret;
}
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index d36c2386c..b6c0392ca 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -1936,7 +1936,7 @@ void BinaryInstWriter::visitThrow(Throw* curr) {
}
void BinaryInstWriter::visitRethrow(Rethrow* curr) {
- o << int8_t(BinaryConsts::Rethrow) << U32LEB(curr->depth);
+ o << int8_t(BinaryConsts::Rethrow) << U32LEB(getBreakIndex(curr->target));
}
void BinaryInstWriter::visitNop(Nop* curr) { o << int8_t(BinaryConsts::Nop); }
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 736d669fb..6bcdb0e7c 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -236,7 +236,8 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
};
std::unordered_map<Name, BreakInfo> breakInfos;
- std::unordered_set<Name> exceptionTargetNames;
+ std::unordered_set<Name> delegateTargetNames;
+ std::unordered_set<Name> rethrowTargetNames;
std::set<Type> returnTypes; // types used in returns
@@ -280,7 +281,7 @@ public:
static void visitPreTry(FunctionValidator* self, Expression** currp) {
auto* curr = (*currp)->cast<Try>();
if (curr->name.is()) {
- self->exceptionTargetNames.insert(curr->name);
+ self->delegateTargetNames.insert(curr->name);
}
}
@@ -300,7 +301,8 @@ public:
static void visitPreCatch(FunctionValidator* self, Expression** currp) {
auto* curr = (*currp)->cast<Try>();
if (curr->name.is()) {
- self->exceptionTargetNames.erase(curr->name);
+ self->delegateTargetNames.erase(curr->name);
+ self->rethrowTargetNames.insert(curr->name);
}
}
@@ -376,7 +378,8 @@ public:
void visitRefIs(RefIs* curr);
void visitRefFunc(RefFunc* curr);
void visitRefEq(RefEq* curr);
- void noteException(Name name, Expression* curr);
+ void noteDelegate(Name name, Expression* curr);
+ void noteRethrow(Name name, Expression* curr);
void visitTry(Try* curr);
void visitThrow(Throw* curr);
void visitRethrow(Rethrow* curr);
@@ -2073,14 +2076,20 @@ void FunctionValidator::visitRefEq(RefEq* curr) {
"ref.eq's right argument should be a subtype of eqref");
}
-void FunctionValidator::noteException(Name name, Expression* curr) {
+void FunctionValidator::noteDelegate(Name name, Expression* curr) {
if (name != DELEGATE_CALLER_TARGET) {
- shouldBeTrue(exceptionTargetNames.find(name) != exceptionTargetNames.end(),
+ shouldBeTrue(delegateTargetNames.count(name) != 0,
curr,
"all delegate targets must be valid");
}
}
+void FunctionValidator::noteRethrow(Name name, Expression* curr) {
+ shouldBeTrue(rethrowTargetNames.count(name) != 0,
+ curr,
+ "all rethrow targets must be valid");
+}
+
void FunctionValidator::visitTry(Try* curr) {
shouldBeTrue(getModule()->features.hasExceptionHandling(),
curr,
@@ -2122,8 +2131,10 @@ void FunctionValidator::visitTry(Try* curr) {
"try should have either catches or a delegate");
if (curr->isDelegate()) {
- noteException(curr->delegateTarget, curr);
+ noteDelegate(curr->delegateTarget, curr);
}
+
+ rethrowTargetNames.erase(curr->name);
}
void FunctionValidator::visitThrow(Throw* curr) {
@@ -2167,8 +2178,7 @@ void FunctionValidator::visitRethrow(Rethrow* curr) {
Type(Type::unreachable),
curr,
"rethrow's type must be unreachable");
- // TODO Validate the depth field. The current LLVM toolchain only generates
- // depth 0 for C++ support.
+ noteRethrow(curr->target, curr);
}
void FunctionValidator::visitTupleMake(TupleMake* curr) {
@@ -2531,11 +2541,9 @@ void FunctionValidator::visitFunction(Function* curr) {
"function result must match, if function has returns");
}
- shouldBeTrue(
- breakInfos.empty(), curr->body, "all named break targets must exist");
- shouldBeTrue(exceptionTargetNames.empty(),
- curr->body,
- "all named delegate targets must exist");
+ assert(breakInfos.empty());
+ assert(delegateTargetNames.empty());
+ assert(rethrowTargetNames.empty());
returnTypes.clear();
labelNames.clear();
// validate optional local names
diff --git a/test/binaryen.js/exception-handling.js b/test/binaryen.js/exception-handling.js
index 40dc9d489..a8d3dd3a4 100644
--- a/test/binaryen.js/exception-handling.js
+++ b/test/binaryen.js/exception-handling.js
@@ -3,7 +3,7 @@ function cleanInfo(info) {
for (var x in info) {
// Filter out address pointers and only print meaningful info
if (x == 'id' || x == 'type' || x == 'name' || x == 'event' ||
- x == 'depth' || x == 'hasCatchAll' || x == 'delegateTarget' ||
+ x == 'target' || x == 'hasCatchAll' || x == 'delegateTarget' ||
x == 'isDelegate') {
ret[x] = info[x];
}
@@ -21,19 +21,19 @@ module.setFeatures(binaryen.Features.ReferenceTypes |
var event_ = module.addEvent("e", 0, binaryen.i32, binaryen.none);
-// (try
+// (try $l0
// (do
// (throw $e (i32.const 0))
// )
// (catch
// (drop (pop i32))
-// (rethrow 0)
+// (rethrow $l0)
// )
// )
var throw_ = module.throw("e", [module.i32.const(0)]);
-var rethrow = module.rethrow(0);
+var rethrow = module.rethrow("l0");
var try_catch = module.try(
- '',
+ "l0",
throw_,
["e"],
[
diff --git a/test/binaryen.js/exception-handling.js.txt b/test/binaryen.js/exception-handling.js.txt
index 350d543e5..434546126 100644
--- a/test/binaryen.js/exception-handling.js.txt
+++ b/test/binaryen.js/exception-handling.js.txt
@@ -3,7 +3,7 @@
(type $i32_=>_none (func (param i32)))
(event $e (attr 0) (param i32))
(func $test
- (try
+ (try $l0
(do
(throw $e
(i32.const 0)
@@ -13,7 +13,7 @@
(drop
(pop i32)
)
- (rethrow 0)
+ (rethrow $l0)
)
)
(try $try_outer
@@ -35,6 +35,6 @@
)
getExpressionInfo(throw) = {"id":48,"type":1,"event":"e"}
-getExpressionInfo(rethrow) = {"id":49,"type":1,"depth":0}
-getExpressionInfo(try_catch) = {"id":47,"type":1,"name":"","hasCatchAll":0,"delegateTarget":"","isDelegate":0}
+getExpressionInfo(rethrow) = {"id":49,"type":1,"target":"l0"}
+getExpressionInfo(try_catch) = {"id":47,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0}
getExpressionInfo(try_delegate) = {"id":47,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0}
diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js
index 688321675..1ffd45362 100644
--- a/test/binaryen.js/expressions.js
+++ b/test/binaryen.js/expressions.js
@@ -1585,14 +1585,14 @@ console.log("# Rethrow");
(function testRethrow() {
const module = new binaryen.Module();
- const theRethrow = binaryen.Rethrow(module.rethrow(0));
+ const theRethrow = binaryen.Rethrow(module.rethrow("l0"));
assert(theRethrow instanceof binaryen.Rethrow);
assert(theRethrow instanceof binaryen.Expression);
- assert(theRethrow.depth === 0);
+ assert(theRethrow.target === "l0");
assert(theRethrow.type === binaryen.unreachable);
- theRethrow.depth = 1
- assert(theRethrow.depth === 1);
+ theRethrow.target = "l1";
+ assert(theRethrow.target === "l1");
theRethrow.type = binaryen.f64;
theRethrow.finalize();
assert(theRethrow.type === binaryen.unreachable);
@@ -1601,7 +1601,7 @@ console.log("# Rethrow");
assert(
theRethrow.toText()
==
- "(rethrow 1)\n"
+ "(rethrow $l1)\n"
);
module.dispose();
diff --git a/test/binaryen.js/expressions.js.txt b/test/binaryen.js/expressions.js.txt
index 8467c55c0..1efbd9e13 100644
--- a/test/binaryen.js/expressions.js.txt
+++ b/test/binaryen.js/expressions.js.txt
@@ -297,7 +297,7 @@
)
# Rethrow
-(rethrow 1)
+(rethrow $l1)
# TupleMake
(tuple.make
diff --git a/test/exception-handling.wast b/test/exception-handling.wast
index 399fef14c..45cb21910 100644
--- a/test/exception-handling.wast
+++ b/test/exception-handling.wast
@@ -130,17 +130,6 @@
)
)
)
-
- ;; rethrow
- (try
- (do
- (throw $e-i32 (i32.const 0))
- )
- (catch $e-i32
- (drop (pop i32))
- (rethrow 0)
- )
- )
)
(func $delegate-test
@@ -200,4 +189,112 @@
(delegate 0)
)
)
+
+ (func $rethrow-test
+ ;; Simple try-catch-rethrow
+ (try $l0
+ (do
+ (call $foo)
+ )
+ (catch $e-i32
+ (drop (pop i32))
+ (rethrow $l0) ;; by label
+ )
+ (catch_all
+ (rethrow 0) ;; by depth
+ )
+ )
+
+ ;; When there are both a branch and a rethrow that target the same try
+ ;; label. Because binaryen only allows blocks and loops to be targetted by
+ ;; branches, we wrap the try with a block and make branches that block
+ ;; instead, resulting in the br and rethrow target different labels in the
+ ;; output.
+ (try $l0
+ (do
+ (call $foo)
+ )
+ (catch $e-i32
+ (drop (pop i32))
+ (rethrow $l0)
+ )
+ (catch_all
+ (br $l0)
+ )
+ )
+
+ ;; One more level deep
+ (try $l0
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try
+ (do
+ (call $foo)
+ )
+ (catch $e-i32
+ (drop (pop i32))
+ (rethrow $l0) ;; by label
+ )
+ (catch_all
+ (rethrow 1) ;; by depth
+ )
+ )
+ )
+ )
+
+ ;; Interleaving block
+ (try $l0
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try
+ (do
+ (call $foo)
+ )
+ (catch $e-i32
+ (drop (pop i32))
+ (block $b0
+ (rethrow $l0) ;; by label
+ )
+ )
+ (catch_all
+ (block $b1
+ (rethrow 2) ;; by depth
+ )
+ )
+ )
+ )
+ )
+
+ ;; Within nested try, but rather in 'try' part and not 'catch'
+ (try $l0
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try
+ (do
+ (rethrow $l0) ;; by label
+ )
+ (catch_all)
+ )
+ )
+ )
+ (try $l0
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try
+ (do
+ (rethrow 1) ;; by depth
+ )
+ (catch_all)
+ )
+ )
+ )
+ )
)
diff --git a/test/exception-handling.wast.from-wast b/test/exception-handling.wast.from-wast
index 5a0a1249d..c397edcd8 100644
--- a/test/exception-handling.wast.from-wast
+++ b/test/exception-handling.wast.from-wast
@@ -169,19 +169,6 @@
)
)
)
- (try $try10
- (do
- (throw $e-i32
- (i32.const 0)
- )
- )
- (catch $e-i32
- (drop
- (pop i32)
- )
- (rethrow 0)
- )
- )
)
(func $delegate-test
(try $l0
@@ -192,7 +179,7 @@
)
(delegate $l0)
)
- (try $try11
+ (try $try10
(do
(call $foo)
)
@@ -203,24 +190,24 @@
(nop)
)
)
- (block $l015
- (try $l012
+ (block $l014
+ (try $l011
(do
- (try $try13
+ (try $try12
(do
- (br_if $l015
+ (br_if $l014
(i32.const 1)
)
)
- (delegate $l012)
+ (delegate $l011)
)
- (try $try14
+ (try $try13
(do
- (br_if $l015
+ (br_if $l014
(i32.const 1)
)
)
- (delegate $l012)
+ (delegate $l011)
)
)
(catch_all
@@ -228,16 +215,124 @@
)
)
)
- (try $l016
+ (try $l015
(do
- (try $try17
+ (try $try16
(do
(call $foo)
)
- (delegate $l016)
+ (delegate $l015)
)
)
(delegate 0)
)
)
+ (func $rethrow-test
+ (try $l0
+ (do
+ (call $foo)
+ )
+ (catch $e-i32
+ (drop
+ (pop i32)
+ )
+ (rethrow $l0)
+ )
+ (catch_all
+ (rethrow $l0)
+ )
+ )
+ (block $l018
+ (try $l017
+ (do
+ (call $foo)
+ )
+ (catch $e-i32
+ (drop
+ (pop i32)
+ )
+ (rethrow $l017)
+ )
+ (catch_all
+ (br $l018)
+ )
+ )
+ )
+ (try $l019
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try $try
+ (do
+ (call $foo)
+ )
+ (catch $e-i32
+ (drop
+ (pop i32)
+ )
+ (rethrow $l019)
+ )
+ (catch_all
+ (rethrow $l019)
+ )
+ )
+ )
+ )
+ (try $l020
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try $try21
+ (do
+ (call $foo)
+ )
+ (catch $e-i32
+ (drop
+ (pop i32)
+ )
+ (block $b0
+ (rethrow $l020)
+ )
+ )
+ (catch_all
+ (block $b1
+ (rethrow $l020)
+ )
+ )
+ )
+ )
+ )
+ (try $l022
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try $try23
+ (do
+ (rethrow $l022)
+ )
+ (catch_all
+ (nop)
+ )
+ )
+ )
+ )
+ (try $l024
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try $try25
+ (do
+ (rethrow $l024)
+ )
+ (catch_all
+ (nop)
+ )
+ )
+ )
+ )
+ )
)
diff --git a/test/exception-handling.wast.fromBinary b/test/exception-handling.wast.fromBinary
index d6dd331b1..89d0bb7fb 100644
--- a/test/exception-handling.wast.fromBinary
+++ b/test/exception-handling.wast.fromBinary
@@ -194,19 +194,6 @@
)
)
)
- (try $label$37
- (do
- (throw $event$0
- (i32.const 0)
- )
- )
- (catch $event$0
- (drop
- (pop i32)
- )
- (rethrow 0)
- )
- )
)
(func $delegate-test
(try $label$9
@@ -269,5 +256,111 @@
(delegate 0)
)
)
+ (func $rethrow-test
+ (try $label$3
+ (do
+ (call $foo)
+ )
+ (catch $event$0
+ (drop
+ (pop i32)
+ )
+ (rethrow $label$3)
+ )
+ (catch_all
+ (rethrow $label$3)
+ )
+ )
+ (block $label$4
+ (try $label$7
+ (do
+ (call $foo)
+ )
+ (catch $event$0
+ (drop
+ (pop i32)
+ )
+ (rethrow $label$7)
+ )
+ (catch_all
+ (br $label$4)
+ )
+ )
+ )
+ (try $label$13
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try $label$12
+ (do
+ (call $foo)
+ )
+ (catch $event$0
+ (drop
+ (pop i32)
+ )
+ (rethrow $label$13)
+ )
+ (catch_all
+ (rethrow $label$13)
+ )
+ )
+ )
+ )
+ (try $label$20
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try $label$19
+ (do
+ (call $foo)
+ )
+ (catch $event$0
+ (drop
+ (pop i32)
+ )
+ (block $label$18
+ (rethrow $label$20)
+ )
+ )
+ (catch_all
+ (rethrow $label$20)
+ )
+ )
+ )
+ )
+ (try $label$26
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try $label$25
+ (do
+ (rethrow $label$26)
+ )
+ (catch_all
+ (nop)
+ )
+ )
+ )
+ )
+ (try $label$32
+ (do
+ (call $foo)
+ )
+ (catch_all
+ (try $label$31
+ (do
+ (rethrow $label$32)
+ )
+ (catch_all
+ (nop)
+ )
+ )
+ )
+ )
+ )
)
diff --git a/test/exception-handling.wast.fromBinary.noDebugInfo b/test/exception-handling.wast.fromBinary.noDebugInfo
index cf7372b6e..47577065c 100644
--- a/test/exception-handling.wast.fromBinary.noDebugInfo
+++ b/test/exception-handling.wast.fromBinary.noDebugInfo
@@ -194,19 +194,6 @@
)
)
)
- (try $label$37
- (do
- (throw $event$0
- (i32.const 0)
- )
- )
- (catch $event$0
- (drop
- (pop i32)
- )
- (rethrow 0)
- )
- )
)
(func $3
(try $label$9
@@ -269,5 +256,111 @@
(delegate 0)
)
)
+ (func $4
+ (try $label$3
+ (do
+ (call $0)
+ )
+ (catch $event$0
+ (drop
+ (pop i32)
+ )
+ (rethrow $label$3)
+ )
+ (catch_all
+ (rethrow $label$3)
+ )
+ )
+ (block $label$4
+ (try $label$7
+ (do
+ (call $0)
+ )
+ (catch $event$0
+ (drop
+ (pop i32)
+ )
+ (rethrow $label$7)
+ )
+ (catch_all
+ (br $label$4)
+ )
+ )
+ )
+ (try $label$13
+ (do
+ (call $0)
+ )
+ (catch_all
+ (try $label$12
+ (do
+ (call $0)
+ )
+ (catch $event$0
+ (drop
+ (pop i32)
+ )
+ (rethrow $label$13)
+ )
+ (catch_all
+ (rethrow $label$13)
+ )
+ )
+ )
+ )
+ (try $label$20
+ (do
+ (call $0)
+ )
+ (catch_all
+ (try $label$19
+ (do
+ (call $0)
+ )
+ (catch $event$0
+ (drop
+ (pop i32)
+ )
+ (block $label$18
+ (rethrow $label$20)
+ )
+ )
+ (catch_all
+ (rethrow $label$20)
+ )
+ )
+ )
+ )
+ (try $label$26
+ (do
+ (call $0)
+ )
+ (catch_all
+ (try $label$25
+ (do
+ (rethrow $label$26)
+ )
+ (catch_all
+ (nop)
+ )
+ )
+ )
+ )
+ (try $label$32
+ (do
+ (call $0)
+ )
+ (catch_all
+ (try $label$31
+ (do
+ (rethrow $label$32)
+ )
+ (catch_all
+ (nop)
+ )
+ )
+ )
+ )
+ )
)
diff --git a/test/passes/code-pushing_all-features.txt b/test/passes/code-pushing_all-features.txt
index 5226a7b3c..19931399d 100644
--- a/test/passes/code-pushing_all-features.txt
+++ b/test/passes/code-pushing_all-features.txt
@@ -105,14 +105,14 @@
(local.set $x
(i32.const 1)
)
- (try $try
+ (try $l0
(do
(throw $e
(i32.const 0)
)
)
(catch_all
- (rethrow 0)
+ (rethrow $l0)
)
)
(drop
diff --git a/test/passes/code-pushing_all-features.wast b/test/passes/code-pushing_all-features.wast
index 0c17c0bab..b7f12bb0a 100644
--- a/test/passes/code-pushing_all-features.wast
+++ b/test/passes/code-pushing_all-features.wast
@@ -72,12 +72,12 @@
;; This local.set cannot be pushed down, because there is 'rethrow' within
;; the inner catch_all
(local.set $x (i32.const 1))
- (try
+ (try $l0
(do
(throw $e (i32.const 0))
)
(catch_all
- (rethrow 0)
+ (rethrow $l0)
)
)
(drop (i32.const 1))
diff --git a/test/passes/dce_all-features.txt b/test/passes/dce_all-features.txt
index 575d04065..a64f033f6 100644
--- a/test/passes/dce_all-features.txt
+++ b/test/passes/dce_all-features.txt
@@ -587,9 +587,15 @@
)
)
(func $rethrow
- (block $label$0
- (block $label$1
- (rethrow 0)
+ (try $l0
+ (do
+ (nop)
+ )
+ (catch $e
+ (drop
+ (i32.const 0)
+ )
+ (rethrow $l0)
)
)
)
diff --git a/test/passes/dce_all-features.wast b/test/passes/dce_all-features.wast
index fac6d7623..104093d08 100644
--- a/test/passes/dce_all-features.wast
+++ b/test/passes/dce_all-features.wast
@@ -774,6 +774,7 @@
)
(func $throw
+ ;; All these wrapping expressions before 'throw' will be dce'd
(drop
(block $label$0 (result externref)
(if
@@ -790,17 +791,16 @@
)
(func $rethrow
- (drop
- (block $label$0 (result externref)
- (if
- (i32.clz
- (block $label$1 (result i32)
- (rethrow 0)
- )
+ (try $l0
+ (do)
+ (catch $e
+ (drop
+ ;; This i32.add will be dce'd
+ (i32.add
+ (i32.const 0)
+ (rethrow $l0)
)
- (nop)
)
- (ref.null extern)
)
)
)
diff --git a/test/passes/dwarf_with_exceptions.bin.txt b/test/passes/dwarf_with_exceptions.bin.txt
index 5dd594461..9b853e179 100644
--- a/test/passes/dwarf_with_exceptions.bin.txt
+++ b/test/passes/dwarf_with_exceptions.bin.txt
@@ -93,7 +93,7 @@
)
)
;; code offset: 0x6c
- (rethrow 0)
+ (rethrow $label$8)
)
)
;; code offset: 0x6f
@@ -515,7 +515,7 @@ file_names[ 1]:
)
)
;; code offset: 0x3e
- (rethrow 0)
+ (rethrow $label$8)
)
)
;; code offset: 0x41
diff --git a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt
index f8e7778c5..756b692c7 100644
--- a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt
+++ b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt
@@ -3,20 +3,20 @@
(type $i32_=>_none (func (param i32)))
(event $e0 (attr 0) (param i32))
(func $eh
- try $try
+ try $l0
i32.const 0
throw $e0
catch $e0
drop
catch_all
- rethrow 0
+ rethrow $l0
end
- try $l0
- try $try0
+ try $l00
+ try $try
i32.const 0
throw $e0
- delegate $l0
+ delegate $l00
unreachable
catch_all
nop
@@ -31,7 +31,7 @@
(type $i32_=>_none (func (param i32)))
(event $e0 (attr 0) (param i32))
(func $eh (; has Stack IR ;)
- (try $try
+ (try $l0
(do
(throw $e0
(i32.const 0)
@@ -43,18 +43,18 @@
)
)
(catch_all
- (rethrow 0)
+ (rethrow $l0)
)
)
- (try $l0
+ (try $l00
(do
- (try $try0
+ (try $try
(do
(throw $e0
(i32.const 0)
)
)
- (delegate $l0)
+ (delegate $l00)
)
)
(catch_all
diff --git a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast
index f82f7e19a..298cb3a3c 100644
--- a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast
+++ b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast
@@ -2,7 +2,7 @@
(event $e0 (attr 0) (param i32))
(func $eh
- (try
+ (try $l0
(do
(throw $e0 (i32.const 0))
)
@@ -10,7 +10,7 @@
(drop (pop i32))
)
(catch_all
- (rethrow 0)
+ (rethrow $l0)
)
)
diff --git a/test/passes/rse_all-features.txt b/test/passes/rse_all-features.txt
index eaef14455..6c9d52585 100644
--- a/test/passes/rse_all-features.txt
+++ b/test/passes/rse_all-features.txt
@@ -569,14 +569,14 @@
(local $x i32)
(try $try
(do
- (try $try6
+ (try $l0
(do
(throw $e
(i32.const 0)
)
)
(catch_all
- (rethrow 0)
+ (rethrow $l0)
)
)
)
@@ -594,7 +594,7 @@
(local $x i32)
(try $try
(do
- (try $try7
+ (try $l0
(do
(throw $e
(i32.const 0)
@@ -604,7 +604,7 @@
(local.set $x
(i32.const 1)
)
- (rethrow 0)
+ (rethrow $l0)
)
)
)
@@ -620,7 +620,7 @@
(local $x i32)
(try $try
(do
- (try $try8
+ (try $l0
(do
(throw $e
(i32.const 0)
@@ -633,7 +633,7 @@
(local.set $x
(i32.const 1)
)
- (rethrow 0)
+ (rethrow $l0)
)
)
)
diff --git a/test/passes/rse_all-features.wast b/test/passes/rse_all-features.wast
index 2a8b81874..b3c684fbd 100644
--- a/test/passes/rse_all-features.wast
+++ b/test/passes/rse_all-features.wast
@@ -357,12 +357,12 @@
(local $x i32)
(try
(do
- (try
+ (try $l0
(do
(throw $e (i32.const 0))
)
(catch_all
- (rethrow 0)
+ (rethrow $l0)
)
)
)
@@ -379,13 +379,13 @@
(local $x i32)
(try
(do
- (try
+ (try $l0
(do
(throw $e (i32.const 0))
)
(catch_all
(local.set $x (i32.const 1))
- (rethrow 0)
+ (rethrow $l0)
)
)
)
@@ -399,14 +399,14 @@
(local $x i32)
(try
(do
- (try
+ (try $l0
(do
(throw $e (i32.const 0))
)
(catch $e
(drop (pop i32))
(local.set $x (i32.const 1))
- (rethrow 0)
+ (rethrow $l0)
)
)
)
diff --git a/test/spec/exception-handling.wast b/test/spec/exception-handling.wast
index 807ee0a52..21dc98bd4 100644
--- a/test/spec/exception-handling.wast
+++ b/test/spec/exception-handling.wast
@@ -92,29 +92,29 @@
)
(func (export "try_throw_rethrow")
- (try
+ (try $l0
(do
(throw $e-i32 (i32.const 5))
)
(catch $e-i32
(drop (pop i32))
- (rethrow 0)
+ (rethrow $l0)
)
)
)
(func (export "try_call_rethrow")
- (try
+ (try $l0
(do
(call $throw_single_value)
)
(catch_all
- (rethrow 0)
+ (rethrow $l0)
)
)
)
- (func (export "rethrow_depth_test1") (result i32)
+ (func (export "rethrow_target_test1") (result i32)
(try (result i32)
(do
(try
@@ -122,13 +122,13 @@
(throw $e-i32 (i32.const 1))
)
(catch_all
- (try
+ (try $l0
(do
(throw $e-i32 (i32.const 2))
)
(catch $e-i32
(drop (pop i32))
- (rethrow 0) ;; rethrow (i32.const 2)
+ (rethrow $l0) ;; rethrow (i32.const 2)
)
)
)
@@ -141,10 +141,10 @@
)
;; Can we handle rethrows with the depth > 0?
- (func (export "rethrow_depth_test2") (result i32)
+ (func (export "rethrow_target_test2") (result i32)
(try (result i32)
(do
- (try
+ (try $l0
(do
(throw $e-i32 (i32.const 1))
)
@@ -168,12 +168,12 @@
)
;; Tests whether the exception stack is managed correctly after rethrows
- (func (export "rethrow_depth_test3") (result i32)
+ (func (export "rethrow_target_test3") (result i32)
(try (result i32)
(do
- (try
+ (try $l0
(do
- (try
+ (try $l1
(do
(throw $e-i32 (i32.const 1))
)
@@ -184,14 +184,14 @@
)
(catch $e-i32
(drop (pop i32))
- (rethrow 1) ;; rethrow (i32.const 1)
+ (rethrow $l1) ;; rethrow (i32.const 1)
)
)
)
)
)
(catch $e-i32
- (rethrow 0) ;; rethrow (i32.const 1) again
+ (rethrow $l0) ;; rethrow (i32.const 1) again
)
)
)
@@ -212,9 +212,9 @@
(assert_return (invoke "try_throw_multivalue_catch") (i32.const 5))
(assert_trap (invoke "try_throw_rethrow"))
(assert_trap (invoke "try_call_rethrow"))
-(assert_return (invoke "rethrow_depth_test1") (i32.const 2))
-(assert_return (invoke "rethrow_depth_test2") (i32.const 1))
-(assert_return (invoke "rethrow_depth_test3") (i32.const 1))
+(assert_return (invoke "rethrow_target_test1") (i32.const 2))
+(assert_return (invoke "rethrow_target_test2") (i32.const 1))
+(assert_return (invoke "rethrow_target_test3") (i32.const 1))
(assert_invalid
(module
@@ -299,3 +299,33 @@
)
"all delegate targets must be valid"
)
+
+(assert_invalid
+ (module
+ (func $f0
+ (block $l0
+ (try
+ (do)
+ (catch_all
+ (rethrow $l0) ;; target is a block
+ )
+ )
+ )
+ )
+ )
+ "all rethrow targets must be valid"
+)
+
+(assert_invalid
+ (module
+ (func $f0
+ (try $l0
+ (do
+ (rethrow $l0) ;; Not within the target try's catch
+ )
+ (catch_all)
+ )
+ )
+ )
+ "all rethrow targets must be valid"
+)