Coverage Report

Created: 2026-08-14 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/validator/component_validator.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "common/errinfo.h"
5
#include "common/spdlog.h"
6
#include "validator/component_name.h"
7
#include "validator/validator.h"
8
9
#include <algorithm>
10
#include <unordered_set>
11
#include <variant>
12
13
namespace WasmEdge {
14
namespace Validator {
15
16
using namespace std::literals;
17
18
namespace {
19
2.75k
std::string toLowerStr(std::string_view SV) {
20
2.75k
  std::string Result(SV);
21
2.75k
  std::transform(
22
2.75k
      Result.begin(), Result.end(), Result.begin(),
23
5.44k
      [](unsigned char C) { return static_cast<char>(std::tolower(C)); });
24
2.75k
  return Result;
25
2.75k
}
26
27
// Maps a component-side ExternDesc::DescType to its Sort::SortType.
28
// Returns nullopt for `CoreType` (= `(core module (type i))`), which has no
29
// representation in Sort::SortType; callers must handle that sort separately
30
// via Sort::CoreSortType::Module.
31
std::optional<AST::Component::Sort::SortType>
32
281
descTypeToSortType(AST::Component::ExternDesc::DescType DT) noexcept {
33
281
  switch (DT) {
34
0
  case AST::Component::ExternDesc::DescType::CoreType:
35
0
    return std::nullopt;
36
19
  case AST::Component::ExternDesc::DescType::FuncType:
37
19
    return AST::Component::Sort::SortType::Func;
38
242
  case AST::Component::ExternDesc::DescType::ValueBound:
39
242
    return AST::Component::Sort::SortType::Value;
40
19
  case AST::Component::ExternDesc::DescType::TypeBound:
41
19
    return AST::Component::Sort::SortType::Type;
42
0
  case AST::Component::ExternDesc::DescType::ComponentType:
43
0
    return AST::Component::Sort::SortType::Component;
44
1
  case AST::Component::ExternDesc::DescType::InstanceType:
45
1
    return AST::Component::Sort::SortType::Instance;
46
0
  default:
47
0
    assumingUnreachable();
48
281
  }
49
281
}
50
51
// Shallow sort-kind match between a Sort and an ExternDesc. The spec's
52
// instantiation / export-ascription rules require the supplied sortidx to be
53
// a subtype of the externdesc. Here we only enforce that the kind agrees;
54
// deep structural subtyping (record fields, func signatures, etc.) is not
55
// yet implemented and is the main remaining correctness gap at these sites.
56
bool sortMatchesDescType(const AST::Component::Sort &S,
57
39
                         AST::Component::ExternDesc::DescType DT) noexcept {
58
39
  auto Mapped = descTypeToSortType(DT);
59
39
  if (S.isCore()) {
60
1
    return !Mapped.has_value() &&
61
0
           S.getCoreSortType() == AST::Component::Sort::CoreSortType::Module;
62
1
  }
63
38
  return Mapped.has_value() && S.getSortType() == *Mapped;
64
39
}
65
66
// Fallback type-index lookup against an InstanceType's own local
67
// type-decl space (used when the outer ComponentContext scope doesn't
68
// own the InstanceType).
69
const AST::Component::InstanceType *
70
resolveNestedInstanceType(const AST::Component::InstanceType &Parent,
71
0
                          uint32_t TypeIdx) noexcept {
72
0
  uint32_t LocalIdx = 0;
73
0
  for (const auto &LocalDecl : Parent.getDecl()) {
74
0
    if (!LocalDecl.isType()) {
75
0
      continue;
76
0
    }
77
0
    if (LocalIdx == TypeIdx) {
78
0
      const auto *LocalDT = LocalDecl.getType();
79
0
      if (LocalDT != nullptr && LocalDT->isInstanceType()) {
80
0
        return &LocalDT->getInstanceType();
81
0
      }
82
0
      return nullptr;
83
0
    }
84
0
    LocalIdx++;
85
0
  }
86
0
  return nullptr;
87
0
}
88
89
// Resolve a type index in `Comp`'s own type index space to an InstanceType.
90
// Returns nullptr when the index does not refer to an inline InstanceType
91
// definition — callers treat nullptr as "no required shape" and fall back
92
// to inferred exports. TypeBound imports and outer-alias type imports
93
// currently fall through to nullptr; a more complete resolver would walk
94
// the alias chain to recover the underlying InstanceType.
95
const AST::Component::InstanceType *
96
resolveChildInstanceType(const AST::Component::Component &Comp,
97
0
                         uint32_t TypeIdx) {
98
0
  uint32_t CurrentIdx = 0;
99
0
  for (const auto &Sec : Comp.getSections()) {
100
0
    if (std::holds_alternative<AST::Component::TypeSection>(Sec)) {
101
0
      const auto &TSec = std::get<AST::Component::TypeSection>(Sec);
102
0
      for (const auto &DT : TSec.getContent()) {
103
0
        if (CurrentIdx == TypeIdx) {
104
0
          if (DT.isInstanceType()) {
105
0
            return &DT.getInstanceType();
106
0
          }
107
0
          return nullptr;
108
0
        }
109
0
        CurrentIdx++;
110
0
      }
111
0
    } else if (std::holds_alternative<AST::Component::ImportSection>(Sec)) {
112
0
      const auto &ISec = std::get<AST::Component::ImportSection>(Sec);
113
0
      for (const auto &Import : ISec.getContent()) {
114
0
        if (Import.getDesc().getDescType() ==
115
0
            AST::Component::ExternDesc::DescType::TypeBound) {
116
0
          if (CurrentIdx == TypeIdx) {
117
0
            return nullptr;
118
0
          }
119
0
          CurrentIdx++;
120
0
        }
121
0
      }
122
0
    } else if (std::holds_alternative<AST::Component::AliasSection>(Sec)) {
123
0
      const auto &ASec = std::get<AST::Component::AliasSection>(Sec);
124
0
      for (const auto &Alias : ASec.getContent()) {
125
0
        if (!Alias.getSort().isCore() &&
126
0
            Alias.getSort().getSortType() ==
127
0
                AST::Component::Sort::SortType::Type) {
128
0
          if (CurrentIdx == TypeIdx) {
129
0
            return nullptr;
130
0
          }
131
0
          CurrentIdx++;
132
0
        }
133
0
      }
134
0
    }
135
0
  }
136
0
  return nullptr;
137
0
}
138
139
// Validate that a name may appear at an export position: reject the
140
// `relative-url=` prefix (not part of the extern-name grammar) and any
141
// plainname/interfacename kind that isn't allowed on an export.
142
1.13k
Expect<ComponentName> validateExportName(std::string_view Name) noexcept {
143
1.13k
  if (Name.rfind("relative-url="sv, 0) == 0) {
144
0
    spdlog::error(ErrCode::Value::InvalidExternName);
145
0
    spdlog::error("    Export name '{}' is not a valid extern name"sv, Name);
146
0
    return Unexpect(ErrCode::Value::InvalidExternName);
147
0
  }
148
2.18k
  EXPECTED_TRY(ComponentName CName, ComponentName::parse(Name));
149
2.18k
  switch (CName.getKind()) {
150
383
  case ComponentNameKind::Label:
151
703
  case ComponentNameKind::Constructor:
152
703
  case ComponentNameKind::Method:
153
756
  case ComponentNameKind::Static:
154
1.04k
  case ComponentNameKind::InterfaceType:
155
1.04k
    return CName;
156
2
  default:
157
2
    spdlog::error(ErrCode::Value::InvalidExportName);
158
2
    spdlog::error("    Export name '{}' kind is not valid for exports"sv, Name);
159
2
    return Unexpect(ErrCode::Value::InvalidExportName);
160
2.18k
  }
161
2.18k
}
162
163
} // namespace
164
165
void Validator::populateInstanceFromType(
166
107
    uint32_t InstIdx, const AST::Component::InstanceType &IT) noexcept {
167
107
  for (const auto &Decl : IT.getDecl()) {
168
16
    if (!Decl.isExportDecl()) {
169
3
      continue;
170
3
    }
171
13
    const auto &Exp = Decl.getExport();
172
13
    const auto &ED = Exp.getExternDesc();
173
13
    auto ST = descTypeToSortType(ED.getDescType());
174
13
    if (!ST.has_value()) {
175
      // InstanceExport::ST has no `(core module)` variant — skip rather
176
      // than crash. TODO: extend InstanceExport with a core-sort alternative.
177
0
      spdlog::debug(
178
0
          "    populateInstanceFromType: skipping `(core module)` export "
179
0
          "'{}'"sv,
180
0
          Exp.getName());
181
0
      continue;
182
0
    }
183
13
    const AST::Component::InstanceType *NestedIT = nullptr;
184
13
    if (ED.getDescType() ==
185
13
        AST::Component::ExternDesc::DescType::InstanceType) {
186
0
      NestedIT = CompCtx.getInstanceType(ED.getTypeIndex());
187
0
      if (NestedIT == nullptr) {
188
0
        NestedIT = resolveNestedInstanceType(IT, ED.getTypeIndex());
189
0
      }
190
0
    }
191
    // Resource-typed exports carry a canonical id so an alias-export of
192
    // the type slot can preserve identity. `(sub resource)` introduces a
193
    // fresh id; `(eq i)` should inherit, but cross-scope resource lookup
194
    // inside an InstanceType body is not yet implemented (TODO).
195
13
    std::optional<uint64_t> ResourceId;
196
13
    if (ED.getDescType() == AST::Component::ExternDesc::DescType::TypeBound &&
197
0
        !ED.isEqType()) {
198
0
      ResourceId = CompCtx.allocateFreshResourceId();
199
0
    }
200
13
    CompCtx.addInstanceExport(InstIdx, Exp.getName(), *ST, NestedIT,
201
13
                              /*NestedInstIdx=*/std::nullopt, ResourceId);
202
13
  }
203
107
}
204
205
bool Validator::exportSatisfies(
206
    const AST::Component::InstanceType &RequiredCtx,
207
    const ComponentContext::InstanceExport &Provided,
208
0
    const AST::Component::ExternDesc &Required) const noexcept {
209
0
  auto RequiredST = descTypeToSortType(Required.getDescType());
210
0
  if (!RequiredST.has_value()) {
211
    // `(core module)` has no InstanceExport::ST variant — no constraint.
212
0
    return true;
213
0
  }
214
0
  if (Provided.ST != *RequiredST) {
215
0
    return false;
216
0
  }
217
  // Instance-on-instance: recurse if both sides resolve to an
218
  // InstanceType. Otherwise sort-kind match (pre-Phase-3 behaviour).
219
0
  if (Required.getDescType() ==
220
0
          AST::Component::ExternDesc::DescType::InstanceType &&
221
0
      Provided.IT != nullptr) {
222
0
    const auto *RequiredIT = CompCtx.getInstanceType(Required.getTypeIndex());
223
0
    if (RequiredIT == nullptr) {
224
      // Required's idx lives in RequiredCtx's local type-decls.
225
0
      RequiredIT =
226
0
          resolveNestedInstanceType(RequiredCtx, Required.getTypeIndex());
227
0
    }
228
0
    if (RequiredIT != nullptr) {
229
0
      return isInstanceSubtype(*Provided.IT, *RequiredIT);
230
0
    }
231
0
  }
232
0
  return true;
233
0
}
234
235
std::optional<std::string> Validator::findMissingRequiredExport(
236
    uint32_t ProvidedInstIdx,
237
0
    const AST::Component::InstanceType &RequiredIT) const noexcept {
238
0
  const auto &Exports = CompCtx.getInstance(ProvidedInstIdx).Exports;
239
0
  for (const auto &Decl : RequiredIT.getDecl()) {
240
0
    if (!Decl.isExportDecl()) {
241
0
      continue;
242
0
    }
243
0
    const auto &Exp = Decl.getExport();
244
0
    auto It = Exports.find(std::string(Exp.getName()));
245
0
    if (It == Exports.end()) {
246
0
      return std::string(Exp.getName());
247
0
    }
248
0
    if (!exportSatisfies(RequiredIT, It->second, Exp.getExternDesc())) {
249
0
      return std::string(Exp.getName());
250
0
    }
251
0
  }
252
0
  return std::nullopt;
253
0
}
254
255
bool Validator::isInstanceSubtype(
256
    const AST::Component::InstanceType &S,
257
0
    const AST::Component::InstanceType &T) const noexcept {
258
  // S subtype T iff every export declared by T is present in S with a
259
  // satisfying type. Build a quick (name → externdesc) lookup of S's
260
  // exports for the lookup.
261
0
  std::unordered_map<std::string, const AST::Component::ExternDesc *> SExports;
262
0
  for (const auto &Decl : S.getDecl()) {
263
0
    if (Decl.isExportDecl()) {
264
0
      const auto &E = Decl.getExport();
265
0
      SExports.emplace(std::string(E.getName()), &E.getExternDesc());
266
0
    }
267
0
  }
268
0
  for (const auto &Decl : T.getDecl()) {
269
0
    if (!Decl.isExportDecl()) {
270
0
      continue;
271
0
    }
272
0
    const auto &E = Decl.getExport();
273
0
    auto It = SExports.find(std::string(E.getName()));
274
0
    if (It == SExports.end()) {
275
0
      return false;
276
0
    }
277
0
    auto SKind = descTypeToSortType(It->second->getDescType());
278
0
    auto TKind = descTypeToSortType(E.getExternDesc().getDescType());
279
0
    if (SKind != TKind) {
280
0
      return false;
281
0
    }
282
    // Instance-on-instance: nested type indices on each side belong to
283
    // that side's own decls, so fall back via resolveNestedInstanceType.
284
0
    if (E.getExternDesc().getDescType() ==
285
0
        AST::Component::ExternDesc::DescType::InstanceType) {
286
0
      const auto *SubIT = CompCtx.getInstanceType(It->second->getTypeIndex());
287
0
      if (SubIT == nullptr) {
288
0
        SubIT = resolveNestedInstanceType(S, It->second->getTypeIndex());
289
0
      }
290
0
      const auto *ReqIT =
291
0
          CompCtx.getInstanceType(E.getExternDesc().getTypeIndex());
292
0
      if (ReqIT == nullptr) {
293
0
        ReqIT = resolveNestedInstanceType(T, E.getExternDesc().getTypeIndex());
294
0
      }
295
0
      if (SubIT != nullptr && ReqIT != nullptr &&
296
0
          !isInstanceSubtype(*SubIT, *ReqIT)) {
297
0
        return false;
298
0
      }
299
0
    }
300
0
  }
301
0
  return true;
302
0
}
303
304
Expect<void>
305
4.36k
Validator::validate(const AST::Component::Component &Comp) noexcept {
306
4.36k
  spdlog::warn("Component Model Validation is in active development."sv);
307
4.36k
  CompCtx.reset();
308
4.36k
  return validateComponent(Comp).and_then([&]() {
309
2.17k
    const_cast<AST::Component::Component &>(Comp).setIsValidated();
310
2.17k
    return Expect<void>{};
311
2.17k
  });
312
4.36k
}
313
314
Expect<void>
315
9.08k
Validator::validateComponent(const AST::Component::Component &Comp) noexcept {
316
  // Validation enters a fresh component scope and walks sections in their
317
  // binary order. The per-sort index spaces are built incrementally as
318
  // definitions are validated, so sortidx references in later sections
319
  // only resolve against entries already introduced. Custom sections are
320
  // ignored. Nested components recurse through this same function.
321
9.08k
  auto ReportError = [](auto E) {
322
2.20k
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Component));
323
2.20k
    return E;
324
2.20k
  };
325
326
9.08k
  CompCtx.enterComponent(&Comp);
327
4.35M
  for (const auto &Sec : Comp.getSections()) {
328
4.35M
    auto Func = [&](auto &&S) -> Expect<void> {
329
4.35M
      using T = std::decay_t<decltype(S)>;
330
4.35M
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
2.39M
      } else {
333
2.39M
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
2.39M
      }
335
2.38M
      return {};
336
4.35M
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::CustomSection const&>(WasmEdge::AST::CustomSection const&) const
Line
Count
Source
328
1.95M
    auto Func = [&](auto &&S) -> Expect<void> {
329
1.95M
      using T = std::decay_t<decltype(S)>;
330
1.95M
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
      } else {
333
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
      }
335
1.95M
      return {};
336
1.95M
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::CoreModuleSection const&>(WasmEdge::AST::Component::CoreModuleSection const&) const
Line
Count
Source
328
1.38k
    auto Func = [&](auto &&S) -> Expect<void> {
329
1.38k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
1.38k
      } else {
333
1.38k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
1.38k
      }
335
1.36k
      return {};
336
1.38k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::CoreInstanceSection const&>(WasmEdge::AST::Component::CoreInstanceSection const&) const
Line
Count
Source
328
17.2k
    auto Func = [&](auto &&S) -> Expect<void> {
329
17.2k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
17.2k
      } else {
333
17.2k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
17.2k
      }
335
17.1k
      return {};
336
17.2k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::CoreTypeSection const&>(WasmEdge::AST::Component::CoreTypeSection const&) const
Line
Count
Source
328
375k
    auto Func = [&](auto &&S) -> Expect<void> {
329
375k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
375k
      } else {
333
375k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
375k
      }
335
375k
      return {};
336
375k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::ComponentSection const&>(WasmEdge::AST::Component::ComponentSection const&) const
Line
Count
Source
328
4.71k
    auto Func = [&](auto &&S) -> Expect<void> {
329
4.71k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
4.71k
      } else {
333
4.71k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
4.71k
      }
335
4.70k
      return {};
336
4.71k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::InstanceSection const&>(WasmEdge::AST::Component::InstanceSection const&) const
Line
Count
Source
328
156k
    auto Func = [&](auto &&S) -> Expect<void> {
329
156k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
156k
      } else {
333
156k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
156k
      }
335
155k
      return {};
336
156k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::AliasSection const&>(WasmEdge::AST::Component::AliasSection const&) const
Line
Count
Source
328
7.64k
    auto Func = [&](auto &&S) -> Expect<void> {
329
7.64k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
7.64k
      } else {
333
7.64k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
7.64k
      }
335
7.45k
      return {};
336
7.64k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::TypeSection const&>(WasmEdge::AST::Component::TypeSection const&) const
Line
Count
Source
328
1.77M
    auto Func = [&](auto &&S) -> Expect<void> {
329
1.77M
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
1.77M
      } else {
333
1.77M
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
1.77M
      }
335
1.77M
      return {};
336
1.77M
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::CanonSection const&>(WasmEdge::AST::Component::CanonSection const&) const
Line
Count
Source
328
30.5k
    auto Func = [&](auto &&S) -> Expect<void> {
329
30.5k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
30.5k
      } else {
333
30.5k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
30.5k
      }
335
30.2k
      return {};
336
30.5k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::StartSection const&>(WasmEdge::AST::Component::StartSection const&) const
Line
Count
Source
328
1.12k
    auto Func = [&](auto &&S) -> Expect<void> {
329
1.12k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
1.12k
      } else {
333
1.12k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
1.12k
      }
335
1.06k
      return {};
336
1.12k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::ImportSection const&>(WasmEdge::AST::Component::ImportSection const&) const
Line
Count
Source
328
19.7k
    auto Func = [&](auto &&S) -> Expect<void> {
329
19.7k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
19.7k
      } else {
333
19.7k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
19.7k
      }
335
18.8k
      return {};
336
19.7k
    };
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validateComponent(WasmEdge::AST::Component::Component const&)::$_0::operator()<WasmEdge::AST::Component::ExportSection const&>(WasmEdge::AST::Component::ExportSection const&) const
Line
Count
Source
328
2.40k
    auto Func = [&](auto &&S) -> Expect<void> {
329
2.40k
      using T = std::decay_t<decltype(S)>;
330
      if constexpr (std::is_same_v<T, AST::CustomSection>) {
331
        // Always pass validation.
332
2.40k
      } else {
333
2.40k
        EXPECTED_TRY(validate(S).map_error(ReportError));
334
2.40k
      }
335
2.27k
      return {};
336
2.40k
    };
337
4.35M
    EXPECTED_TRY(std::visit(Func, Sec));
338
4.35M
  }
