/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 |