Skip to content

Commit 71b9cc0

Browse files
authored
[Parser] Parse try/catch/catch_all/delegate (#6128)
Parse the legacy v3 syntax for try/catch/catch_all/delegate in both its folded and unfolded forms. The first sources of significant complexity is the optional IDs after `catch` and `catch_all` in the unfolded form, which can be confused for tag indices and require backtracking to parse correctly. The second source of complexity is the handling of delegate labels, which are relative to the try's parent scope despite being parsed after the try's scope has already started. Handling this correctly requires punching a whole big enough to drive a truck through through both the parser and IRBuilder abstractions.
1 parent 2474258 commit 71b9cc0

7 files changed

Lines changed: 902 additions & 51 deletions

File tree

scripts/gen-s-parser.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -709,15 +709,13 @@ def insert(self, inst, expr):
709709
def instruction_parser(new_parser=False):
710710
"""Build a trie out of all the instructions, then emit it as C++ code."""
711711
global instructions
712-
if new_parser:
713-
# Filter out instructions that the new parser does not need.
714-
instructions = [(inst, code) for (inst, code) in instructions
715-
if inst not in ('block', 'loop', 'if', 'then', 'else')]
716712
trie = Node()
717713
inst_length = 0
718714
for inst, expr in instructions:
719-
if new_parser and inst in {"then", "else"}:
720-
# These are not real instructions! skip them.
715+
if new_parser and inst in {"block", "loop", "if", "try", "then",
716+
"else"}:
717+
# These are either control flow handled manually or not real
718+
# instructions. Skip them.
721719
continue
722720
inst_length = max(inst_length, len(inst))
723721
trie.insert(inst, expr)

src/gen-s-parser.inc

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8649,12 +8649,6 @@ switch (buf[0]) {
86498649
return Ok{};
86508650
}
86518651
goto parse_error;
8652-
case 'r':
8653-
if (op == "try"sv) {
8654-
CHECK_ERR(makeTry(ctx, pos));
8655-
return Ok{};
8656-
}
8657-
goto parse_error;
86588652
case 'u': {
86598653
switch (buf[6]) {
86608654
case 'e':

src/parser/contexts.h

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,8 @@ struct NullInstrParserCtx {
308308
MemoryIdxT getMemoryFromName(Name) { return Ok{}; }
309309
DataIdxT getDataFromIdx(uint32_t) { return Ok{}; }
310310
DataIdxT getDataFromName(Name) { return Ok{}; }
311-
LabelIdxT getLabelFromIdx(uint32_t) { return Ok{}; }
312-
LabelIdxT getLabelFromName(Name) { return Ok{}; }
311+
LabelIdxT getLabelFromIdx(uint32_t, bool) { return Ok{}; }
312+
LabelIdxT getLabelFromName(Name, bool) { return Ok{}; }
313313
TagIdxT getTagFromIdx(uint32_t) { return Ok{}; }
314314
TagIdxT getTagFromName(Name) { return Ok{}; }
315315

@@ -328,6 +328,13 @@ struct NullInstrParserCtx {
328328
Result<> makeLoop(Index, std::optional<Name>, BlockTypeT) {
329329
return Ok{};
330330
}
331+
template<typename BlockTypeT>
332+
Result<> makeTry(Index, std::optional<Name>, BlockTypeT) {
333+
return Ok{};
334+
}
335+
Result<> visitCatch(Index, TagIdxT) { return Ok{}; }
336+
Result<> visitCatchAll(Index) { return Ok{}; }
337+
Result<> visitDelegate(Index, LabelIdxT) { return Ok{}; }
331338
Result<> visitEnd() { return Ok{}; }
332339

333340
Result<> makeUnreachable(Index) { return Ok{}; }
@@ -1020,10 +1027,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
10201027
return name;
10211028
}
10221029

1023-
Result<Index> getLabelFromIdx(uint32_t idx) { return idx; }
1030+
Result<Index> getLabelFromIdx(uint32_t idx, bool) { return idx; }
10241031

1025-
Result<Index> getLabelFromName(Name name) {
1026-
return irBuilder.getLabelIndex(name);
1032+
Result<Index> getLabelFromName(Name name, bool inDelegate) {
1033+
return irBuilder.getLabelIndex(name, inDelegate);
10271034
}
10281035

10291036
Result<Name> getTagFromIdx(uint32_t idx) {
@@ -1109,6 +1116,26 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
11091116
irBuilder.makeLoop(label ? *label : Name{}, type.getSignature().results));
11101117
}
11111118

1119+
Result<> makeTry(Index pos, std::optional<Name> label, HeapType type) {
1120+
// TODO: validate labels?
1121+
// TODO: Move error on input types to here?
1122+
return withLoc(
1123+
pos,
1124+
irBuilder.makeTry(label ? *label : Name{}, type.getSignature().results));
1125+
}
1126+
1127+
Result<> visitCatch(Index pos, Name tag) {
1128+
return withLoc(pos, irBuilder.visitCatch(tag));
1129+
}
1130+
1131+
Result<> visitCatchAll(Index pos) {
1132+
return withLoc(pos, irBuilder.visitCatchAll());
1133+
}
1134+
1135+
Result<> visitDelegate(Index pos, Index label) {
1136+
return withLoc(pos, irBuilder.visitDelegate(label));
1137+
}
1138+
11121139
Result<> visitEnd() { return withLoc(irBuilder.visitEnd()); }
11131140

11141141
Result<> makeUnreachable(Index pos) {

src/parser/parsers.h

Lines changed: 174 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ template<typename Ctx> Result<typename Ctx::BlockTypeT> blocktype(Ctx&);
5656
template<typename Ctx> MaybeResult<> block(Ctx&, bool);
5757
template<typename Ctx> MaybeResult<> ifelse(Ctx&, bool);
5858
template<typename Ctx> MaybeResult<> loop(Ctx&, bool);
59+
template<typename Ctx> MaybeResult<> trycatch(Ctx&, bool);
5960
template<typename Ctx> Result<> makeUnreachable(Ctx&, Index);
6061
template<typename Ctx> Result<> makeNop(Ctx&, Index);
6162
template<typename Ctx> Result<> makeBinary(Ctx&, Index, BinaryOp op);
@@ -113,9 +114,6 @@ template<typename Ctx> Result<> makeTableSize(Ctx&, Index);
113114
template<typename Ctx> Result<> makeTableGrow(Ctx&, Index);
114115
template<typename Ctx> Result<> makeTableFill(Ctx&, Index);
115116
template<typename Ctx> Result<> makeTableCopy(Ctx&, Index);
116-
template<typename Ctx> Result<> makeTry(Ctx&, Index);
117-
template<typename Ctx>
118-
Result<> makeTryOrCatchBody(Ctx&, Index, Type type, bool isTry);
119117
template<typename Ctx> Result<> makeThrow(Ctx&, Index);
120118
template<typename Ctx> Result<> makeRethrow(Ctx&, Index);
121119
template<typename Ctx> Result<> makeTupleMake(Ctx&, Index);
@@ -173,7 +171,8 @@ template<typename Ctx> Result<typename Ctx::MemoryIdxT> memidx(Ctx&);
173171
template<typename Ctx> MaybeResult<typename Ctx::MemoryIdxT> maybeMemuse(Ctx&);
174172
template<typename Ctx> Result<typename Ctx::GlobalIdxT> globalidx(Ctx&);
175173
template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx&);
176-
template<typename Ctx> Result<typename Ctx::LabelIdxT> labelidx(Ctx&);
174+
template<typename Ctx>
175+
Result<typename Ctx::LabelIdxT> labelidx(Ctx&, bool inDelegate = false);
177176
template<typename Ctx> Result<typename Ctx::TagIdxT> tagidx(Ctx&);
178177
template<typename Ctx> Result<typename Ctx::TypeUseT> typeuse(Ctx&);
179178
MaybeResult<ImportNames> inlineImport(ParseInput&);
@@ -573,6 +572,9 @@ template<typename Ctx> MaybeResult<> foldedBlockinstr(Ctx& ctx) {
573572
if (auto i = loop(ctx, true)) {
574573
return i;
575574
}
575+
if (auto i = trycatch(ctx, true)) {
576+
return i;
577+
}
576578
// TODO: Other block instructions
577579
return {};
578580
}
@@ -587,6 +589,9 @@ template<typename Ctx> MaybeResult<> unfoldedBlockinstr(Ctx& ctx) {
587589
if (auto i = loop(ctx, false)) {
588590
return i;
589591
}
592+
if (auto i = trycatch(ctx, false)) {
593+
return i;
594+
}
590595
// TODO: Other block instructions
591596
return {};
592597
}
@@ -619,7 +624,9 @@ template<typename Ctx> MaybeResult<> instr(Ctx& ctx) {
619624
// Check for valid strings that are not instructions.
620625
if (auto tok = ctx.in.peek()) {
621626
if (auto keyword = tok->getKeyword()) {
622-
if (keyword == "end"sv || keyword == "then"sv || keyword == "else"sv) {
627+
if (keyword == "end"sv || keyword == "then"sv || keyword == "else"sv ||
628+
keyword == "catch"sv || keyword == "catch_all"sv ||
629+
keyword == "delegate"sv) {
623630
return {};
624631
}
625632
}
@@ -860,7 +867,7 @@ template<typename Ctx> MaybeResult<> ifelse(Ctx& ctx, bool folded) {
860867
return ctx.visitEnd();
861868
}
862869

863-
// loop ::= 'loop' label blocktype instr* end id?
870+
// loop ::= 'loop' label blocktype instr* 'end' id?
864871
// | '(' 'loop' label blocktype instr* ')'
865872
template<typename Ctx> MaybeResult<> loop(Ctx& ctx, bool folded) {
866873
auto pos = ctx.in.getPos();
@@ -895,6 +902,163 @@ template<typename Ctx> MaybeResult<> loop(Ctx& ctx, bool folded) {
895902
return ctx.visitEnd();
896903
}
897904

905+
// trycatch ::= 'try' label blocktype instr* ('catch' id? tagidx instr*)*
906+
// ('catch_all' id? instr*)? 'end' id?
907+
// | '(' 'try' label blocktype '(' 'do' instr* ')'
908+
// ('(' 'catch' tagidx instr* ')')*
909+
// ('(' 'catch_all' instr* ')')? ')'
910+
// | 'try' label blocktype instr* 'deledate' label
911+
// | '(' 'try' label blocktype '(' 'do' instr* ')'
912+
// '(' 'delegate' label ')' ')'
913+
template<typename Ctx> MaybeResult<> trycatch(Ctx& ctx, bool folded) {
914+
auto pos = ctx.in.getPos();
915+
916+
if ((folded && !ctx.in.takeSExprStart("try"sv)) ||
917+
(!folded && !ctx.in.takeKeyword("try"sv))) {
918+
return {};
919+
}
920+
921+
auto label = ctx.in.takeID();
922+
923+
auto type = blocktype(ctx);
924+
CHECK_ERR(type);
925+
926+
CHECK_ERR(ctx.makeTry(pos, label, *type));
927+
928+
if (folded) {
929+
if (!ctx.in.takeSExprStart("do"sv)) {
930+
return ctx.in.err("expected 'do' in try");
931+
}
932+
}
933+
934+
CHECK_ERR(instrs(ctx));
935+
936+
if (folded) {
937+
if (!ctx.in.takeRParen()) {
938+
return ctx.in.err("expected ')' at end of do");
939+
}
940+
}
941+
942+
if ((folded && ctx.in.takeSExprStart("delegate")) ||
943+
(!folded && ctx.in.takeKeyword("delegate"))) {
944+
auto delegatePos = ctx.in.getPos();
945+
946+
auto label = labelidx(ctx, true);
947+
CHECK_ERR(label);
948+
949+
if (folded) {
950+
if (!ctx.in.takeRParen()) {
951+
return ctx.in.err("expected ')' at end of delegate");
952+
}
953+
if (!ctx.in.takeRParen()) {
954+
return ctx.in.err("expected ')' at end of try");
955+
}
956+
}
957+
958+
CHECK_ERR(ctx.visitDelegate(delegatePos, *label));
959+
return Ok{};
960+
}
961+
962+
while (true) {
963+
auto catchPos = ctx.in.getPos();
964+
965+
if ((folded && !ctx.in.takeSExprStart("catch"sv)) ||
966+
(!folded && !ctx.in.takeKeyword("catch"sv))) {
967+
break;
968+
}
969+
970+
// It can be ambiguous whether the name after `catch` is intended to be the
971+
// optional ID or the tag identifier. For example:
972+
//
973+
// (tag $t)
974+
// (func $ambiguous
975+
// try $t
976+
// catch $t
977+
// end
978+
// )
979+
//
980+
// When parsing the `catch`, the parser first tries to parse an optional ID
981+
// that must match the label of the `try`, and it succeeds because it sees
982+
// `$t` after the catch. However, when it then tries to parse the mandatory
983+
// tag index, it fails because the next token is `end`. The problem is that
984+
// the `$t` after the `catch` was the tag name and there was no optional ID
985+
// after all. The parser sets `parseID = false` and resets to just after the
986+
// `catch`, and now it skips parsing the optional ID so it correctly parses
987+
// the `$t` as a tag name.
988+
bool parseID = !folded;
989+
auto afterCatchPos = ctx.in.getPos();
990+
while (true) {
991+
if (!folded && parseID) {
992+
auto id = ctx.in.takeID();
993+
if (id && id != label) {
994+
// Instead of returning an error, retry without the ID.
995+
parseID = false;
996+
ctx.in.lexer.setIndex(afterCatchPos);
997+
continue;
998+
}
999+
}
1000+
1001+
auto tag = tagidx(ctx);
1002+
if (parseID && tag.getErr()) {
1003+
// Instead of returning an error, retry without the ID.
1004+
parseID = false;
1005+
ctx.in.lexer.setIndex(afterCatchPos);
1006+
continue;
1007+
}
1008+
CHECK_ERR(tag);
1009+
1010+
CHECK_ERR(ctx.visitCatch(catchPos, *tag));
1011+
1012+
CHECK_ERR(instrs(ctx));
1013+
1014+
if (folded) {
1015+
if (!ctx.in.takeRParen()) {
1016+
return ctx.in.err("expected ')' at end of catch");
1017+
}
1018+
}
1019+
break;
1020+
}
1021+
}
1022+
1023+
if ((folded && ctx.in.takeSExprStart("catch_all"sv)) ||
1024+
(!folded && ctx.in.takeKeyword("catch_all"sv))) {
1025+
auto catchPos = ctx.in.getPos();
1026+
1027+
if (!folded) {
1028+
auto id = ctx.in.takeID();
1029+
if (id && id != label) {
1030+
return ctx.in.err("catch_all label does not match try label");
1031+
}
1032+
}
1033+
1034+
CHECK_ERR(ctx.visitCatchAll(catchPos));
1035+
1036+
CHECK_ERR(instrs(ctx));
1037+
1038+
if (folded) {
1039+
if (!ctx.in.takeRParen()) {
1040+
return ctx.in.err("expected ')' at end of catch_all");
1041+
}
1042+
}
1043+
}
1044+
1045+
if (folded) {
1046+
if (!ctx.in.takeRParen()) {
1047+
return ctx.in.err("expected ')' at end of try");
1048+
}
1049+
} else {
1050+
if (!ctx.in.takeKeyword("end"sv)) {
1051+
return ctx.in.err("expected 'end' at end of try");
1052+
}
1053+
1054+
auto id = ctx.in.takeID();
1055+
if (id && id != label) {
1056+
return ctx.in.err("end label does not match try label");
1057+
}
1058+
}
1059+
return ctx.visitEnd();
1060+
}
1061+
8981062
template<typename Ctx> Result<> makeUnreachable(Ctx& ctx, Index pos) {
8991063
return ctx.makeUnreachable(pos);
9001064
}
@@ -1266,15 +1430,6 @@ template<typename Ctx> Result<> makeTableCopy(Ctx& ctx, Index pos) {
12661430
return ctx.in.err("unimplemented instruction");
12671431
}
12681432

1269-
template<typename Ctx> Result<> makeTry(Ctx& ctx, Index pos) {
1270-
return ctx.in.err("unimplemented instruction");
1271-
}
1272-
1273-
template<typename Ctx>
1274-
Result<> makeTryOrCatchBody(Ctx& ctx, Index pos, Type type, bool isTry) {
1275-
return ctx.in.err("unimplemented instruction");
1276-
}
1277-
12781433
template<typename Ctx> Result<> makeThrow(Ctx& ctx, Index pos) {
12791434
auto tag = tagidx(ctx);
12801435
CHECK_ERR(tag);
@@ -1621,12 +1776,13 @@ template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx& ctx) {
16211776

16221777
// labelidx ::= x:u32 => x
16231778
// | v:id => x (if labels[x] = v)
1624-
template<typename Ctx> Result<typename Ctx::LabelIdxT> labelidx(Ctx& ctx) {
1779+
template<typename Ctx>
1780+
Result<typename Ctx::LabelIdxT> labelidx(Ctx& ctx, bool inDelegate) {
16251781
if (auto x = ctx.in.takeU32()) {
1626-
return ctx.getLabelFromIdx(*x);
1782+
return ctx.getLabelFromIdx(*x, inDelegate);
16271783
}
16281784
if (auto id = ctx.in.takeID()) {
1629-
return ctx.getLabelFromName(*id);
1785+
return ctx.getLabelFromName(*id, inDelegate);
16301786
}
16311787
return ctx.in.err("expected label index or identifier");
16321788
}

0 commit comments

Comments
 (0)