339
6.87k
  CompCtx.exitComponent();
340
6.87k
  return {};
341
9.08k
}
342
343
Expect<void>
344
1.38k
Validator::validate(const AST::Component::CoreModuleSection &ModSec) noexcept {
345
1.38k
  EXPECTED_TRY(validate(ModSec.getContent()).map_error([](auto E) {
346
1.36k
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreMod));
347
1.36k
    return E;
348
1.36k
  }));
349
1.36k
  const_cast<AST::Module &>(ModSec.getContent()).setIsValidated();
350
1.36k
  CompCtx.addCoreModule(ModSec.getContent());
351
1.36k
  return {};
352
1.38k
}
353
354
Expect<void> Validator::validate(
355
17.2k
    const AST::Component::CoreInstanceSection &InstSec) noexcept {
356
17.2k
  for (const auto &Inst : InstSec.getContent()) {
357
16.7k
    EXPECTED_TRY(validate(Inst).map_error([](auto E) {
358
16.7k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreInstance));
359
16.7k
      return E;
360
16.7k
    }));
361
16.7k
  }
362
17.1k
  return {};
363
17.2k
}
364
365
Expect<void>
366
375k
Validator::validate(const AST::Component::CoreTypeSection &TypeSec) noexcept {
367
375k
  for (const auto &Type : TypeSec.getContent()) {
368
296k
    EXPECTED_TRY(validate(Type).map_error([](auto E) {
369
296k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreType));
370
296k
      return E;
371
296k
    }));
372
296k
  }
373
375k
  return {};
374
375k
}
375
376
Expect<void>
377
4.71k
Validator::validate(const AST::Component::ComponentSection &CompSec) noexcept {
378
4.71k
  EXPECTED_TRY(validateComponent(CompSec.getContent()).map_error([](auto E) {
379
4.70k
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Component));
380
4.70k
    return E;
381
4.70k
  }));
382
4.70k
  CompCtx.addComponent(CompSec.getContent());
383
4.70k
  return {};
384
4.71k
}
385
386
Expect<void>
387
156k
Validator::validate(const AST::Component::InstanceSection &InstSec) noexcept {
388
156k
  for (const auto &Inst : InstSec.getContent()) {
389
65.3k
    EXPECTED_TRY(validate(Inst).map_error([](auto E) {
390
65.3k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Instance));
391
65.3k
      return E;
392
65.3k
    }));
393
65.3k
  }
394
155k
  return {};
395
156k
}
396
397
Expect<void>
398
7.64k
Validator::validate(const AST::Component::AliasSection &AliasSec) noexcept {
399
7.64k
  for (const auto &Alias : AliasSec.getContent()) {
400
7.15k
    EXPECTED_TRY(validate(Alias).map_error([](auto E) {
401
6.96k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Alias));
402
6.96k
      return E;
403
6.96k
    }));
404
6.96k
    const auto &Sort = Alias.getSort();
405
6.96k
    const bool IsOuter =
406
6.96k
        Alias.getTargetType() == AST::Component::Alias::TargetType::Outer;
407
6.96k
    if (Sort.isCore()) {
408
584
      uint32_t NewCoreIdx =
409
584
          CompCtx.incCoreSortIndexSize(Sort.getCoreSortType());
410
      // Carry the outer-aliased module's slot so the alias stays enumerable
411
      // when instantiated.
412
584
      if (IsOuter && Sort.getCoreSortType() ==
413
275
                         AST::Component::Sort::CoreSortType::Module) {
414
0
        CompCtx.carryOuterCoreModule(NewCoreIdx, Alias.getOuter().first,
415
0
                                     Alias.getOuter().second);
416
0
      }
417
6.38k
    } else {
418
6.38k
      uint32_t NewIdx = CompCtx.incSortIndexSize(Sort.getSortType());
419
      // Component analogue of the outer core-module carry above.
420
6.38k
      if (IsOuter &&
421
5.18k
          Sort.getSortType() == AST::Component::Sort::SortType::Component) {
422
507
        CompCtx.carryOuterComponent(NewIdx, Alias.getOuter().first,
423
507
                                    Alias.getOuter().second);
424
507
      }
425
      // Outer-aliasing a resource type keeps the resource's identity in this
426
      // scope so later own/borrow and (eq i) checks treat the slot correctly.
427
6.38k
      if (IsOuter &&
428
5.18k
          Sort.getSortType() == AST::Component::Sort::SortType::Type) {
429
4.67k
        CompCtx.carryOuterResource(NewIdx, Alias.getOuter().first,
430
4.67k
                                   Alias.getOuter().second);
431
4.67k
      }
432
      // If the alias creates a new instance entry out of an `alias export`
433
      // on another instance, propagate the source instance's export table
434
      // into the new slot so a subsequent `alias export` on this slot can
435
      // resolve nested exports.
436
6.38k
      if (Alias.getTargetType() == AST::Component::Alias::TargetType::Export) {
437
1.19k
        const auto SrcInstIdx = Alias.getExport().first;
438
1.19k
        const auto &SrcName = Alias.getExport().second;
439
1.19k
        const auto &SrcExports = CompCtx.getInstance(SrcInstIdx).Exports;
440
1.19k
        auto It = SrcExports.find(std::string(SrcName));
441
1.19k
        if (It != SrcExports.end()) {
442
1.19k
          if (Sort.getSortType() == AST::Component::Sort::SortType::Instance) {
443
1.19k
            if (It->second.IT != nullptr) {
444
0
              populateInstanceFromType(NewIdx, *It->second.IT);
445
1.19k
            } else if (It->second.NestedInstIdx.has_value()) {
446
1.19k
              const auto &NestedExports =
447
1.19k
                  CompCtx.getInstance(*It->second.NestedInstIdx).Exports;
448
1.19k
              for (const auto &[Name, IE] : NestedExports) {
449
1.19k
                CompCtx.addInstanceExport(NewIdx, Name, IE.ST, IE.IT,
450
1.19k
                                          IE.NestedInstIdx, IE.ResourceId);
451
1.19k
              }
452
1.19k
            }
453
1.19k
          } else if (Sort.getSortType() ==
454
0
                         AST::Component::Sort::SortType::Type &&
455
0
                     It->second.ResourceId.has_value()) {
456
            // Alias-export of a resource type — same identity.
457
0
            CompCtx.addResource(NewIdx, {*It->second.ResourceId,
458
0
                                         /*LocallyDefined=*/false});
459
0
          }
460
1.19k
        }
461
1.19k
      }
462
6.38k
    }
463
6.96k
  }
464
7.45k
  return {};
465
7.64k
}
466
467
Expect<void>
468
1.77M
Validator::validate(const AST::Component::TypeSection &TypeSec) noexcept {
469
1.77M
  for (const auto &Type : TypeSec.getContent()) {
470
1.77M
    EXPECTED_TRY(validate(Type).map_error([](auto E) {
471
1.77M
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Type));
472
1.77M
      return E;
473
1.77M
    }));
474
1.77M
  }
475
1.77M
  return {};
476
1.77M
}
477
478
Expect<void>
479
30.5k
Validator::validate(const AST::Component::CanonSection &CanonSec) noexcept {
480
30.5k
  for (const auto &C : CanonSec.getContent()) {
481
9.08k
    EXPECTED_TRY(validate(C).map_error([](auto E) {
482
9.08k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Canon));
483
9.08k
      return E;
484
9.08k
    }));
485
9.08k
  }
486
30.2k
  return {};
487
30.5k
}
488
489
Expect<void>
490
1.12k
Validator::validate(const AST::Component::StartSection &StartSec) noexcept {
491
  // Validation steps:
492
  //   1. `f` is in bounds of the component func index space.
493
  //   2. `f`'s functype param arity equals |arg*| and result arity equals
494
  //      `r`. Per-argument value-type subtype is checked once subtype
495
  //      machinery exists (Phase 3).
496
  //   3. Each argument index is in bounds of the value index space.
497
  //      Per-value linearity (consume-once) is the responsibility of
498
  //      GAP-EX-4: the per-value `consumed` flag and the end-of-component
499
  //      "all consumed" pass. They are not yet wired here.
500
  //   4. The function's result types are appended to the value index space
501
  //      as fresh values, so later definitions can reference them.
502
1.12k
  const auto &Start = StartSec.getContent();
503
504
  // 1. Function index bounds.
505
1.12k
  const uint32_t FuncIdx = Start.getFunctionIndex();
506
1.12k
  const uint32_t FuncSpaceSize =
507
1.12k
      CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Func);
508
1.12k
  if (FuncIdx >= FuncSpaceSize) {
509
26
    spdlog::error(ErrCode::Value::InvalidIndex);
510
26
    spdlog::error(
511
26
        "    Start: function index {} exceeds func index space size {}"sv,
512
26
        FuncIdx, FuncSpaceSize);
513
26
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start));
514
26
    return Unexpect(ErrCode::Value::InvalidIndex);
515
26
  }
516
517
  // 2. Look up the func type. If null (e.g. imported component func without
518
  // populated FuncType yet), skip arity check — the bound check above is
519
  // enough for the moment.
520
1.09k
  const AST::Component::FuncType *FT = CompCtx.getFunc(FuncIdx);
521
1.09k
  if (FT != nullptr) {
522
519
    const auto Args = Start.getArguments();
523
519
    const auto &ParamList = FT->getParamList();
524
519
    if (Args.size() != ParamList.size()) {
525
1
      spdlog::error(ErrCode::Value::InvalidIndex);
526
1
      spdlog::error(
527
1
          "    Start: argument count {} does not match func {} param arity {}"sv,
528
1
          Args.size(), FuncIdx, ParamList.size());
529
1
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start));
530
1
      return Unexpect(ErrCode::Value::InvalidIndex);
531
1
    }
532
518
    const uint32_t ResultArity =
533
518
        static_cast<uint32_t>(FT->getResultList().size());
534
518
    if (Start.getResult() != ResultArity) {
535
4
      spdlog::error(ErrCode::Value::InvalidIndex);
536
4
      spdlog::error(
537
4
          "    Start: declared result count {} does not match func {} result arity {}"sv,
538
4
          Start.getResult(), FuncIdx, ResultArity);
539
4
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start));
540
4
      return Unexpect(ErrCode::Value::InvalidIndex);
541
4
    }
542
518
  }
543
544
  // 3. Argument indices must be in value index space bounds. Per-arg
545
  // consume-once tracking is deferred (GAP-EX-4).
546
1.09k
  const uint32_t ValueSpaceSize =
547
1.09k
      CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Value);
548
1.09k
  for (const uint32_t ArgIdx : Start.getArguments()) {
549
774
    if (ArgIdx >= ValueSpaceSize) {
550
28
      spdlog::error(ErrCode::Value::InvalidIndex);
551
28
      spdlog::error(
552
28
          "    Start: argument value index {} exceeds value index space size {}"sv,
553
28
          ArgIdx, ValueSpaceSize);
554
28
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start));
555
28
      return Unexpect(ErrCode::Value::InvalidIndex);
556
28
    }
557
774
  }
558
559
  // 4. Append result values to the value index space.
560
1.43G
  for (uint32_t I = 0; I < Start.getResult(); ++I) {
561
1.43G
    CompCtx.addValue();
562
1.43G
  }
563
1.06k
  return {};
564
1.09k
}
565
566
Expect<void>
567
19.7k
Validator::validate(const AST::Component::ImportSection &ImpSec) noexcept {
568
19.7k
  for (const auto &Imp : ImpSec.getContent()) {
569
6.95k
    EXPECTED_TRY(validate(Imp).map_error([](auto E) {
570
6.95k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Import));
571
6.95k
      return E;
572
6.95k
    }));
573
6.95k
  }
574
18.8k
  return {};
575
19.7k
}
576
577
Expect<void>
578
2.40k
Validator::validate(const AST::Component::ExportSection &ExpSec) noexcept {
579
2.40k
  for (const auto &Exp : ExpSec.getContent()) {
580
894
    EXPECTED_TRY(validate(Exp).map_error([](auto E) {
581
894
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Export));
582
894
      return E;
583
894
    }));
584
894
  }
585
2.27k
  return {};
586
2.40k
}
587
588
Expect<void>
589
16.7k
Validator::validate(const AST::Component::CoreInstance &Inst) noexcept {
590
16.7k
  if (Inst.isInstantiateModule()) {
591
    // Instantiate module case.
592
593
    // Check the module index bound first.
594
259
    const uint32_t ModIdx = Inst.getModuleIndex();
595
259
    if (ModIdx >= CompCtx.getCoreSortIndexSize(
596
259
                      AST::Component::Sort::CoreSortType::Module)) {
597
38
      spdlog::error(ErrCode::Value::InvalidIndex);
598
38
      spdlog::error(
599
38
          "    CoreInstance: Module index {} exceeds available core modules {}"sv,
600
38
          ModIdx,
601
38
          CompCtx.getCoreSortIndexSize(
602
38
              AST::Component::Sort::CoreSortType::Module));
603
38
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_CoreInstance));
604
38
      return Unexpect(ErrCode::Value::InvalidIndex);
605
38
    }
606
    // Reject duplicate argument names on an instantiate expression. The
607
    // spec requires argument names to be strongly-unique per instantiation.
608
221
    {
609
221
      std::unordered_set<std::string_view> SeenArgs;
610
221
      for (const auto &Arg : Inst.getInstantiateArgs()) {
611
0
        if (!SeenArgs.insert(Arg.getName()).second) {
612
0
          spdlog::error(ErrCode::Value::ComponentDuplicateName);
613
0
          spdlog::error("    CoreInstance: Duplicate argument name '{}'"sv,
614
0
                        Arg.getName());
615
0
          spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_CoreInstance));
616
0
          return Unexpect(ErrCode::Value::ComponentDuplicateName);
617
0
        }
618
0
      }
619
221
    }
620
621
    // Imports + exports come from the raw Module (inline) or the
622
    // CoreModuleType (imported / aliased) — GAP-CI-1.
623
221
    const auto &CoreModSlot = CompCtx.getCoreModule(ModIdx);
624
221
    const auto *Mod = CoreModSlot.Body;
625
221
    const auto *ModTy = CoreModSlot.Type;
626
627
    // Required arg module-names (one per distinct CoreImportDecl module).
