kind_switch.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. #ifndef CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_
  5. #define CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_
  6. #include <type_traits>
  7. #include "llvm/ADT/STLExtras.h"
  8. #include "toolchain/base/for_each_macro.h"
  9. // This library provides switch-like behaviors for Carbon's kind-based types.
  10. //
  11. // An expected use case is to mix regular switch `case` statements and
  12. // `CARBON_KIND`. However, the `switch` must be defined using
  13. // `CARBON_KIND_SWITCH`. For example:
  14. //
  15. // CARBON_KIND_SWITCH(untyped_inst) {
  16. // case CARBON_KIND(SomeInstType inst): {
  17. // return inst.typed_field;
  18. // }
  19. // case OtherType1::Kind:
  20. // case OtherType2::Kind:
  21. // return value;
  22. // default:
  23. // return default_value;
  24. // }
  25. //
  26. // For compatibility, this requires:
  27. //
  28. // - The type passed to `CARBON_KIND_SWITCH` has `.kind()` to switch on, and
  29. // `.As<CaseT>` for `CARBON_KIND` to cast to.
  30. // - Each type passed to `CARBON_KIND` (`CaseT` above) provides
  31. // `CaseT::Kind`, which is passed to the `case` keyword.
  32. // `CaseT::Kind::RawEnumType` is the type returned by `.kind()`.
  33. //
  34. // Note, this is currently used primarily for Inst in toolchain. When more
  35. // use-cases are added, it would be worth considering whether the API
  36. // requirements should change.
  37. namespace Carbon::Internal::Kind {
  38. template <typename T>
  39. constexpr bool IsStdVariantValue = false;
  40. template <typename... Ts>
  41. constexpr bool IsStdVariantValue<std::variant<Ts...>> = true;
  42. template <typename T>
  43. concept IsStdVariant = IsStdVariantValue<std::decay_t<T>>;
  44. #define CARBON_INTERNAL_KIND_IDENTIFIER(name) T##name
  45. // Turns a list of numbers into a list `T0, T1, ...`.
  46. #define CARBON_INTERNAL_KIND_IDENTIFIERS(...) \
  47. CARBON_FOR_EACH(CARBON_INTERNAL_KIND_IDENTIFIER, CARBON_FOR_EACH_COMMA, \
  48. __VA_ARGS__)
  49. #define CARBON_INTERNAL_KIND_TYPENAME(name) \
  50. typename CARBON_INTERNAL_KIND_IDENTIFIER(name)
  51. // Turns a list of numbers into a list `typename T0, typename T1, ...`.
  52. #define CARBON_INTERNAL_KIND_TYPENAMES(...) \
  53. CARBON_FOR_EACH(CARBON_INTERNAL_KIND_TYPENAME, CARBON_FOR_EACH_COMMA, \
  54. __VA_ARGS__)
  55. #define CARBON_INTERNAL_KIND_ENUM_NAME(n) VariantType##n##NotHandledInSwitch
  56. // Turns a list of numbers into a list `VariantType0NotHandledInSwitch, ...`.
  57. #define CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES(...) \
  58. CARBON_FOR_EACH(CARBON_INTERNAL_KIND_ENUM_NAME, CARBON_FOR_EACH_COMMA, \
  59. __VA_ARGS__)
  60. // Turns a list of numbers into a set of template specializations of the
  61. // variable `EnumType EnumValue`, with each specialization having the Nth value
  62. // in the EnumType (as defined by CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES).
  63. #define CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME(n) \
  64. template <> \
  65. constexpr EnumType EnumValue<CARBON_INTERNAL_KIND_IDENTIFIER(n)> = \
  66. EnumType::CARBON_INTERNAL_KIND_ENUM_NAME(n)
  67. // Used to provide a reason in the compiler error from `ValidCaseType`, which
  68. // will state that "T does not satisfy TypeFoundInVariant".
  69. template <typename T>
  70. concept TypeFoundInVariant = false;
  71. // Used to cause a compler error, which will state that "ValidCaseType was not
  72. // satisfied" for T and std::variant<...>.
  73. template <typename T, typename StdVariant>
  74. requires TypeFoundInVariant<T>
  75. struct ValidCaseType;
  76. template <typename T>
  77. struct StdVariantTypeMap;
  78. #define CARBON_INTERNAL_KIND_TYPE_MAP(...) \
  79. template <CARBON_INTERNAL_KIND_TYPENAMES(__VA_ARGS__)> \
  80. struct StdVariantTypeMap< \
  81. std::variant<CARBON_INTERNAL_KIND_IDENTIFIERS(__VA_ARGS__)>> { \
  82. /* An enum with a value for each type in the std::variant. The switch will \
  83. * be on this enum so that we get a warning if one of the enum values is \
  84. * not handled. They are named in a way to help explain the warning, that \
  85. * it means a type in the std::variant<...> type list does not have a \
  86. * matching case statement. \
  87. */ \
  88. enum class EnumType { \
  89. CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES(__VA_ARGS__) \
  90. }; \
  91. /* A mapping from a single type in the std::variant<...> type list to a \
  92. * value in the EnumType. This value is only used in the case a type is \
  93. * queried which is not part of the type list, and ValidCaseType is used \
  94. * to produce a diagnostic explaining the situation. */ \
  95. template <typename Tn> \
  96. static constexpr EnumType EnumValue = ValidCaseType< \
  97. Tn, std::variant<CARBON_INTERNAL_KIND_IDENTIFIERS(__VA_ARGS__)>>(); \
  98. /**/ \
  99. CARBON_FOR_EACH(CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME, \
  100. CARBON_FOR_EACH_SEMI, __VA_ARGS__); \
  101. }
  102. template <typename... Ts>
  103. struct StdVariantTypeMap<std::variant<Ts...>> {
  104. // The number here should match the number of arguments in the largest
  105. // `CARBON_INTERNAL_KIND_TYPE_MAP` invocation below.
  106. static_assert(sizeof...(Ts) <= 24,
  107. "CARBON_KIND_SWITCH supports std::variant with up to 24 types. "
  108. "Add more if needed.");
  109. };
  110. // Generate StdVariantTypeMap specializations for each number of types in the
  111. // std::variant<...> type list. The numbers here represent which number is
  112. // printed in diagnostics stating a type in the variant has no matching case
  113. // statement. Duplicate numbers would create an error.
  114. CARBON_INTERNAL_KIND_TYPE_MAP(0);
  115. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1);
  116. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2);
  117. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3);
  118. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4);
  119. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5);
  120. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6);
  121. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7);
  122. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8);
  123. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
  124. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  125. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
  126. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
  127. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
  128. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
  129. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  130. 15);
  131. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  132. 15, 16);
  133. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  134. 15, 16, 17);
  135. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  136. 15, 16, 17, 18);
  137. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  138. 15, 16, 17, 18, 19);
  139. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  140. 15, 16, 17, 18, 19, 20);
  141. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  142. 15, 16, 17, 18, 19, 20, 21);
  143. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  144. 15, 16, 17, 18, 19, 20, 21, 22);
  145. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  146. 15, 16, 17, 18, 19, 20, 21, 22, 23);
  147. #undef CARBON_INTERNAL_KIND_IDENTIFIER
  148. #undef CARBON_INTERNAL_KIND_IDENTIFIERS
  149. #undef CARBON_INTERNAL_KIND_TYPENAME
  150. #undef CARBON_INTERNAL_KIND_TYPENAMES
  151. #undef CARBON_INTERNAL_KIND_ENUM_NAME
  152. #undef CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES
  153. #undef CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME
  154. #undef CARBON_INTERNAL_KIND_TYPE_MAP
  155. // Uses the above `CARBON_INTERNAL_KIND_TYPE_MAP` expansions to make an enum
  156. // with a value for each type in a std::variant<...> type list.
  157. template <typename StdVariant>
  158. using StdVariantEnum = StdVariantTypeMap<std::decay_t<StdVariant>>::EnumType;
  159. // Uses the `CARBON_INTERNAL_KIND_TYPE_MAP` expanstions to find the enum value
  160. // in `StdVariantEnum` for a given type `T` in the type list of a
  161. // std::variant<...>.
  162. template <typename T, typename StdVariant>
  163. constexpr auto CaseValueOfTypeInStdVariant =
  164. StdVariantTypeMap<std::decay_t<StdVariant>>::template EnumValue<T>;
  165. // Given `CARBON_KIND_SWITCH(value)` this returns the actual value to switch on.
  166. //
  167. // For types with a `kind()` accessor, this is the just the value of `kind()`.
  168. // The type returned from `kind()` is expected to be a `TypeEnum`, as it
  169. // is required to have its API, including a nested `RawEnumType`.
  170. //
  171. // For std::variant<...> this is an enum synthesized from the types in the
  172. // variant's type list.
  173. template <typename SwitchT>
  174. constexpr auto SwitchOn(SwitchT&& switch_value) -> auto {
  175. if constexpr (IsStdVariant<SwitchT>) {
  176. return static_cast<StdVariantEnum<SwitchT>>(switch_value.index());
  177. } else {
  178. return switch_value.kind();
  179. }
  180. }
  181. // Given `CARBON_KIND(CaseT name)` this generates the case value to compare
  182. // against the switch value from `SwitchOn`.
  183. //
  184. // For types with a `kind()` accessor that returns a `TypeEnum`,
  185. // this gets the `TypeEnum<...>::RawTypeEnum` for the case type `CaseT`.
  186. //
  187. // For std::variant<...> this returns the value corresponding to the case type
  188. // from the enum synthesized (in `SwitchOn`) for the types in the variant's
  189. // type list.
  190. template <typename SwitchT, typename CaseFnT>
  191. consteval auto ForCase() -> auto {
  192. using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  193. if constexpr (IsStdVariant<SwitchT>) {
  194. using NoRefCaseT = std::remove_cvref_t<CaseT>;
  195. return CaseValueOfTypeInStdVariant<NoRefCaseT, SwitchT>;
  196. } else {
  197. using KindT = llvm::function_traits<
  198. decltype(&std::remove_cvref_t<SwitchT>::kind)>::result_t;
  199. return static_cast<KindT::RawEnumType>(KindT::template For<CaseT>);
  200. }
  201. }
  202. // Given `CARBON_KIND_SWITCH(value)` and `CARBON_KIND(CaseT name)` this converts
  203. // the `value` to `CaseT`.
  204. //
  205. // For types with a `kind()` accessor this uses `value.As<CaseT>`.
  206. //
  207. // For `std::variant<...>` this uses `std::get<CaseT>(value)`.
  208. template <typename CaseFnT, typename SwitchT>
  209. auto Cast(SwitchT&& kind_switch_value) -> decltype(auto) {
  210. using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  211. if constexpr (IsStdVariant<SwitchT>) {
  212. using NoRefCaseT = std::remove_cvref_t<CaseT>;
  213. return std::get<NoRefCaseT>(std::forward<SwitchT>(kind_switch_value));
  214. } else {
  215. return kind_switch_value.template As<CaseT>();
  216. }
  217. }
  218. #define CARBON_INTERNAL_KIND_MERGE(Prefix, Line) Prefix##Line
  219. #define CARBON_INTERNAL_KIND_LABEL(Line) \
  220. CARBON_INTERNAL_KIND_MERGE(carbon_internal_kind_case_, Line)
  221. } // namespace Carbon::Internal::Kind
  222. // Produces a switch statement on value.kind().
  223. #define CARBON_KIND_SWITCH(value) \
  224. switch ( \
  225. auto&& carbon_internal_kind_switch_value = value; \
  226. ::Carbon::Internal::Kind::SwitchOn(carbon_internal_kind_switch_value))
  227. // Produces a case-compatible block of code that also instantiates a local typed
  228. // variable. typed_variable_decl looks like `int i`, with a space.
  229. //
  230. // This uses `if` to scope the variable, and provides a dangling `else` in order
  231. // to prevent accidental `else` use. The label allows `:` to follow the macro
  232. // name, making it look more like a typical `case`.
  233. //
  234. // Because of the dangling else, braces should always be used with a `case
  235. // CARBON_KIND`. Otherwise, only the first statement is going to see the
  236. // variable. Even if that sometimes works, it can lead to confusing issues when
  237. // statements are added, and `if`/`else` coding style already requires braces.
  238. #define CARBON_KIND(typed_variable_decl) \
  239. ::Carbon::Internal::Kind::ForCase< \
  240. decltype(carbon_internal_kind_switch_value), \
  241. decltype([]([[maybe_unused]] typed_variable_decl) {})>() \
  242. : if (typed_variable_decl = ::Carbon::Internal::Kind::Cast< \
  243. decltype([]([[maybe_unused]] typed_variable_decl) {})>( \
  244. std::forward<decltype(carbon_internal_kind_switch_value)>( \
  245. carbon_internal_kind_switch_value)); \
  246. false) {} \
  247. else [[maybe_unused]] CARBON_INTERNAL_KIND_LABEL(__LINE__)
  248. #endif // CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_