|
| 1 | +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM |
| 2 | +// Exceptions. See /LICENSE for license information. |
| 3 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 4 | + |
| 5 | +#ifndef CARBON_TOOLCHAIN_SEM_IR_ID_KIND_H_ |
| 6 | +#define CARBON_TOOLCHAIN_SEM_IR_ID_KIND_H_ |
| 7 | + |
| 8 | +#include <algorithm> |
| 9 | + |
| 10 | +#include "toolchain/sem_ir/ids.h" |
| 11 | + |
| 12 | +namespace Carbon::SemIR { |
| 13 | + |
| 14 | +// An enum whose values are the specified types. |
| 15 | +template <typename... Types> |
| 16 | +class TypeEnum { |
| 17 | + public: |
| 18 | + static constexpr std::size_t NumTypes = sizeof...(Types); |
| 19 | + static constexpr std::size_t NumValues = NumTypes + 2; |
| 20 | + |
| 21 | + static_assert(NumValues <= 256, "Too many types for raw enum."); |
| 22 | + |
| 23 | + // The underlying raw enumeration type. |
| 24 | + enum class RawEnumType : uint8_t { |
| 25 | + // The first sizeof...(Types) values correspond to the types. |
| 26 | + |
| 27 | + // An explicitly invalid value. |
| 28 | + Invalid = NumTypes, |
| 29 | + |
| 30 | + // Indicates that no type should be used. |
| 31 | + // TODO: This doesn't really fit the model of this type, but it's convenient |
| 32 | + // for all of its users. |
| 33 | + None, |
| 34 | + }; |
| 35 | + |
| 36 | + // Accesses the type given an enum value. |
| 37 | + template <RawEnumType K> |
| 38 | + requires(K != RawEnumType::Invalid) |
| 39 | + using TypeFor = __type_pack_element<static_cast<size_t>(K), Types...>; |
| 40 | + |
| 41 | + // Workarond for Clang bug https://github.com/llvm/llvm-project/issues/85461 |
| 42 | + template <RawEnumType Value> |
| 43 | + static constexpr auto FromRaw = TypeEnum(Value); |
| 44 | + |
| 45 | + // Names for the `Invalid` and `None` enumeration values. |
| 46 | + static constexpr const TypeEnum& Invalid = FromRaw<RawEnumType::Invalid>; |
| 47 | + static constexpr const TypeEnum& None = FromRaw<RawEnumType::None>; |
| 48 | + |
| 49 | + // Accesses the enumeration value for the type `IdT`. If `AllowInvalid` is |
| 50 | + // set, any unexpected type is mapped to `Invalid`, otherwise an invalid type |
| 51 | + // results in a compile error. |
| 52 | + // |
| 53 | + // The `Self` parameter is an implementation detail to allow `ForImpl` to be |
| 54 | + // defined after this template, and should not be specified. |
| 55 | + template <typename IdT, bool AllowInvalid = false, typename Self = TypeEnum> |
| 56 | + static constexpr auto For = Self::template ForImpl<IdT, AllowInvalid>(); |
| 57 | + |
| 58 | + // This bool indicates whether the specified type corresponds to a value in |
| 59 | + // this enum. |
| 60 | + template <typename IdT> |
| 61 | + static constexpr bool Contains = For<IdT, true>.is_valid(); |
| 62 | + |
| 63 | + // Explicitly convert from the raw enum type. |
| 64 | + explicit constexpr TypeEnum(RawEnumType value) : value_(value) {} |
| 65 | + |
| 66 | + // Implicitly convert to the raw enum type, for use in `switch`. |
| 67 | + // |
| 68 | + // NOLINTNEXTLINE(google-explicit-constructor) |
| 69 | + constexpr operator RawEnumType() const { return value_; } |
| 70 | + |
| 71 | + // Conversion to bool is deleted to prevent direct use in an `if` condition |
| 72 | + // instead of comparing with another value. |
| 73 | + explicit operator bool() const = delete; |
| 74 | + |
| 75 | + // Returns the raw enum value. |
| 76 | + constexpr auto ToRaw() const -> RawEnumType { return value_; } |
| 77 | + |
| 78 | + // Returns a value that can be used as an array index. Returned value will be |
| 79 | + // < NumValues. |
| 80 | + constexpr auto ToIndex() const -> std::size_t { |
| 81 | + return static_cast<std::size_t>(value_); |
| 82 | + } |
| 83 | + |
| 84 | + // Returns whether this is a valid value, not `Invalid`. |
| 85 | + constexpr auto is_valid() const -> bool { |
| 86 | + return value_ != RawEnumType::Invalid; |
| 87 | + } |
| 88 | + |
| 89 | + private: |
| 90 | + // Translates a type to its enum value, or `Invalid`. |
| 91 | + template <typename IdT, bool AllowInvalid> |
| 92 | + static constexpr auto ForImpl() -> TypeEnum { |
| 93 | + // A bool for each type saying whether it matches. The result is the index |
| 94 | + // of the first `true` in this list. If none matches, then the result is the |
| 95 | + // length of the list, which is mapped to `Invalid`. |
| 96 | + constexpr bool TypeMatches[] = {std::same_as<IdT, Types>...}; |
| 97 | + constexpr int Index = |
| 98 | + std::find(TypeMatches, TypeMatches + NumTypes, true) - TypeMatches; |
| 99 | + static_assert(Index != NumTypes || AllowInvalid, |
| 100 | + "Unexpected type passed to TypeEnum::For<...>"); |
| 101 | + return TypeEnum(static_cast<RawEnumType>(Index)); |
| 102 | + } |
| 103 | + |
| 104 | + RawEnumType value_; |
| 105 | +}; |
| 106 | + |
| 107 | +// An enum of all the ID types used as instruction operands. |
| 108 | +using IdKind = TypeEnum< |
| 109 | + // From sem_ir/builtin_kind.h. |
| 110 | + BuiltinKind, |
| 111 | + // From base/value_store.h. |
| 112 | + IntId, RealId, StringLiteralValueId, |
| 113 | + // From sem_ir/id.h. |
| 114 | + InstId, ConstantId, BindNameId, FunctionId, ClassId, InterfaceId, ImplId, |
| 115 | + ImportIRId, BoolValue, NameId, NameScopeId, InstBlockId, TypeId, |
| 116 | + TypeBlockId, ElementIndex>; |
| 117 | + |
| 118 | +} // namespace Carbon::SemIR |
| 119 | + |
| 120 | +#endif // CARBON_TOOLCHAIN_SEM_IR_ID_KIND_H_ |
0 commit comments