628
221
    std::vector<std::string_view> RequiredArgNames;
629
221
    if (Mod != nullptr) {
630
221
      for (const auto &Import : Mod->getImportSection().getContent()) {
631
5
        RequiredArgNames.push_back(Import.getModuleName());
632
5
      }
633
221
    } else if (ModTy != nullptr && ModTy->isModuleType()) {
634
0
      for (const auto &Decl : ModTy->getModuleType()) {
635
0
        if (Decl.isImport()) {
636
0
          RequiredArgNames.push_back(Decl.getImport().getModuleName());
637
0
        }
638
0
      }
639
0
    }
640
641
221
    auto Args = Inst.getInstantiateArgs();
642
221
    for (const auto ImportName : RequiredArgNames) {
643
3
      const auto ArgIt =
644
3
          std::find_if(Args.begin(), Args.end(), [&](const auto &Arg) {
645
0
            return Arg.getName() == ImportName;
646
0
          });
647
3
      if (ArgIt == Args.end()) {
648
3
        spdlog::error(ErrCode::Value::MissingArgument);
649
3
        spdlog::error(
650
3
            "    CoreInstance: Module index {} missing argument for import '{}'"sv,
651
3
            Inst.getModuleIndex(), ImportName);
652
3
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_CoreInstance));
653
3
        return Unexpect(ErrCode::Value::MissingArgument);
654
3
      }
655
3
    }
656
657
    // Allocate the core:instance and bind exports to it.
658
218
    uint32_t InstanceIdx = CompCtx.addCoreInstance();
659
218
    if (Mod != nullptr) {
660
218
      for (const auto &ExportDesc : Mod->getExportSection().getContent()) {
661
0
        CompCtx.addCoreInstanceExport(InstanceIdx, ExportDesc.getExternalName(),
662
0
                                      ExportDesc.getExternalType());
663
0
      }
664
218
    } else if (ModTy != nullptr && ModTy->isModuleType()) {
665
0
      for (const auto &Decl : ModTy->getModuleType()) {
666
0
        if (!Decl.isExport()) {
667
0
          continue;
668
0
        }
669
0
        const auto &Exp = Decl.getExport();
670
0
        const auto &ImpDesc = Exp.getImportDesc();
671
0
        ExternalType ET;
672
0
        if (ImpDesc.isFunc()) {
673
0
          ET = ExternalType::Function;
674
0
        } else if (ImpDesc.isTable()) {
675
0
          ET = ExternalType::Table;
676
0
        } else if (ImpDesc.isMemory()) {
677
0
          ET = ExternalType::Memory;
678
0
        } else if (ImpDesc.isGlobal()) {
679
0
          ET = ExternalType::Global;
680
0
        } else if (ImpDesc.isTag()) {
681
0
          ET = ExternalType::Tag;
682
0
        } else {
683
0
          continue;
684
0
        }
685
0
        CompCtx.addCoreInstanceExport(InstanceIdx, Exp.getName(), ET);
686
0
      }
687
0
    }
688
16.4k
  } else if (Inst.isInlineExport()) {
689
    // Inline export case.
690
    // Allocate the core instance first, then register each inline export.
691
16.4k
    uint32_t InstanceIdx = CompCtx.addCoreInstance();
692
693
    // Check the core:sort index bound and register the inline exports.
694
    // Inline-export names on a core instance must be strongly-unique.
695
16.4k
    std::unordered_set<std::string_view> SeenExports;
696
16.4k
    for (const auto &Export : Inst.getInlineExports()) {
697
1.92k
      if (!SeenExports.insert(Export.getName()).second) {
698
1
        spdlog::error(ErrCode::Value::ComponentDuplicateName);
699
1
        spdlog::error("    CoreInstance: Duplicate inline-export name '{}'"sv,
700
1
                      Export.getName());
701
1
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_CoreInstance));
702
1
        return Unexpect(ErrCode::Value::ComponentDuplicateName);
703
1
      }
704
1.91k
      const auto &Sort = Export.getSortIdx().getSort();
705
1.91k
      uint32_t Idx = Export.getSortIdx().getIdx();
706
1.91k
      assuming(Sort.isCore());
707
1.91k
      if (Idx >= CompCtx.getCoreSortIndexSize(Sort.getCoreSortType())) {
708
        // The error message differs of the tag core sort.
709
51
        ErrCode::Value ErrValue = ErrCode::Value::InvalidIndex;
710
51
        if (Sort.getCoreSortType() == AST::Component::Sort::CoreSortType::Tag) {
711
2
          ErrValue = ErrCode::Value::UnknownCoreTag;
712
2
        }
713
51
        spdlog::error(ErrValue);
714
51
        spdlog::error(
715
51
            "    CoreInstance: Inline export '{}' refers to invalid index {}"sv,
716
51
            Export.getName(), Idx);
717
51
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_CoreInstance));
718
51
        return Unexpect(ErrValue);
719
51
      }
720
      // Map CoreSortType to ExternalType for the instance export map.
721
1.86k
      ExternalType ET;
722
1.86k
      switch (Sort.getCoreSortType()) {
723
1.86k
      case AST::Component::Sort::CoreSortType::Func:
724
1.86k
        ET = ExternalType::Function;
725
1.86k
        break;
726
0
      case AST::Component::Sort::CoreSortType::Table:
727
0
        ET = ExternalType::Table;
728
0
        break;
729
0
      case AST::Component::Sort::CoreSortType::Memory:
730
0
        ET = ExternalType::Memory;
731
0
        break;
732
0
      case AST::Component::Sort::CoreSortType::Global:
733
0
        ET = ExternalType::Global;
734
0
        break;
735
0
      case AST::Component::Sort::CoreSortType::Tag:
736
0
        ET = ExternalType::Tag;
737
0
        break;
738
3
      default:
739
3
        spdlog::error(ErrCode::Value::InvalidIndex);
740
3
        spdlog::error(
741
3
            "    CoreInstance: Inline export '{}' has unsupported core sort"sv,
742
3
            Export.getName());
743
3
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_CoreInstance));
744
3
        return Unexpect(ErrCode::Value::InvalidIndex);
745
1.86k
      }
746
1.86k
      CompCtx.addCoreInstanceExport(InstanceIdx, Export.getName(), ET);
747
1.86k
    }
748
16.4k
  } else {
749
0
    assumingUnreachable();
750
0
  }
751
16.6k
  return {};
752
16.7k
}
753
754
Expect<void>
755
65.3k
Validator::validate(const AST::Component::Instance &Inst) noexcept {
756
65.3k
  if (Inst.isInstantiateModule()) {
757
    // Instantiate module case.
758
759
    // Check the component index bound first.
760
4.08k
    const uint32_t CompIdx = Inst.getComponentIndex();
761
4.08k
    if (CompIdx >=
762
4.08k
        CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Component)) {
763
104
      spdlog::error(ErrCode::Value::InvalidIndex);
764
104
      spdlog::error(
765
104
          "    Instance: Component index {} exceeds available components {}"sv,
766
104
          CompIdx,
767
104
          CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Component));
768
104
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
769
104
      return Unexpect(ErrCode::Value::InvalidIndex);
770
104
    }
771
    // Reject duplicate argument names on an instantiate expression. The
772
    // spec requires argument names to be strongly-unique per instantiation.
773
3.97k
    {
774
3.97k
      std::unordered_set<std::string_view> SeenArgs;
775
4.24k
      for (const auto &Arg : Inst.getInstantiateArgs()) {
776
4.24k
        if (!SeenArgs.insert(Arg.getName()).second) {
777
20
          spdlog::error(ErrCode::Value::ComponentDuplicateName);
778
20
          spdlog::error("    Instance: Duplicate argument name '{}'"sv,
779
20
                        Arg.getName());
780
20
          spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
781
20
          return Unexpect(ErrCode::Value::ComponentDuplicateName);
782
20
        }
783
4.24k
      }
784
3.97k
    }
785
786
    // Source: raw Component (inline) or ComponentType (imported / aliased).
787
3.95k
    const auto &CompSlot = CompCtx.getComponent(CompIdx);
788
3.95k
    const auto *Comp = CompSlot.Body;
789
3.95k
    const auto *CompTy = CompSlot.Type;
790
791
    // Verify each component import is satisfied by some instantiate arg.
792
3.95k
    auto Args = Inst.getInstantiateArgs();
793
3.95k
    auto checkImport =
794
3.95k
        [&](std::string_view ImportName,
795
3.95k
            const AST::Component::ExternDesc &ImportDesc) -> Expect<void> {
796
32
      const auto ArgIt =
797
107
          std::find_if(Args.begin(), Args.end(), [&](const auto &Arg) {
798
107
            return Arg.getName() == ImportName;
799
107
          });
800
32
      if (ArgIt == Args.end()) {
801
10
        spdlog::error(ErrCode::Value::MissingArgument);
802
10
        spdlog::error(
803
10
            "    Instance: Component index {} missing argument for import '{}'"sv,
804
10
            Inst.getComponentIndex(), ImportName);
805
10
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
806
10
        return Unexpect(ErrCode::Value::MissingArgument);
807
10
      }
808
22
      const auto &Sort = ArgIt->getIndex().getSort();
809
22
      const uint32_t Idx = ArgIt->getIndex().getIdx();
810
      // Only `core module` is admissible as a core-side import externdesc.
811
22
      if (Sort.isCore() && Sort.getCoreSortType() !=
812
4
                               AST::Component::Sort::CoreSortType::Module) {
813
3
        spdlog::error(ErrCode::Value::ArgTypeMismatch);
814
3
        spdlog::error("    Instance: Argument '{}' uses a core sort other than "
815
3
                      "`core module`, which no import externdesc can accept"sv,
816
3
                      ImportName);
817
3
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
818
3
        return Unexpect(ErrCode::Value::ArgTypeMismatch);
819
3
      }
820
19
      if (!sortMatchesDescType(Sort, ImportDesc.getDescType())) {
821
4
        spdlog::error(ErrCode::Value::ArgTypeMismatch);
822
4
        spdlog::error("    Instance: Argument '{}' sort mismatch for import"sv,
823
4
                      ImportName);
824
4
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
825
4
        return Unexpect(ErrCode::Value::ArgTypeMismatch);
826
4
      }
827
15
      if (Sort.isCore()) {
828
0
        if (Idx >= CompCtx.getCoreSortIndexSize(Sort.getCoreSortType())) {
829
0
          spdlog::error(ErrCode::Value::InvalidIndex);
830
0
          spdlog::error(
831
0
              "    Instance: Argument '{}' refers to invalid index {}"sv,
832
0
              ImportName, Idx);
833
0
          spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
834
0
          return Unexpect(ErrCode::Value::InvalidIndex);
835
0
        }
836
0
        return {};
837
0
      }
838
15
      if (Idx >= CompCtx.getSortIndexSize(Sort.getSortType())) {
839
7
        spdlog::error(ErrCode::Value::InvalidIndex);
840
7
        spdlog::error(
841
7
            "    Instance: Argument '{}' refers to invalid index {}"sv,
842
7
            ImportName, Idx);
843
7
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
844
7
        return Unexpect(ErrCode::Value::InvalidIndex);
845
7
      }
846
      // Partial subtype: for instance-typed imports, verify the provided
847
      // instance has every required export (raw-Component path only;
848
      // ComponentType path needs GAP-DECL-ED).
849
8
      if (Comp != nullptr &&
850
8
          ImportDesc.getDescType() ==
851
8
              AST::Component::ExternDesc::DescType::InstanceType &&
852
0
          Sort.getSortType() == AST::Component::Sort::SortType::Instance) {
853
0
        const auto *RequiredIT =
854
0
            resolveChildInstanceType(*Comp, ImportDesc.getTypeIndex());
855
0
        if (RequiredIT != nullptr) {
856
0
          if (auto Missing = findMissingRequiredExport(Idx, *RequiredIT)) {
857
0
            spdlog::error(ErrCode::Value::InstanceMissingExpectedExport);
858
0
            spdlog::error("    Instance: Argument '{}' missing required export "
859
0
                          "'{}' for import"sv,
860
0
                          ImportName, *Missing);
861
0
            spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
862
0
            return Unexpect(ErrCode::Value::InstanceMissingExpectedExport);
863
0
          }
864
0
        }
865
0
      }
866
8
      return {};
867
8
    };
868
3.95k
    if (Comp != nullptr) {
869
7.84k
      for (const auto &Sec : Comp->getSections()) {
870
7.84k
        if (const auto *IS = std::get_if<AST::Component::ImportSection>(&Sec)) {
871
596
          for (const auto &Imp : IS->getContent()) {
872
31
            EXPECTED_TRY(checkImport(Imp.getName(), Imp.getDesc()));
873
31
          }
874
596
        }
875
7.84k
      }
876
2.28k
    } else if (CompTy != nullptr) {
877
593
      for (const auto &CD : CompTy->getDecl()) {
878
230
        if (CD.isImportDecl()) {
879
1
          const auto &ID = CD.getImport();
880
1
          EXPECTED_TRY(checkImport(ID.getName(), ID.getExternDesc()));
881
1
        }
882
230
      }
883
593
    }
884
885
    // Allocate the slot + populate exports so alias-export can resolve.
886
    // Raw-Component path resolves the IT for instance-typed exports;
887
    // ComponentType path registers sort-kind only (GAP-DECL-ED).
888
    // TODO (GAP-I-3): fresh ResourceIds + per-export resource remapping.
889
3.93k
    uint32_t InstanceIdx = CompCtx.addInstance();
890
3.93k
    if (Comp != nullptr) {
891
7.73k
      for (const auto &Sec : Comp->getSections()) {
892
7.73k
        const auto *ES = std::get_if<AST::Component::ExportSection>(&Sec);
893
7.73k
        if (ES == nullptr) {
894
6.17k
          continue;
895
6.17k
        }
896
1.55k
        for (const auto &Exp : ES->getContent()) {
897
0
          const auto &ExpSort = Exp.getSortIndex().getSort();
898
0
          if (ExpSort.isCore()) {
899
0
            continue;
900
0
          }
901
0
          const AST::Component::InstanceType *IT = nullptr;
902
0
          if (ExpSort.getSortType() ==
903
0
                  AST::Component::Sort::SortType::Instance &&
904
0
              Exp.getDesc().has_value() &&
905
0
              Exp.getDesc()->getDescType() ==
906
0
                  AST::Component::ExternDesc::DescType::InstanceType) {
907
0
            IT = resolveChildInstanceType(*Comp, Exp.getDesc()->getTypeIndex());
908
0
          }
909
0
          CompCtx.addInstanceExport(InstanceIdx, Exp.getName(),
910
0
                                    ExpSort.getSortType(), IT);
911
0
        }
912
1.55k
      }
913
2.28k
    } else if (CompTy != nullptr) {
914
592
      for (const auto &CD : CompTy->getDecl()) {
915
229
        if (!CD.isInstanceDecl()) {
916
0
          continue;
917
0
        }
918
229
        const auto &ID = CD.getInstance();
919
229
        if (!ID.isExportDecl()) {
920
0
          continue;
921
0
        }
922
229
        const auto &ED = ID.getExport();
923
229
        const auto OptST = descTypeToSortType(ED.getExternDesc().getDescType());
924
229
        if (!OptST.has_value()) {
925
0
          continue; // `(core module)` export — not a component-side entry.
926
0
        }
927
229
        CompCtx.addInstanceExport(InstanceIdx, ED.getName(), *OptST);
928
229
      }
929
592
    }
930
61.2k
  } else if (Inst.isInlineExport()) {
931
    // Allocate the instance first so exports can be registered on it.
932
61.2k
    uint32_t InstanceIdx = CompCtx.addInstance();
933
934
    // Check the sort index bound of the inline exports, then record each
935
    // export on the new instance with a NestedInstIdx fallback so alias
936
    // chains through inline-export instances can follow the source.
937
    // Inline-export names on a component instance must be strongly-unique.
938
61.2k
    std::unordered_set<std::string_view> SeenExports;
939
61.2k
    for (const auto &Export : Inst.getInlineExports()) {
940
2.38k
      if (!SeenExports.insert(Export.getName()).second) {
941
1
        spdlog::error(ErrCode::Value::ComponentDuplicateName);
942
1
        spdlog::error("    Instance: Duplicate inline-export name '{}'"sv,
943
1
                      Export.getName());
944
1
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
945
1
        return Unexpect(ErrCode::Value::ComponentDuplicateName);
946
1
      }
947
2.38k
      const auto &Sort = Export.getSortIdx().getSort();
948
2.38k
      uint32_t Idx = Export.getSortIdx().getIdx();
949
2.38k
      if (Sort.isCore()) {
950
215
        if (Idx >= CompCtx.getCoreSortIndexSize(Sort.getCoreSortType())) {
951
23
          spdlog::error(ErrCode::Value::InvalidIndex);
952
23
          spdlog::error(
953
23
              "    Instance: Inline export '{}' refers to invalid index {}"sv,
954
23
              Export.getName(), Idx);
955
23
          spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
956
23
          return Unexpect(ErrCode::Value::InvalidIndex);
957
23
        }
958
192
        continue;
959
215
      }
960
2.16k
      if (Idx >= CompCtx.getSortIndexSize(Sort.getSortType())) {
961
38
        spdlog::error(ErrCode::Value::InvalidIndex);
962
38
        spdlog::error(
963
38
            "    Instance: Inline export '{}' refers to invalid index {}"sv,
964
38
            Export.getName(), Idx);
965
38
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Instance));
966
38
        return Unexpect(ErrCode::Value::InvalidIndex);
967
38
      }
