From 08634b40c017ceccfa447ffe7919ae03e10fc15d Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 21 Jul 2026 18:21:12 -0700 Subject: [PATCH 1/3] Parse text format for compact import sections Update the parser for imports to support both forms of compact imports. Because imports are no longer 1:1 with import statements, the locations of imported items the parser collects and re-parses in various phases must now be the locations of importdesc productions rather than import productions. Since e.g. a function importdesc cannot be differentiated from a function definition with an empty body without context, we must add a mechanism for the parser context to tell the parser whether or not it is parsing an import. --- src/parser/contexts.h | 38 ++++++++ src/parser/parsers.h | 144 +++++++++++++++++++++------- test/lit/basic/compact-imports.wast | 63 ++++++++++++ 3 files changed, 208 insertions(+), 37 deletions(-) create mode 100644 test/lit/basic/compact-imports.wast diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 31a3aa253e3..b8a84d90ede 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1007,6 +1007,12 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { Lexer in; + bool isFunctionImported() { return false; } + bool isTableImported() { return false; } + bool isMemoryImported() { return false; } + bool isGlobalImported() { return false; } + bool isTagImported() { return false; } + // At this stage we only look at types to find implicit type definitions, // which are inserted directly into the context. We cannot materialize or // validate any types because we don't know what types exist yet. @@ -1470,6 +1476,22 @@ struct ParseModuleTypesCtx : TypeParserCtx, bool skipFunctionBody() { return true; } + bool isFunctionImported() { + return index < wasm.functions.size() && wasm.functions[index]->imported(); + } + bool isTableImported() { + return index < wasm.tables.size() && wasm.tables[index]->imported(); + } + bool isMemoryImported() { + return index < wasm.memories.size() && wasm.memories[index]->imported(); + } + bool isGlobalImported() { + return index < wasm.globals.size() && wasm.globals[index]->imported(); + } + bool isTagImported() { + return index < wasm.tags.size() && wasm.tags[index]->imported(); + } + Result getHeapTypeFromIdx(Index idx) { if (idx >= types.size()) { return in.err("type index out of bounds"); @@ -1644,6 +1666,22 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { Lexer in; + bool isFunctionImported() { + return index < wasm.functions.size() && wasm.functions[index]->imported(); + } + bool isTableImported() { + return index < wasm.tables.size() && wasm.tables[index]->imported(); + } + bool isMemoryImported() { + return index < wasm.memories.size() && wasm.memories[index]->imported(); + } + bool isGlobalImported() { + return index < wasm.globals.size() && wasm.globals[index]->imported(); + } + bool isTagImported() { + return index < wasm.tags.size() && wasm.tags[index]->imported(); + } + Module& wasm; Builder builder; diff --git a/src/parser/parsers.h b/src/parser/parsers.h index d6f2297ff57..4e36f0526eb 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -393,6 +393,8 @@ template Result<> subtype(Ctx&); template MaybeResult<> typedef_(Ctx&); template MaybeResult<> rectype(Ctx&); template MaybeResult locals(Ctx&); +template +Result<> importdesc(Ctx&, Name, Name, std::optional); template MaybeResult<> import_(Ctx&); template MaybeResult<> func(Ctx&); template MaybeResult<> table(Ctx&); @@ -3424,60 +3426,51 @@ template MaybeResult locals(Ctx& ctx) { return {}; } -// import ::= '(' 'import' mod:name nm:name importdesc ')' // importdesc ::= '(' 'func' id? exacttypeuse ')' // | '(' 'table' id? tabletype ')' // | '(' 'memory' id? memtype ')' // | '(' 'global' id? globaltype ')' // | '(' 'tag' id? typeuse ')' -template MaybeResult<> import_(Ctx& ctx) { +template +Result<> importdesc(Ctx& ctx, Name mod, Name nm, std::optional id) { auto pos = ctx.in.getPos(); - - if (!ctx.in.takeSExprStart("import"sv)) { - return {}; - } - - auto mod = ctx.in.takeName(); - if (!mod) { - return ctx.in.err("expected import module name"); - } - - auto nm = ctx.in.takeName(); - if (!nm) { - return ctx.in.err("expected import name"); - } - ImportNames names{*mod, *nm}; - + // We only try to parse an ID if `id` is nullopt. + auto getID = [&]() { + if (id) { + return *id; + } + auto parsed = ctx.in.takeID(); + return parsed ? *parsed : Name{}; + }; + ImportNames names{mod, nm}; if (ctx.in.takeSExprStart("func"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto use = exacttypeuse(ctx); CHECK_ERR(use); auto [type, exact] = *use; // TODO: function import annotations - CHECK_ERR(ctx.addFunc( - name ? *name : Name{}, {}, &names, type, exact, std::nullopt, {}, pos)); + CHECK_ERR( + ctx.addFunc(name, {}, &names, type, exact, std::nullopt, {}, pos)); } else if (ctx.in.takeSExprStart("table"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto type = tabletype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addTable( - name ? *name : Name{}, {}, &names, *type, std::nullopt, pos)); + CHECK_ERR(ctx.addTable(name, {}, &names, *type, std::nullopt, pos)); } else if (ctx.in.takeSExprStart("memory"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto type = memtype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addMemory(name ? *name : Name{}, {}, &names, *type, pos)); + CHECK_ERR(ctx.addMemory(name, {}, &names, *type, pos)); } else if (ctx.in.takeSExprStart("global"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto type = globaltype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addGlobal( - name ? *name : Name{}, {}, &names, *type, std::nullopt, pos)); + CHECK_ERR(ctx.addGlobal(name, {}, &names, *type, std::nullopt, pos)); } else if (ctx.in.takeSExprStart("tag"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto type = typeuse(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addTag(name ? *name : Name{}, {}, &names, *type, pos)); + CHECK_ERR(ctx.addTag(name, {}, &names, *type, pos)); } else { return ctx.in.err("expected import description"); } @@ -3485,6 +3478,75 @@ template MaybeResult<> import_(Ctx& ctx) { if (!ctx.in.takeRParen()) { return ctx.in.err("expected end of import description"); } + return Ok{}; +} + +// import ::= '(' 'import' mod:name nm:name importdesc ')' +// | '(' 'import' mod:name (item id? nm:name importdesc)+ ')' +// | '(' 'import' mod:name (item id? nm:name)+ importdesc ')' +template MaybeResult<> import_(Ctx& ctx) { + if (!ctx.in.takeSExprStart("import"sv)) { + return {}; + } + + auto mod = ctx.in.takeName(); + if (!mod) { + return ctx.in.err("expected import module name"); + } + + if (auto nm = ctx.in.takeName()) { + CHECK_ERR(importdesc(ctx, *mod, *nm, std::nullopt)); + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of import"); + } + return Ok{}; + } + + struct CompactItem { + Name id; + Name name; + }; + std::vector items; + std::optional hasSharedImportDesc; + while (ctx.in.takeSExprStart("item"sv)) { + auto id = ctx.in.takeID(); + if (!id) { + // Do not allow parsing an id in the importdesc. + id = Name{}; + } + + auto nm = ctx.in.takeName(); + if (!nm) { + return ctx.in.err("expected import name"); + } + + if (!hasSharedImportDesc.has_value()) { + hasSharedImportDesc = ctx.in.peekRParen(); + } + + if (*hasSharedImportDesc) { + items.emplace_back(*id, *nm); + } else { + CHECK_ERR(importdesc(ctx, *mod, *nm, id)) + } + + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of import item"); + } + } + if (!hasSharedImportDesc.has_value()) { + // There were no items. + return ctx.in.err("expected import name or item"); + } + + if (*hasSharedImportDesc) { + auto pos = ctx.in.getPos(); + for (auto item : items) { + ctx.in.setPos(pos); + CHECK_ERR(importdesc(ctx, *mod, item.name, item.id)); + } + } + if (!ctx.in.takeRParen()) { return ctx.in.err("expected end of import"); } @@ -3515,12 +3577,14 @@ template MaybeResult<> func(Ctx& ctx) { auto import = inlineImport(ctx.in); CHECK_ERR(import); + bool isImport = import || ctx.isFunctionImported(); + typename Ctx::TypeUseT type; Exactness exact = Exact; std::optional localVars; bool skipped = false; - if (import) { + if (isImport) { auto use = exacttypeuse(ctx); CHECK_ERR(use); type = use->first; @@ -3540,7 +3604,7 @@ template MaybeResult<> func(Ctx& ctx) { } } - if ((import || !skipped) && !ctx.in.takeRParen()) { + if ((isImport || !skipped) && !ctx.in.takeRParen()) { return ctx.in.err("expected end of function"); } @@ -3580,6 +3644,8 @@ template MaybeResult<> table(Ctx& ctx) { auto import = inlineImport(ctx.in); CHECK_ERR(import); + bool isImport = import || ctx.isTableImported(); + auto addressType = Type::i32; if (ctx.in.takeKeyword("i64"sv)) { addressType = Type::i64; @@ -3599,7 +3665,7 @@ template MaybeResult<> table(Ctx& ctx) { if (!ctx.in.takeSExprStart("elem"sv)) { return ctx.in.err("expected table limits or inline elements"); } - if (import) { + if (isImport) { return ctx.in.err("imported tables cannot have inline elements"); } @@ -3628,7 +3694,7 @@ template MaybeResult<> table(Ctx& ctx) { auto tabtype = tabletypeContinued(ctx, addressType); CHECK_ERR(tabtype); ttype = *tabtype; - if (ctx.in.peekLParen() && !import) { + if (ctx.in.peekLParen() && !isImport) { // Imported tables cannot have initialization expression. auto e = expr(ctx); CHECK_ERR(e); @@ -3670,6 +3736,8 @@ template MaybeResult<> memory(Ctx& ctx) { auto import = inlineImport(ctx.in); CHECK_ERR(import); + bool isImport = import || ctx.isMemoryImported(); + auto addressType = Type::i32; if (ctx.in.takeKeyword("i64"sv)) { addressType = Type::i64; @@ -3682,7 +3750,7 @@ template MaybeResult<> memory(Ctx& ctx) { MaybeResult mempageSize = mempagesize(ctx); CHECK_ERR(mempageSize); if (ctx.in.takeSExprStart("data"sv)) { - if (import) { + if (isImport) { return ctx.in.err("imported memories cannot have inline data"); } auto datastr = datastring(ctx); @@ -3740,11 +3808,13 @@ template MaybeResult<> global(Ctx& ctx) { auto import = inlineImport(ctx.in); CHECK_ERR(import); + bool isImport = import || ctx.isGlobalImported(); + auto type = globaltype(ctx); CHECK_ERR(type); std::optional exp; - if (!import) { + if (!isImport) { auto e = expr(ctx); CHECK_ERR(e); exp = *e; diff --git a/test/lit/basic/compact-imports.wast b/test/lit/basic/compact-imports.wast new file mode 100644 index 00000000000..52823f9bf21 --- /dev/null +++ b/test/lit/basic/compact-imports.wast @@ -0,0 +1,63 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-opt %s -all -S -o - | filecheck %s + +(module + ;; CHECK: (type $sig1 (func (param i32) (result i32))) + (type $sig1 (func (param i32) (result i32))) + + ;; Compact Encoding 1: per-item import descriptions + (import "env" + (item $f1 "f1" (func (type $sig1))) + (item $f2 "f2" (func (param i32) (result i32))) + (item $g1 "g1" (global i32)) + (item $t1 "t1" (table 1 10 funcref)) + (item $m1 "m1" (memory 1 2)) + ) + + ;; Compact Encoding 2: shared import description + (import "math" + (item $sin "sin") + (item $cos "cos") + (item $tan "tan") + (func (param i32) (result i32)) + ) + + ;; Compact Encoding 2: shared global import description + (import "constants" + (item $pi "pi") + (item $e "e") + (global f64) + ) + + ;; CHECK: (type $1 (func (result i32))) + + ;; CHECK: (import "env" "m1" (memory $m1 1 2)) + + ;; CHECK: (import "env" "t1" (table $t1 1 10 funcref)) + + ;; CHECK: (import "env" "g1" (global $g1 i32)) + + ;; CHECK: (import "constants" "pi" (global $pi f64)) + + ;; CHECK: (import "constants" "e" (global $e f64)) + + ;; CHECK: (import "env" "f1" (func $f1 (type $sig1) (param i32) (result i32))) + + ;; CHECK: (import "env" "f2" (func $f2 (type $sig1) (param i32) (result i32))) + + ;; CHECK: (import "math" "sin" (func $sin (type $sig1) (param i32) (result i32))) + + ;; CHECK: (import "math" "cos" (func $cos (type $sig1) (param i32) (result i32))) + + ;; CHECK: (import "math" "tan" (func $tan (type $sig1) (param i32) (result i32))) + + ;; CHECK: (func $main (type $1) (result i32) + ;; CHECK-NEXT: (call $f1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $main (result i32) + (call $f1 (i32.const 1)) + ) +) From e401ce075d4f08fd550c74e696a29bfece0dc3ff Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 22 Jul 2026 17:09:37 -0700 Subject: [PATCH 2/3] [NFC] Track grammar of definitions in parser When e.g. a function is declared in the text format, it can either be with a normal `func` production (which may or may not have an inline import declaration) or it can be an `import` production. Previously the parser would just store the location of the declaration but not the kind of production it used. In later parser phases where the declaration has to be parsed again, we would just try both productions to see which one worked. This is rather hacky and brittle and will not play nicely with the text format for compact imports, so refactor to explicitly track how the declaration should be parsed. --- src/parser/context-decls.cpp | 31 ++++++++------ src/parser/context-defs.cpp | 6 ++- src/parser/contexts.h | 71 ++++++++++++++++++++++---------- src/parser/parse-5-defs.cpp | 8 ++-- src/parser/parsers.h | 50 ++++++++++++++++------ src/parser/wat-parser-internal.h | 8 ++-- 6 files changed, 119 insertions(+), 55 deletions(-) diff --git a/src/parser/context-decls.cpp b/src/parser/context-decls.cpp index 0298f1cee9a..2174b1bad83 100644 --- a/src/parser/context-decls.cpp +++ b/src/parser/context-decls.cpp @@ -71,13 +71,14 @@ Result<> ParseDeclsCtx::addFunc(Name name, Exactness exact, std::optional, std::vector&& annotations, - Index pos) { + Index pos, + DefKind kind) { CHECK_ERR(checkImport(pos, import)); auto f = addFuncDecl(pos, name, import); CHECK_ERR(f); CHECK_ERR(addExports(in, wasm, *f, exports, ExternalKind::Function)); funcDefs.push_back( - {name, pos, Index(funcDefs.size()), std::move(annotations)}); + {name, pos, Index(funcDefs.size()), std::move(annotations), kind}); return Ok{}; } @@ -110,13 +111,14 @@ Result<> ParseDeclsCtx::addTable(Name name, ImportNames* import, TableType type, std::optional, - Index pos) { + Index pos, + DefKind kind) { CHECK_ERR(checkImport(pos, import)); auto t = addTableDecl(pos, name, import, type); CHECK_ERR(t); CHECK_ERR(addExports(in, wasm, *t, exports, ExternalKind::Table)); // TODO: table annotations - tableDefs.push_back({name, pos, Index(tableDefs.size()), {}}); + tableDefs.push_back({name, pos, Index(tableDefs.size()), {}, kind}); return Ok{}; } @@ -167,13 +169,14 @@ Result<> ParseDeclsCtx::addMemory(Name name, const std::vector& exports, ImportNames* import, MemType type, - Index pos) { + Index pos, + DefKind kind) { CHECK_ERR(checkImport(pos, import)); auto m = addMemoryDecl(pos, name, import, type); CHECK_ERR(m); CHECK_ERR(addExports(in, wasm, *m, exports, ExternalKind::Memory)); // TODO: memory annotations - memoryDefs.push_back({name, pos, Index(memoryDefs.size()), {}}); + memoryDefs.push_back({name, pos, Index(memoryDefs.size()), {}, kind}); return Ok{}; } @@ -213,13 +216,14 @@ Result<> ParseDeclsCtx::addGlobal(Name name, ImportNames* import, GlobalTypeT, std::optional, - Index pos) { + Index pos, + DefKind kind) { CHECK_ERR(checkImport(pos, import)); auto g = addGlobalDecl(pos, name, import); CHECK_ERR(g); CHECK_ERR(addExports(in, wasm, *g, exports, ExternalKind::Global)); // TODO: global annotations - globalDefs.push_back({name, pos, Index(globalDefs.size()), {}}); + globalDefs.push_back({name, pos, Index(globalDefs.size()), {}, kind}); return Ok{}; } @@ -239,7 +243,8 @@ Result<> ParseDeclsCtx::addElem( e->name = name; } // TODO: element segment annotations - elemDefs.push_back({name, pos, Index(wasm.elementSegments.size()), {}}); + elemDefs.push_back( + {name, pos, Index(wasm.elementSegments.size()), {}, DefKind::Definition}); wasm.addElementSegment(std::move(e)); return Ok{}; } @@ -264,7 +269,8 @@ Result<> ParseDeclsCtx::addData(Name name, } d->data = std::move(data); // TODO: data segment annotations - dataDefs.push_back({name, pos, Index(wasm.dataSegments.size()), {}}); + dataDefs.push_back( + {name, pos, Index(wasm.dataSegments.size()), {}, DefKind::Definition}); wasm.addDataSegment(std::move(d)); return Ok{}; } @@ -292,13 +298,14 @@ Result<> ParseDeclsCtx::addTag(Name name, const std::vector& exports, ImportNames* import, TypeUseT type, - Index pos) { + Index pos, + DefKind kind) { CHECK_ERR(checkImport(pos, import)); auto t = addTagDecl(pos, name, import); CHECK_ERR(t); CHECK_ERR(addExports(in, wasm, *t, exports, ExternalKind::Tag)); // TODO: tag annotations - tagDefs.push_back({name, pos, Index(tagDefs.size()), {}}); + tagDefs.push_back({name, pos, Index(tagDefs.size()), {}, kind}); return Ok{}; } diff --git a/src/parser/context-defs.cpp b/src/parser/context-defs.cpp index d0d170ed2c5..f191ea638a9 100644 --- a/src/parser/context-defs.cpp +++ b/src/parser/context-defs.cpp @@ -55,7 +55,8 @@ Result<> ParseDefsCtx::addGlobal(Name, ImportNames*, GlobalTypeT, std::optional exp, - Index) { + Index, + DefKind) { if (exp) { wasm.globals[index]->init = *exp; } @@ -67,7 +68,8 @@ Result<> ParseDefsCtx::addTable(Name, ImportNames*, TableTypeT, std::optional init, - Index) { + Index, + DefKind) { if (init) { wasm.tables[index]->init = *init; } diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 31a3aa253e3..0a403cf1073 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -63,6 +63,11 @@ struct TableType { Limits limits; }; +enum class DefKind { + ImportDesc, + Definition, +}; + // The location, possible name, and index in the respective module index space // of a module-level definition in the input. struct DefPos { @@ -70,6 +75,7 @@ struct DefPos { Index pos; Index index; std::vector annotations; + DefKind kind; }; struct GlobalType { @@ -1074,13 +1080,15 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { void setSupertype(HeapTypeT) {} void finishTypeDef(Name name, Index pos) { // TODO: type annotations - typeDefs.push_back({name, pos, Index(typeDefs.size()), {}}); + typeDefs.push_back( + {name, pos, Index(typeDefs.size()), {}, DefKind::Definition}); } size_t getRecGroupStartIndex() { return 0; } void addRecGroup(Index, size_t) {} void finishRectype(Index pos) { // TODO: type annotations - recTypeDefs.push_back({{}, pos, Index(recTypeDefs.size()), {}}); + recTypeDefs.push_back( + {{}, pos, Index(recTypeDefs.size()), {}, DefKind::Definition}); } bool skipFunctionBody(); @@ -1135,7 +1143,8 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { Exactness exact, std::optional, std::vector&&, - Index pos); + Index pos, + DefKind kind); Result addTableDecl(Index pos, Name name, @@ -1146,7 +1155,8 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { ImportNames*, TableType, std::optional, - Index); + Index, + DefKind kind); // TODO: Record index of implicit elem for use when parsing types and instrs. Result<> addImplicitElems(TypeT, ElemListT&& elems); @@ -1158,7 +1168,8 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { const std::vector& exports, ImportNames* import, MemType type, - Index pos); + Index pos, + DefKind kind); Result<> addImplicitData(DataStringT&& data); @@ -1169,14 +1180,15 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { ImportNames* import, GlobalTypeT, std::optional, - Index pos); + Index pos, + DefKind kind); Result<> addStart(FuncIdxT, Index pos) { if (!startDefs.empty()) { return Err{"unexpected extra 'start' function"}; } // TODO: start function annotations. - startDefs.push_back({{}, pos, 0, {}}); + startDefs.push_back({{}, pos, 0, {}, DefKind::Definition}); return Ok{}; } @@ -1196,7 +1208,8 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { const std::vector& exports, ImportNames* import, TypeUseT type, - Index pos); + Index pos, + DefKind kind); Result<> addExport(Index pos, Ok, Name, ExternalKind) { exportDefs.push_back(pos); @@ -1526,7 +1539,8 @@ struct ParseModuleTypesCtx : TypeParserCtx, Exactness exact, std::optional locals, std::vector&& annotations, - Index pos) { + Index pos, + DefKind kind) { auto& f = wasm.functions[index]; if (!type.type.isSignature()) { return in.err(pos, "expected signature type"); @@ -1556,7 +1570,8 @@ struct ParseModuleTypesCtx : TypeParserCtx, ImportNames*, Type ttype, std::optional init, - Index pos) { + Index pos, + DefKind kind) { auto& t = wasm.tables[index]; if (!ttype.isRef()) { return in.err(pos, "expected reference type"); @@ -1572,8 +1587,12 @@ struct ParseModuleTypesCtx : TypeParserCtx, return Ok{}; } - Result<> - addMemory(Name, const std::vector&, ImportNames*, MemTypeT, Index) { + Result<> addMemory(Name, + const std::vector&, + ImportNames*, + MemTypeT, + Index, + DefKind kind) { return Ok{}; } @@ -1584,7 +1603,8 @@ struct ParseModuleTypesCtx : TypeParserCtx, ImportNames*, GlobalType type, std::optional, - Index) { + Index, + DefKind kind) { auto& g = wasm.globals[index]; g->mutable_ = type.mutability; g->type = type.type; @@ -1600,8 +1620,12 @@ struct ParseModuleTypesCtx : TypeParserCtx, Result<> addDeclareElem(Name, ElemListT&&, Index) { return Ok{}; } - Result<> - addTag(Name, const std::vector&, ImportNames*, TypeUse use, Index pos) { + Result<> addTag(Name, + const std::vector&, + ImportNames*, + TypeUse use, + Index pos, + DefKind kind) { auto& t = wasm.tags[index]; if (!use.type.isSignature()) { return in.err(pos, "tag type must be a signature"); @@ -1916,7 +1940,8 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { Exactness, std::optional, std::vector&&, - Index) { + Index, + DefKind) { return Ok{}; } @@ -1925,10 +1950,11 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { ImportNames*, TableTypeT, std::optional, - Index); + Index, + DefKind); - Result<> - addMemory(Name, const std::vector&, ImportNames*, TableTypeT, Index) { + Result<> addMemory( + Name, const std::vector&, ImportNames*, TableTypeT, Index, DefKind) { return Ok{}; } @@ -1937,7 +1963,8 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { ImportNames*, GlobalTypeT, std::optional exp, - Index); + Index, + DefKind); Result<> addStart(Name name, Index pos) { wasm.start = name; @@ -1961,8 +1988,8 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { Result<> addData(Name, Name* mem, std::optional offset, DataStringT, Index pos); - Result<> - addTag(Name, const std::vector, ImportNames*, TypeUseT, Index) { + Result<> addTag( + Name, const std::vector, ImportNames*, TypeUseT, Index, DefKind) { return Ok{}; } diff --git a/src/parser/parse-5-defs.cpp b/src/parser/parse-5-defs.cpp index 82909ea1a16..14ad9ffcdd5 100644 --- a/src/parser/parse-5-defs.cpp +++ b/src/parser/parse-5-defs.cpp @@ -49,12 +49,14 @@ Result<> parseDefinitions( if (!f->imported()) { CHECK_ERR(ctx.visitFunctionStart(f)); } - if (auto parsed = func(ctx)) { - CHECK_ERR(parsed); - } else { + if (decls.funcDefs[i].kind == DefKind::ImportDesc) { auto im = import_(ctx); assert(im); CHECK_ERR(im); + } else { + auto parsed = func(ctx); + assert(parsed); + CHECK_ERR(parsed); } if (!f->imported()) { auto end = ctx.irBuilder.visitEnd(); diff --git a/src/parser/parsers.h b/src/parser/parsers.h index d6f2297ff57..8c43fb3e30c 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -3454,30 +3454,49 @@ template MaybeResult<> import_(Ctx& ctx) { CHECK_ERR(use); auto [type, exact] = *use; // TODO: function import annotations - CHECK_ERR(ctx.addFunc( - name ? *name : Name{}, {}, &names, type, exact, std::nullopt, {}, pos)); + CHECK_ERR(ctx.addFunc(name ? *name : Name{}, + {}, + &names, + type, + exact, + std::nullopt, + {}, + pos, + DefKind::ImportDesc)); } else if (ctx.in.takeSExprStart("table"sv)) { auto name = ctx.in.takeID(); auto type = tabletype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addTable( - name ? *name : Name{}, {}, &names, *type, std::nullopt, pos)); + CHECK_ERR(ctx.addTable(name ? *name : Name{}, + {}, + &names, + *type, + std::nullopt, + pos, + DefKind::ImportDesc)); } else if (ctx.in.takeSExprStart("memory"sv)) { auto name = ctx.in.takeID(); auto type = memtype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addMemory(name ? *name : Name{}, {}, &names, *type, pos)); + CHECK_ERR(ctx.addMemory( + name ? *name : Name{}, {}, &names, *type, pos, DefKind::ImportDesc)); } else if (ctx.in.takeSExprStart("global"sv)) { auto name = ctx.in.takeID(); auto type = globaltype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addGlobal( - name ? *name : Name{}, {}, &names, *type, std::nullopt, pos)); + CHECK_ERR(ctx.addGlobal(name ? *name : Name{}, + {}, + &names, + *type, + std::nullopt, + pos, + DefKind::ImportDesc)); } else if (ctx.in.takeSExprStart("tag"sv)) { auto name = ctx.in.takeID(); auto type = typeuse(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addTag(name ? *name : Name{}, {}, &names, *type, pos)); + CHECK_ERR(ctx.addTag( + name ? *name : Name{}, {}, &names, *type, pos, DefKind::ImportDesc)); } else { return ctx.in.err("expected import description"); } @@ -3551,7 +3570,8 @@ template MaybeResult<> func(Ctx& ctx) { exact, localVars, std::move(annotations), - pos)); + pos, + DefKind::Definition)); return Ok{}; } @@ -3640,7 +3660,8 @@ template MaybeResult<> table(Ctx& ctx) { return ctx.in.err("expected end of table declaration"); } - CHECK_ERR(ctx.addTable(name, *exports, import.getPtr(), *ttype, init, pos)); + CHECK_ERR(ctx.addTable( + name, *exports, import.getPtr(), *ttype, init, pos, DefKind::Definition)); if (elems) { CHECK_ERR(ctx.addImplicitElems(*type, std::move(*elems))); @@ -3711,7 +3732,8 @@ template MaybeResult<> memory(Ctx& ctx) { return ctx.in.err("expected end of memory declaration"); } - CHECK_ERR(ctx.addMemory(name, *exports, import.getPtr(), *mtype, pos)); + CHECK_ERR(ctx.addMemory( + name, *exports, import.getPtr(), *mtype, pos, DefKind::Definition)); if (data) { CHECK_ERR(ctx.addImplicitData(std::move(*data))); @@ -3754,7 +3776,8 @@ template MaybeResult<> global(Ctx& ctx) { return ctx.in.err("expected end of global"); } - CHECK_ERR(ctx.addGlobal(name, *exports, import.getPtr(), *type, exp, pos)); + CHECK_ERR(ctx.addGlobal( + name, *exports, import.getPtr(), *type, exp, pos, DefKind::Definition)); return Ok{}; } @@ -4027,7 +4050,8 @@ template MaybeResult<> tag(Ctx& ctx) { return ctx.in.err("expected end of tag"); } - CHECK_ERR(ctx.addTag(name, *exports, import.getPtr(), *type, pos)); + CHECK_ERR(ctx.addTag( + name, *exports, import.getPtr(), *type, pos, DefKind::Definition)); return Ok{}; } diff --git a/src/parser/wat-parser-internal.h b/src/parser/wat-parser-internal.h index 2f4ecc7c3e1..9219b62012c 100644 --- a/src/parser/wat-parser-internal.h +++ b/src/parser/wat-parser-internal.h @@ -81,12 +81,14 @@ Result<> parseDefs(Ctx& ctx, ctx.index = def.index; WithPosition with(ctx, def.pos); ctx.in.setAnnotations(def.annotations); - if (auto parsed = parser(ctx)) { - CHECK_ERR(parsed); - } else { + if (def.kind == DefKind::ImportDesc) { auto im = import_(ctx); assert(im); CHECK_ERR(im); + } else { + auto parsed = parser(ctx); + assert(parsed); + CHECK_ERR(parsed); } } return Ok{}; From cb734a3d3fa6de357da1dcbe24702e47744a784d Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 23 Jul 2026 18:18:14 -0700 Subject: [PATCH 3/3] remove incorrect assertion --- src/parser/wat-parser-internal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser/wat-parser-internal.h b/src/parser/wat-parser-internal.h index 4317890f9fb..33528701be5 100644 --- a/src/parser/wat-parser-internal.h +++ b/src/parser/wat-parser-internal.h @@ -83,7 +83,6 @@ Result<> parseDefs(Ctx& ctx, ctx.in.setAnnotations(def.annotations); if (def.kind == DefKind::ImportDesc) { auto im = importdesc(ctx, Name{}, Name{}, std::nullopt); - assert(!im.getErr()); CHECK_ERR(im); } else { auto parsed = parser(ctx);