1/*
2 * Copyright 2017 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef wasm_wasm_type_h
18#define wasm_wasm_type_h
19
20#include <functional>
21#include <optional>
22#include <ostream>
23#include <string>
24#include <unordered_map>
25#include <variant>
26#include <vector>
27
28#include "support/index.h"
29#include "support/name.h"
30#include "support/parent_index_iterator.h"
31#include "wasm-features.h"
32
33// TODO: At various code locations we were assuming that single types are basic
34// types, but this is going to change with the introduction of the compound
35// Signature, Struct and Array types that will be single but not basic. To
36// prepare for this change, the following macro marks affected code locations.
37#define TODO_SINGLE_COMPOUND(type) \
38 assert(!type.isTuple() && "Unexpected tuple type"); \
39 assert(type.isBasic() && "TODO: handle compound types");
40
41namespace wasm {
42
43// Dangerous! Frees all types and heap types that have ever been created and
44// resets the type system's internal state. This is only really meant to be used
45// for tests.
46void destroyAllTypesForTestingPurposesOnly();
47
48// The types defined in this file. All of them are small and typically passed by
49// value except for `Tuple` and `Struct`, which may own an unbounded amount of
50// data.
51class Type;
52class HeapType;
53class RecGroup;
54struct Signature;
55struct Field;
56struct Struct;
57struct Array;
58
59using TypeList = std::vector<Type>;
60using Tuple = TypeList;
61
62enum Nullability { NonNullable, Nullable };
63enum Mutability { Immutable, Mutable };
64
65// HeapType name information used for printing.
66struct TypeNames {
67 // The name of the type.
68 Name name;
69 // For a Struct, names of fields.
70 std::unordered_map<Index, Name> fieldNames;
71};
72
73// Used to generate HeapType names.
74using HeapTypeNameGenerator = std::function<TypeNames(HeapType)>;
75
76// The type used for interning IDs in the public interfaces of Type and
77// HeapType.
78using TypeID = uint64_t;
79
80class Type {
81 // The `id` uniquely represents each type, so type equality is just a
82 // comparison of the ids. For basic types the `id` is just the `BasicType`
83 // enum value below, and for constructed types the `id` is the address of the
84 // canonical representation of the type, making lookups cheap for all types.
85 // Since `Type` is really just a single integer, it should be passed by value.
86 // This is a uintptr_t rather than a TypeID (uint64_t) to save memory on
87 // 32-bit platforms.
88 uintptr_t id;
89
90public:
91 enum BasicType : uint32_t {
92 none,
93 unreachable,
94 i32,
95 i64,
96 f32,
97 f64,
98 v128,
99 };
100 static constexpr BasicType _last_basic_type = v128;
101
102 Type() : id(none) {}
103
104 // BasicType can be implicitly upgraded to Type
105 constexpr Type(BasicType id) : id(id) {}
106
107 // But converting raw TypeID is more dangerous, so make it explicit
108 explicit Type(TypeID id) : id(id) {}
109
110 // Construct tuple from a list of single types
111 Type(std::initializer_list<Type>);
112
113 // Construct from tuple description
114 Type(const Tuple&);
115 Type(Tuple&&);
116
117 // Construct from a heap type description. Also covers construction from
118 // Signature, Struct or Array via implicit conversion to HeapType.
119 Type(HeapType, Nullability nullable);
120
121 // Predicates
122 // Compound Concrete
123 // Type Basic │ Single│
124 // ╒═════════════╦═│═╤═│═╤═│═╤═│═╤═══════╕
125 // │ none ║ x │ │ │ │ │
126 // │ unreachable ║ x │ │ │ │ │
127 // ├─────────────╫───┼───┼───┼───┤───────┤
128 // │ i32 ║ x │ │ x │ x │ I │ ┐ Number
129 // │ i64 ║ x │ │ x │ x │ I │ │ I_nteger
130 // │ f32 ║ x │ │ x │ x │ F │ │ F_loat
131 // │ f64 ║ x │ │ x │ x │ F │ │ V_ector
132 // │ v128 ║ x │ │ x │ x │ V │ ┘
133 // ├─ Aliases ───╫───┼───┼───┼───┤───────┤
134 // │ funcref ║ x │ │ x │ x │ f n │ ┐ Ref
135 // │ anyref ║ x │ │ x │ x │ f? n │ │ f_unc
136 // │ eqref ║ x │ │ x │ x │ n │ │ n_ullable
137 // │ i31ref ║ x │ │ x │ x │ n │ │
138 // │ structref ║ x │ │ x │ x │ n │ │
139 // ├─ Compound ──╫───┼───┼───┼───┤───────┤ │
140 // │ Ref ║ │ x │ x │ x │ f? n? │◄┘
141 // │ Tuple ║ │ x │ │ x │ │
142 // └─────────────╨───┴───┴───┴───┴───────┘
143 constexpr bool isBasic() const { return id <= _last_basic_type; }
144 constexpr bool isConcrete() const { return id >= i32; }
145 constexpr bool isInteger() const { return id == i32 || id == i64; }
146 constexpr bool isFloat() const { return id == f32 || id == f64; }
147 constexpr bool isVector() const { return id == v128; };
148 constexpr bool isNumber() const { return id >= i32 && id <= v128; }
149 bool isTuple() const;
150 bool isSingle() const { return isConcrete() && !isTuple(); }
151 bool isRef() const;
152 bool isFunction() const;
153 // See literal.h.
154 bool isData() const;
155 // Checks whether a type is a reference and is nullable. This returns false
156 // for a value that is not a reference, that is, for which nullability is
157 // irrelevant.
158 bool isNullable() const;
159 // Checks whether a type is a reference and is non-nullable. This returns
160 // false for a value that is not a reference, that is, for which nullability
161 // is irrelevant. (For that reason, this is only the negation of isNullable()
162 // on references, but both return false on non-references.)
163 bool isNonNullable() const;
164 // Whether this type is only inhabited by null values.
165 bool isNull() const;
166 bool isStruct() const;
167 bool isArray() const;
168 bool isString() const;
169 bool isDefaultable() const;
170
171 Nullability getNullability() const;
172
173private:
174 template<bool (Type::*pred)() const> bool hasPredicate() {
175 for (const auto& type : *this) {
176 if ((type.*pred)()) {
177 return true;
178 }
179 }
180 return false;
181 }
182
183public:
184 bool hasVector() { return hasPredicate<&Type::isVector>(); }
185 bool hasRef() { return hasPredicate<&Type::isRef>(); }
186
187 constexpr TypeID getID() const { return id; }
188 constexpr BasicType getBasic() const {
189 assert(isBasic() && "Basic type expected");
190 return static_cast<BasicType>(id);
191 }
192
193 // (In)equality must be defined for both Type and BasicType because it is
194 // otherwise ambiguous whether to convert both this and other to int or
195 // convert other to Type.
196 bool operator==(const Type& other) const { return id == other.id; }
197 bool operator==(const BasicType& other) const { return id == other; }
198 bool operator!=(const Type& other) const { return id != other.id; }
199 bool operator!=(const BasicType& other) const { return id != other; }
200
201 // Returns the type size in bytes. Only single types are supported.
202 unsigned getByteSize() const;
203
204 // Returns whether the type has a size in bytes. This is the same as whether
205 // it can be stored in linear memory. Things like references do not have this
206 // property, while numbers do. Tuples may or may not depending on their
207 // contents.
208 unsigned hasByteSize() const;
209
210 // Reinterpret an integer type to a float type with the same size and vice
211 // versa. Only single integer and float types are supported.
212 Type reinterpret() const;
213
214 // Returns the feature set required to use this type.
215 FeatureSet getFeatures() const;
216
217 // Returns the tuple, assuming that this is a tuple type. Note that it is
218 // normally simpler to use operator[] and size() on the Type directly.
219 const Tuple& getTuple() const;
220
221 // Gets the heap type corresponding to this type, assuming that it is a
222 // reference type.
223 HeapType getHeapType() const;
224
225 // Returns a number type based on its size in bytes and whether it is a float
226 // type.
227 static Type get(unsigned byteSize, bool float_);
228
229 // Returns true if left is a subtype of right. Subtype includes itself.
230 static bool isSubType(Type left, Type right);
231
232 // Return the ordered HeapType children, looking through child Types.
233 std::vector<HeapType> getHeapTypeChildren();
234
235 // Computes the least upper bound from the type lattice.
236 // If one of the type is unreachable, the other type becomes the result. If
237 // the common supertype does not exist, returns none, a poison value.
238 static bool hasLeastUpperBound(Type a, Type b);
239 static Type getLeastUpperBound(Type a, Type b);
240 template<typename T> static bool hasLeastUpperBound(const T& types) {
241 auto first = types.begin(), end = types.end();
242 if (first == end) {
243 return false;
244 }
245 for (auto second = std::next(first); second != end;) {
246 if (!hasLeastUpperBound(*first++, *second++)) {
247 return false;
248 }
249 }
250 return true;
251 }
252 template<typename T> static Type getLeastUpperBound(const T& types) {
253 auto it = types.begin(), end = types.end();
254 if (it == end) {
255 return Type::none;
256 }
257 Type lub = *it++;
258 for (; it != end; ++it) {
259 lub = getLeastUpperBound(lub, *it);
260 if (lub == Type::none) {
261 return Type::none;
262 }
263 }
264 return lub;
265 }
266
267 // Helper allowing the value of `print(...)` to be sent to an ostream. Stores
268 // a `TypeID` because `Type` is incomplete at this point and using a reference
269 // makes it less convenient to use.
270 struct Printed {
271 TypeID typeID;
272 HeapTypeNameGenerator generateName;
273 };
274
275 // Given a function for generating non-basic HeapType names, print this Type
276 // to `os`.`generateName` should return the same name each time it is called
277 // with the same HeapType and it should return different names for different
278 // types.
279 Printed print(HeapTypeNameGenerator generateName) {
280 return Printed{getID(), generateName};
281 }
282
283 std::string toString() const;
284
285 size_t size() const;
286
287 struct Iterator : ParentIndexIterator<const Type*, Iterator> {
288 using value_type = Type;
289 using pointer = const Type*;
290 using reference = const Type&;
291 reference operator*() const;
292 };
293
294 Iterator begin() const { return Iterator{{this, 0}}; }
295 Iterator end() const { return Iterator{{this, size()}}; }
296 std::reverse_iterator<Iterator> rbegin() const {
297 return std::make_reverse_iterator(end());
298 }
299 std::reverse_iterator<Iterator> rend() const {
300 return std::make_reverse_iterator(begin());
301 }
302 const Type& operator[](size_t i) const { return *Iterator{{this, i}}; }
303};
304
305class HeapType {
306 // Unlike `Type`, which represents the types of values on the WebAssembly
307 // stack, `HeapType` is used to describe the structures that reference types
308 // refer to. HeapTypes are canonicalized and interned exactly like Types and
309 // should also be passed by value.
310 uintptr_t id;
311
312public:
313 enum BasicHeapType : uint32_t {
314 ext,
315 func,
316 any,
317 eq,
318 i31,
319 struct_,
320 array,
321 string,
322 stringview_wtf8,
323 stringview_wtf16,
324 stringview_iter,
325 none,
326 noext,
327 nofunc,
328 };
329 static constexpr BasicHeapType _last_basic_type = nofunc;
330
331 // BasicHeapType can be implicitly upgraded to HeapType
332 constexpr HeapType(BasicHeapType id) : id(id) {}
333
334 // But converting raw TypeID is more dangerous, so make it explicit
335 explicit HeapType(TypeID id) : id(id) {}
336
337 // Choose an arbitrary heap type as the default.
338 constexpr HeapType() : HeapType(func) {}
339
340 // Construct a HeapType referring to the single canonical HeapType for the
341 // given signature. In nominal mode, this is the first HeapType created with
342 // this signature.
343 HeapType(Signature signature);
344
345 // Create a HeapType with the given structure. In equirecursive mode, this may
346 // be the same as a previous HeapType created with the same contents. In
347 // nominal mode, this will be a fresh type distinct from all previously
348 // created HeapTypes.
349 // TODO: make these explicit to differentiate them.
350 HeapType(const Struct& struct_);
351 HeapType(Struct&& struct_);
352 HeapType(Array array);
353
354 constexpr bool isBasic() const { return id <= _last_basic_type; }
355 bool isFunction() const;
356 bool isData() const;
357 bool isSignature() const;
358 bool isStruct() const;
359 bool isArray() const;
360 bool isString() const;
361 bool isBottom() const;
362 bool isFinal() const;
363
364 Signature getSignature() const;
365 const Struct& getStruct() const;
366 Array getArray() const;
367
368 // If there is a nontrivial (i.e. non-basic) nominal supertype, return it,
369 // else an empty optional.
370 std::optional<HeapType> getSuperType() const;
371
372 // Return the depth of this heap type in the nominal type hierarchy, i.e. the
373 // number of supertypes in its supertype chain.
374 size_t getDepth() const;
375
376 // Get the bottom heap type for this heap type's hierarchy.
377 BasicHeapType getBottom() const;
378
379 // Get the recursion group for this non-basic type.
380 RecGroup getRecGroup() const;
381 size_t getRecGroupIndex() const;
382
383 constexpr TypeID getID() const { return id; }
384 constexpr BasicHeapType getBasic() const {
385 assert(isBasic() && "Basic heap type expected");
386 return static_cast<BasicHeapType>(id);
387 }
388
389 // (In)equality must be defined for both HeapType and BasicHeapType because it
390 // is otherwise ambiguous whether to convert both this and other to int or
391 // convert other to HeapType.
392 bool operator==(const HeapType& other) const { return id == other.id; }
393 bool operator==(const BasicHeapType& other) const { return id == other; }
394 bool operator!=(const HeapType& other) const { return id != other.id; }
395 bool operator!=(const BasicHeapType& other) const { return id != other; }
396
397 // Returns true if left is a subtype of right. Subtype includes itself.
398 static bool isSubType(HeapType left, HeapType right);
399
400 std::vector<Type> getTypeChildren() const;
401
402 // Return the ordered HeapType children, looking through child Types.
403 std::vector<HeapType> getHeapTypeChildren() const;
404
405 // Similar to `getHeapTypeChildren`, but also includes the supertype if it
406 // exists.
407 std::vector<HeapType> getReferencedHeapTypes() const;
408
409 // Return the LUB of two HeapTypes, which may or may not exist.
410 static std::optional<HeapType> getLeastUpperBound(HeapType a, HeapType b);
411
412 // Helper allowing the value of `print(...)` to be sent to an ostream. Stores
413 // a `TypeID` because `Type` is incomplete at this point and using a reference
414 // makes it less convenient to use.
415 struct Printed {
416 TypeID typeID;
417 HeapTypeNameGenerator generateName;
418 };
419
420 // Given a function for generating HeapType names, print the definition of
421 // this HeapType to `os`. `generateName` should return the same
422 // name each time it is called with the same HeapType and it should return
423 // different names for different types.
424 Printed print(HeapTypeNameGenerator generateName) {
425 return Printed{getID(), generateName};
426 }
427
428 std::string toString() const;
429};
430
431inline bool Type::isNull() const { return isRef() && getHeapType().isBottom(); }
432
433// A recursion group consisting of one or more HeapTypes. HeapTypes with single
434// members are encoded without using any additional memory, which is why
435// `getHeapTypes` has to return a vector by value; it might have to create one
436// on the fly.
437class RecGroup {
438 uintptr_t id;
439
440public:
441 explicit RecGroup(uintptr_t id) : id(id) {}
442 constexpr TypeID getID() const { return id; }
443 bool operator==(const RecGroup& other) const { return id == other.id; }
444 bool operator!=(const RecGroup& other) const { return id != other.id; }
445 size_t size() const;
446
447 struct Iterator : ParentIndexIterator<const RecGroup*, Iterator> {
448 using value_type = HeapType;
449 using pointer = const HeapType*;
450 using reference = const HeapType&;
451 value_type operator*() const;
452 };
453
454 Iterator begin() const { return Iterator{{this, 0}}; }
455 Iterator end() const { return Iterator{{this, size()}}; }
456 HeapType operator[](size_t i) const { return *Iterator{{this, i}}; }
457};
458
459struct Signature {
460 Type params;
461 Type results;
462 Signature() : params(Type::none), results(Type::none) {}
463 Signature(Type params, Type results) : params(params), results(results) {}
464 bool operator==(const Signature& other) const {
465 return params == other.params && results == other.results;
466 }
467 bool operator!=(const Signature& other) const { return !(*this == other); }
468 std::string toString() const;
469};
470
471struct Field {
472 Type type;
473 enum PackedType {
474 not_packed,
475 i8,
476 i16,
477 } packedType; // applicable iff type=i32
478 Mutability mutable_;
479
480 // Arbitrary defaults for convenience.
481 Field() : type(Type::i32), packedType(not_packed), mutable_(Mutable) {}
482 Field(Type type, Mutability mutable_)
483 : type(type), packedType(not_packed), mutable_(mutable_) {}
484 Field(PackedType packedType, Mutability mutable_)
485 : type(Type::i32), packedType(packedType), mutable_(mutable_) {}
486
487 constexpr bool isPacked() const {
488 if (packedType != not_packed) {
489 assert(type == Type::i32 && "unexpected type");
490 return true;
491 }
492 return false;
493 }
494
495 bool operator==(const Field& other) const {
496 return type == other.type && packedType == other.packedType &&
497 mutable_ == other.mutable_;
498 }
499 bool operator!=(const Field& other) const { return !(*this == other); }
500 std::string toString() const;
501
502 unsigned getByteSize() const;
503};
504
505using FieldList = std::vector<Field>;
506
507// Passed by reference rather than by value because it can own an unbounded
508// amount of data.
509struct Struct {
510 FieldList fields;
511 Struct() = default;
512 Struct(const Struct& other) : fields(other.fields) {}
513 Struct(const FieldList& fields) : fields(fields) {}
514 Struct(FieldList&& fields) : fields(std::move(fields)) {}
515 bool operator==(const Struct& other) const { return fields == other.fields; }
516 bool operator!=(const Struct& other) const { return !(*this == other); }
517 std::string toString() const;
518
519 // Prevent accidental copies
520 Struct& operator=(const Struct&) = delete;
521 Struct& operator=(Struct&&) = default;
522};
523
524struct Array {
525 Field element;
526 Array() = default;
527 Array(const Array& other) : element(other.element) {}
528 Array(Field element) : element(element) {}
529 bool operator==(const Array& other) const { return element == other.element; }
530 bool operator!=(const Array& other) const { return !(*this == other); }
531 std::string toString() const;
532
533 Array& operator=(const Array& other) = default;
534};
535
536// TypeBuilder - allows for the construction of recursive types. Contains a
537// table of `n` mutable HeapTypes and can construct temporary types that are
538// backed by those HeapTypes, refering to them by reference. Those temporary
539// types are owned by the TypeBuilder and should only be used in the
540// construction of HeapTypes to insert into the TypeBuilder. Temporary types
541// should never be used in the construction of normal Types, only other
542// temporary types.
543struct TypeBuilder {
544 struct Impl;
545 std::unique_ptr<Impl> impl;
546
547 TypeBuilder(size_t n);
548 TypeBuilder() : TypeBuilder(0) {}
549 ~TypeBuilder();
550
551 TypeBuilder(TypeBuilder& other) = delete;
552 TypeBuilder& operator=(TypeBuilder&) = delete;
553
554 TypeBuilder(TypeBuilder&& other);
555 TypeBuilder& operator=(TypeBuilder&& other);
556
557 // Append `n` new uninitialized HeapType slots to the end of the TypeBuilder.
558 void grow(size_t n);
559
560 // The number of HeapType slots in the TypeBuilder.
561 size_t size();
562
563 // Sets the heap type at index `i`. May only be called before `build`.
564 void setHeapType(size_t i, Signature signature);
565 void setHeapType(size_t i, const Struct& struct_);
566 void setHeapType(size_t i, Struct&& struct_);
567 void setHeapType(size_t i, Array array);
568
569 // Gets the temporary HeapType at index `i`. This HeapType should only be used
570 // to construct temporary Types using the methods below.
571 HeapType getTempHeapType(size_t i);
572
573 // Gets a temporary type or heap type for use in initializing the
574 // TypeBuilder's HeapTypes. For Ref types, the HeapType may be a temporary
575 // HeapType owned by this builder or a canonical HeapType.
576 Type getTempTupleType(const Tuple&);
577 Type getTempRefType(HeapType heapType, Nullability nullable);
578
579 // In nominal mode, or for nominal types, declare the HeapType being built at
580 // index `i` to be an immediate subtype of the given HeapType. Does nothing
581 // for equirecursive types.
582 void setSubType(size_t i, HeapType super);
583
584 // Create a new recursion group covering slots [i, i + length). Groups must
585 // not overlap or go out of bounds.
586 void createRecGroup(size_t i, size_t length);
587
588 void setFinal(size_t i, bool final = true);
589
590 enum class ErrorReason {
591 // There is a cycle in the supertype relation.
592 SelfSupertype,
593 // The declared supertype of a type is invalid.
594 InvalidSupertype,
595 // The declared supertype is an invalid forward reference.
596 ForwardSupertypeReference,
597 // A child of the type is an invalid forward reference.
598 ForwardChildReference,
599 };
600
601 struct Error {
602 // The index of the type causing the failure.
603 size_t index;
604 ErrorReason reason;
605 };
606
607 struct BuildResult : std::variant<std::vector<HeapType>, Error> {
608 operator bool() const {
609 return bool(std::get_if<std::vector<HeapType>>(this));
610 }
611 const std::vector<HeapType>& operator*() const {
612 return std::get<std::vector<HeapType>>(*this);
613 }
614 const std::vector<HeapType>* operator->() const { return &*(*this); }
615 const Error* getError() const { return std::get_if<Error>(this); }
616 };
617
618 // Returns all of the newly constructed heap types. May only be called once
619 // all of the heap types have been initialized with `setHeapType`. In nominal
620 // mode, all of the constructed HeapTypes will be fresh and distinct. In
621 // nominal mode, will also produce a fatal error if the declared subtype
622 // relationships are not valid.
623 BuildResult build();
624
625 // Utility for ergonomically using operator[] instead of explicit setHeapType
626 // and getTempHeapType methods.
627 struct Entry {
628 TypeBuilder& builder;
629 size_t index;
630 operator HeapType() const { return builder.getTempHeapType(index); }
631 Entry& operator=(Signature signature) {
632 builder.setHeapType(index, signature);
633 return *this;
634 }
635 Entry& operator=(const Struct& struct_) {
636 builder.setHeapType(index, struct_);
637 return *this;
638 }
639 Entry& operator=(Struct&& struct_) {
640 builder.setHeapType(index, std::move(struct_));
641 return *this;
642 }
643 Entry& operator=(Array array) {
644 builder.setHeapType(index, array);
645 return *this;
646 }
647 Entry& subTypeOf(HeapType other) {
648 builder.setSubType(index, other);
649 return *this;
650 }
651 Entry& setFinal(bool final = true) {
652 builder.setFinal(index, final);
653 return *this;
654 }
655 };
656
657 Entry operator[](size_t i) { return Entry{*this, i}; }
658};
659
660std::ostream& operator<<(std::ostream&, Type);
661std::ostream& operator<<(std::ostream&, Type::Printed);
662std::ostream& operator<<(std::ostream&, HeapType);
663std::ostream& operator<<(std::ostream&, HeapType::Printed);
664std::ostream& operator<<(std::ostream&, Tuple);
665std::ostream& operator<<(std::ostream&, Signature);
666std::ostream& operator<<(std::ostream&, Field);
667std::ostream& operator<<(std::ostream&, Struct);
668std::ostream& operator<<(std::ostream&, Array);
669std::ostream& operator<<(std::ostream&, TypeBuilder::ErrorReason);
670
671} // namespace wasm
672
673namespace std {
674
675template<> class hash<wasm::Type> {
676public:
677 size_t operator()(const wasm::Type&) const;
678};
679template<> class hash<wasm::Signature> {
680public:
681 size_t operator()(const wasm::Signature&) const;
682};
683template<> class hash<wasm::Field> {
684public:
685 size_t operator()(const wasm::Field&) const;
686};
687template<> class hash<wasm::Struct> {
688public:
689 size_t operator()(const wasm::Struct&) const;
690};
691template<> class hash<wasm::Array> {
692public:
693 size_t operator()(const wasm::Array&) const;
694};
695template<> class hash<wasm::HeapType> {
696public:
697 size_t operator()(const wasm::HeapType&) const;
698};
699template<> class hash<wasm::RecGroup> {
700public:
701 size_t operator()(const wasm::RecGroup&) const;
702};
703
704} // namespace std
705
706#endif // wasm_wasm_type_h
707

source code of dart_sdk/third_party/binaryen/src/src/wasm-type.h