968
2.12k
      if (Sort.getSortType() == AST::Component::Sort::SortType::Type) {
969
1.02k
        auto SubstitutedIdx =
970
1.02k
            CompCtx.getSubstitutedType(std::string(Export.getName()));
971
1.02k
        if (SubstitutedIdx.has_value() && Idx != SubstitutedIdx.value()) {
972
0
          spdlog::error(ErrCode::Value::InvalidTypeReference);
973
0
          spdlog::error(
974
0
              "    Instance: Inline export '{}' type index {} does not match substituted type index {}"sv,
975
0
              Export.getName(), Idx, *SubstitutedIdx);
976
0
          return Unexpect(ErrCode::Value::InvalidTypeReference);
977
0
        }
978
1.02k
      }
979
2.12k
      std::optional<uint32_t> NestedIdx;
980
2.12k
      const AST::Component::InstanceType *PropagatedIT = nullptr;
981
2.12k
      if (Sort.getSortType() == AST::Component::Sort::SortType::Instance) {
982
973
        NestedIdx = Idx;
983
        // GAP-I-5b: forward source's InstanceType so later ascription /
984
        // subtype checks have a concrete type. Populated only when the
985
        // source slot was bound via ExternDesc::InstanceType; inline-
986
        // export / instantiate sources read nullptr (Phase-3 follow-up).
987
973
        PropagatedIT = CompCtx.getInstance(Idx).Type;
988
973
      }
989
2.12k
      CompCtx.addInstanceExport(InstanceIdx, Export.getName(),
990
2.12k
                                Sort.getSortType(), PropagatedIT, NestedIdx);
991
2.12k
    }
992
61.2k
  } else {
993
0
    assumingUnreachable();
994
0
  }
995
65.1k
  return {};
996
65.3k
}
997
998
199
Expect<void> Validator::validate(const AST::Component::CoreAlias &A) noexcept {
999
  // CoreAlias is always an outer alias.
1000
199
  uint32_t Ct = A.getComponentJump();
1001
199
  uint32_t Idx = A.getIndex();
1002
1003
199
  uint32_t OutLinkCompCnt = 0;
1004
199
  const auto *TargetCtx = &CompCtx.getCurrentContext();
1005
239
  while (Ct > OutLinkCompCnt && TargetCtx != nullptr) {
1006
40
    TargetCtx = TargetCtx->Parent;
1007
40
    OutLinkCompCnt++;
1008
40
  }
1009
199
  if (TargetCtx == nullptr) {
1010
10
    spdlog::error(ErrCode::Value::InvalidIndex);
1011
    // The final hop is the one that ran off the top of the scope chain, so the
1012
    // number of actually-enclosing components is one less than the hop count.
1013
10
    spdlog::error(
1014
10
        "    CoreAlias: outer count {} exceeds enclosing component count {}"sv,
1015
10
        Ct, OutLinkCompCnt - 1);
1016
10
    return Unexpect(ErrCode::Value::InvalidIndex);
1017
10
  }
1018
1019
189
  const auto &Sort = A.getSort();
1020
189
  if (Sort.isCore()) {
1021
189
    if (Idx >= TargetCtx->getCoreSortIndexSize(Sort.getCoreSortType())) {
1022
23
      spdlog::error(ErrCode::Value::InvalidIndex);
1023
23
      spdlog::error("    CoreAlias: outer index {} out of bounds"sv, Idx);
1024
23
      return Unexpect(ErrCode::Value::InvalidIndex);
1025
23
    }
1026
166
    CompCtx.incCoreSortIndexSize(Sort.getCoreSortType());
1027
166
  }
1028
166
  return {};
1029
189
}
1030
1031
7.33k
Expect<void> Validator::validate(const AST::Component::Alias &Alias) noexcept {
1032
7.33k
  const auto &Sort = Alias.getSort();
1033
7.33k
  switch (Alias.getTargetType()) {
1034
1.25k
  case AST::Component::Alias::TargetType::Export: {
1035
1.25k
    const auto Idx = Alias.getExport().first;
1036
1.25k
    const auto &Name = Alias.getExport().second;
1037
1038
1.25k
    if (Sort.isCore()) {
1039
2
      spdlog::error(ErrCode::Value::InvalidTypeReference);
1040
2
      spdlog::error("    Alias export: Mapping an export '{}' to core:sort"sv,
1041
2
                    Name);
1042
2
      return Unexpect(ErrCode::Value::InvalidTypeReference);
1043
2
    }
1044
1045
1.25k
    if (Idx >=
1046
1.25k
        CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Instance)) {
1047
36
      spdlog::error(ErrCode::Value::InvalidIndex);
1048
36
      spdlog::error(
1049
36
          "    Alias export: Export index {} exceeds available component instance index {}"sv,
1050
36
          Idx,
1051
36
          CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Instance));
1052
36
      return Unexpect(ErrCode::Value::InvalidIndex);
1053
36
    }
1054
1055
1.22k
    const auto &InstExports = CompCtx.getInstance(Idx).Exports;
1056
1.22k
    auto It = InstExports.find(std::string(Name));
1057
1.22k
    if (It == InstExports.cend()) {
1058
21
      spdlog::error(ErrCode::Value::ExportNotFound);
1059
21
      spdlog::error(
1060
21
          "    Alias export: No matching export '{}' found in component instance index {}"sv,
1061
21
          Name, Idx);
1062
21
      return Unexpect(ErrCode::Value::ExportNotFound);
1063
21
    }
1064
1065
1.20k
    if (It->second.ST != Sort.getSortType()) {
1066
2
      spdlog::error(ErrCode::Value::InvalidTypeReference);
1067
2
      spdlog::error("    Alias export: Type mapping mismatch for export '{}'"sv,
1068
2
                    Name);
1069
2
      return Unexpect(ErrCode::Value::InvalidTypeReference);
1070
2
    }
1071
1.19k
    return {};
1072
1.20k
  }
1073
362
  case AST::Component::Alias::TargetType::CoreExport: {
1074
362
    const auto Idx = Alias.getExport().first;
1075
362
    const auto &Name = Alias.getExport().second;
1076
1077
362
    if (!Sort.isCore()) {
1078
4
      spdlog::error(ErrCode::Value::InvalidTypeReference);
1079
4
      spdlog::error("    Alias core:export: Mapping a export '{}' to sort"sv,
1080
4
                    Name);
1081
4
      return Unexpect(ErrCode::Value::InvalidTypeReference);
1082
4
    }
1083
1084
358
    if (Idx >= CompCtx.getCoreSortIndexSize(
1085
358
                   AST::Component::Sort::CoreSortType::Instance)) {
1086
35
      spdlog::error(ErrCode::Value::InvalidIndex);
1087
35
      spdlog::error(
1088
35
          "    Alias core:export: Export index {} exceeds available core instance index {}"sv,
1089
35
          Idx,
1090
35
          CompCtx.getCoreSortIndexSize(
1091
35
              AST::Component::Sort::CoreSortType::Instance) -
1092
35
              1);
1093
35
      return Unexpect(ErrCode::Value::InvalidIndex);
1094
35
    }
1095
1096
323
    const auto &CoreExports = CompCtx.getCoreInstance(Idx);
1097
323
    auto It = CoreExports.find(std::string(Name));
1098
323
    if (It == CoreExports.end()) {
1099
10
      spdlog::error(ErrCode::Value::ExportNotFound);
1100
10
      spdlog::error(
1101
10
          "    Alias core:export: No matching export '{}' found in core instance index {}"sv,
1102
10
          Name, Idx);
1103
10
      return Unexpect(ErrCode::Value::ExportNotFound);
1104
10
    }
1105
1106
313
    const auto ExternTy = It->second;
1107
313
    AST::Component::Sort::CoreSortType ST;
1108
313
    switch (ExternTy) {
1109
313
    case ExternalType::Function:
1110
313
      ST = AST::Component::Sort::CoreSortType::Func;
1111
313
      break;
1112
0
    case ExternalType::Table:
1113
0
      ST = AST::Component::Sort::CoreSortType::Table;
1114
0
      break;
1115
0
    case ExternalType::Memory:
1116
0
      ST = AST::Component::Sort::CoreSortType::Memory;
1117
0
      break;
1118
0
    case ExternalType::Global:
1119
0
      ST = AST::Component::Sort::CoreSortType::Global;
1120
0
      break;
1121
0
    case ExternalType::Tag:
1122
0
      ST = AST::Component::Sort::CoreSortType::Tag;
1123
0
      break;
1124
0
    default:
1125
0
      spdlog::error(ErrCode::Value::InvalidTypeReference);
1126
0
      spdlog::error(
1127
0
          "    Alias core:export: Type mapping mismatch for export '{}'"sv,
1128
0
          Name);
1129
0
      return Unexpect(ErrCode::Value::InvalidTypeReference);
1130
313
    }
1131
313
    if (ST != Sort.getCoreSortType()) {
1132
      // The error message differs of the tag core sort.
1133
4
      ErrCode::Value ErrValue = ErrCode::Value::InvalidIndex;
1134
4
      if (Sort.getCoreSortType() == AST::Component::Sort::CoreSortType::Tag) {
1135
3
        ErrValue = ErrCode::Value::UnknownCoreTag;
1136
3
      }
1137
4
      spdlog::error(ErrValue);
1138
4
      spdlog::error(
1139
4
          "    Alias core:export: Type mapping mismatch for export '{}'"sv,
1140
4
          Name);
1141
4
      return Unexpect(ErrValue);
1142
4
    }
1143
309
    return {};
1144
313
  }
1145
5.71k
  case AST::Component::Alias::TargetType::Outer: {
1146
5.71k
    const auto Ct = Alias.getOuter().first;
1147
5.71k
    const auto Idx = Alias.getOuter().second;
1148
1149
5.71k
    uint32_t OutLinkCompCnt = 0;
1150
5.71k
    const auto *TargetCtx = &CompCtx.getCurrentContext();
1151
6.19k
    while (Ct > OutLinkCompCnt && TargetCtx != nullptr) {
1152
481
      TargetCtx = TargetCtx->Parent;
1153
481
      OutLinkCompCnt++;
1154
481
    }
1155
5.71k
    if (TargetCtx == nullptr) {
1156
65
      spdlog::error(ErrCode::Value::InvalidIndex);
1157
      // The final hop is the one that ran off the top of the scope chain, so
1158
      // the number of actually-enclosing components is one less than the hop
1159
      // count.
1160
65
      spdlog::error(
1161
65
          "    Alias outer: Component out-link count {} is exceeding the enclosing component count {}"sv,
1162
65
          Ct, OutLinkCompCnt - 1);
1163
65
      return Unexpect(ErrCode::Value::InvalidIndex);
1164
65
    }
1165
1166
5.65k
    if (Sort.isCore()) {
1167
292
      if (Sort.getCoreSortType() !=
1168
292
              AST::Component::Sort::CoreSortType::Module &&
1169
282
          Sort.getCoreSortType() != AST::Component::Sort::CoreSortType::Type) {
1170
4
        spdlog::error(ErrCode::Value::InvalidTypeReference);
1171
4
        spdlog::error(
1172
4
            "    Alias outer: Invalid core:sort for outer alias. Only type, module, or component are allowed."sv);
1173
4
        return Unexpect(ErrCode::Value::InvalidTypeReference);
1174
4
      }
1175
288
      if (Idx >= TargetCtx->getCoreSortIndexSize(Sort.getCoreSortType())) {
1176
13
        spdlog::error(ErrCode::Value::InvalidIndex);
1177
13
        spdlog::error(
1178
13
            "    Alias outer: core:sort index {} invalid in component context"sv,
1179
13
            Idx);
1180
13
        return Unexpect(ErrCode::Value::InvalidIndex);
1181
13
      }
1182
5.35k
    } else {
1183
5.35k
      if (Sort.getSortType() != AST::Component::Sort::SortType::Type &&
1184
512
          Sort.getSortType() != AST::Component::Sort::SortType::Component) {
1185
2
        spdlog::error(ErrCode::Value::InvalidTypeReference);
1186
2
        spdlog::error(
1187
2
            "    Alias outer: Invalid sort for outer alias. Only type, module, or component are allowed."sv);
1188
2
        return Unexpect(ErrCode::Value::InvalidTypeReference);
1189
2
      }
1190
5.35k
      if (Idx >= TargetCtx->getSortIndexSize(Sort.getSortType())) {
1191
6
        spdlog::error(ErrCode::Value::InvalidIndex);
1192
6
        spdlog::error(
1193
6
            "    Alias outer: sort index {} invalid in component context"sv,
1194
6
            Idx);
1195
6
        return Unexpect(ErrCode::Value::InvalidIndex);
1196
6
      }
1197
      // Outer-aliasing a resource type is permitted: the alias preserves the
1198
      // resource's type identity rather than introducing a fresh generative
1199
      // resource, so a nested type/component can legitimately refer to a
1200
      // resource in an enclosing scope (e.g. an instance type referring to a
1201
      // resource imported by its enclosing component type).
1202
      //
1203
      // TODO: implement wasm-tools' full free-variable rule — reject an outer
1204
      // alias to a type whose transitive free variables include resources that
1205
      // would escape a crossed component boundary. That needs a free-variable
1206
      // walker over type bodies, tracked alongside the Phase 3/4 type-handle
1207
      // work (GAP-TH-1, GAP-EX-5).
1208
5.35k
    }
1209
5.62k
    return {};
1210
5.65k
  }
1211
0
  default:
1212
0
    assumingUnreachable();
1213
7.33k
  }
1214
7.33k
}
1215
1216
Expect<void>
1217
296k
Validator::validate(const AST::Component::CoreDefType &DType) noexcept {
1218
296k
  if (DType.isRecType()) {
1219
    // Each sub-type in the rec group gets its own entry in core:type.
1220
158k
    for (const auto &ST : DType.getSubTypes()) {
1221
158k
      CompCtx.addCoreType(&ST);
1222
158k
    }
1223
158k
  } else if (DType.isModuleType()) {
1224
    // Module types are validated with an initially-empty type index space.
1225
137k
    CompCtx.enterTypeDefinition();
1226
137k
    for (const auto &Decl : DType.getModuleType()) {
1227
1.74k
      EXPECTED_TRY(validate(Decl).map_error([](auto E) {
1228
1.74k
        spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_CoreDefType));
1229
1.74k
        return E;
1230
1.74k
      }));
1231
1.74k
    }
1232
137k
    CompCtx.exitComponent();
1233
    // Module type gets a core:type slot (Body=nullptr) bound to the
1234
    // CoreDefType so (core module (type i)) can recover the body — GAP-CI-1.
1235
137k
    uint32_t NewTypeIdx = CompCtx.addCoreType();
1236
137k
    CompCtx.setCoreModuleType(NewTypeIdx, &DType);
1237
137k
  } else {
1238
0
    assumingUnreachable();
1239
0
  }
1240
296k
  return {};
1241
296k
}
1242
1243
Expect<void>
1244
1.77M
Validator::validate(const AST::Component::DefType &DType) noexcept {
1245
1.77M
  auto ReportError = [](auto E) {
1246
391
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_DefType));
1247
391
    return E;
1248
391
  };
1249
1250
1.77M
  if (DType.isDefValType()) {
1251
86.5k
    EXPECTED_TRY(validate(DType.getDefValType()).map_error(ReportError));
1252
1.69M
  } else if (DType.isFuncType()) {
1253
11.7k
    EXPECTED_TRY(validate(DType.getFuncType()).map_error(ReportError));
1254
1.68M
  } else if (DType.isComponentType()) {
1255
1.12M
    EXPECTED_TRY(validate(DType.getComponentType()).map_error(ReportError));
1256
1.12M
  } else if (DType.isInstanceType()) {
1257
535k
    EXPECTED_TRY(validate(DType.getInstanceType()).map_error(ReportError));
1258
535k
  } else if (DType.isResourceType()) {
1259
21.0k
    EXPECTED_TRY(validate(DType.getResourceType()).map_error(ReportError));
1260
21.0k
  } else {
1261
0
    assumingUnreachable();
1262
0
  }
1263
  // addType records body/id/locality for resource DefTypes in one step.
1264
1.77M
  CompCtx.addType(&DType);
1265
1.77M
  return {};
