Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/validator/component_context.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- component_context.cpp - Component state ---------------------------===//
5
//
6
// The bodies of Context.
7
//
8
//===----------------------------------------------------------------------===//
9
#include "validator/component_context.h"
10
11
#include "common/errinfo.h"
12
#include "common/spdlog.h"
13
14
#include <algorithm>
15
#include <cctype>
16
#include <string>
17
#include <unordered_map>
18
#include <unordered_set>
19
#include <vector>
20
21
namespace WasmEdge {
22
namespace Validator {
23
namespace Component {
24
25
using namespace std::literals;
26
27
// ---------------------------------------------------------------------------
28
// Index-space registration and resolution.
29
// ---------------------------------------------------------------------------
30
31
6.81k
void Context::defineExtern(const ExternInfo &Info) noexcept {
32
6.81k
  auto &S = top();
33
6.81k
  switch (Info.Kind) {
34
37
  case ExternKind::CoreType:
35
37
    S.addCoreModule(Info.CoreMod);
36
37
    break;
37
459
  case ExternKind::FuncType:
38
459
    S.addFunc(Info.Func);
39
459
    break;
40
369
  case ExternKind::ValueBound:
41
369
    S.addValue(Info.Value);
42
369
    break;
43
5.22k
  case ExternKind::TypeBound:
44
5.22k
    S.addType(Info.Type);
45
5.22k
    break;
46
151
  case ExternKind::InstanceType:
47
151
    S.addInstance(Info.Shape);
48
151
    break;
49
572
  case ExternKind::ComponentType:
50
572
    S.addComponent(Info.Shape);
51
572
    break;
52
6.81k
  }
53
6.81k
}
54
55
Expect<ExternInfo>
56
4.20k
Context::resolveSortIndex(const AST::Component::SortIndex &SI) noexcept {
57
4.20k
  ExternInfo Info;
58
4.20k
  const auto &S = top();
59
4.20k
  const auto &Sort = SI.getSort();
60
4.20k
  const uint32_t Idx = SI.getIdx();
61
4.20k
  if (Sort.isCore()) {
62
26
    if (Sort.getCoreSortType() == AST::Component::Sort::CoreSortType::Module) {
63
6
      const auto *Mod = S.getCoreModule(Idx);
64
6
      if (Mod == nullptr) {
65
6
        spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
66
6
        spdlog::error("    Core module index {} out of bounds (size {})."sv,
67
6
                      Idx, S.CoreModules.size());
68
6
        return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
69
6
      }
70
0
      Info.Kind = ExternKind::CoreType;
71
0
      Info.CoreMod = Mod;
72
0
      return Info;
73
6
    }
74
20
    spdlog::error(ErrCode::Value::InvalidTypeReference);
75
20
    spdlog::error(
76
20
        "    Core sorts other than module cannot be used at component level."sv);
77
20
    return Unexpect(ErrCode::Value::InvalidTypeReference);
78
26
  }
79
4.17k
  switch (Sort.getSortType()) {
80
243
  case AST::Component::Sort::SortType::Func: {
81
243
    const auto *F = S.getFunc(Idx);
82
243
    if (F == nullptr) {
83
11
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
84
11
      spdlog::error("    Function index {} out of bounds (size {})."sv, Idx,
85
11
                    S.Funcs.size());
86
11
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
87
11
    }
88
232
    Info.Kind = ExternKind::FuncType;
89
232
    Info.Func = *F;
90
232
    return Info;
91
243
  }
92
350
  case AST::Component::Sort::SortType::Value: {
93
350
    if (Idx >= S.Values.size()) {
94
193
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
95
193
      spdlog::error("    Value index {} out of bounds (size {})."sv, Idx,
96
193
                    S.Values.size());
97
193
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
98
193
    }
99
157
    Info.Kind = ExternKind::ValueBound;
100
157
    Info.Value = S.Values[Idx].Type;
101
157
    return Info;
102
350
  }
103
1.05k
  case AST::Component::Sort::SortType::Type: {
104
1.05k
    const auto *E = S.getType(Idx);
105
1.05k
    if (E == nullptr) {
106
45
      spdlog::error(ErrCode::Value::ComponentTypeIndexOutOfBounds);
107
45
      spdlog::error("    Type index {} out of bounds (size {})."sv, Idx,
108
45
                    S.Types.size());
109
45
      return Unexpect(ErrCode::Value::ComponentTypeIndexOutOfBounds);
110
45
    }
111
1.01k
    Info.Kind = ExternKind::TypeBound;
112
1.01k
    Info.Type = *E;
113
1.01k
    return Info;
114
1.05k
  }
115
2.28k
  case AST::Component::Sort::SortType::Component: {
116
2.28k
    const auto *C = S.getComponent(Idx);
117
2.28k
    if (C == nullptr) {
118
40
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
119
40
      spdlog::error("    Component index {} out of bounds (size {})."sv, Idx,
120
40
                    S.Components.size());
121
40
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
122
40
    }
123
2.24k
    Info.Kind = ExternKind::ComponentType;
124
2.24k
    Info.Shape = C;
125
2.24k
    return Info;
126
2.28k
  }
127
242
  case AST::Component::Sort::SortType::Instance: {
128
242
    const auto *I = S.getInstance(Idx);
129
242
    if (I == nullptr) {
130
9
      spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
131
9
      spdlog::error("    Instance index {} out of bounds (size {})."sv, Idx,
132
9
                    S.Instances.size());
133
9
      return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
134
9
    }
135
233
    Info.Kind = ExternKind::InstanceType;
136
233
    Info.Shape = I;
137
233
    return Info;
138
242
  }
139
0
  default:
140
0
    spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds);
141
0
    return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds);
142
4.17k
  }
143
4.17k
}
144
145
// External view of an inline core module: its imports and export types.
146
Expect<const CoreShape *>
147
3.81k
Context::buildCoreShape(const AST::Module &Mod) noexcept {
148
3.81k
  auto *Info = Types.addCoreShape();
149
3.81k
  const auto &ModTypes = Mod.getTypeSection().getContent();
150
151
3.81k
  auto GetSubType = [&ModTypes](uint32_t Idx) noexcept -> const AST::SubType * {
152
889
    return Idx < ModTypes.size() ? &ModTypes[Idx] : nullptr;
153
889
  };
154
155
  // Core index spaces: imports first, then definitions.
156
3.81k
  std::vector<CoreExternInfo> Funcs, Tables, Memories, Globals, Tags;
157
3.81k
  for (const auto &Imp : Mod.getImportSection().getContent()) {
158
928
    CoreExternInfo Ext;
159
928
    switch (Imp.getExternalType()) {
160
372
    case ExternalType::Function:
161
372
      Ext = CoreExternInfo(ExternalType::Function,
162
372
                           GetSubType(Imp.getExternalFuncTypeIdx()));
163
372
      Funcs.push_back(Ext);
164
372
      break;
165
40
    case ExternalType::Table:
166
40
      Ext = CoreExternInfo(&Imp.getExternalTableType());
167
40
      Tables.push_back(Ext);
168
40
      break;
169
262
    case ExternalType::Memory:
170
262
      Ext = CoreExternInfo(&Imp.getExternalMemoryType());
171
262
      Memories.push_back(Ext);
172
262
      break;
173
121
    case ExternalType::Global:
174
121
      Ext = CoreExternInfo(&Imp.getExternalGlobalType());
175
121
      Globals.push_back(Ext);
176
121
      break;
177
133
    case ExternalType::Tag:
178
133
      Ext = CoreExternInfo(ExternalType::Tag,
179
133
                           GetSubType(Imp.getExternalTagType().getTypeIdx()));
180
133
      Tags.push_back(Ext);
181
133
      break;
182
0
    default:
183
0
      break;
184
928
    }
185
928
    for (const auto &[ModName, Name, Prev] : Info->Imports) {
186
113
      if (ModName == Imp.getModuleName() && Name == Imp.getExternalName()) {
187
4
        spdlog::error(ErrCode::Value::ComponentDuplicateImportName);
188
4
        spdlog::error("    Module import '{}'.'{}' name conflict."sv, ModName,
189
4
                      Name);
190
4
        return Unexpect(ErrCode::Value::ComponentDuplicateImportName);
191
4
      }
192
113
    }
193
924
    Info->Imports.emplace_back(std::string(Imp.getModuleName()),
194
924
                               std::string(Imp.getExternalName()), Ext);
195
924
  }
196
3.80k
  for (const auto TIdx : Mod.getFunctionSection().getContent()) {
197
0
    Funcs.emplace_back(ExternalType::Function, GetSubType(TIdx));
198
0
  }
199
3.80k
  for (const auto &Seg : Mod.getTableSection().getContent()) {
200
2.33k
    Tables.emplace_back(&Seg.getTableType());
201
2.33k
  }
202
3.80k
  for (const auto &MT : Mod.getMemorySection().getContent()) {
203
227
    Memories.emplace_back(&MT);
204
227
  }
205
3.80k
  for (const auto &Seg : Mod.getGlobalSection().getContent()) {
206
434
    Globals.emplace_back(&Seg.getGlobalType());
207
434
  }
208
3.80k
  for (const auto &TT : Mod.getTagSection().getContent()) {
209
384
    Tags.emplace_back(ExternalType::Tag, GetSubType(TT.getTypeIdx()));
210
384
  }
211
212
3.80k
  for (const auto &Exp : Mod.getExportSection().getContent()) {
213
17
    const uint32_t Idx = Exp.getExternalIndex();
214
17
    CoreExternInfo Ext;
215
17
    switch (Exp.getExternalType()) {
216
0
    case ExternalType::Function:
217
0
      if (Idx >= Funcs.size()) {
218
0
        continue;
219
0
      }
220
0
      Ext = Funcs[Idx];
221
0
      break;
222
0
    case ExternalType::Table:
223
0
      if (Idx >= Tables.size()) {
224
0
        continue;
225
0
      }
226
0
      Ext = Tables[Idx];
227
0
      break;
228
0
    case ExternalType::Memory:
229
0
      if (Idx >= Memories.size()) {
230
0
        continue;
231
0
      }
232
0
      Ext = Memories[Idx];
233
0
      break;
234
17
    case ExternalType::Global:
235
17
      if (Idx >= Globals.size()) {
236
0
        continue;
237
0
      }
238
17
      Ext = Globals[Idx];
239
17
      break;
240
0
    case ExternalType::Tag:
241
0
      if (Idx >= Tags.size()) {
242
0
        continue;
243
0
      }
244
0
      Ext = Tags[Idx];
245
0
      break;
246
0
    default:
247
0
      continue;
248
17
    }
249
17
    Info->Exports.emplace(std::string(Exp.getExternalName()), Ext);
250
17
  }
251
3.80k
  return Info;
252
3.80k
}
253
254
// ---------------------------------------------------------------------------
255
// Canonical options.
256
// ---------------------------------------------------------------------------
257
258
ValType Context::getCanonPtrType(
259
19.4k
    const AST::Component::Canonical &Canon) const noexcept {
260
19.4k
  for (const auto &Opt : Canon.getOptions()) {
261
7.38k
    if (Opt.getCode() == ComponentCanonOptCode::Memory) {
262
43
      const uint32_t Idx = Opt.getIndex();
263
43
      if (Idx < top().CoreMemories.size()) {
264
0
        const auto *Mem = top().CoreMemories[Idx];
265
0
        return ValType(Mem != nullptr && Mem->getLimit().is64()
266
0
                           ? TypeCode::I64
267
0
                           : TypeCode::I32);
268
0
      }
269
43
    }
270
7.38k
  }
271
19.4k
  return ValType(TypeCode::I32);
272
19.4k
}
273
274
Expect<void> Context::checkOptions(const AST::Component::Canonical &Canon,
275
9.77k
                                   bool IsLift) const noexcept {
276
9.77k
  const auto &S = top();
277
9.77k
  const ValType I32V{TypeCode::I32};
278
9.77k
  bool SeenEncoding = false, SeenMemory = false, SeenRealloc = false,
279
9.77k
       SeenPostReturn = false, SeenAsync = false, SeenCallback = false;
280
  // The pointer width of realloc follows the selected memory.
281
9.77k
  const ValType Ptr = getCanonPtrType(Canon);
282
9.77k
  for (const auto &Opt : Canon.getOptions()) {
283
3.68k
    switch (Opt.getCode()) {
284
2.37k
    case ComponentCanonOptCode::Encode_UTF8:
285
3.11k
    case ComponentCanonOptCode::Encode_UTF16:
286
3.40k
    case ComponentCanonOptCode::Encode_Latin1:
287
3.40k
      if (SeenEncoding) {
288
3
        spdlog::error(ErrCode::Value::CanonEncodingConflict);
289
3
        spdlog::error("    Duplicate string-encoding canonical option."sv);
290
3
        return Unexpect(ErrCode::Value::CanonEncodingConflict);
291
3
      }
292
3.40k
      SeenEncoding = true;
293
3.40k
      break;
294
14
    case ComponentCanonOptCode::Memory: {
295
14
      if (SeenMemory) {
296
0
        spdlog::error(ErrCode::Value::CanonMemoryDuplicated);
297
0
        spdlog::error("    Duplicate memory canonical option."sv);
298
0
        return Unexpect(ErrCode::Value::CanonMemoryDuplicated);
299
0
      }
300
14
      SeenMemory = true;
301
14
      const uint32_t Idx = Opt.getIndex();
302
14
      if (Idx >= S.CoreMemories.size()) {
303
14
        spdlog::error(ErrCode::Value::ComponentMemoryIndexOutOfBounds);
304
14
        spdlog::error("    Canonical option memory index {} out of bounds."sv,
305
14
                      Idx);
306
14
        return Unexpect(ErrCode::Value::ComponentMemoryIndexOutOfBounds);
307
14
      }
308
0
      break;
309
14
    }
310
10
    case ComponentCanonOptCode::Realloc: {
311
10
      if (SeenRealloc) {
312
0
        spdlog::error(ErrCode::Value::CanonReallocDuplicated);
313
0
        spdlog::error("    Duplicate realloc canonical option."sv);
314
0
        return Unexpect(ErrCode::Value::CanonReallocDuplicated);
315
0
      }
316
10
      SeenRealloc = true;
317
10
      const auto *Func = S.getCoreFunc(Opt.getIndex());
318
10
      if (Func == nullptr) {
319
7
        spdlog::error(ErrCode::Value::InvalidIndex);
320
7
        spdlog::error(
321
7
            "    Canonical option realloc function index {} out of bounds."sv,
322
7
            Opt.getIndex());
323
7
        return Unexpect(ErrCode::Value::InvalidIndex);
324
7
      }
325
      // realloc has type [ptr ptr ptr ptr] -> [ptr] for the selected memory.
326
3
      const std::vector<ValType> ReallocParams(4, Ptr);
327
3
      const std::vector<ValType> ReallocResults(1, Ptr);
328
3
      const auto &CT = Func->getCompositeType();
329
3
      if (!CT.isFunc() || CT.getFuncType().getParamTypes() != ReallocParams ||
330
3
          CT.getFuncType().getReturnTypes() != ReallocResults) {
331
3
        spdlog::error(ErrCode::Value::CanonReallocSignature);
332
3
        spdlog::error(
333
3
            "    realloc must have type [ptr ptr ptr ptr] -> [ptr]."sv);
334
3
        return Unexpect(ErrCode::Value::CanonReallocSignature);
335
3
      }
336
0
      break;
337
3
    }
338
201
    case ComponentCanonOptCode::PostReturn:
339
201
      if (!IsLift) {
340
2
        spdlog::error(ErrCode::Value::CanonPostReturnOnLower);
341
2
        spdlog::error("    post-return cannot be specified for lowerings."sv);
342
2
        return Unexpect(ErrCode::Value::CanonPostReturnOnLower);
343
2
      }
344
199
      if (SeenPostReturn) {
345
1
        spdlog::error(ErrCode::Value::CanonPostReturnDuplicated);
346
1
        spdlog::error("    post-return is specified more than once."sv);
347
1
        return Unexpect(ErrCode::Value::CanonPostReturnDuplicated);
348
1
      }
349
198
      SeenPostReturn = true;
350
      // The signature is checked by the caller, once the flat type is known.
351
198
      break;
352
29
    case ComponentCanonOptCode::Async:
353
29
      if (SeenAsync) {
354
1
        spdlog::error(ErrCode::Value::CanonAsyncDuplicated);
355
1
        spdlog::error("    async is specified more than once."sv);
356
1
        return Unexpect(ErrCode::Value::CanonAsyncDuplicated);
357
1
      }
358
28
      SeenAsync = true;
359
28
      break;
360
26
    case ComponentCanonOptCode::Callback: {
361
      // `callback` may only appear on `canon lift`, and only with `async`.
362
26
      if (!IsLift) {
363
2
        spdlog::error(ErrCode::Value::CanonCallbackOnLower);
364
2
        spdlog::error("    callback cannot be specified for lowerings."sv);
365
2
        return Unexpect(ErrCode::Value::CanonCallbackOnLower);
366
2
      }
367
24
      if (SeenCallback) {
368
0
        spdlog::error(ErrCode::Value::CanonCallbackDuplicated);
369
0
        spdlog::error("    callback is specified more than once."sv);
370
0
        return Unexpect(ErrCode::Value::CanonCallbackDuplicated);
371
0
      }
372
24
      SeenCallback = true;
373
24
      const auto *Func = S.getCoreFunc(Opt.getIndex());
374
24
      if (Func == nullptr) {
375
22
        spdlog::error(ErrCode::Value::InvalidIndex);
376
22
        spdlog::error("    Canonical option callback function index {} out "
377
22
                      "of bounds."sv,
378
22
                      Opt.getIndex());
379
22
        return Unexpect(ErrCode::Value::InvalidIndex);
380
22
      }
381
      // callback has type [i32 i32 i32] -> [i32].
382
2
      const std::vector<ValType> CallbackParams(3, I32V);
383
2
      const std::vector<ValType> CallbackResults(1, I32V);
384
2
      const auto &CT = Func->getCompositeType();
385
2
      if (!CT.isFunc() || CT.getFuncType().getParamTypes() != CallbackParams ||
386
2
          CT.getFuncType().getReturnTypes() != CallbackResults) {
387
2
        spdlog::error(ErrCode::Value::CanonCallbackSignature);
388
2
        spdlog::error("    callback must have type [i32 i32 i32] -> [i32]."sv);
389
2
        return Unexpect(ErrCode::Value::CanonCallbackSignature);
390
2
      }
391
0
      break;
392
2
    }
393
0
    default:
394
0
      spdlog::error(ErrCode::Value::UnknownCanonicalOption);
395
0
      return Unexpect(ErrCode::Value::UnknownCanonicalOption);
396
3.68k
    }
397
3.68k
  }
398
9.71k
  if (SeenRealloc && !SeenMemory) {
399
0
    spdlog::error(ErrCode::Value::CanonMemoryRequired);
400
0
    spdlog::error("    realloc requires the memory canonical option."sv);
401
0
    return Unexpect(ErrCode::Value::CanonMemoryRequired);
402
0
  }
403
9.71k
  return {};
404
9.71k
}
405
406
Expect<void> Context::requireOptions(const AST::Component::Canonical &Canon,
407
                                     bool NeedMemory, bool NeedRealloc,
408
9.69k
                                     std::string_view What) const noexcept {
409
9.69k
  if (NeedMemory && !Canon.hasOption(ComponentCanonOptCode::Memory)) {
410
110
    spdlog::error(ErrCode::Value::CanonMemoryRequired);
411
110
    spdlog::error("    {} requires the memory option."sv, What);
412
110
    return Unexpect(ErrCode::Value::CanonMemoryRequired);
413
110
  }
414
9.58k
  if (NeedRealloc && !Canon.hasOption(ComponentCanonOptCode::Realloc)) {
415
0
    spdlog::error(ErrCode::Value::CanonReallocRequired);
416
0
    spdlog::error("    {} requires the realloc option."sv, What);
417
0
    return Unexpect(ErrCode::Value::CanonReallocRequired);
418
0
  }
419
9.58k
  return {};
420
9.58k
}
421
422
// ---------------------------------------------------------------------------
423
// Name grammar and strong uniqueness.
424
// ---------------------------------------------------------------------------
425
426
Expect<ExternName> Context::parseExternName(std::string_view Name,
427
7.60k
                                            bool IsImport) const noexcept {
428
7.60k
  const auto Position = IsImport ? "Import"sv : "Export"sv;
429
  // `relative-url=` is not part of the extern-name grammar.
430
7.60k
  if (Name.rfind("relative-url="sv, 0) == 0) {
431
1
    spdlog::error(ErrCode::Value::InvalidExternName);
432
1
    spdlog::error("    {} name '{}' is not a valid extern name."sv, Position,
433
1
                  Name);
434
1
    return Unexpect(ErrCode::Value::InvalidExternName);
435
1
  }
436
7.60k
  ExternName CN;
437
7.60k
  EXPECTED_TRY(CN.parse(Name));
438
7.16k
  if (CN.getKind() == ExternName::Kind::Invalid) {
439
0
    spdlog::error(ErrCode::Value::InvalidExternName);
440
0
    spdlog::error("    {} name '{}' is not a valid extern name."sv, Position,
441
0
                  Name);
442
0
    return Unexpect(ErrCode::Value::InvalidExternName);
443
0
  }
444
  // Dep / url / hash names are import-only.
445
7.16k
  if (!IsImport && (CN.getKind() == ExternName::Kind::LockedDep ||
446
2.06k
                    CN.getKind() == ExternName::Kind::UnlockedDep ||
447
2.06k
                    CN.getKind() == ExternName::Kind::Url ||
448
2.06k
                    CN.getKind() == ExternName::Kind::Integrity)) {
449
5
    spdlog::error(ErrCode::Value::InvalidExportName);
450
5
    spdlog::error("    Export name '{}' kind is not valid for exports."sv,
451
5
                  Name);
452
5
    return Unexpect(ErrCode::Value::InvalidExportName);
453
5
  }
454
7.15k
  return CN;
455
7.16k
}
456
457
7.13k
NameRecord Context::makeNameRecord(const ExternName &Name) const noexcept {
458
7.13k
  NameRecord R;
459
7.13k
  R.Original = std::string(Name.getOriginalName());
460
7.13k
  switch (Name.getKind()) {
461
11
  case ExternName::Kind::Constructor:
462
11
    R.HasAnnotation = true;
463
11
    R.IsConstructor = true;
464
11
    R.StrippedExact = std::string(Name.getNoTagName());
465
11
    break;
466
9
  case ExternName::Kind::Method:
467
38
  case ExternName::Kind::Static: {
468
38
    R.HasAnnotation = true;
469
38
    R.StrippedExact = std::string(Name.getNoTagName());
470
38
    auto Dot = R.StrippedExact.find('.');
471
38
    if (Dot != std::string::npos) {
472
38
      R.DottedFirst = R.StrippedExact.substr(0, Dot);
473
38
      R.IsDottedSame = (R.DottedFirst == R.StrippedExact.substr(Dot + 1));
474
38
    }
475
38
    break;
476
9
  }
477
6.87k
  case ExternName::Kind::Label:
478
6.87k
    R.IsPlainLabel = true;
479
6.87k
    R.StrippedExact = std::string(Name.getOriginalName());
480
6.87k
    break;
481
211
  default:
482
211
    R.StrippedExact = std::string(Name.getOriginalName());
483
211
    break;
484
7.13k
  }
485
  // Dep, url, and integrity names compare exactly; the rest case-fold.
486
7.13k
  switch (Name.getKind()) {
487
1
  case ExternName::Kind::LockedDep:
488
1
  case ExternName::Kind::UnlockedDep:
489
17
  case ExternName::Kind::Url:
490
17
  case ExternName::Kind::Integrity:
491
17
    R.Stripped = R.StrippedExact;
492
17
    break;
493
7.11k
  default:
494
7.11k
    R.Stripped = R.StrippedExact;
495
7.11k
    std::transform(
496
7.11k
        R.Stripped.begin(), R.Stripped.end(), R.Stripped.begin(),
497
39.2k
        [](unsigned char C) { return static_cast<char>(std::tolower(C)); });
498
7.11k
    break;
499
7.13k
  }
500
7.13k
  return R;
501
7.13k
}
502
503
Expect<void> Context::addUniqueName(std::vector<NameRecord> &Names,
504
                                    const NameRecord &N,
505
7.13k
                                    bool IsImport) const noexcept {
506
  // Declarator clashes carry their own code; exact duplicates never do.
507
7.13k
  const auto SideCode = IsImport ? ErrCode::Value::ComponentImportNameConflict
508
7.13k
                                 : ErrCode::Value::ComponentExportNameConflict;
509
7.13k
  const auto ConflictCode =
510
7.13k
      top().Kind == ScopeKind::Component
511
7.13k
          ? SideCode
512
7.13k
          : (IsImport ? ErrCode::Value::ComponentDeclImportNameConflict
513
409
                      : ErrCode::Value::ComponentDeclExportNameConflict);
514
7.13k
  auto ReportClash = [&](ErrCode::Value Code) noexcept {
515
50
    spdlog::error(Code);
516
50
    spdlog::error("    {} name '{}' is not strongly-unique."sv,
517
50
                  IsImport ? "Import"sv : "Export"sv, N.Original);
518
50
    return Unexpect(Code);
519
50
  };
520
10.4k
  for (const auto &E : Names) {
521
10.4k
    if (E.Original == N.Original) {
522
44
      return ReportClash(SideCode);
523
44
    }
524
10.4k
    if (E.Stripped == N.Stripped) {
525
      // `l` and `[constructor]l` of the same label are the one allowed pair.
526
8
      const bool CtorException = ((E.IsConstructor && N.IsPlainLabel) ||
527
8
                                  (N.IsConstructor && E.IsPlainLabel)) &&
528
3
                                 E.StrippedExact == N.StrippedExact;
529
8
      if (!CtorException) {
530
6
        return ReportClash(ConflictCode);
531
6
      }
532
2
      continue;
533
8
    }
534
    // `l` clashes with `[method]l.l` / `[static]l.l` for the same label.
535
10.4k
    if ((N.IsPlainLabel && E.IsDottedSame &&
536
0
         E.DottedFirst == N.StrippedExact) ||
537
10.4k
        (E.IsPlainLabel && N.IsDottedSame &&
538
0
         N.DottedFirst == E.StrippedExact)) {
539
0
      return ReportClash(ConflictCode);
540
0
    }
541
10.4k
  }
542
7.08k
  Names.push_back(N);
543
7.08k
  return {};
544
7.13k
}
545
546
Expect<void> Context::checkNameAttributes(const ExternName &CN,
547
                                          Span<const std::string> Impls,
548
                                          Span<const std::string> ExtIds,
549
                                          Span<const std::string> VSuffixes,
550
7.15k
                                          bool IsInstance) const noexcept {
551
7.15k
  if (Impls.size() > 1) {
552
1
    spdlog::error(ErrCode::Value::ComponentImplementsDuplicate);
553
1
    spdlog::error("    name `{}` has more than one `implements`"sv,
554
1
                  CN.getOriginalName());
555
1
    return Unexpect(ErrCode::Value::ComponentImplementsDuplicate);
556
1
  }
557
7.15k
  if (ExtIds.size() > 1) {
558
1
    spdlog::error(ErrCode::Value::ComponentExternalIdDuplicate);
559
1
    spdlog::error("    name `{}` has more than one `external-id`"sv,
560
1
                  CN.getOriginalName());
561
1
    return Unexpect(ErrCode::Value::ComponentExternalIdDuplicate);
562
1
  }
563
7.15k
  if (VSuffixes.size() > 1) {
564
1
    spdlog::error(ErrCode::Value::ComponentVersionSuffixDuplicate);
565
1
    spdlog::error("    name `{}` has more than one `versionsuffix`"sv,
566
1
                  CN.getOriginalName());
567
1
    return Unexpect(ErrCode::Value::ComponentVersionSuffixDuplicate);
568
1
  }
569
7.15k
  for (const auto &V : VSuffixes) {
570
19
    EXPECTED_TRY(CN.checkVersionSuffix(V));
571
19
  }
572
7.13k
  if (Impls.empty()) {
573
7.13k
    return {};
574
7.13k
  }
575
3
  for (const auto &I : Impls) {
576
3
    auto Parsed = parseExternName(I, true);
577
3
    if (!Parsed.has_value()) {
578
2
      spdlog::error(ErrCode::Value::ComponentImplementsName);
579
2
      spdlog::error("    `implements` value `{}` is not a valid name"sv, I);
580
2
      return Unexpect(ErrCode::Value::ComponentImplementsName);
581
2
    }
582
1
    if (Parsed->getKind() != ExternName::Kind::InterfaceType) {
583
1
      spdlog::error(ErrCode::Value::ComponentImplementsInterface);
584
1
      spdlog::error("    `implements` value `{}` must be an interface"sv, I);
585
1
      return Unexpect(ErrCode::Value::ComponentImplementsInterface);
586
1
    }
587
1
  }
588
0
  if (CN.getKind() != ExternName::Kind::Label &&
589
0
      CN.getKind() != ExternName::Kind::Constructor &&
590
0
      CN.getKind() != ExternName::Kind::Method &&
591
0
      CN.getKind() != ExternName::Kind::Static) {
592
0
    spdlog::error(ErrCode::Value::ComponentImplementsPlain);
593
0
    spdlog::error("    name `{}` is not valid with `implements`"sv,
594
0
                  CN.getOriginalName());
595
0
    return Unexpect(ErrCode::Value::ComponentImplementsPlain);
596
0
  }
597
0
  if (!IsInstance) {
598
0
    spdlog::error(ErrCode::Value::ComponentImplementsInstance);
599
0
    spdlog::error("    only instances can have an `implements`"sv);
600
0
    return Unexpect(ErrCode::Value::ComponentImplementsInstance);
601
0
  }
602
0
  return {};
603
0
}
604
605
// ---------------------------------------------------------------------------
606
// Declaring an import or an export.
607
// ---------------------------------------------------------------------------
608
609
Expect<ExternInfo>
610
Context::defineImport(std::string_view Name, const ExternInfo &Resolved,
611
                      Span<const std::string> Impls,
612
                      Span<const std::string> ExtIds,
613
5.30k
                      Span<const std::string> VSuffixes) noexcept {
614
  // Each instance import mints fresh identities for its declared resources.
615
5.30k
  ExternInfo Info = Resolved;
616
5.30k
  if (Info.Kind == ExternKind::InstanceType) {
617
38
    Info.Shape = freshenDeclaredResources(Info.Shape, true);
618
38
  }
619
5.30k
  EXPECTED_TRY(ExternName CN, parseExternName(Name, true));
620
5.09k
  EXPECTED_TRY(checkNameAttributes(CN, Impls, ExtIds, VSuffixes,
621
5.07k
                                   Info.Kind == ExternKind::InstanceType));
622
5.07k
  EXPECTED_TRY(addUniqueName(top().ImportSide.Names, makeNameRecord(CN), true));
623
5.04k
  defineExtern(Info);
624
5.04k
  EXPECTED_TRY(checkNamedTypesRule(Info, true));
625
5.03k
  EXPECTED_TRY(checkAnnotatedName(CN, Info, true));
626
4.99k
  recordResourceLabel(CN, Info, true);
627
4.99k
  return Info;
628
5.03k
}
629
630
Expect<ExternName>
631
Context::registerExportName(std::string_view Name, bool IsInstance,
632
                            Span<const std::string> Impls,
633
                            Span<const std::string> ExtIds,
634
1.73k
                            Span<const std::string> VSuffixes) noexcept {
635
1.73k
  EXPECTED_TRY(ExternName CN, parseExternName(Name, false));
636
1.50k
  EXPECTED_TRY(checkNameAttributes(CN, Impls, ExtIds, VSuffixes, IsInstance));
637
1.50k
  EXPECTED_TRY(
638
1.48k
      addUniqueName(top().ExportSide.Names, makeNameRecord(CN), false));
639
1.48k
  return CN;
640
1.50k
}
641
642
Expect<ExternInfo>
643
Context::defineExport(const ExternName &CN, const ExternInfo &Inferred,
644
1.47k
                      const std::optional<ExternInfo> &Ascribed) noexcept {
645
1.47k
  ExternInfo Result = Inferred;
646
1.47k
  if (Ascribed.has_value()) {
647
154
    Matcher M(Types);
648
154
    if (!M.matchExtern(Inferred, *Ascribed)) {
649
7
      spdlog::error(ErrCode::Value::ExportAscriptionIncompatible);
650
7
      spdlog::error(
651
7
          "    Ascribed type of export '{}' is not compatible with the "
652
7
          "exported definition."sv,
653
7
          CN.getOriginalName());
654
7
      return Unexpect(ErrCode::Value::ExportAscriptionIncompatible);
655
7
    }
656
147
    Result = *Ascribed;
657
147
  }
658
  // An export of a type re-introduces it under a fresh naming identity.
659
1.47k
  if (Result.Kind == ExternKind::TypeBound) {
660
727
    Result.Type.NameId = Types.nextNameId();
661
727
  }
662
1.47k
  if (Result.Kind == ExternKind::InstanceType) {
663
115
    Result.Shape = freshenDeclaredResources(Result.Shape, false);
664
115
  }
665
1.47k
  defineExtern(Result);
666
1.47k
  EXPECTED_TRY(checkNamedTypesRule(Result, false));
667
1.45k
  EXPECTED_TRY(checkAnnotatedName(CN, Result, false));
668
1.43k
  recordResourceLabel(CN, Result, false);
669
1.43k
  return Result;
670
1.45k
}
671
672
// ---------------------------------------------------------------------------
673
// Annotated plainnames.
674
// ---------------------------------------------------------------------------
675
676
Expect<void> Context::checkAnnotatedName(const ExternName &Name,
677
                                         const ExternInfo &Info,
678
7.03k
                                         bool IsImport) noexcept {
679
7.03k
  const auto Kind = Name.getKind();
680
7.03k
  if (Kind != ExternName::Kind::Constructor &&
681
7.02k
      Kind != ExternName::Kind::Method && Kind != ExternName::Kind::Static) {
682
6.98k
    return {};
683
6.98k
  }
684
48
  if (Info.Kind != ExternKind::FuncType || Info.Func.FT == nullptr) {
685
44
    spdlog::error(ErrCode::Value::ComponentIsNotFunc);
686
44
    spdlog::error(
687
44
        "    Annotated name '{}' is only allowed on function imports/exports."sv,
688
44
        Name.getOriginalName());
689
44
    return Unexpect(ErrCode::Value::ComponentIsNotFunc);
690
44
  }
691
4
  const std::string_view ResourceLabel = Name.getDetail().Resource;
692
4
  auto &S = top();
693
4
  const auto &Labels = S.getNameSide(IsImport).ResourceLabels;
694
4
  const auto &Names = S.getNameSide(IsImport).ResourceNames;
695
4
  const auto &FT = *Info.Func.FT;
696
  // The signature's resource must be named here as the annotation's label.
697
4
  auto CheckTarget = [&](uint32_t Target) noexcept -> Expect<void> {
698
0
    auto NameIt = Names.find(Target);
699
0
    if (NameIt == Names.end()) {
700
0
      spdlog::error(ErrCode::Value::ComponentResourceNotNamed);
701
0
      spdlog::error("    Resource used in '{}' has no name in this "
702
0
                    "context."sv,
703
0
                    Name.getOriginalName());
704
0
      return Unexpect(ErrCode::Value::ComponentResourceNotNamed);
705
0
    }
706
0
    if (NameIt->second != ResourceLabel) {
707
0
      spdlog::error(ErrCode::Value::AnnotatedFuncResourceName);
708
0
      spdlog::error("    '{}' does not match resource '{}'."sv,
709
0
                    Name.getOriginalName(), NameIt->second);
710
0
      return Unexpect(ErrCode::Value::AnnotatedFuncResourceName);
711
0
    }
712
0
    return {};
713
0
  };
714
715
  // Resolve a valtype to the resource behind its own or borrow handle.
716
4
  auto HandleOf = [this](const QualValType &Q,
717
4
                         bool WantOwn) noexcept -> std::optional<uint32_t> {
718
1
    TypeEntry Storage;
719
1
    const auto *Entry = Types.resolveQualType(Q, Storage);
720
1
    const auto *Def = Entry != nullptr ? Entry->getDefValType() : nullptr;
721
1
    if (Def == nullptr) {
722
1
      return std::nullopt;
723
1
    }
724
0
    const auto &DVT = *Def;
725
0
    uint32_t HandleIdx = 0;
726
0
    if (WantOwn && DVT.isOwnTy()) {
727
0
      HandleIdx = DVT.getOwn().Idx;
728
0
    } else if (!WantOwn && DVT.isBorrowTy()) {
729
0
      HandleIdx = DVT.getBorrow().Idx;
730
0
    } else {
731
0
      return std::nullopt;
732
0
    }
733
0
    const auto *Res = Entry->Home->getType(HandleIdx);
734
0
    if (Res == nullptr || !Res->isResource()) {
735
0
      return std::nullopt;
736
0
    }
737
0
    return Types.applyRemap(Entry->Remap, *Res->ResourceId);
738
0
  };
739
740
4
  if (Kind == ExternName::Kind::Constructor) {
741
    // Signature first: exactly one result of (own T) or (result (own T) e?).
742
4
    if (FT.getResultList().size() != 1) {
743
3
      spdlog::error(ErrCode::Value::AnnotatedCtorReturnOne);
744
3
      spdlog::error("    Constructor '{}' should return one value."sv,
745
3
                    Name.getOriginalName());
746
3
      return Unexpect(ErrCode::Value::AnnotatedCtorReturnOne);
747
3
    }
748
1
    QualValType Q{FT.getResultList()[0].getValType(), Info.Func.Home,
749
1
                  Info.Func.Remap};
750
1
    auto Target = HandleOf(Q, true);
751
1
    if (!Target.has_value()) {
752
      // Unwrap (result (own T) e?).
753
1
      TypeEntry Storage;
754
1
      const auto *Entry = Types.resolveQualType(Q, Storage);
755
1
      const auto *Def = Entry != nullptr ? Entry->getDefValType() : nullptr;
756
1
      if (Def != nullptr && Def->isResultTy()) {
757
0
        const auto &Res = Def->getResult();
758
0
        if (Res.ValTy.has_value()) {
759
0
          Target = HandleOf({*Res.ValTy, Entry->Home, Entry->Remap}, true);
760
0
        }
761
0
      }
762
1
    }
763
1
    if (!Target.has_value()) {
764
1
      spdlog::error(ErrCode::Value::AnnotatedCtorReturn);
765
1
      spdlog::error("    Constructor '{}' must return (own {})."sv,
766
1
                    Name.getOriginalName(), ResourceLabel);
767
1
      return Unexpect(ErrCode::Value::AnnotatedCtorReturn);
768
1
    }
769
0
    EXPECTED_TRY(CheckTarget(*Target));
770
0
  } else if (Kind == ExternName::Kind::Method) {
771
0
    if (FT.getParamList().empty()) {
772
0
      spdlog::error(ErrCode::Value::AnnotatedMethodArgs);
773
0
      spdlog::error("    Method '{}' should have at least one argument."sv,
774
0
                    Name.getOriginalName());
775
0
      return Unexpect(ErrCode::Value::AnnotatedMethodArgs);
776
0
    }
777
0
    const auto Params = FT.getParamList();
778
0
    const auto &Self = Params[0];
779
0
    if (Self.getLabel() != "self"sv) {
780
0
      spdlog::error(ErrCode::Value::AnnotatedMethodSelf);
781
0
      spdlog::error(
782
0
          "    Method '{}' should have a first argument called `self`."sv,
783
0
          Name.getOriginalName());
784
0
      return Unexpect(ErrCode::Value::AnnotatedMethodSelf);
785
0
    }
786
0
    auto Target =
787
0
        HandleOf({Self.getValType(), Info.Func.Home, Info.Func.Remap}, false);
788
0
    if (!Target.has_value()) {
789
0
      spdlog::error(ErrCode::Value::AnnotatedMethodBorrow);
790
0
      spdlog::error(
791
0
          "    Method '{}' should take a first argument of (borrow {})."sv,
792
0
          Name.getOriginalName(), ResourceLabel);
793
0
      return Unexpect(ErrCode::Value::AnnotatedMethodBorrow);
794
0
    }
795
0
    EXPECTED_TRY(CheckTarget(*Target));
796
0
  } else {
797
0
    const bool LabelKnown = Labels.count(std::string(ResourceLabel)) != 0;
798
0
    if (!LabelKnown) {
799
0
      spdlog::error(ErrCode::Value::AnnotatedStaticUnknown);
800
0
      spdlog::error(
801
0
          "    Static '{}' resource name is not known in this context."sv,
802
0
          Name.getOriginalName());
803
0
      return Unexpect(ErrCode::Value::AnnotatedStaticUnknown);
804
0
    }
805
0
  }
806
0
  return {};
807
4
}
808
809
void Context::recordResourceLabel(const ExternName &Name,
810
                                  const ExternInfo &Info,
811
6.73k
                                  bool IsImport) noexcept {
812
6.73k
  if (Name.getKind() == ExternName::Kind::Label &&
813
6.52k
      Info.Kind == ExternKind::TypeBound && Info.Type.isResource()) {
814
3.89k
    auto &S = top();
815
3.89k
    auto &Labels = S.getNameSide(IsImport).ResourceLabels;
816
3.89k
    auto &Names = S.getNameSide(IsImport).ResourceNames;
817
3.89k
    Labels.emplace(std::string(Name.getOriginalName()), *Info.Type.ResourceId);
818
3.89k
    Names.emplace(*Info.Type.ResourceId, std::string(Name.getOriginalName()));
819
3.89k
  }
820
6.73k
}
821
822
// ---------------------------------------------------------------------------
823
// The named-types rule: flags, enums, records, variants, resources are named.
824
// ---------------------------------------------------------------------------
825
826
1.62k
bool Context::isIntroduced(const QualValType &Q, bool IsImport) noexcept {
827
1.62k
  if (Q.VT.isPrimValType() || Q.Home == nullptr) {
828
1.10k
    return true;
829
1.10k
  }
830
518
  TypeEntry Storage;
831
518
  const auto *Entry = Types.resolveQualType(Q, Storage);
832
518
  if (Entry == nullptr) {
833
0
    return true;
834
0
  }
835
518
  return isIntroduced(*Entry, IsImport);
836
518
}
837
838
518
bool Context::isIntroduced(const TypeEntry &E, bool IsImport) noexcept {
839
518
  auto &S = top();
840
518
  const auto &NamedTys = S.getNameSide(IsImport).NamedTypes;
841
518
  const auto &NamedRes = S.getNameSide(IsImport).NamedResources;
842
518
  if (E.isResource()) {
843
0
    return E.NameId.has_value() && NamedRes.count(*E.NameId) != 0;
844
0
  }
845
518
  const auto *Def = E.getDefValType();
846
518
  if (Def == nullptr) {
847
0
    return true;
848
0
  }
849
518
  const auto &D = *Def;
850
518
  if (D.isPrimValType()) {
851
12
    return true;
852
12
  }
853
  // Local: the introduced identity. Foreign: a structurally equal named type.
854
506
  if (D.isFlagsTy() || D.isEnumTy() || D.isRecordTy() || D.isVariantTy()) {
855
40
    const auto &NamedIds = S.getNameSide(IsImport).NamedIds;
856
40
    if (E.Home == &S) {
857
40
      return E.NameId.has_value() && NamedIds.count(*E.NameId) != 0;
858
40
    }
859
0
    if (E.NameId.has_value() && NamedIds.count(*E.NameId) != 0) {
860
0
      return true;
861
0
    }
862
0
    if (NamedTys.count(E.DT) != 0) {
863
0
      return true;
864
0
    }
865
0
    for (const auto &[Named, Home] : NamedTys) {
866
0
      if (Named->isDefValType()) {
867
0
        TypeEntry Probe;
868
0
        Probe.DT = Named;
869
0
        Probe.Home = Home;
870
0
        Matcher M(Types);
871
0
        if (M.matchValType(E, Probe)) {
872
0
          return true;
873
0
        }
874
0
      }
875
0
    }
876
0
    return false;
877
0
  }
878
466
  auto Sub = [&](const ComponentValType &VT) noexcept {
879
377
    return isIntroduced({VT, E.Home, E.Remap}, IsImport);
880
377
  };
881
466
  if (D.isStreamTy() || D.isFutureTy()) {
882
169
    const auto &Elem =
883
169
        D.isStreamTy() ? D.getStream().ValTy : D.getFuture().ValTy;
884
169
    return !Elem.has_value() || Sub(*Elem);
885
169
  }
886
297
  if (D.isOwnTy() || D.isBorrowTy()) {
887
6
    const uint32_t Idx = D.isOwnTy() ? D.getOwn().Idx : D.getBorrow().Idx;
888
6
    const auto *Res = E.Home->getType(Idx);
889
6
    if (Res == nullptr || !Res->isResource()) {
890
0
      return true;
891
0
    }
892
6
    const uint32_t Eff = Types.applyRemap(E.Remap, *Res->ResourceId);
893
6
    const uint32_t NameId =
894
6
        Eff != *Res->ResourceId
895
6
            ? Types.getResource(Eff).NameId
896
6
            : Res->NameId.value_or(Types.getResource(Eff).NameId);
897
6
    return NamedRes.count(NameId) != 0;
898
6
  }
899
291
  bool All = true;
900
291
  Types.forEachValType(
901
362
      D, [&](const ComponentValType &VT) noexcept { All = All && Sub(VT); });
902
291
  return All;
903
297
}
904
905
// The introduced type is exempt; its immediate components must be named.
906
bool Context::areInnerTypesIntroduced(const TypeEntry &E,
907
5.06k
                                      bool IsImport) noexcept {
908
5.06k
  if (E.isResource()) {
909
3.73k
    return true;
910
3.73k
  }
911
1.32k
  if (E.Comp != nullptr) {
912
93
    return true;
913
93
  }
914
1.23k
  if (E.Inst != nullptr) {
915
51
    for (const auto &[Name, Sub] : E.Inst->Exports) {
916
10
      if (!introduceExternTypes(Sub, IsImport)) {
917
0
        return false;
918
0
      }
919
10
    }
920
51
    return true;
921
51
  }
922
1.18k
  if (const auto *FT = E.getFuncType()) {
923
102
    for (const auto &P : FT->getParamList()) {
924
16
      if (!isIntroduced({P.getValType(), E.Home, E.Remap}, IsImport)) {
925
1
        return false;
926
1
      }
927
16
    }
928
101
    for (const auto &R : FT->getResultList()) {
929
91
      if (!isIntroduced({R.getValType(), E.Home, E.Remap}, IsImport)) {
930
6
        return false;
931
6
      }
932
91
    }
933
95
    return true;
934
101
  }
935
1.07k
  const auto *Def = E.getDefValType();
936
1.07k
  if (Def == nullptr) {
937
0
    return true;
938
0
  }
939
1.07k
  const auto &D = *Def;
940
1.07k
  if (D.isPrimValType() || D.isFlagsTy() || D.isEnumTy()) {
941
48
    return true;
942
48
  }
943
1.03k
  auto Sub = [&](const ComponentValType &VT) noexcept {
944
734
    return isIntroduced({VT, E.Home, E.Remap}, IsImport);
945
734
  };
946
1.03k
  if (D.isStreamTy() || D.isFutureTy()) {
947
550
    const auto &Elem =
948
550
        D.isStreamTy() ? D.getStream().ValTy : D.getFuture().ValTy;
949
550
    return !Elem.has_value() || Sub(*Elem);
950
550
  }
951
480
  if (D.isOwnTy() || D.isBorrowTy()) {
952
10
    const auto &NamedRes = top().getNameSide(IsImport).NamedResources;
953
10
    const uint32_t Idx = D.isOwnTy() ? D.getOwn().Idx : D.getBorrow().Idx;
954
10
    const auto *Res = E.Home->getType(Idx);
955
10
    if (Res == nullptr || !Res->isResource()) {
956
0
      return true;
957
0
    }
958
10
    const uint32_t Eff = Types.applyRemap(E.Remap, *Res->ResourceId);
959
10
    const uint32_t NameId =
960
10
        Eff != *Res->ResourceId
961
10
            ? Types.getResource(Eff).NameId
962
10
            : Res->NameId.value_or(Types.getResource(Eff).NameId);
963
10
    return NamedRes.count(NameId) != 0;
964
10
  }
965
470
  bool All = true;
966
470
  Types.forEachValType(
967
640
      D, [&](const ComponentValType &VT) noexcept { All = All && Sub(VT); });
968
470
  return All;
969
480
}
970
971
// Validate and register an extern for the named-types rule.
972
bool Context::introduceExternTypes(const ExternInfo &Info,
973
6.59k
                                   bool IsImport) noexcept {
974
6.59k
  auto &S = top();
975
6.59k
  switch (Info.Kind) {
976
37
  case ExternKind::CoreType:
977
635
  case ExternKind::ComponentType:
978
635
    return true;
979
5.06k
  case ExternKind::TypeBound: {
980
5.06k
    if (!areInnerTypesIntroduced(Info.Type, IsImport)) {
981
25
      return false;
982
25
    }
983
    // Introduce: imported types are usable by exports as well.
984
5.03k
    if (Info.Type.isResource() && Info.Type.NameId.has_value()) {
985
3.73k
      if (IsImport) {
986
3.70k
        S.ImportSide.NamedResources.insert(*Info.Type.NameId);
987
3.70k
        S.ExportSide.NamedResources.insert(*Info.Type.NameId);
988
3.70k
      } else {
989
35
        S.ExportSide.NamedResources.insert(*Info.Type.NameId);
990
35
      }
991
3.73k
    } else if (Info.Type.getDefValType() != nullptr) {
992
1.06k
      if (IsImport) {
993
486
        S.ImportSide.NamedTypes.emplace(Info.Type.DT, Info.Type.Home);
994
486
        S.ExportSide.NamedTypes.emplace(Info.Type.DT, Info.Type.Home);
995
486
        if (Info.Type.NameId.has_value()) {
996
486
          S.ImportSide.NamedIds.insert(*Info.Type.NameId);
997
486
          S.ExportSide.NamedIds.insert(*Info.Type.NameId);
998
486
        }
999
574
      } else {
1000
574
        S.ExportSide.NamedTypes.emplace(Info.Type.DT, Info.Type.Home);
1001
574
        if (Info.Type.NameId.has_value()) {
1002
574
          S.ExportSide.NamedIds.insert(*Info.Type.NameId);
1003
574
        }
1004
574
      }
1005
1.06k
    }
1006
5.03k
    return true;
1007
5.06k
  }
1008
174
  case ExternKind::InstanceType: {
1009
174
    if (Info.Shape == nullptr) {
1010
0
      return true;
1011
0
    }
1012
174
    auto Walk = [&](const ExternInfo &Sub) noexcept {
1013
49
      return introduceExternTypes(Sub, IsImport);
1014
49
    };
1015
174
    if (!Info.Shape->ExportOrder.empty()) {
1016
49
      for (const auto &Name : Info.Shape->ExportOrder) {
1017
49
        auto It = Info.Shape->Exports.find(Name);
1018
49
        if (It != Info.Shape->Exports.end() && !Walk(It->second)) {
1019
0
          return false;
1020
0
        }
1021
49
      }
1022
49
      return true;
1023
49
    }
1024
125
    for (const auto &[Name, Sub] : Info.Shape->Exports) {
1025
0
      if (!Walk(Sub)) {
1026
0
        return false;
1027
0
      }
1028
0
    }
1029
125
    return true;
1030
125
  }
1031
459
  case ExternKind::FuncType: {
1032
459
    if (Info.Func.FT == nullptr) {
1033
0
      return true;
1034
0
    }
1035
459
    for (const auto &P : Info.Func.FT->getParamList()) {
1036
2
      if (!isIntroduced({P.getValType(), Info.Func.Home, Info.Func.Remap},
1037
2
                        IsImport)) {
1038
1
        return false;
1039
1
      }
1040
2
    }
1041
458
    for (const auto &R : Info.Func.FT->getResultList()) {
1042
132
      if (!isIntroduced({R.getValType(), Info.Func.Home, Info.Func.Remap},
1043
132
                        IsImport)) {
1044
1
        return false;
1045
1
      }
1046
132
    }
1047
457
    return true;
1048
458
  }
1049
270
  case ExternKind::ValueBound:
1050
270
    return isIntroduced(Info.Value, IsImport);
1051
6.59k
  }
1052
0
  return true;
1053
6.59k
}
1054
1055
Expect<void> Context::checkNamedTypesRule(const ExternInfo &Info,
1056
6.81k
                                          bool IsImport) noexcept {
1057
  // Instance-type declarations do not enforce the named-types rule.
1058
6.81k
  if (top().Kind == ScopeKind::InstanceType) {
1059
276
    return {};
1060
276
  }
1061
6.54k
  if (introduceExternTypes(Info, IsImport)) {
1062
6.50k
    return {};
1063
6.50k
  }
1064
35
  ErrCode::Value Code;
1065
35
  switch (Info.Kind) {
1066
2
  case ExternKind::FuncType:
1067
2
    Code = IsImport ? ErrCode::Value::ComponentFuncNotValidImport
1068
2
                    : ErrCode::Value::ComponentFuncNotValidExport;
1069
2
    break;
1070
0
  case ExternKind::InstanceType:
1071
0
    Code = IsImport ? ErrCode::Value::ComponentInstanceNotValidImport
1072
0
                    : ErrCode::Value::ComponentInstanceNotValidExport;
1073
0
    break;
1074
33
  default:
1075
33
    Code = IsImport ? ErrCode::Value::ComponentTypeNotValidImport
1076
33
                    : ErrCode::Value::ComponentTypeNotValidExport;
1077
33
    break;
1078
35
  }
1079
35
  spdlog::error(Code);
1080
35
  spdlog::error(
1081
35
      "    A referenced type or resource was not introduced by a preceding "
1082
35
      "{}."sv,
1083
35
      IsImport ? "import"sv : "import or export"sv);
1084
35
  return Unexpect(Code);
1085
35
}
1086
1087
// ---------------------------------------------------------------------------
1088
// Instantiation.
1089
// ---------------------------------------------------------------------------
1090
1091
Expect<const Shape *> Context::instantiateComponentShape(
1092
    const Shape &CI,
1093
    Span<const AST::Component::InstantiateArg<AST::Component::SortIndex>>
1094
283k
        Args) noexcept {
1095
  // Resolve the arguments. The names must be unique.
1096
283k
  std::unordered_map<std::string_view, ExternInfo> ArgMap;
1097
283k
  for (const auto &Arg : Args) {
1098
1.92k
    EXPECTED_TRY(auto Info, resolveSortIndex(Arg.getIndex()));
1099
1.88k
    if (!ArgMap.emplace(Arg.getName(), Info).second) {
1100
3
      spdlog::error(ErrCode::Value::ComponentDuplicateArg);
1101
3
      spdlog::error("    Duplicate instantiation argument '{}'."sv,
1102
3
                    Arg.getName());
1103
3
      return Unexpect(ErrCode::Value::ComponentDuplicateArg);
1104
3
    }
1105
    // Values are consumed by being passed as arguments.
1106
1.87k
    if (!Arg.getIndex().getSort().isCore() &&
1107
1.87k
        Arg.getIndex().getSort().getSortType() ==
1108
1.87k
            AST::Component::Sort::SortType::Value) {
1109
0
      EXPECTED_TRY(top().consumeValue(Arg.getIndex().getIdx()));
1110
0
    }
1111
1.87k
  }
1112
  // Match every import. The matcher accumulates the resource substitution.
1113
283k
  Matcher M(Types);
1114
283k
  for (const auto &[Name, Req] : CI.Imports) {
1115
1
    auto It = ArgMap.find(Name);
1116
1
    if (It == ArgMap.end()) {
1117
1
      spdlog::error(ErrCode::Value::ComponentMissingImport);
1118
1
      spdlog::error("    Missing instantiation argument '{}'."sv, Name);
1119
1
      return Unexpect(ErrCode::Value::ComponentMissingImport);
1120
1
    }
1121
0
    if (!M.matchExtern(It->second, Req)) {
1122
0
      const auto Code = M.getFailCode() != ErrCode::Value::Success
1123
0
                            ? M.getFailCode()
1124
0
                            : ErrCode::Value::ArgTypeMismatch;
1125
0
      spdlog::error(Code);
1126
0
      spdlog::error("    Instantiation argument '{}' has an incompatible "
1127
0
                    "type."sv,
1128
0
                    Name);
1129
0
      return Unexpect(Code);
1130
0
    }
1131
0
  }
1132
1133
  // Combined remap: substituted imports + freshened defined resources.
1134
283k
  std::unordered_set<uint32_t> Reachable;
1135
283k
  for (const auto &[Name, E] : CI.Exports) {
1136
1.46k
    Types.collectResources(E, Reachable);
1137
1.46k
  }
1138
283k
  auto *Node = Types.addResourceMap();
1139
283k
  Node->Map = M.getSubst().Map;
1140
283k
  for (const uint32_t Id : Reachable) {
1141
0
    if (Node->Map.count(Id) != 0) {
1142
0
      continue;
1143
0
    }
1144
0
    const auto &Entry = Types.getResource(Id);
1145
0
    if (!Entry.FromImport && CI.DeclScope != nullptr &&
1146
0
        Types.originatesIn(Id, *CI.DeclScope)) {
1147
      // A fresh resource belongs to the created instance: no definition body.
1148
0
      Node->Map.emplace(Id, Types.addResource(nullptr, &top(), false));
1149
0
    }
1150
0
  }
1151
1152
283k
  const auto *Result = Types.rebuildInstanceExports(CI, Node);
1153
283k
  ExternInfo Probe;
1154
283k
  Probe.Kind = ExternKind::InstanceType;
1155
283k
  Probe.Shape = Result;
1156
283k
  EXPECTED_TRY(Types.checkTypeLimits(Probe));
1157
283k
  return Result;
1158
283k
}
1159
1160
} // namespace Component
1161
} // namespace Validator
1162
} // namespace WasmEdge