#pragma once #include #include #include #include #include #include #include #include "lend-handle.h" // NOLINT(build/include_directory) #include "lend-static-string.h" // NOLINT(build/include_directory) #include "lend-forward.h" // NOLINT(build/include_directory) #include "lend-vector.h" // NOLINT(build/include_directory) namespace lend { class TypeList : public Vector { using Base = Vector; public: using Base::Base; constexpr TypeList(Base base) : Base(base) {} constexpr TypeList(Vector base) : Base(base) {} constexpr TypeList() = default; }; class Type { public: static constexpr uint8_t kIsHeapObject = 1u << 0; static constexpr uint8_t kIsCollectable = 1u << 1; static constexpr uint64_t kMask = 1u << 8; static constexpr uint64_t kFlagsMask = 1u << 16; static constexpr uint64_t kFlagsShift = 8; static constexpr uint64_t kInfoExtraShift = 16; enum class Is : uint8_t { Undefined = 0, Null, False, True, Long, Double, String, Array, Object, Resource, Reference, ConstantAst, Callable, Iterable, Void, Static, Mixed, Never, Bool, Number, BigInt, /* internal types */ Indirect = 12, Pointer = 13, AliasPointer = 14, Error = 15, InternedString = String, }; template struct Deduce { using type = TVal *; }; #define LEND_TYPE_DEDUCE(t, v) \ template <> \ struct Deduce { \ using type = v; \ }; LEND_TYPE_DEDUCE(Undefined, std::nullptr_t) LEND_TYPE_DEDUCE(Null, std::nullptr_t) LEND_TYPE_DEDUCE(False, bool) LEND_TYPE_DEDUCE(True, bool) LEND_TYPE_DEDUCE(Bool, bool) LEND_TYPE_DEDUCE(Number, double) LEND_TYPE_DEDUCE(Long, Long) LEND_TYPE_DEDUCE(Double, double) LEND_TYPE_DEDUCE(String, Handle) LEND_TYPE_DEDUCE(Array, Handle) LEND_TYPE_DEDUCE(Object, Handle) LEND_TYPE_DEDUCE(Resource, Handle) LEND_TYPE_DEDUCE(Reference, Handle) LEND_TYPE_DEDUCE(ConstantAst, Handle) #undef LEND_TYPE_DEDUCE template struct DeduceFromCpp { static constexpr Is value = Is::Mixed; }; #define LEND_TYPE_DEDUCE_FROM_HANDLE(t, v) \ template <> \ struct DeduceFromCpp { \ static constexpr Is value = Is::t; \ }; LEND_TYPE_DEDUCE_FROM_HANDLE(String, Handle) LEND_TYPE_DEDUCE_FROM_HANDLE(Array, Handle) LEND_TYPE_DEDUCE_FROM_HANDLE(Object, Handle) LEND_TYPE_DEDUCE_FROM_HANDLE(Resource, Handle) LEND_TYPE_DEDUCE_FROM_HANDLE(Reference, Handle) LEND_TYPE_DEDUCE_FROM_HANDLE(ConstantAst, Handle) template struct Mask { static constexpr uint64_t value = 1u << static_cast(t); }; using IsField = base::BitField; struct MayBeFields { private: template using Field = base::BitField(t), Size>; public: using Undefined = Field; using Null = Field; using False = Field; using True = Field; using Long = Field; using Double = Field; using String = Field; using Array = Field; using Object = Field; using Resource = Field; using Reference = Field; using Callable = Field; using Void = Field; using Static = Field; using Never = Field; using Last = Never; template using Next = Last::Next; static constexpr auto kMask = Next::kMask - 1; // HeapObject and Collectable only apply to Array and Object using HeapObject = Object; using Collectable = HeapObject::Next; //Array of Field using ArrayOfField = base::BitField; static constexpr auto kArrayMask = ArrayOfField::kMax; using BigInt = Field; }; enum class MayBe : uint32_t { None = 0, Undefined = MayBeFields::Undefined::kMask, Null = MayBeFields::Null::kMask, False = MayBeFields::False::kMask, True = MayBeFields::True::kMask, Long = MayBeFields::Long::kMask, Double = MayBeFields::Double::kMask, String = MayBeFields::String::kMask, Array = MayBeFields::Array::kMask, Object = MayBeFields::Object::kMask, Resource = MayBeFields::Resource::kMask, Reference = MayBeFields::Reference::kMask, Callable = MayBeFields::Callable::kMask, Void = MayBeFields::Void::kMask, Never = MayBeFields::Never::kMask, Static = MayBeFields::Static::kMask, BigInt = MayBeFields::BigInt::kMask, Bool = (False | True), Number = (Long | Double), Any = Null | Bool | Long | Double | String | Array | Object | Resource, ArrayShift = MayBeFields::ArrayOfField::kShift, ArrayOfNull = Null << ArrayShift, ArrayOfFalse = False << ArrayShift, ArrayOfTrue = True << ArrayShift, ArrayOfLong = Long << ArrayShift, ArrayOfDouble = Double << ArrayShift, ArrayOfString = String << ArrayShift, ArrayOfArray = Array << ArrayShift, ArrayOfObject = Object << ArrayShift, ArrayOfResource = Resource << ArrayShift, ArrayOfAny = Any << ArrayShift, ArrayOfRef = Reference << ArrayShift, ArrayPacked = 1u << 21, ArrayNumericHash = 1u << 22, ArrayStringHash = 1u << 23, Class = 1u << 24, Indirect = 1u << 25, PackedGuard = 1u << 27, ClassGuard = 1u << 27, Guard = 1u << 28, Rc1 = 1u << 30, Rcn = 1u << 31, ArrayKeyLong = ArrayPacked | ArrayNumericHash, ArrayKeyString = ArrayStringHash, ArrayKeyAny = ArrayKeyLong | ArrayKeyString, AnyArray = Array | ArrayKeyAny | ArrayOfAny | ArrayOfRef, }; enum class Flags : std::uint32_t { None = 0, // Whether the type is a union type UnionBit = 1u << 18, // Whether the type list is an intersection type IntersectionBit = 1u << 19, // Whether the type list is zone allocated ArenaBit = 1u << 20, // For BC behaviour with iterable type IterableBit = 1u << 21, // Only one of these bits may be set. ListBit = 1u << 22, LiteralNameBit = 1u << 23, NameBit = 1u << 24, // Common mask for complex types KindMask = ListBit | NameBit, Union = UnionBit | ListBit, Intersection = IntersectionBit | ListBit, // Type mask excluding the flags above. MayBeMask = UnionBit - 1, // Must have same value as MayBe::Null NullableBit = Mask::value, ExtraFlagsShift = 25, /* Extra flags */ ReferenceBit = 1u << ExtraFlagsShift, // Whether the type is variadic VariadicBit = 1u << (ExtraFlagsShift + 2), // Whether the type is promoted PromotedBit = 1u << (ExtraFlagsShift + 3), // Whether the type is tentative TentativeBit = 1u << (ExtraFlagsShift + 4), Mask = ReferenceBit - 1, }; friend constexpr Flags operator|(Flags a, unsigned b) { return static_cast(static_cast(a) | static_cast(b)); } friend constexpr Flags operator|(MayBe a, unsigned b) { return static_cast(static_cast(a) | static_cast(b)); } friend constexpr Flags operator|(Flags a, Flags b) { return static_cast(static_cast(a) | static_cast(b)); } friend constexpr Flags operator&(Flags a, unsigned b) { return static_cast(static_cast(a) & static_cast(b)); } friend constexpr Flags operator&(MayBe a, unsigned b) { return static_cast(static_cast(a) & static_cast(b)); } friend constexpr Flags operator&(Flags a, Flags b) { return static_cast(static_cast(a) & static_cast(b)); } friend constexpr Flags operator~(Flags a) { return static_cast(~static_cast(a)); } friend constexpr Flags &operator|=(Flags &a, Flags b) { return a = a | static_cast(b); } enum class ComplexKind : std::uint32_t { List = 1, LiteralName = 2, Name = 3 }; enum class ComplexKind2 : std::uint32_t { List, LiteralName, Name }; // using FlagsField = IsField::Next; // using ExtraField = FlagsField::Next; using UnionField = MayBeFields::Next; using IntersectionField = UnionField::Next; using ArenaField = IntersectionField::Next; using IterableField = ArenaField::Next; using ListField = IterableField::Next; using LiteralNameField = ListField::Next; using NameField = LiteralNameField::Next; using SendModeField = NameField::Next; using ReferenceField = NameField::Next; using VariadicField = ReferenceField::Next; using PromotedField = VariadicField::Next; using TentativeField = PromotedField::Next; using KindField = base::BitFieldUnion; using ListKindField = base::BitFieldUnion; // ListField is always true enum class ListKind : uint32_t { Union = ListKindField::encode(true, false, true), Intersection = ListKindField::encode(false, true, true), }; // List, Name, ListName using ComplexField = IterableField::Next; using ComplexField2 = IterableField::Next; // static_assert(ComplexField::kMask == int(Flags::KindMask)); static_assert(int(Flags::KindMask) == (ListField::kMask | NameField::kMask)); static_assert(KindField::kMask == int(Flags::KindMask)); static_assert(UnionField::kMask == int(Flags::UnionBit)); static_assert(IntersectionField::kMask == int(Flags::IntersectionBit)); static_assert(int(ListKind::Union) == int(Flags::Union)); static_assert(int(ListKind::Intersection) == int(Flags::Intersection)); struct Check; friend constexpr MayBe operator|(MayBe a, MayBe b) { return static_cast(static_cast(a) | static_cast(b)); } friend constexpr MayBe operator&(MayBe a, MayBe b) { return static_cast(static_cast(a) & static_cast(b)); } friend constexpr MayBe operator~(MayBe a) { return static_cast(~static_cast(a)); } friend constexpr MayBe &operator|=(MayBe &a, MayBe b) { return a = a | b; } friend constexpr Is operator|(Is a, Is b) { return static_cast(static_cast(a) | static_cast(b)); } friend constexpr Is operator&(Is a, Is b) { return static_cast(static_cast(a) & static_cast(b)); } friend constexpr Is operator~(Is a) { return static_cast(~static_cast(a)); } friend constexpr Is operator|(Is a, uint8_t b) { return static_cast(static_cast(a) | b); } friend constexpr Is &operator|=(Is &a, Is b) { return a = a | b; } friend constexpr Is &operator&=(Is &a, Is b) { return a = a & b; } friend constexpr bool operator!(Is a) { return !static_cast(a); } friend constexpr bool operator==(Is a, Is b) { return static_cast(a) == static_cast(b); } friend constexpr bool operator!=(Is a, Is b) { return static_cast(a) != static_cast(b); } friend constexpr bool operator<(Is a, Is b) { return static_cast(a) < static_cast(b); } friend constexpr bool operator>(Is a, Is b) { return static_cast(a) > static_cast(b); } friend constexpr bool operator<=(Is a, Is b) { return static_cast(a) <= static_cast(b); } friend constexpr bool operator>=(Is a, Is b) { return static_cast(a) >= static_cast(b); } friend constexpr Is operator<<(Is a, unsigned b) { return static_cast(static_cast(a) << b); } friend constexpr Is operator>>(Is a, unsigned b) { return static_cast(static_cast(a) >> b); } friend constexpr auto operator<<(unsigned a, Is b) { return static_cast(a << static_cast(b)); } friend constexpr auto operator>>(unsigned a, Is b) { return static_cast(a >> static_cast(b)); } constexpr uint64_t type_mask() const { return bits_; } constexpr auto ptr() const { return addr_; } template constexpr static uint64_t Has(unsigned mask) { return mask & (static_cast(f) | ...); } template constexpr static uint64_t Has(unsigned mask) { return (MayBeFields::kMask & mask) & (static_cast(f) | ...); } template constexpr static bool Has(unsigned mask) { return ((MayBeFields::kMask & mask) & (MayBeTypeMask(f) | ...)) != 0; } template constexpr static uint64_t Add(unsigned mask) { return mask | static_cast(f); } template constexpr static uint64_t Remove(unsigned mask) { return mask & ~static_cast(f); } template constexpr uint64_t Has() const { return Has(type_mask()); } template constexpr uint64_t Has() const { return Has(type_mask()); } template constexpr bool Has() const { return Has(type_mask()); } template constexpr void Add() { type_mask() = Add(type_mask()); } template constexpr void Remove() { type_mask() = Remove(type_mask()); } static constexpr uint64_t Bits(Type t) { return t.type_mask(); } static constexpr uint64_t PureMask(unsigned mask) { return mask & MayBeFields::kMask; } static constexpr uint64_t FullMaskWithoutNull(unsigned mask) { return mask & ~MayBeFields::Null::kMask; } static constexpr uint64_t PureMaskWithoutNull(unsigned mask) { return FullMaskWithoutNull(PureMask(mask)); } static constexpr bool ContainsCode(uint64_t mask, uint32_t code) { return (mask & (1u << code)) != 0; } static constexpr bool ContainsCode(uint64_t mask, Type::Is code) { return ContainsCode(mask, static_cast(code)); } static constexpr bool AllowsNull(unsigned mask) { return MayBeFields::Null::decode(mask); } static constexpr bool IsSet(unsigned mask) { return Has(mask); } static constexpr bool IsComplex(unsigned mask) { return Has(mask); } static constexpr bool IsUnion(unsigned mask) { return UnionField::decode(mask); } static constexpr bool IsIntersection(unsigned mask) { return IntersectionField::decode(mask); } static constexpr bool IsVariadic(unsigned mask) { return VariadicField::decode(mask); } static constexpr bool IsPromoted(unsigned mask) { return PromotedField::decode(mask); } static constexpr bool IsTentative(unsigned mask) { return TentativeField::decode(mask); } static constexpr bool UsesArena(unsigned mask) { return ArenaField::decode(mask); } static constexpr bool HasName(unsigned mask) { return NameField::decode(mask); } static constexpr bool HasLiteralName(unsigned mask) { return LiteralNameField::decode(mask); } static constexpr bool HasList(unsigned mask) { return ListField::decode(mask); } static constexpr bool IsIterableFallback(unsigned mask) { return IterableField::decode(mask); } static constexpr uint64_t PureMask(Type t) { return PureMask(t.type_mask()); } static constexpr uint64_t FullMaskWithoutNull(Type t) { return FullMaskWithoutNull(t.type_mask()); } static constexpr uint64_t PureMaskWithoutNull(Type t) { return PureMaskWithoutNull(t.type_mask()); } static constexpr bool ContainsCode(Type t, uint32_t code) { return ContainsCode(t.type_mask(), code); } static constexpr bool AllowsNull(Type t) { return AllowsNull(t.type_mask()); } static constexpr bool IsSet(Type t) { return IsSet(t.type_mask()); } static constexpr bool IsComplex(Type t) { return IsComplex(t.type_mask()); } static constexpr bool IsUnion(Type t) { return IsUnion(t.type_mask()); } static constexpr bool IsIntersection(Type t) { return IsIntersection(t.type_mask()); } static constexpr bool IsVariadic(Type t) { return IsVariadic(t.type_mask()); } static constexpr bool IsPromoted(Type t) { return IsPromoted(t.type_mask()); } static constexpr bool IsTentative(Type t) { return IsTentative(t.type_mask()); } static constexpr bool UsesArena(Type t) { return UsesArena(t.type_mask()); } static constexpr bool HasName(Type t) { return HasName(t.type_mask()); } static constexpr bool HasLiteralName(Type t) { return HasLiteralName(t.type_mask()); } static constexpr bool HasList(Type t) { return HasList(t.type_mask()); } static constexpr bool IsIterableFallback(Type t) { return IsIterableFallback(t.type_mask()); } constexpr uint64_t PureMask() const { return PureMask(type_mask()); } constexpr uint64_t FullMaskWithoutNull() const { return FullMaskWithoutNull(type_mask()); } constexpr uint64_t PureMaskWithoutNull() const { return PureMaskWithoutNull(type_mask()); } constexpr bool ContainsCode(uint32_t code) const { return (type_mask() & (1u << code)) != 0; } constexpr bool ContainsCode(Type::Is code) const { return ContainsCode(static_cast(code)); } constexpr bool AllowsNull() const { return AllowsNull(type_mask()); } constexpr bool IsSet() const { return IsSet(type_mask()); } constexpr bool IsComplex() const { return IsComplex(type_mask()); } constexpr bool IsUnion() const { return IsUnion(type_mask()); } constexpr bool IsIntersection() const { return IsIntersection(type_mask()); } constexpr bool IsVariadic() const { return IsVariadic(type_mask()); } constexpr bool IsPromoted() const { return IsPromoted(type_mask()); } constexpr bool IsTentative() const { return IsTentative(type_mask()); } constexpr bool UsesArena() const { return UsesArena(type_mask()); } constexpr bool HasName() const { return HasName(type_mask()); } constexpr bool HasLiteralName() const { return HasLiteralName(type_mask()); } constexpr bool HasList() const { return HasList(type_mask()); } constexpr bool IsIterableFallback() const { return IsIterableFallback(type_mask()); } bool operator==(const Type &other) const; bool operator==(Is t) const { return type_mask() == static_cast(t); } bool operator==(MayBe t) const { return PureMask() == static_cast(t); } bool operator!=(const Type &other) const; bool operator<(const Type &other) const; bool operator>(const Type &other) const; bool operator<=(const Type &other) const; bool operator>=(const Type &other) const; bool IsOnlyMask() const { return (IsSet() && ValueFactory::IsEmpty(ptr())); } Handle Name() const { return Handle::New(ptr()); } Handle List() const { return Handle::New(ptr()); } const char *LiteralName() const { return (const char *) ptr(); } void SetPtr(void *p) { set_ptr(p); } template void SetField(bool value) { bits_ = Field::update(bits_, value); } template void ToggleField() { bits_ = SetField(!Field::decode(bits_)); } void SetPtrAndKind(void *p, uint32_t kind_bit) { set_ptr(p); bits_ &= ~KindField::kMask; bits_ |= kind_bit; } void SetList(TypeList *list) { set_ptr(list); bits_ &= ~KindField::kMask; SetField(true); } static constexpr bool MayBePacked(MayBe t) { return (t & MayBe::ArrayPacked) != MayBe::None; } static constexpr bool MayBeHash(MayBe t) { return (t & (MayBe::ArrayNumericHash | MayBe::ArrayKeyString)) != MayBe::None; } static constexpr bool MayBePackedOnly(MayBe t) { return MayBePacked(t) && !MayBeHash(t); } static constexpr bool MayBeHashOnly(MayBe t) { return MayBeHash(t) && !MayBePacked(t); } static constexpr std::string_view GetCName(Is type) { switch (type) { case Is::False: case Is::True: case Is::Bool: return "bool"; case Is::Long: return "int"; case Is::Double: return "float"; case Is::String: return "string"; case Is::Object: return "object"; case Is::Resource: return "resource"; case Is::Null: return "null"; case Is::Callable: return "callable"; case Is::Iterable: return "iterable"; case Is::Array: return "array"; case Is::Void: return "void"; case Is::Mixed: return "mixed"; case Is::Number: return "int|float"; case Is::Never: return "never"; default: // should never happen return "unknown"; } } // concatenate strings with | constexpr std::string_view GetCName() const { return GetCName(static_cast(type_mask() & kMask)); } static constexpr uint64_t ResolveFlags(SendMode send_mode, bool allow_variadic, bool allow_tentative, bool allow_null, bool is_promoted = false) { return SendModeField::encode(send_mode) | VariadicField::encode(allow_variadic) | TentativeField::encode(allow_tentative) | MayBeFields::Null::encode(allow_null) | PromotedField::encode(is_promoted); } static constexpr uint64_t ResolveFlags(bool allow_reference, bool allow_variadic, bool allow_tentative, bool allow_null, bool is_promoted = false) { return ResolveFlags(SendMode(allow_reference), allow_variadic, allow_tentative, allow_null, is_promoted); } static constexpr MayBe Encode(Is t) { return (t) == Is::Bool ? MayBe::Bool : ((t) == Is::Iterable ? static_cast(IterableField::encode(true)) : ((t) == Is::Mixed ? MayBe::Any : static_cast(1 << t))); } static constexpr uint64_t MayBeTypeMask(Is t) { return uint64_t(Encode(t)); } template static constexpr MayBe Encode() { return Encode(t); } struct ConfigObject { bool allow_reference{false}; bool allow_variadic{false}; bool allow_tentative{false}; bool allow_null{false}; bool is_promoted{false}; constexpr ConfigObject(bool allow_reference = false, bool allow_variadic = false, bool allow_tentative = false, bool allow_null = false, bool is_promoted = false) : allow_reference(allow_reference), allow_variadic(allow_variadic), allow_tentative(allow_tentative), allow_null(allow_null), is_promoted(is_promoted) {} constexpr operator uint64_t() const { return ResolveFlags(allow_reference, allow_variadic, allow_tentative, allow_null, is_promoted); } }; template constexpr Type(const StaticString &p, ConfigObject config = {}) : // addr_(p.address()), ptr_(p.void_ptr()), bits_(LiteralNameField::encode(true) | config) {} constexpr Type(const TypeList *list, ConfigObject config = {}) : // addr_(std::bit_cast(const_cast(list))), ptr_(static_cast(const_cast(list))), bits_(config) {} template constexpr Type(const char (&p)[N], ConfigObject config = {}) : addr_(AsAddress(p)), bits_(LiteralNameField::encode(true) | config) {} constexpr Type(const char *p, ConfigObject config = {}) : addr_(AsAddress(p)), bits_(LiteralNameField::encode(true) | config) {} constexpr Type(TString *p, ConfigObject config = {}) : addr_(AsAddress(p)), bits_(NameField::encode(true) | config) {} constexpr Type(ClassEntry *p, ConfigObject config = {}) : addr_(AsAddress(p)), bits_(NameField::encode(true) | config) {} constexpr Type(Handle p) : addr_(p.ptr()), bits_(NameField::encode(true)) {} constexpr Type(Handle p) : addr_(p.ptr()), bits_(NameField::encode(true)) {} constexpr Type(std::nullptr_t, ConfigObject config = {}) : addr_(ValueFactory::kEmpty), bits_(config) {} constexpr Type(ConfigObject config) : addr_(ValueFactory::kEmpty), bits_(config) {} constexpr Type(MayBe t, ConfigObject config = {}) : addr_(ValueFactory::kEmpty), bits_(static_cast(t) | config) {} constexpr Type(Is t, ConfigObject config = {}) : Type(Encode(t), config) {} constexpr Type(Flags flags) : addr_(ValueFactory::kEmpty), bits_(static_cast(flags)) {} constexpr Type(Is t, Flags flags, i::Address ptr = ValueFactory::kEmpty) : addr_(ptr), bits_(MayBeTypeMask(t) | static_cast(flags)) {} constexpr Type(MayBe t, Flags flags, i::Address ptr = ValueFactory::kEmpty) : addr_(ptr), bits_(static_cast(t) | static_cast(flags)) {} constexpr Type() : Type(Is::Undefined) {} template static constexpr Type Create() { return Type(t, (static_cast(flags) | ...)); } template static constexpr Type Union(TypeList list) { return FromList(list); } template static constexpr Type Intersection(TypeList list) { return FromList(list); } // static constexpr Type LiteralName(std::string_view name, uint32_t flags) { // return {name.data(), LiteralNameField::encode(true) | flags}; // } template static constexpr Type FromLiteralName(const char (&name)[N], uint32_t flags) { return {name, LiteralNameField::encode(true) | flags}; } // class Iterator; using iterator = TypeList::iterator; inline iterator begin() const { if (HasList()) { return List()->begin(); } return iterator(this); } inline iterator end() const { if (HasList()) { return List()->end(); } return iterator(this); } union { void *ptr_; internal::Address addr_{ValueFactory::kEmpty}; }; uint64_t bits_; private: using ValueFactory = internal::ValueHelper; template inline static i::Address AsAddress(T *ptr) { return ValueFactory::ValueAsAddress(ptr); } template void set_ptr(T *ptr) { addr_ = AsAddress(ptr); } }; LEND_EXPORT inline constexpr auto GetTypeByConst(Type::Is type) { using namespace base::literals; switch (type) { case Type::Is::False: case Type::Is::True: case Type::Is::Bool: return "bool"_csv; case Type::Is::Long: return "int"_csv; case Type::Is::Double: return "float"_csv; case Type::Is::String: return "string"_csv; case Type::Is::Object: return "object"_csv; case Type::Is::Resource: return "resource"_csv; case Type::Is::Null: return "null"_csv; case Type::Is::Callable: return "callable"_csv; case Type::Is::Iterable: return "iterable"_csv; case Type::Is::Array: return "array"_csv; case Type::Is::Void: return "void"_csv; case Type::Is::Mixed: return "mixed"_csv; case Type::Is::Number: return "int|float"_csv; case Type::Is::Undefined: return "undefined"_csv; case Type::Is::Reference: return "reference"_csv; case Type::Is::ConstantAst: return "ast"_csv; case Type::Is::Static: return "static"_csv; case Type::Is::Never: return "never"_csv; default: return "unknown"_csv; } } inline std::ostream &operator<<(std::ostream &os, const Type &type) { if (type.HasName()) { os << type.Name(); } else if (type.HasLiteralName()) { os << type.LiteralName(); } else if (type.HasList()) { os << "["; for (auto &t : type) { os << t << ", "; } os << "]"; } else { os << GetTypeByConst(static_cast(type.type_mask() & Type::kMask)).data(); } return os; } } // namespace lend