1266
1.77M
}
1267
1268
Expect<void>
1269
9.08k
Validator::validate(const AST::Component::Canonical &Canon) noexcept {
1270
9.08k
  switch (Canon.getOpCode()) {
1271
4.88k
  case ComponentCanonOpCode::Lift:
1272
4.88k
    return validateCanonLift(Canon);
1273
1.21k
  case ComponentCanonOpCode::Lower:
1274
1.21k
    return validateCanonLower(Canon);
1275
782
  case ComponentCanonOpCode::Resource__new:
1276
782
    return validateCanonResourceNew(Canon);
1277
456
  case ComponentCanonOpCode::Resource__rep:
1278
456
    return validateCanonResourceRep(Canon);
1279
1.33k
  case ComponentCanonOpCode::Resource__drop:
1280
1.72k
  case ComponentCanonOpCode::Resource__drop_async:
1281
1.72k
    return validateCanonResourceDrop(Canon);
1282
11
  default:
1283
11
    spdlog::error(ErrCode::Value::ComponentNotImplValidator);
1284
11
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1285
11
    return Unexpect(ErrCode::Value::ComponentNotImplValidator);
1286
9.08k
  }
1287
9.08k
}
1288
1289
Expect<void> Validator::validateCanonOptions(
1290
    ComponentCanonOpCode Code,
1291
8.84k
    Span<const AST::Component::CanonOpt> Opts) noexcept {
1292
8.84k
  using OptCode = ComponentCanonOptCode;
1293
8.84k
  using CanonOp = ComponentCanonOpCode;
1294
1295
  // Only canon lift/lower accept canonical options. Any other built-in
1296
  // (resource.new/rep/drop, drop_async, ...) must be invoked without options.
1297
8.84k
  if (Code != CanonOp::Lift && Code != CanonOp::Lower && !Opts.empty()) {
1298
0
    spdlog::error(ErrCode::Value::InvalidCanonOption);
1299
0
    spdlog::error(
1300
0
        "    canonical options are not allowed for this canon built-in"sv);
1301
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1302
0
    return Unexpect(ErrCode::Value::InvalidCanonOption);
1303
0
  }
1304
1305
8.84k
  bool HasEncoding = false;
1306
8.84k
  bool HasMemory = false;
1307
8.84k
  bool HasRealloc = false;
1308
8.84k
  bool HasPostReturn = false;
1309
8.84k
  bool HasAsync = false;
1310
8.84k
  bool HasCallback = false;
1311
8.84k
  bool HasAlwaysTaskReturn = false;
1312
8.84k
  uint32_t ReallocIdx = 0;
1313
8.84k
  uint32_t CallbackIdx = 0;
1314
8.84k
  uint32_t PostReturnIdx = 0;
1315
8.84k
  uint32_t MemoryIdx = 0;
1316
1317
8.84k
  auto RejectDup = [&](const char *Name) -> Expect<void> {
1318
13
    spdlog::error(ErrCode::Value::InvalidCanonOption);
1319
13
    spdlog::error("    canonical option '{}' appears more than once"sv, Name);
1320
13
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1321
13
    return Unexpect(ErrCode::Value::InvalidCanonOption);
1322
13
  };
1323
8.84k
  auto RejectSite = [&](const char *Name) -> Expect<void> {
1324
3
    spdlog::error(ErrCode::Value::InvalidCanonOption);
1325
3
    spdlog::error(
1326
3
        "    canonical option '{}' is not allowed in this canon built-in"sv,
1327
3
        Name);
1328
3
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1329
3
    return Unexpect(ErrCode::Value::InvalidCanonOption);
1330
3
  };
1331
1332
8.84k
  for (const auto &Opt : Opts) {
1333
1.25k
    switch (Opt.getCode()) {
1334
558
    case OptCode::Encode_UTF8:
1335
1.04k
    case OptCode::Encode_UTF16:
1336
1.15k
    case OptCode::Encode_Latin1:
1337
1.15k
      if (HasEncoding) {
1338
8
        return RejectDup("string-encoding");
1339
8
      }
1340
1.14k
      HasEncoding = true;
1341
1.14k
      break;
1342
10
    case OptCode::Memory:
1343
10
      if (HasMemory) {
1344
1
        return RejectDup("memory");
1345
1
      }
1346
9
      HasMemory = true;
1347
9
      MemoryIdx = Opt.getIndex();
1348
9
      break;
1349
5
    case OptCode::Realloc:
1350
5
      if (HasRealloc) {
1351
1
        return RejectDup("realloc");
1352
1
      }
1353
4
      HasRealloc = true;
1354
4
      ReallocIdx = Opt.getIndex();
1355
4
      break;
1356
3
    case OptCode::PostReturn:
1357
3
      if (Code != CanonOp::Lift) {
1358
1
        return RejectSite("post-return");
1359
1
      }
1360
2
      if (HasPostReturn) {
1361
0
        return RejectDup("post-return");
1362
0
      }
1363
2
      HasPostReturn = true;
1364
2
      PostReturnIdx = Opt.getIndex();
1365
2
      break;
1366
72
    case OptCode::Async:
1367
72
      if (HasAsync) {
1368
1
        return RejectDup("async");
1369
1
      }
1370
71
      HasAsync = true;
1371
71
      break;
1372
5
    case OptCode::Callback:
1373
5
      if (Code != CanonOp::Lift) {
1374
1
        return RejectSite("callback");
1375
1
      }
1376
4
      if (HasCallback) {
1377
0
        return RejectDup("callback");
1378
0
      }
1379
4
      HasCallback = true;
1380
4
      CallbackIdx = Opt.getIndex();
1381
4
      break;
1382
8
    case OptCode::AlwaysTaskReturn:
1383
8
      if (Code != CanonOp::Lift) {
1384
1
        return RejectSite("always-task-return");
1385
1
      }
1386
7
      if (HasAlwaysTaskReturn) {
1387
2
        return RejectDup("always-task-return");
1388
2
      }
1389
5
      HasAlwaysTaskReturn = true;
1390
5
      break;
1391
0
    default:
1392
0
      spdlog::error(ErrCode::Value::UnknownCanonicalOption);
1393
0
      spdlog::error("    unknown canonical option code 0x{:02x}"sv,
1394
0
                    static_cast<unsigned>(Opt.getCode()));
1395
0
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1396
0
      return Unexpect(ErrCode::Value::UnknownCanonicalOption);
1397
1.25k
    }
1398
1.25k
  }
1399
1400
  // Structural rules.
1401
8.82k
  if (HasPostReturn && HasAsync) {
1402
0
    spdlog::error(ErrCode::Value::InvalidCanonOption);
1403
0
    spdlog::error(
1404
0
        "    canonical options 'post-return' and 'async' are mutually exclusive"sv);
1405
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1406
0
    return Unexpect(ErrCode::Value::InvalidCanonOption);
1407
0
  }
1408
8.82k
  if (HasCallback && !HasAsync) {
1409
0
    spdlog::error(ErrCode::Value::InvalidCanonOption);
1410
0
    spdlog::error("    canonical option 'callback' requires 'async'"sv);
1411
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1412
0
    return Unexpect(ErrCode::Value::InvalidCanonOption);
1413
0
  }
1414
8.82k
  if (HasAlwaysTaskReturn && !HasAsync) {
1415
0
    spdlog::error(ErrCode::Value::InvalidCanonOption);
1416
0
    spdlog::error(
1417
0
        "    canonical option 'always-task-return' requires 'async'"sv);
1418
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1419
0
    return Unexpect(ErrCode::Value::InvalidCanonOption);
1420
0
  }
1421
8.82k
  if (HasRealloc && !HasMemory) {
1422
1
    spdlog::error(ErrCode::Value::InvalidCanonOption);
1423
1
    spdlog::error(
1424
1
        "    canonical option 'realloc' requires 'memory' to also be specified"sv);
1425
1
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1426
1
    return Unexpect(ErrCode::Value::InvalidCanonOption);
1427
1
  }
1428
1429
  // Index bounds checks. Core func signature body checks (realloc/callback)
1430
  // deferred as GAP-C-5b once getCoreFunc signatures are populated.
1431
8.82k
  if (HasMemory &&
1432
7
      MemoryIdx >= CompCtx.getCoreSortIndexSize(
1433
7
                       AST::Component::Sort::CoreSortType::Memory)) {
1434
7
    spdlog::error(ErrCode::Value::InvalidIndex);
1435
7
    spdlog::error(
1436
7
        "    canonical option 'memory': core memory index {} out of bounds"sv,
1437
7
        MemoryIdx);
1438
7
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1439
7
    return Unexpect(ErrCode::Value::InvalidIndex);
1440
7
  }
1441
8.81k
  const uint32_t CoreFuncSpaceSize =
1442
8.81k
      CompCtx.getCoreSortIndexSize(AST::Component::Sort::CoreSortType::Func);
1443
8.81k
  if (HasRealloc && ReallocIdx >= CoreFuncSpaceSize) {
1444
0
    spdlog::error(ErrCode::Value::InvalidIndex);
1445
0
    spdlog::error(
1446
0
        "    canonical option 'realloc': core func index {} out of bounds"sv,
1447
0
        ReallocIdx);
1448
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1449
0
    return Unexpect(ErrCode::Value::InvalidIndex);
1450
0
  }
1451
8.81k
  if (HasCallback && CallbackIdx >= CoreFuncSpaceSize) {
1452
0
    spdlog::error(ErrCode::Value::InvalidIndex);
1453
0
    spdlog::error(
1454
0
        "    canonical option 'callback': core func index {} out of bounds"sv,
1455
0
        CallbackIdx);
1456
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1457
0
    return Unexpect(ErrCode::Value::InvalidIndex);
1458
0
  }
1459
8.81k
  if (HasPostReturn && PostReturnIdx >= CoreFuncSpaceSize) {
1460
0
    spdlog::error(ErrCode::Value::InvalidIndex);
1461
0
    spdlog::error(
1462
0
        "    canonical option 'post-return': core func index {} out of bounds"sv,
1463
0
        PostReturnIdx);
1464
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1465
0
    return Unexpect(ErrCode::Value::InvalidIndex);
1466
0
  }
1467
8.81k
  return {};
1468
8.81k
}
1469
1470
Expect<void>
1471
4.88k
Validator::validateCanonLift(const AST::Component::Canonical &Canon) noexcept {
1472
4.88k
  const uint32_t CoreFuncIdx = Canon.getIndex();
1473
4.88k
  const uint32_t CoreFuncSpaceSize =
1474
4.88k
      CompCtx.getCoreSortIndexSize(AST::Component::Sort::CoreSortType::Func);
1475
  // 1. Core func index bounds.
1476
4.88k
  if (CoreFuncIdx >= CoreFuncSpaceSize) {
1477
35
    spdlog::error(ErrCode::Value::InvalidIndex);
1478
35
    spdlog::error(
1479
35
        "    canon lift: core func index {} exceeds core func index space size {}"sv,
1480
35
        CoreFuncIdx, CoreFuncSpaceSize);
1481
35
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1482
35
    return Unexpect(ErrCode::Value::InvalidIndex);
1483
35
  }
1484
4.85k
  const uint32_t TypeIdx = Canon.getTargetIndex();
1485
4.85k
  const uint32_t TypeSpaceSize =
1486
4.85k
      CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type);
1487
  // 2. Target type index bounds.
1488
4.85k
  if (TypeIdx >= TypeSpaceSize) {
1489
8
    spdlog::error(ErrCode::Value::InvalidIndex);
1490
8
    spdlog::error(
1491
8
        "    canon lift: type index {} exceeds type index space size {}"sv,
1492
8
        TypeIdx, TypeSpaceSize);
1493
8
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1494
8
    return Unexpect(ErrCode::Value::InvalidIndex);
1495
8
  }
1496
  // 3. Target type must be a component FuncType.
1497
4.84k
  const auto *DT = CompCtx.getDefType(TypeIdx);
1498
4.84k
  if (DT == nullptr) {
1499
    // Unresolved slot: the index was registered by an import or outer alias
1500
    // but the concrete definition has not been filled in yet.
1501
1
    spdlog::error(ErrCode::Value::InvalidTypeReference);
1502
1
    spdlog::error(
1503
1
        "    canon lift: target type index {} is an unresolved type slot"sv,
1504
1
        TypeIdx);
1505
1
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1506
1
    return Unexpect(ErrCode::Value::InvalidTypeReference);
1507
1
  }
1508
4.84k
  if (!DT->isFuncType()) {
1509
2
    spdlog::error(ErrCode::Value::InvalidTypeReference);
1510
2
    spdlog::error(
1511
2
        "    canon lift: target type index {} does not reference a component func type"sv,
1512
2
        TypeIdx);
1513
2
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1514
2
    return Unexpect(ErrCode::Value::InvalidTypeReference);
1515
2
  }
1516
  // 4. Validate canonical options (Lift site allows all).
1517
4.84k
  EXPECTED_TRY(validateCanonOptions(Canon.getOpCode(), Canon.getOptions()));
1518
  // 5. Allocate component func, binding the resolved FuncType. Full ABI
1519
  // signature match (flat_lifted) deferred as GAP-C-1b.
1520
4.83k
  CompCtx.addFunc(&DT->getFuncType());
1521
4.83k
  return {};
1522
4.84k
}
1523
1524
Expect<void>
1525
1.21k
Validator::validateCanonLower(const AST::Component::Canonical &Canon) noexcept {
1526
1.21k
  const uint32_t FuncIdx = Canon.getIndex();
1527
1.21k
  const uint32_t FuncSpaceSize =
1528
1.21k
      CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Func);
1529
  // 1. Component func index bounds.
1530
1.21k
  if (FuncIdx >= FuncSpaceSize) {
1531
6
    spdlog::error(ErrCode::Value::InvalidIndex);
1532
6
    spdlog::error(
1533
6
        "    canon lower: component func index {} exceeds func index space size {}"sv,
1534
6
        FuncIdx, FuncSpaceSize);
1535
6
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1536
6
    return Unexpect(ErrCode::Value::InvalidIndex);
1537
6
  }
1538
  // 2. Validate canonical options (per-site rules for Lower).
1539
1.21k
  EXPECTED_TRY(validateCanonOptions(Canon.getOpCode(), Canon.getOptions()));
1540
  // 3. Allocate the resulting core func. Full ABI signature synthesis
1541
  // (flatten_functype for lower) deferred as GAP-C-2b.
1542
1.19k
  CompCtx.addCoreFunc();
1543
1.19k
  return {};
1544
1.21k
}
1545
1546
Expect<void> Validator::validateCanonResourceNew(
1547
782
    const AST::Component::Canonical &Canon) noexcept {
1548
782
  const uint32_t Idx = Canon.getIndex();
1549
782
  const uint32_t TypeSpaceSize =
1550
782
      CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type);
1551
  // 1. Type index bounds.
1552
782
  if (Idx >= TypeSpaceSize) {
1553
22
    spdlog::error(ErrCode::Value::InvalidIndex);
1554
22
    spdlog::error(
1555
22
        "    canon resource.new: type index {} exceeds type index space size {}"sv,
1556
22
        Idx, TypeSpaceSize);
1557
22
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1558
22
    return Unexpect(ErrCode::Value::InvalidIndex);
1559
22
  }
1560
  // 2. Type must be a locally-defined resource.
1561
760
  const auto *RInfo = CompCtx.getResource(Idx);
1562
760
  if (RInfo == nullptr) {
1563
19
    spdlog::error(ErrCode::Value::InvalidTypeReference);
1564
19
    spdlog::error(
1565
19
        "    canon resource.new: type index {} does not reference a resource"sv,
1566
19
        Idx);
1567
19
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1568
19
    return Unexpect(ErrCode::Value::InvalidTypeReference);
1569
19
  }
1570
741
  if (!RInfo->LocallyDefined) {
1571
1
    spdlog::error(ErrCode::Value::InvalidTypeReference);
1572
1
    spdlog::error(
1573
1
        "    canon resource.new: type index {} is not locally defined (imported or outer-aliased resources are not allowed)"sv,
1574
1
        Idx);
1575
1
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1576
1
    return Unexpect(ErrCode::Value::InvalidTypeReference);
1577
1
  }
1578
  // 4. Validate canonical options.
1579
740
  EXPECTED_TRY(validateCanonOptions(Canon.getOpCode(), Canon.getOptions()));
1580
  // 5. Allocate the resulting core func with synthesized signature
1581
  // [i32] -> [i32] (rep i32 in, new handle out).
1582
740
  CompCtx.addCoreFunc(&CoreFuncType_I32_I32);
1583
740
  return {};
1584
740
}
1585
1586
Expect<void> Validator::validateCanonResourceRep(
1587
456
    const AST::Component::Canonical &Canon) noexcept {
1588
456
  const uint32_t Idx = Canon.getIndex();
1589
456
  const uint32_t TypeSpaceSize =
1590
456
      CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type);
1591
  // 1. Type index bounds.
1592
456
  if (Idx >= TypeSpaceSize) {
1593
31
    spdlog::error(ErrCode::Value::InvalidIndex);
1594
31
    spdlog::error(
1595
31
        "    canon resource.rep: type index {} exceeds type index space size {}"sv,
1596
31
        Idx, TypeSpaceSize);
1597
31
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1598
31
    return Unexpect(ErrCode::Value::InvalidIndex);
1599
31
  }
1600
  // 2. Type must be a locally-defined resource.
1601
425
  const auto *RInfo = CompCtx.getResource(Idx);
1602
425
  if (RInfo == nullptr) {
1603
34
    spdlog::error(ErrCode::Value::InvalidTypeReference);
1604
34
    spdlog::error(
1605
34
        "    canon resource.rep: type index {} does not reference a resource"sv,
1606
34
        Idx);
1607
34
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1608
34
    return Unexpect(ErrCode::Value::InvalidTypeReference);
1609
34
  }
1610
391
  if (!RInfo->LocallyDefined) {
1611
3
    spdlog::error(ErrCode::Value::InvalidTypeReference);
1612
3
    spdlog::error(
1613
3
        "    canon resource.rep: type index {} is not locally defined (imported or outer-aliased resources are not allowed)"sv,
1614
3
        Idx);
1615
3
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1616
3
    return Unexpect(ErrCode::Value::InvalidTypeReference);
1617
3
  }
1618
  // 4. Validate canonical options.
1619
388
  EXPECTED_TRY(validateCanonOptions(Canon.getOpCode(), Canon.getOptions()));
1620
  // 5. Allocate the resulting core func with synthesized signature
1621
  // [i32] -> [i32] (handle in, rep out).
1622
388
  CompCtx.addCoreFunc(&CoreFuncType_I32_I32);
1623
388
  return {};
1624
388
}
1625
1626
Expect<void> Validator::validateCanonResourceDrop(
1627
1.72k
    const AST::Component::Canonical &Canon) noexcept {
1628
1.72k
  const uint32_t Idx = Canon.getIndex();
1629
1.72k
  const uint32_t TypeSpaceSize =
1630
1.72k
      CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type);
1631
  // 1. Type index bounds.
1632
1.72k
  if (Idx >= TypeSpaceSize) {
1633
41
    spdlog::error(ErrCode::Value::InvalidIndex);
1634
41
    spdlog::error(
1635
41
        "    canon resource.drop: type index {} exceeds type index space size {}"sv,
1636
41
        Idx, TypeSpaceSize);
1637
41
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1638
41
    return Unexpect(ErrCode::Value::InvalidIndex);
1639
41
  }
1640
  // 2. Type must be a resource type.
1641
1.68k
  if (CompCtx.getResource(Idx) == nullptr) {
1642
24
    spdlog::error(ErrCode::Value::InvalidTypeReference);
1643
24
    spdlog::error(
1644
24
        "    canon resource.drop: type index {} does not reference a resource"sv,
1645
24
        Idx);
1646
24
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Canonical));
1647
24
    return Unexpect(ErrCode::Value::InvalidTypeReference);
1648
24
  }
1649
  // 3. resource.drop accepts both local and imported resources — no locality
1650
  // check.
1651
  // 4. Validate canonical options.
1652
1.66k
  EXPECTED_TRY(validateCanonOptions(Canon.getOpCode(), Canon.getOptions()));
1653
  // 5. Allocate the resulting core func with synthesized signature
1654
  // [i32] -> [] (handle in, no result — matches the resource destructor
1655
  // shape required by validate(ResourceType)).
1656
1.66k
  CompCtx.addCoreFunc(&CoreFuncType_I32_Void);
1657
1.66k
  return {};
1658
1.66k
}
1659
1660
6.95k
Expect<void> Validator::validate(const AST::Component::Import &Im) noexcept {
1661
  // Validation steps:
1662
  //   1. Validate the externdesc and introduce the imported entity into
1663
  //      its sort's index space.
1664
  //   2. Parse the import name per the structured import-name grammar.
1665
  //   3. Enforce the annotated-name constraints ([constructor]/[method]/
1666
  //      [static] only on func imports).
1667
  //   4. Reject duplicate import names (strongly-unique across imports).
1668
  //
1669
  // The per-annotated-name structural checks (constructor result type,
1670
  // method `self` param, static resource name in scope) are not yet
1671
  // implemented and tracked as follow-ups below.
1672
  // Snapshot the type space size before validating the desc so we can
1673
  // recover the new type index allocated by a TypeBound (sub resource).
1674
6.95k
  const uint32_t TypeSpaceBefore =
1675
6.95k
      CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type);
1676
1677
6.95k
  EXPECTED_TRY(validate(Im.getDesc()).map_error([](auto E) {
1678
6.87k
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Import));
1679
6.87k
    return E;
1680
6.87k
  }));
1681
1682
13.1k
  EXPECTED_TRY(ComponentName CName,
1683
13.1k
               ComponentName::parse(Im.getName()).map_error([](auto E) {
1684
13.1k
                 spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Import));
1685
13.1k
                 return E;
1686
13.1k
               }));
1687
1688
  // Annotated plainnames ([constructor], [method], [static]) can only appear
1689
  // on func imports.
1690
13.1k
  switch (CName.getKind()) {
1691
6
  case ComponentNameKind::Constructor:
1692
10
  case ComponentNameKind::Method:
1693
20
  case ComponentNameKind::Static:
1694
20
    if (Im.getDesc().getDescType() !=
1695
20
        AST::Component::ExternDesc::DescType::FuncType) {
1696
20
      spdlog::error(ErrCode::Value::ComponentInvalidName);
1697
20
      spdlog::error("    Import: annotated name requires func type"sv);
1698
20
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Import));
1699
20
      return Unexpect(ErrCode::Value::ComponentInvalidName);
1700
20
    }
1701
0
    break;
1702
6.21k
  default:
1703
6.21k
    break;
1704
13.1k
  }
1705
1706
  // Resolve the resource name referenced by an annotated name. The resource
1707
  // must have been previously introduced by a TypeBound import or export
1708
  // with a kebab-case label in this scope.
1709
  // TODO: extend to the full structural checks once func-type bodies are
1710
  // walked here:
1711
  //   - [constructor]R: result type must be (own $R).
1712
  //   - [method]R.f:    first param must be (borrow $R).
1713
  //   - [static]R.f:    no `self` param of (borrow $R).
1714
6.21k
  std::string_view ResourceLabel;
1715
6.21k
  switch (CName.getKind()) {
1716
0
  case ComponentNameKind::Constructor:
1717
0
    ResourceLabel = CName.getDetail().get<ConstructorDetail>().Label;
1718
0
    break;
1719
0
  case ComponentNameKind::Method:
1720
0
    ResourceLabel = CName.getDetail().get<MethodDetail>().Resource;
1721
0
    break;
1722
0
  case ComponentNameKind::Static:
1723
0
    ResourceLabel = CName.getDetail().get<StaticDetail>().Resource;
1724
0
    break;
1725
6.21k
  default:
1726
6.21k
    break;
1727
6.21k
  }
1728
6.21k
  if (!ResourceLabel.empty() && !CompCtx.hasResourceLabel(ResourceLabel)) {
1729
0
    spdlog::error(ErrCode::Value::ComponentInvalidName);
1730
0
    spdlog::error(
1731
0
        "    Import: annotated name references unknown resource '{}'"sv,
1732
0
        ResourceLabel);
1733
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Import));
1734
0
    return Unexpect(ErrCode::Value::ComponentInvalidName);
1735
0
  }
1736
1737
6.21k
  if (!CompCtx.addImportedName(CName)) {
1738
93
    spdlog::error(ErrCode::Value::ComponentDuplicateName);
1739
93
    spdlog::error("    Import: Duplicate import name"sv);
1740
93
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Import));
1741
93
    return Unexpect(ErrCode::Value::ComponentDuplicateName);
1742
93
  }
1743
1744
  // If this import introduced a TypeBound resource with a label name,
1745
  // register the label so subsequent annotated names can reference it.
1746
6.12k
  if (Im.getDesc().getDescType() ==
1747
6.12k
          AST::Component::ExternDesc::DescType::TypeBound &&
1748
3.01k
      CName.getKind() == ComponentNameKind::Label) {
1749
2.97k
    CompCtx.addResourceLabel(Im.getName(), TypeSpaceBefore);
1750
2.97k
  }
1751
1752
6.12k
  return {};
1753
6.21k
}
1754
1755
894
Expect<void> Validator::validate(const AST::Component::Export &Ex) noexcept {
1756
  // Validation steps:
1757
  //   1. `sortidx` is in-bounds and (for core sorts) must be `core module`.
1758
  //   2. If an `externdesc` ascription is present, it must be a supertype
1759
  //      of the inferred externdesc of the `sortidx` (kind-only check for
1760
  //      now; structural subtype is a follow-up).
1761
  //   3. The export name parses under the export-name grammar.
1762
  //   4. The export name is strongly-unique across exports.
1763
  //   5. The export introduces a new index aliasing the definition in the
1764
  //      component's own index space for its sort.
1765
  //
1766
  // Not yet enforced: transitive resource-avoidance in exported types;
1767
  // flipping the "consumed" flag for value exports.
1768
1769
  // Validate the sortidx bounds.
1770
894
  const auto &Sort = Ex.getSortIndex().getSort();
1771
894
  uint32_t Idx = Ex.getSortIndex().getIdx();
1772
894
  if (Sort.isCore()) {
1773
    // The externdesc grammar permits only `core module` as a component-level
1774
    // core export — no other core sort is exportable from a component.
1775
10
    if (Sort.getCoreSortType() != AST::Component::Sort::CoreSortType::Module) {
1776
2
      spdlog::error(ErrCode::Value::InvalidTypeReference);
1777
2
      spdlog::error(
1778
2
          "    Export: core sort other than `core module` is not allowed"sv);
1779
2
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Export));
1780
2
      return Unexpect(ErrCode::Value::InvalidTypeReference);
1781
2
    }
1782
8
    if (Idx >= CompCtx.getCoreSortIndexSize(Sort.getCoreSortType())) {
1783
7
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
1784
7
      spdlog::error("    Export: sort index {} out of bounds"sv, Idx);
1785
7
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Export));
1786
7
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
1787
7
    }
1788
884
  } else {
1789
884
    if (Idx >= CompCtx.getSortIndexSize(Sort.getSortType())) {
1790
10
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
1791
10
      spdlog::error("    Export: sort index {} out of bounds"sv, Idx);
1792
10
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Export));
1793
10
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
1794
10
    }
1795
884
  }
1796
1797
  // If an externdesc ascription is present, the spec requires it to be a
1798
  // supertype of the inferred externdesc of the sortidx. For now we only
1799
  // enforce that the ascription's kind matches the sortidx's sort; full
1800
  // structural subtype between ascription and inferred type is not yet
1801
  // implemented.
1802
875
  if (Ex.getDesc().has_value() &&
1803
20
      !sortMatchesDescType(Sort, Ex.getDesc()->getDescType())) {
1804
1
    spdlog::error(ErrCode::Value::ExportAscriptionIncompatible);
1805
1
    spdlog::error(
1806
1
        "    Export: ascribed externdesc kind does not match sortidx sort"sv);
1807
1
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Export));
1808
1
    return Unexpect(ErrCode::Value::ExportAscriptionIncompatible);
1809
1
  }
1810
1811
  // Validate name grammar, then enforce strong-uniqueness across exports.
1812
1.66k
  EXPECTED_TRY(ComponentName CName,
1813
1.66k
               validateExportName(Ex.getName()).map_error([](auto E) {
1814
1.66k
                 spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Export));
1815
1.66k
                 return E;
1816
1.66k
               }));
1817
1.66k
  if (!CompCtx.addExportedName(CName)) {
1818
20
    spdlog::error(ErrCode::Value::ComponentDuplicateName);
1819
20
    spdlog::error("    Export: Duplicate export name '{}'"sv, Ex.getName());
1820
20
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Export));
1821
20
    return Unexpect(ErrCode::Value::ComponentDuplicateName);
1822
20
  }
1823
1824
  // Every export introduces a new index that aliases the exported
1825
  // definition in the component's own index space for that sort. For
1826
  // instance-sort exports we also copy/build the export table so that a
1827
  // later alias-export against this slot can resolve its sub-exports.
1828
769
  if (Sort.isCore()) {
1829
0
    CompCtx.incCoreSortIndexSize(Sort.getCoreSortType());
1830
769
  } else {
1831
769
    const AST::Component::InstanceType *IT = nullptr;
1832
769
    const bool IsInst =
1833
769
        Sort.getSortType() == AST::Component::Sort::SortType::Instance;
1834
769
    const bool HasInstAscription =
1835
769
        IsInst && Ex.getDesc().has_value() &&
1836
0
        Ex.getDesc()->getDescType() ==
1837
0
            AST::Component::ExternDesc::DescType::InstanceType;
1838
769
    if (HasInstAscription) {
1839
0
      IT = CompCtx.getInstanceType(Ex.getDesc()->getTypeIndex());
1840
0
      if (IT != nullptr) {
1841
0
        if (auto Missing = findMissingRequiredExport(Idx, *IT)) {
1842
0
          spdlog::error(ErrCode::Value::ExportAscriptionIncompatible);
1843
0
          spdlog::error(
1844
0
              "    Export: ascribed instance type requires export '{}' "
1845
0
              "not present in inferred type"sv,
1846
0
              *Missing);
1847
0
          spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Export));
1848
0
          return Unexpect(ErrCode::Value::ExportAscriptionIncompatible);
1849
0
        }
1850
0
      }
1851
0
    }
1852
769
    uint32_t NewIdx = CompCtx.incSortIndexSize(Sort.getSortType());
1853
769
    if (IsInst) {
1854
6
      if (IT != nullptr) {
1855
0
        populateInstanceFromType(NewIdx, *IT);
1856
6
      } else {
1857
        // Either no ascription, or the ascription's type index didn't
1858
        // resolve to an inline InstanceType here (cross-scope type-index
1859
        // resolution is not yet implemented). Copy the source instance's
1860
        // inferred exports so a later alias-export on this slot can still
1861
        // find them.
1862
6
        const auto &SrcExports = CompCtx.getInstance(Idx).Exports;
1863
6
        for (const auto &[Name, IE] : SrcExports) {
1864
3
          CompCtx.addInstanceExport(NewIdx, Name, IE.ST, IE.IT,
1865
3
                                    IE.NestedInstIdx);
1866
3
        }
1867
6
      }
1868
6
    }
1869
769
  }
1870
769
  return {};
1871
769
}
1872
1873
Expect<void>
1874
7.90k
Validator::validate(const AST::Component::ExternDesc &Desc) noexcept {
1875
  // Validating an externdesc introduces a new entry into the index space
1876
  // matching the descriptor's sort:
1877
  //   * core module (CoreType) → core:module
1878
  //   * func                   → func
1879
  //   * value                  → value (consumed=false; not yet tracked)
1880
  //   * type (eq i)            → type aliased to i (resource property
1881
  //                              propagated when i refers to a resource)
1882
  //   * type (sub resource)    → fresh abstract resource type
1883
  //   * component / instance   → component / instance
1884
  //
1885
  // GAP-ED-1: bounds-check the referenced type index. The CoreType
1886
  // externdesc indexes the core:type space (the moduletype lives there);
1887
  // FuncType / ComponentType / InstanceType all index the component-level
1888
  // type space. Kind-of-type checks (e.g. that a FuncType index actually
1889
  // resolves to a component func type, not a record) are a follow-up that
1890
  // needs the type body to be retained on every type entry.
1891
7.90k
  switch (Desc.getDescType()) {
1892
174
  case AST::Component::ExternDesc::DescType::CoreType: {
1893
174
    const uint32_t RefIdx = Desc.getTypeIndex();
1894
174
    const uint32_t CoreTypeSize =
1895
174
        CompCtx.getCoreSortIndexSize(AST::Component::Sort::CoreSortType::Type);
1896
174
    if (RefIdx >= CoreTypeSize) {
1897
16
      spdlog::error(ErrCode::Value::InvalidIndex);
1898
16
      spdlog::error(
1899
16
          "    ExternDesc: core type index {} exceeds core:type index space size {}"sv,
1900
16
          RefIdx, CoreTypeSize);
1901
16
      return Unexpect(ErrCode::Value::InvalidIndex);
1902
16
    }
1903
158
    break;
1904
174
  }
1905
559
  case AST::Component::ExternDesc::DescType::FuncType:
1906
961
  case AST::Component::ExternDesc::DescType::ComponentType:
1907
1.24k
  case AST::Component::ExternDesc::DescType::InstanceType: {
1908
1.24k
    const uint32_t RefIdx = Desc.getTypeIndex();
1909
1.24k
    const uint32_t TypeSize =
1910
1.24k
        CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type);
1911
1.24k
    if (RefIdx >= TypeSize) {
1912
50
      spdlog::error(ErrCode::Value::InvalidIndex);
1913
50
      spdlog::error(
1914
50
          "    ExternDesc: referenced type index {} exceeds type index space size {}"sv,
1915
50
          RefIdx, TypeSize);
1916
50
      return Unexpect(ErrCode::Value::InvalidIndex);
1917
50
    }
1918
1.19k
    break;
1919
1.24k
  }
1920
6.48k
  default:
1921
6.48k
    break;
1922
7.90k
  }
1923
1924
7.83k
  switch (Desc.getDescType()) {
1925
158
  case AST::Component::ExternDesc::DescType::CoreType: {
1926
    // The CoreType externdesc is `(core module (type i))`, so it introduces
1927
    // a new core:module slot bound to the moduletype at core:type idx i.
1928
158
    const auto *CT = CompCtx.getCoreModuleType(Desc.getTypeIndex());
1929
158
    CompCtx.addCoreModule(CT);
1930
158
    break;
1931
0
  }
1932
523
  case AST::Component::ExternDesc::DescType::FuncType:
1933
523
    CompCtx.addFunc();
1934
523
    break;
1935
3.39k
  case AST::Component::ExternDesc::DescType::ValueBound:
1936
3.39k
    CompCtx.addValue();
1937
3.39k
    break;
1938
3.09k
  case AST::Component::ExternDesc::DescType::TypeBound:
1939
3.09k
    if (Desc.isEqType()) {
1940
      // (type (eq i)) — alias type i
1941
91
      uint32_t RefIdx = Desc.getTypeIndex();
1942
91
      if (RefIdx >=
1943
91
          CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type)) {
1944
18
        spdlog::error(ErrCode::Value::InvalidIndex);
1945
18
        spdlog::error("    ExternDesc: eq type bound index {} out of bounds"sv,
1946
18
                      RefIdx);
1947
18
        return Unexpect(ErrCode::Value::InvalidIndex);
1948
18
      }
1949
      // (eq i): inherits the source resource's id; body lives on the
1950
      // shared registry entry.
1951
73
      uint32_t NewIdx = CompCtx.addType(nullptr, /*IsLocal=*/false);
1952
73
      if (const auto *SrcInfo = CompCtx.getResource(RefIdx)) {
1953
25
        CompCtx.addResource(NewIdx, {SrcInfo->Id, /*LocallyDefined=*/false});
1954
25
      }
1955
3.00k
    } else {
1956
      // (sub resource): abstract import — fresh id with no body.
1957
3.00k
      uint32_t NewIdx = CompCtx.addType(nullptr, /*IsLocal=*/false);
1958
3.00k
      CompCtx.addResource(NewIdx, {CompCtx.allocateFreshResourceId(),
1959
3.00k
                                   /*LocallyDefined=*/false});
1960
3.00k
    }
1961
3.07k
    break;
1962
3.07k
  case AST::Component::ExternDesc::DescType::ComponentType: {
1963
    // Bind the new component slot to its ComponentType so a later
1964
    // instantiation can pull imports/exports from it (GAP-I-1).
1965
396
    const auto *CT = CompCtx.getComponentType(Desc.getTypeIndex());
1966
396
    CompCtx.addComponent(CT);
1967
396
    break;
1968
3.09k
  }
1969
272
  case AST::Component::ExternDesc::DescType::InstanceType: {
1970
    // GAP-ED-2: populate exports from the referenced InstanceType so
1971
    // alias-export resolves. GAP-I-5b: also bind it to the slot.
1972
272
    const auto *IT = CompCtx.getInstanceType(Desc.getTypeIndex());
1973
272
    uint32_t InstIdx = CompCtx.addInstance(IT);
1974
272
    if (IT != nullptr) {
1975
107
      populateInstanceFromType(InstIdx, *IT);
1976
107
    }
1977
    // If the type index resolved against an outer scope's InstanceType,
1978
    // populating from it requires cross-scope walking (the rest of
1979
    // GAP-DECL-ED). For now the instance keeps an empty export table when
1980
    // the InstanceType body isn't visible in this scope.
1981
272
    break;
1982
3.09k
  }
1983
0
  default:
1984
0
    assumingUnreachable();
1985
7.83k
  }
1986
1987
7.81k
  return {};
1988
7.83k
}
1989
1990
Expect<void>
1991
1.48k
Validator::validate(const AST::Component::CoreImportDesc &Desc) noexcept {
1992
1.48k
  if (Desc.isFunc()) {
1993
42
    uint32_t TypeIdx = Desc.getTypeIndex();
1994
42
    if (TypeIdx >= CompCtx.getCoreSortIndexSize(
1995
42
                       AST::Component::Sort::CoreSortType::Type)) {
1996
36
      spdlog::error(ErrCode::Value::InvalidIndex);
1997
36
      spdlog::error("    CoreImportDesc: func type index {} out of bounds"sv,
1998
36
                    TypeIdx);
1999
36
      return Unexpect(ErrCode::Value::InvalidIndex);
2000
36
    }
2001
6
    CompCtx.addCoreFunc();
2002
1.44k
  } else if (Desc.isTable()) {
2003
389
    CompCtx.addCoreTable();
2004
1.05k
  } else if (Desc.isMemory()) {
2005
474
    CompCtx.addCoreMemory();
2006
578
  } else if (Desc.isGlobal()) {
2007
380
    CompCtx.addCoreGlobal();
2008
380
  } else if (Desc.isTag()) {
2009
198
    CompCtx.addCoreTag();
2010
198
  } else {
2011
0
    assumingUnreachable();
2012
0
  }
2013
1.44k
  return {};
2014
1.48k
}
2015
2016
Expect<void>
2017
661
Validator::validate(const AST::Component::CoreImportDecl &Decl) noexcept {
2018
661
  return validate(Decl.getImportDesc());
2019
661
}
2020
2021
Expect<void>
2022
822
Validator::validate(const AST::Component::CoreExportDecl &Decl) noexcept {
2023
822
  return validate(Decl.getImportDesc());
2024
822
}
2025
2026
Expect<void>
2027
1.74k
Validator::validate(const AST::Component::CoreModuleDecl &Decl) noexcept {
2028
1.74k
  auto ReportError = [](auto E) {
2029
83
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_CoreModule));
2030
83
    return E;
2031
83
  };
2032
2033
1.74k
  if (Decl.isImport()) {
2034
661
    EXPECTED_TRY(validate(Decl.getImport()).map_error(ReportError));
2035
1.08k
  } else if (Decl.isType()) {
2036
66
    EXPECTED_TRY(validate(*Decl.getType()).map_error(ReportError));
2037
1.02k
  } else if (Decl.isAlias()) {
2038
199
    EXPECTED_TRY(validate(Decl.getAlias()).map_error(ReportError));
2039
822
  } else if (Decl.isExport()) {
2040
822
    EXPECTED_TRY(validate(Decl.getExport()).map_error(ReportError));
2041
822
  } else {
2042
0
    assumingUnreachable();
2043
0
  }
2044
1.66k
  return {};
2045
1.74k
}
2046
2047
Expect<void>
2048
694
Validator::validate(const AST::Component::ImportDecl &Decl) noexcept {
2049
694
  auto ReportError = [](auto E) {
2050
11
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_Import));
2051
11
    return E;
2052
11
  };
2053
2054
  // Validate the extern descriptor (also increments sort index spaces).
2055
694
  EXPECTED_TRY(validate(Decl.getExternDesc()).map_error(ReportError));
2056
2057
  // Parse and validate the import name.
2058
1.37k
  EXPECTED_TRY(ComponentName CName,
2059
1.37k
               ComponentName::parse(Decl.getName()).map_error(ReportError));
2060
2061
  // Annotated plainnames can only appear on func imports.
2062
1.37k
  switch (CName.getKind()) {
2063
0
  case ComponentNameKind::Constructor:
2064
0
  case ComponentNameKind::Method:
2065
0
  case ComponentNameKind::Static:
2066
0
    if (Decl.getExternDesc().getDescType() !=
2067
0
        AST::Component::ExternDesc::DescType::FuncType) {
2068
0
      spdlog::error(ErrCode::Value::ComponentInvalidName);
2069
0
      spdlog::error("    ImportDecl: annotated name requires func type"sv);
2070
0
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_Import));
2071
0
      return Unexpect(ErrCode::Value::ComponentInvalidName);
2072
0
    }
2073
0
    break;
2074
683
  default:
2075
683
    break;
2076
1.37k
  }
2077
2078
  // Check import name uniqueness.
2079
683
  if (!CompCtx.addImportedName(CName)) {
2080
0
    spdlog::error(ErrCode::Value::ComponentDuplicateName);
2081
0
    spdlog::error("    ImportDecl: Duplicate import name"sv);
2082
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_Import));
2083
0
    return Unexpect(ErrCode::Value::ComponentDuplicateName);
2084
0
  }
2085
2086
683
  return {};
2087
683
}
2088
2089
Expect<void>
2090
258
Validator::validate(const AST::Component::ExportDecl &Decl) noexcept {
2091
  // Check export name grammar and uniqueness before mutating the index
2092
  // spaces so a duplicate or malformed name doesn't widen the scope's
2093
  // sort counts.
2094
258
  EXPECTED_TRY(ComponentName CName,
2095
257
               validateExportName(Decl.getName()).map_error([](auto E) {
2096
257
                 spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_Export));
2097
257
                 return E;
2098
257
               }));
2099
257
  if (!CompCtx.addExportedName(CName)) {
2100
0
    spdlog::error(ErrCode::Value::ComponentDuplicateName);
2101
0
    spdlog::error("    ExportDecl: Duplicate export name '{}'"sv,
2102
0
                  Decl.getName());
2103
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_Export));
2104
0
    return Unexpect(ErrCode::Value::ComponentDuplicateName);
2105
0
  }
2106
2107
  // Validate the extern descriptor (also increments sort index spaces and
2108
  // performs type-index bounds checking). For nested ExportDecls whose
2109
  // ExternDesc references a type index defined in an outer scope, the
2110
  // current scope's lookup may return nullptr — validate(ExternDesc) handles
2111
  // that gracefully by allocating an empty instance slot. Cross-scope
2112
  // type-index resolution (walking the parent CompCtxs chain to resolve
2113
  // names like `(export "f" (func (type 0)))` where type 0 lives outside
2114
  // the InstanceType body) is the structural follow-up tracked by the rest
2115
  // of GAP-DECL-ED / GAP-ED-1 / GAP-ED-2.
2116
257
  EXPECTED_TRY(validate(Decl.getExternDesc()).map_error([](auto E) {
2117
255
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_Export));
2118
255
    return E;
2119
255
  }));
2120
2121
255
  return {};
2122
257
}
2123
2124
Expect<void>
2125
1.27k
Validator::validate(const AST::Component::InstanceDecl &Decl) noexcept {
2126
1.27k
  auto ReportError = [](auto E) {
2127
73
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_Instance));
2128
73
    return E;
2129
73
  };
2130
2131
1.27k
  if (Decl.isCoreType()) {
2132
38
    EXPECTED_TRY(validate(*Decl.getCoreType()).map_error(ReportError));
2133
1.23k
  } else if (Decl.isType()) {
2134
799
    EXPECTED_TRY(validate(*Decl.getType()).map_error(ReportError));
2135
799
  } else if (Decl.isAlias()) {
2136
179
    EXPECTED_TRY(validate(Decl.getAlias()).map_error(ReportError));
2137
166
    const auto &A = Decl.getAlias();
2138
166
    const auto &Sort = A.getSort();
2139
166
    if (Sort.isCore()) {
2140
0
      CompCtx.incCoreSortIndexSize(Sort.getCoreSortType());
2141
166
    } else {
2142
166
      uint32_t NewInstIdx = CompCtx.incSortIndexSize(Sort.getSortType());
2143
      // Outer-aliasing a resource type keeps the resource's identity in this
2144
      // scope so later own/borrow and (eq i) checks treat the slot correctly.
2145
166
      if (A.getTargetType() == AST::Component::Alias::TargetType::Outer &&
2146
166
          Sort.getSortType() == AST::Component::Sort::SortType::Type) {
2147
166
        CompCtx.carryOuterResource(NewInstIdx, A.getOuter().first,
2148
166
                                   A.getOuter().second);
2149
166
      }
2150
      // When aliasing an instance export, propagate either the source
2151
      // instance's export table (for Instance targets) or the source
2152
      // export's resource identity (for Type targets that are resources)
2153
      // — mirrors the AliasSection handling.
2154
166
      if (A.getTargetType() == AST::Component::Alias::TargetType::Export) {
2155
0
        const auto SrcInstIdx = A.getExport().first;
2156
0
        const auto &SrcName = A.getExport().second;
2157
0
        const auto &SrcExports = CompCtx.getInstance(SrcInstIdx).Exports;
2158
0
        auto It = SrcExports.find(std::string(SrcName));
2159
0
        if (It != SrcExports.end()) {
2160
0
          if (Sort.getSortType() == AST::Component::Sort::SortType::Instance) {
2161
0
            if (It->second.IT != nullptr) {
2162
0
              populateInstanceFromType(NewInstIdx, *It->second.IT);
2163
0
            } else if (It->second.NestedInstIdx.has_value()) {
2164
0
              const auto &NestedExports =
2165
0
                  CompCtx.getInstance(*It->second.NestedInstIdx).Exports;
2166
0
              for (const auto &[Name, IE] : NestedExports) {
2167
0
                CompCtx.addInstanceExport(NewInstIdx, Name, IE.ST, IE.IT,
2168
0
                                          IE.NestedInstIdx, IE.ResourceId);
2169
0
              }
2170
0
            }
2171
0
          } else if (Sort.getSortType() ==
2172
0
                         AST::Component::Sort::SortType::Type &&
2173
0
                     It->second.ResourceId.has_value()) {
2174
0
            CompCtx.addResource(NewInstIdx, {*It->second.ResourceId,
2175
0
                                             /*LocallyDefined=*/false});
2176
0
          }
2177
0
        }
2178
0
      }
2179
166
    }
2180
258
  } else if (Decl.isExportDecl()) {
2181
258
    EXPECTED_TRY(validate(Decl.getExport()).map_error(ReportError));
2182
258
  } else {
2183
0
    assumingUnreachable();
2184
0
  }
2185
1.20k
  return {};
2186
1.27k
}
2187
2188
Expect<void>
2189
1.48k
Validator::validate(const AST::Component::ComponentDecl &Decl) noexcept {
2190
1.48k
  auto ReportError = [](auto E) {
2191
45
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Decl_Component));
2192
45
    return E;
2193
45
  };
2194
2195
1.48k
  if (Decl.isImportDecl()) {
2196
694
    EXPECTED_TRY(validate(Decl.getImport()).map_error(ReportError));
2197
790
  } else if (Decl.isInstanceDecl()) {
2198
790
    EXPECTED_TRY(validate(Decl.getInstance()).map_error(ReportError));
2199
790
  } else {
2200
0
    assumingUnreachable();
2201
0
  }
2202
1.43k
  return {};
2203
1.48k
}
2204
2205
23.8k
Expect<void> Validator::validate(const ComponentValType &VT) noexcept {
2206
23.8k
  if (VT.getCode() == ComponentTypeCode::TypeIndex) {
2207
19.2k
    uint32_t Idx = VT.getTypeIndex();
2208
19.2k
    if (Idx >= CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type)) {
2209
80
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
2210
80
      spdlog::error("    ComponentValType: type index {} out of bounds"sv, Idx);
2211
80
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
2212
80
    }
2213
19.1k
    const auto *DT = CompCtx.getDefType(Idx);
2214
19.1k
    if (DT != nullptr && !DT->isDefValType() && !DT->isResourceType()) {
2215
13
      spdlog::error(ErrCode::Value::NotADefinedType);
2216
13
      spdlog::error(
2217
13
          "    ComponentValType: type index {} is not a defined value type"sv,
2218
13
          Idx);
2219
13
      return Unexpect(ErrCode::Value::NotADefinedType);
2220
13
    }
2221
19.1k
  }
2222
23.7k
  return {};
2223
23.8k
}
2224
2225
Expect<void>
2226
86.5k
Validator::validate(const AST::Component::DefValType &DVT) noexcept {
2227
86.5k
  if (DVT.isOwnTy()) {
2228
565
    uint32_t Idx = DVT.getOwn().Idx;
2229
565
    if (Idx >= CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type)) {
2230
34
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
2231
34
      spdlog::error("    DefValType: own type index {} out of bounds"sv, Idx);
2232
34
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
2233
34
    }
2234
531
    if (CompCtx.getResource(Idx) == nullptr) {
2235
53
      spdlog::error(ErrCode::Value::NotADefinedType);
2236
53
      spdlog::error(
2237
53
          "    DefValType: own type index {} does not refer to a resource type"sv,
2238
53
          Idx);
2239
53
      return Unexpect(ErrCode::Value::NotADefinedType);
2240
53
    }
2241
85.9k
  } else if (DVT.isBorrowTy()) {
2242
692
    uint32_t Idx = DVT.getBorrow().Idx;
2243
692
    if (Idx >= CompCtx.getSortIndexSize(AST::Component::Sort::SortType::Type)) {
2244
39
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
2245
39
      spdlog::error("    DefValType: borrow type index {} out of bounds"sv,
2246
39
                    Idx);
2247
39
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
2248
39
    }
2249
653
    if (CompCtx.getResource(Idx) == nullptr) {
2250
18
      spdlog::error(ErrCode::Value::NotADefinedType);
2251
18
      spdlog::error(
2252
18
          "    DefValType: borrow type index {} does not refer to a resource type"sv,
2253
18
          Idx);
2254
18
      return Unexpect(ErrCode::Value::NotADefinedType);
2255
18
    }
2256
85.2k
  } else if (DVT.isRecordTy()) {
2257
1.03k
    const auto &Rec = DVT.getRecord();
2258
1.03k
    if (Rec.LabelTypes.empty()) {
2259
0
      spdlog::error(ErrCode::Value::InvalidTypeReference);
2260
0
      spdlog::error("    DefValType: record must have at least one field"sv);
2261
0
      return Unexpect(ErrCode::Value::InvalidTypeReference);
2262
0
    }
2263
1.03k
    std::unordered_set<std::string> Seen;
2264
1.04k
    for (const auto &LT : Rec.LabelTypes) {
2265
1.04k
      if (LT.getLabel().empty()) {
2266
3
        spdlog::error(ErrCode::Value::NameCannotBeEmpty);
2267
3
        return Unexpect(ErrCode::Value::NameCannotBeEmpty);
2268
3
      }
2269
1.04k
      if (!isKebabString(LT.getLabel())) {
2270
3
        spdlog::error(ErrCode::Value::ComponentInvalidName);
2271
3
        spdlog::error(
2272
3
            "    DefValType: record field '{}' is not valid kebab-case"sv,
2273
3
            LT.getLabel());
2274
3
        return Unexpect(ErrCode::Value::ComponentInvalidName);
2275
3
      }
2276
1.03k
      if (!Seen.insert(toLowerStr(LT.getLabel())).second) {
2277
1
        spdlog::error(ErrCode::Value::RecordFieldNameConflicts);
2278
1
        spdlog::error("    DefValType: duplicate record field '{}'"sv,
2279
1
                      LT.getLabel());
2280
1
        return Unexpect(ErrCode::Value::RecordFieldNameConflicts);
2281
1
      }
2282
1.03k
      EXPECTED_TRY(validate(LT.getValType()));
2283
1.03k
    }
2284
84.2k
  } else if (DVT.isVariantTy()) {
2285
637
    const auto &Var = DVT.getVariant();
2286
637
    if (Var.Cases.empty()) {
2287
2
      spdlog::error(ErrCode::Value::VariantMustHaveCase);
2288
2
      return Unexpect(ErrCode::Value::VariantMustHaveCase);
2289
2
    }
2290
635
    std::unordered_set<std::string> Seen;
2291
635
    for (const auto &C : Var.Cases) {
2292
635
      if (C.first.empty()) {
2293
1
        spdlog::error(ErrCode::Value::NameCannotBeEmpty);
2294
1
        return Unexpect(ErrCode::Value::NameCannotBeEmpty);
2295
1
      }
2296
634
      if (!isKebabString(C.first)) {
2297
4
        spdlog::error(ErrCode::Value::ComponentInvalidName);
2298
4
        spdlog::error(
2299
4
            "    DefValType: variant case '{}' is not valid kebab-case"sv,
2300
4
            C.first);
2301
4
        return Unexpect(ErrCode::Value::ComponentInvalidName);
2302
4
      }
2303
630
      if (!Seen.insert(toLowerStr(C.first)).second) {
2304
0
        spdlog::error(ErrCode::Value::VariantCaseNameConflicts);
2305
0
        spdlog::error("    DefValType: duplicate variant case '{}'"sv, C.first);
2306
0
        return Unexpect(ErrCode::Value::VariantCaseNameConflicts);
2307
0
      }
2308
630
      if (C.second.has_value()) {
2309
551
        EXPECTED_TRY(validate(*C.second));
2310
551
      }
2311
630
    }
2312
83.6k
  } else if (DVT.isTupleTy()) {
2313
867
    if (DVT.getTuple().Types.empty()) {
2314
0
      spdlog::error(ErrCode::Value::InvalidTypeReference);
2315
0
      spdlog::error("    DefValType: tuple must have at least one element"sv);
2316
0
      return Unexpect(ErrCode::Value::InvalidTypeReference);
2317
0
    }
2318
978
    for (const auto &T : DVT.getTuple().Types) {
2319
978
      EXPECTED_TRY(validate(T));
2320
978
    }
2321
82.7k
  } else if (DVT.isListTy()) {
2322
2.08k
    EXPECTED_TRY(validate(DVT.getList().ValTy));
2323
80.6k
  } else if (DVT.isOptionTy()) {
2324
617
    EXPECTED_TRY(validate(DVT.getOption().ValTy));
2325
80.0k
  } else if (DVT.isResultTy()) {
2326
2.74k
    const auto &R = DVT.getResult();
2327
2.74k
    if (R.ValTy.has_value()) {
2328
1.01k
      EXPECTED_TRY(validate(*R.ValTy));
2329
1.01k
    }
2330
2.74k
    if (R.ErrTy.has_value()) {
2331
1.72k
      EXPECTED_TRY(validate(*R.ErrTy));
2332
1.72k
    }
2333
77.3k
  } else if (DVT.isFlagsTy()) {
2334
114
    const auto &Flags = DVT.getFlags();
2335
114
    if (Flags.Labels.empty()) {
2336
0
      spdlog::error(ErrCode::Value::InvalidTypeReference);
2337
0
      spdlog::error("    DefValType: flags must have at least one label"sv);
2338
0
      return Unexpect(ErrCode::Value::InvalidTypeReference);
2339
0
    }
2340
114
    if (Flags.Labels.size() > 32) {
2341
0
      spdlog::error(ErrCode::Value::CannotHaveMoreThan32Flags);
2342
0
      return Unexpect(ErrCode::Value::CannotHaveMoreThan32Flags);
2343
0
    }
2344
114
    std::unordered_set<std::string> Seen;
2345
118
    for (const auto &L : Flags.Labels) {
2346
118
      if (L.empty()) {
2347
3
        spdlog::error(ErrCode::Value::NameCannotBeEmpty);
2348
3
        return Unexpect(ErrCode::Value::NameCannotBeEmpty);
2349
3
      }
2350
115
      if (!isKebabString(L)) {
2351
14
        spdlog::error(ErrCode::Value::ComponentInvalidName);
2352
14
        spdlog::error(
2353
14
            "    DefValType: flags label '{}' is not valid kebab-case"sv, L);
2354
14
        return Unexpect(ErrCode::Value::ComponentInvalidName);
2355
14
      }
2356
101
      if (!Seen.insert(toLowerStr(L)).second) {
2357
2
        spdlog::error(ErrCode::Value::FlagNameConflicts);
2358
2
        spdlog::error("    DefValType: duplicate flags label '{}'"sv, L);
2359
2
        return Unexpect(ErrCode::Value::FlagNameConflicts);
2360
2
      }
2361
101
    }
2362
77.1k
  } else if (DVT.isEnumTy()) {
2363
994
    const auto &Enm = DVT.getEnum();
2364
994
    if (Enm.Labels.empty()) {
2365
3
      spdlog::error(ErrCode::Value::InvalidTypeReference);
2366
3
      spdlog::error("    DefValType: enum must have at least one label"sv);
2367
3
      return Unexpect(ErrCode::Value::InvalidTypeReference);
2368
3
    }
2369
991
    std::unordered_set<std::string> Seen;
2370
997
    for (const auto &L : Enm.Labels) {
2371
997
      if (L.empty()) {
2372
3
        spdlog::error(ErrCode::Value::NameCannotBeEmpty);
2373
3
        return Unexpect(ErrCode::Value::NameCannotBeEmpty);
2374
3
      }
2375
994
      if (!isKebabString(L)) {
2376
6
        spdlog::error(ErrCode::Value::ComponentInvalidName);
2377
6
        spdlog::error(
2378
6
            "    DefValType: enum label '{}' is not valid kebab-case"sv, L);
2379
6
        return Unexpect(ErrCode::Value::ComponentInvalidName);
2380
6
      }
2381
988
      if (!Seen.insert(toLowerStr(L)).second) {
2382
1
        spdlog::error(ErrCode::Value::EnumTagNameConflicts);
2383
1
        spdlog::error("    DefValType: duplicate enum label '{}'"sv, L);
2384
1
        return Unexpect(ErrCode::Value::EnumTagNameConflicts);
2385
1
      }
2386
988
    }
2387
76.1k
  } else if (DVT.isStreamTy()) {
2388
848
    if (DVT.getStream().ValTy.has_value()) {
2389
422
      EXPECTED_TRY(validate(*DVT.getStream().ValTy));
2390
422
    }
2391
75.3k
  } else if (DVT.isFutureTy()) {
2392
593
    if (DVT.getFuture().ValTy.has_value()) {
2393
241
      EXPECTED_TRY(validate(*DVT.getFuture().ValTy));
2394
241
    }
2395
593
  }
2396
86.2k
  return {};
2397
86.5k
}
2398
2399
11.7k
Expect<void> Validator::validate(const AST::Component::FuncType &FT) noexcept {
2400
  // Validate param names: kebab-case + unique
2401
11.7k
  std::unordered_set<std::string_view> ParamNames;
2402
11.7k
  for (const auto &P : FT.getParamList()) {
2403
4.30k
    if (!P.getLabel().empty()) {
2404
2.17k
      if (!isKebabString(P.getLabel())) {
2405
12
        spdlog::error(ErrCode::Value::ComponentInvalidName);
2406
12
        spdlog::error(
2407
12
            "    FuncType: parameter name '{}' is not valid kebab-case"sv,
2408
12
            P.getLabel());
2409
12
        return Unexpect(ErrCode::Value::ComponentInvalidName);
2410
12
      }
2411
2.16k
      if (!ParamNames.insert(P.getLabel()).second) {
2412
1
        spdlog::error(ErrCode::Value::ComponentDuplicateName);
2413
1
        spdlog::error("    FuncType: duplicate parameter name '{}'"sv,
2414
1
                      P.getLabel());
2415
1
        return Unexpect(ErrCode::Value::ComponentDuplicateName);
2416
1
      }
2417
2.16k
    }
2418
4.28k
    EXPECTED_TRY(validate(P.getValType()));
2419
4.28k
  }
2420
  // Reject transitive use of borrow in results
2421
11.6k
  for (const auto &R : FT.getResultList()) {
2422
10.8k
    EXPECTED_TRY(validate(R.getValType()));
2423
10.8k
    if (containsBorrow(R.getValType())) {
2424
0
      spdlog::error(ErrCode::Value::InvalidTypeReference);
2425
0
      spdlog::error(
2426
0
          "    FuncType: borrow type not allowed in function results"sv);
2427
0
      return Unexpect(ErrCode::Value::InvalidTypeReference);
2428
0
    }
2429
10.8k
  }
2430
11.6k
  return {};
2431
11.6k
}
2432
2433
Expect<void>
2434
535k
Validator::validate(const AST::Component::InstanceType &IT) noexcept {
2435
  // Instance types are validated with an initially-empty index space.
2436
535k
  CompCtx.enterTypeDefinition();
2437
535k
  for (const auto &Decl : IT.getDecl()) {
2438
484
    EXPECTED_TRY(validate(Decl).map_error([](auto E) {
2439
484
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_DefType));
2440
484
      return E;
2441
484
    }));
2442
484
  }
2443
535k
  CompCtx.exitComponent();
2444
535k
  return {};
2445
535k
}
2446
2447
Expect<void>
2448
1.12M
Validator::validate(const AST::Component::ComponentType &CT) noexcept {
2449
  // Component types are validated with an initially-empty index space.
2450
1.12M
  CompCtx.enterTypeDefinition();
2451
1.12M
  for (const auto &Decl : CT.getDecl()) {
2452
1.48k
    EXPECTED_TRY(validate(Decl).map_error([](auto E) {
2453
1.48k
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_DefType));
2454
1.48k
      return E;
2455
1.48k
    }));
2456
1.48k
  }
2457
1.12M
  CompCtx.exitComponent();
2458
1.12M
  return {};
2459
1.12M
}
2460
2461
Expect<void>
2462
21.0k
Validator::validate(const AST::Component::ResourceType &RT) noexcept {
2463
  // Resource types are not allowed inside componenttype/instancetype scopes.
2464
21.0k
  if (CompCtx.isTypeDefinitionScope()) {
2465
1
    spdlog::error(ErrCode::Value::InvalidTypeReference);
2466
1
    spdlog::error("    ResourceType: resource types cannot be defined inside "
2467
1
                  "componenttype or instancetype"sv);
2468
1
    return Unexpect(ErrCode::Value::InvalidTypeReference);
2469
1
  }
2470
21.0k
  if (RT.getDestructor().has_value()) {
2471
1.12k
    uint32_t DtorIdx = *RT.getDestructor();
2472
1.12k
    if (DtorIdx >= CompCtx.getCoreSortIndexSize(
2473
1.12k
                       AST::Component::Sort::CoreSortType::Func)) {
2474
9
      spdlog::error(ErrCode::Value::InvalidIndex);
2475
9
      spdlog::error(
2476
9
          "    ResourceType: destructor core func index {} out of bounds"sv,
2477
9
          DtorIdx);
2478
9
      return Unexpect(ErrCode::Value::InvalidIndex);
2479
9
    }
2480
    // Verify the destructor's signature is `[i32] -> []`. The signature is
2481
    // only available for core funcs the validator has populated (canon
2482
    // resource.* synthesise it; aliased-from-core-module funcs are tracked
2483
    // as a TODO that needs CoreInstance export-type plumbing). Skip the
2484
    // check when the SubType pointer is null — the bounds check above
2485
    // already caught the obvious "no such func" mistake.
2486
    // TODO: also accept `[i64] -> []` when the resource rep is i64
2487
    // (memory64 proposal); needs ResourceType to carry rep size.
2488
1.12k
    const AST::SubType *DtorST = CompCtx.getCoreFunc(DtorIdx);
2489
1.12k
    if (DtorST != nullptr) {
2490
966
      const auto &DtorType = DtorST->getCompositeType();
2491
966
      const auto &ExpType = CoreFuncType_I32_Void.getCompositeType();
2492
966
      if (!DtorType.isFunc() ||
2493
966
          DtorType.getFuncType() != ExpType.getFuncType()) {
2494
1
        spdlog::error(ErrCode::Value::InvalidTypeReference);
2495
1
        spdlog::error(
2496
1
            "    ResourceType: destructor core func {} must have signature [i32] -> []"sv,
2497
1
            DtorIdx);
2498
1
        return Unexpect(ErrCode::Value::InvalidTypeReference);
2499
1
      }
2500
966
    }
2501
1.12k
  }
2502
21.0k
  return {};
2503
21.0k
}
2504
2505
23.9k
bool Validator::containsBorrow(const ComponentValType &VT) const noexcept {
2506
23.9k
  if (VT.getCode() == ComponentTypeCode::Borrow) {
2507
0
    return true;
2508
0
  }
2509
23.9k
  if (VT.getCode() != ComponentTypeCode::TypeIndex) {
2510
6.36k
    return false;
2511
6.36k
  }
2512
17.6k
  uint32_t Idx = VT.getTypeIndex();
2513
17.6k
  const auto *DT = CompCtx.getDefType(Idx);
2514
17.6k
  if (DT == nullptr || !DT->isDefValType()) {
2515
2.72k
    return false;
2516
2.72k
  }
2517
14.9k
  return containsBorrow(DT->getDefValType());
2518
17.6k
}
2519
2520
bool Validator::containsBorrow(
2521
14.9k
    const AST::Component::DefValType &DVT) const noexcept {
2522
14.9k
  if (DVT.isBorrowTy()) {
2523
0
    return true;
2524
0
  }
2525
14.9k
  if (DVT.isRecordTy()) {
2526
2.90k
    for (const auto &F : DVT.getRecord().LabelTypes) {
2527
2.90k
      if (containsBorrow(F.getValType())) {
2528
0
        return true;
2529
0
      }
2530
2.90k
    }
2531
2.90k
    return false;
2532
2.90k
  }
2533
11.9k
  if (DVT.isVariantTy()) {
2534
2.04k
    for (const auto &C : DVT.getVariant().Cases) {
2535
2.04k
      if (C.second.has_value() && containsBorrow(*C.second)) {
2536
0
        return true;
2537
0
      }
2538
2.04k
    }
2539
2.04k
    return false;
2540
2.04k
  }
2541
9.95k
  if (DVT.isListTy()) {
2542
1.30k
    return containsBorrow(DVT.getList().ValTy);
2543
1.30k
  }
2544
8.64k
  if (DVT.isTupleTy()) {
2545
1.15k
    for (const auto &T : DVT.getTuple().Types) {
2546
1.15k
      if (containsBorrow(T)) {
2547
0
        return true;
2548
0
      }
2549
1.15k
    }
2550
1.05k
    return false;
2551
1.05k
  }
2552
7.59k
  if (DVT.isOptionTy()) {
2553
68
    return containsBorrow(DVT.getOption().ValTy);
2554
68
  }
2555
7.52k
  if (DVT.isResultTy()) {
2556
5.47k
    const auto &R = DVT.getResult();
2557
5.47k
    return (R.ValTy.has_value() && containsBorrow(*R.ValTy)) ||
2558
5.47k
           (R.ErrTy.has_value() && containsBorrow(*R.ErrTy));
2559
5.47k
  }
2560
2.04k
  if (DVT.isStreamTy()) {
2561
334
    return DVT.getStream().ValTy.has_value() &&
2562
194
           containsBorrow(*DVT.getStream().ValTy);
2563
334
  }
2564
1.71k
  if (DVT.isFutureTy()) {
2565
138
    return DVT.getFuture().ValTy.has_value() &&
2566
66
           containsBorrow(*DVT.getFuture().ValTy);
2567
138
  }
2568
1.57k
  return false; // PrimValType, OwnTy, FlagsTy, EnumTy
2569
1.71k
}
2570
2571
} // namespace Validator
2572
} // namespace WasmEdge