kind_switch.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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 CARBON_KIND_ANY(AnyKind, any_inst): {
  20. // return any_inst.typed_field;
  21. // }
  22. //
  23. // case OtherType1::Kind:
  24. // case OtherType2::Kind:
  25. // return value;
  26. // default:
  27. // return default_value;
  28. // }
  29. //
  30. // When used on kind-like types, this requires:
  31. //
  32. // - The type passed to `CARBON_KIND_SWITCH` has `.kind()` to switch on, and
  33. // `.As<CaseT>` for `CARBON_KIND` to cast to.
  34. // - Each type passed to `CARBON_KIND` (`CaseT` above) provides
  35. // `CaseT::Kind`, which is passed to the `case` keyword.
  36. // `CaseT::Kind::RawEnumType` is the type returned by `.kind()`.
  37. // - Each type passed to `CARBON_KIND_ANY` must have a macro of the form:
  38. // ```
  39. // #define AnyKind_CARBON_KIND_ANY_EXPAND \
  40. // CARBON_KIND_ANY_EXPAND_BEGIN() CARBON_KIND_ANY_EXPAND_CASE(Kind1) \
  41. // CARBON_KIND_ANY_EXPAND_SEP() CARBON_KIND_ANY_EXPAND_CASE(Kind2) \
  42. // ...
  43. // CARBON_KIND_ANY_EXPAND_SEP() CARBON_KIND_ANY_EXPAND_CASE(KindN)
  44. // ```
  45. // Note the prefix `,` is required.
  46. //
  47. // When used with `std::variant` (e.g., `CARBON_KIND_SWITCH(variant_value)`),
  48. // members of the variant are passed to `CARBON_KIND`, instead of types that
  49. // have a `::Kind` member.
  50. // Produces a switch statement on value.kind().
  51. #define CARBON_KIND_SWITCH(value) \
  52. switch ( \
  53. auto&& carbon_internal_kind_switch_value = value; \
  54. ::Carbon::Internal::Kind::SwitchOn(carbon_internal_kind_switch_value))
  55. // Produces a case-compatible block of code that also instantiates a local typed
  56. // variable. typed_variable_decl looks like `int i`; the `CARBON_KIND` value
  57. // will be cast to the provided type.
  58. //
  59. // Because of the dangling else, braces should always be used with a `case
  60. // CARBON_KIND`. Otherwise, only the first statement is going to see the
  61. // variable. Even if that sometimes works, it can lead to confusing issues when
  62. // statements are added, and `if`/`else` coding style already requires braces.
  63. #define CARBON_KIND(typed_variable_decl) \
  64. CARBON_KIND_INTERNAL_CASE_VALUE(typed_variable_decl) \
  65. : CARBON_KIND_INTERNAL_DECLARE(typed_variable_decl)
  66. // Macros for clients to add support for `Type_CARBON_KIND_ANY_EXPAND` (see
  67. // example above). Empty parameters are used to allow delaying macro expansion.
  68. #define CARBON_KIND_ANY_EXPAND_CASE(X) CARBON_KIND_INTERNAL_CASE_VALUE(X)
  69. #define CARBON_KIND_ANY_EXPAND_BEGIN() ,
  70. #define CARBON_KIND_ANY_EXPAND_SEP() : case
  71. // Produces a case-compatible block of code that also instantiates a local typed
  72. // variable. Versus `CARBON_KIND(int i)`, note this requires a comma after the
  73. // type, as in `CARBON_KIND_ANY(AnyKind, i)`.
  74. #define CARBON_KIND_ANY(Type, variable_name) \
  75. CARBON_KIND_ANY_INTERNAL_WITH_SUFFIX(Type variable_name, \
  76. Type##_CARBON_KIND_ANY_EXPAND)
  77. // -----------------------------------------------------------------------------
  78. // Internal implementation details follow.
  79. // -----------------------------------------------------------------------------
  80. namespace Carbon::Internal::Kind {
  81. template <typename T>
  82. constexpr bool IsStdVariantValue = false;
  83. template <typename... Ts>
  84. constexpr bool IsStdVariantValue<std::variant<Ts...>> = true;
  85. template <typename T>
  86. concept IsStdVariant = IsStdVariantValue<std::decay_t<T>>;
  87. #define CARBON_INTERNAL_KIND_IDENTIFIER(name) T##name
  88. // Turns a list of numbers into a list `T0, T1, ...`.
  89. #define CARBON_INTERNAL_KIND_IDENTIFIERS(...) \
  90. CARBON_FOR_EACH(CARBON_INTERNAL_KIND_IDENTIFIER, CARBON_FOR_EACH_COMMA, \
  91. __VA_ARGS__)
  92. #define CARBON_INTERNAL_KIND_TYPENAME(name) \
  93. typename CARBON_INTERNAL_KIND_IDENTIFIER(name)
  94. // Turns a list of numbers into a list `typename T0, typename T1, ...`.
  95. #define CARBON_INTERNAL_KIND_TYPENAMES(...) \
  96. CARBON_FOR_EACH(CARBON_INTERNAL_KIND_TYPENAME, CARBON_FOR_EACH_COMMA, \
  97. __VA_ARGS__)
  98. #define CARBON_INTERNAL_KIND_ENUM_NAME(n) VariantType##n##NotHandledInSwitch
  99. // Turns a list of numbers into a list `VariantType0NotHandledInSwitch, ...`.
  100. #define CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES(...) \
  101. CARBON_FOR_EACH(CARBON_INTERNAL_KIND_ENUM_NAME, CARBON_FOR_EACH_COMMA, \
  102. __VA_ARGS__)
  103. // Turns a list of numbers into a set of template specializations of the
  104. // variable `EnumType EnumValue`, with each specialization having the Nth value
  105. // in the EnumType (as defined by CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES).
  106. #define CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME(n) \
  107. template <> \
  108. constexpr EnumType EnumValue<CARBON_INTERNAL_KIND_IDENTIFIER(n)> = \
  109. EnumType::CARBON_INTERNAL_KIND_ENUM_NAME(n)
  110. // Used to provide a reason in the compiler error from `ValidCaseType`, which
  111. // will state that "T does not satisfy TypeFoundInVariant".
  112. template <typename T>
  113. concept TypeFoundInVariant = false;
  114. // Used to cause a compler error, which will state that "ValidCaseType was not
  115. // satisfied" for T and std::variant<...>.
  116. template <typename T, typename StdVariant>
  117. requires TypeFoundInVariant<T>
  118. struct ValidCaseType;
  119. template <typename T>
  120. struct StdVariantTypeMap;
  121. #define CARBON_INTERNAL_KIND_TYPE_MAP(...) \
  122. template <CARBON_INTERNAL_KIND_TYPENAMES(__VA_ARGS__)> \
  123. struct StdVariantTypeMap< \
  124. std::variant<CARBON_INTERNAL_KIND_IDENTIFIERS(__VA_ARGS__)>> { \
  125. /* An enum with a value for each type in the std::variant. The switch will \
  126. * be on this enum so that we get a warning if one of the enum values is \
  127. * not handled. They are named in a way to help explain the warning, that \
  128. * it means a type in the std::variant<...> type list does not have a \
  129. * matching case statement. \
  130. */ \
  131. enum class EnumType { \
  132. CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES(__VA_ARGS__) \
  133. }; \
  134. /* A mapping from a single type in the std::variant<...> type list to a \
  135. * value in the EnumType. This value is only used in the case a type is \
  136. * queried which is not part of the type list, and ValidCaseType is used \
  137. * to produce a diagnostic explaining the situation. */ \
  138. template <typename Tn> \
  139. static constexpr EnumType EnumValue = ValidCaseType< \
  140. Tn, std::variant<CARBON_INTERNAL_KIND_IDENTIFIERS(__VA_ARGS__)>>(); \
  141. /**/ \
  142. CARBON_FOR_EACH(CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME, \
  143. CARBON_FOR_EACH_SEMI, __VA_ARGS__); \
  144. }
  145. template <typename... Ts>
  146. struct StdVariantTypeMap<std::variant<Ts...>> {
  147. // The number here should match the number of arguments in the largest
  148. // `CARBON_INTERNAL_KIND_TYPE_MAP` invocation below.
  149. static_assert(sizeof...(Ts) <= 24,
  150. "CARBON_KIND_SWITCH supports std::variant with up to 24 types. "
  151. "Add more if needed.");
  152. };
  153. // Generate StdVariantTypeMap specializations for each number of types in the
  154. // std::variant<...> type list. The numbers here represent which number is
  155. // printed in diagnostics stating a type in the variant has no matching case
  156. // statement. Duplicate numbers would create an error.
  157. CARBON_INTERNAL_KIND_TYPE_MAP(0);
  158. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1);
  159. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2);
  160. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3);
  161. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4);
  162. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5);
  163. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6);
  164. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7);
  165. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8);
  166. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
  167. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  168. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
  169. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
  170. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
  171. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
  172. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  173. 15);
  174. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  175. 15, 16);
  176. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  177. 15, 16, 17);
  178. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  179. 15, 16, 17, 18);
  180. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  181. 15, 16, 17, 18, 19);
  182. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  183. 15, 16, 17, 18, 19, 20);
  184. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  185. 15, 16, 17, 18, 19, 20, 21);
  186. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  187. 15, 16, 17, 18, 19, 20, 21, 22);
  188. CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  189. 15, 16, 17, 18, 19, 20, 21, 22, 23);
  190. #undef CARBON_INTERNAL_KIND_IDENTIFIER
  191. #undef CARBON_INTERNAL_KIND_IDENTIFIERS
  192. #undef CARBON_INTERNAL_KIND_TYPENAME
  193. #undef CARBON_INTERNAL_KIND_TYPENAMES
  194. #undef CARBON_INTERNAL_KIND_ENUM_NAME
  195. #undef CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES
  196. #undef CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME
  197. #undef CARBON_INTERNAL_KIND_TYPE_MAP
  198. // Uses the above `CARBON_INTERNAL_KIND_TYPE_MAP` expansions to make an enum
  199. // with a value for each type in a std::variant<...> type list.
  200. template <typename StdVariant>
  201. using StdVariantEnum = StdVariantTypeMap<std::decay_t<StdVariant>>::EnumType;
  202. // Uses the `CARBON_INTERNAL_KIND_TYPE_MAP` expanstions to find the enum value
  203. // in `StdVariantEnum` for a given type `T` in the type list of a
  204. // std::variant<...>.
  205. template <typename T, typename StdVariant>
  206. constexpr auto CaseValueOfTypeInStdVariant =
  207. StdVariantTypeMap<std::decay_t<StdVariant>>::template EnumValue<T>;
  208. // Given `CARBON_KIND_SWITCH(value)` this returns the actual value to switch on.
  209. //
  210. // For types with a `kind()` accessor, this is the just the value of `kind()`.
  211. // The type returned from `kind()` is expected to be a `TypeEnum`, as it
  212. // is required to have its API, including a nested `RawEnumType`.
  213. //
  214. // For std::variant<...> this is an enum synthesized from the types in the
  215. // variant's type list.
  216. template <typename SwitchT>
  217. constexpr auto SwitchOn(SwitchT&& switch_value) -> auto {
  218. if constexpr (IsStdVariant<SwitchT>) {
  219. return static_cast<StdVariantEnum<SwitchT>>(switch_value.index());
  220. } else {
  221. return switch_value.kind();
  222. }
  223. }
  224. // Given `CARBON_KIND(CaseT name)` this generates the case value to compare
  225. // against the switch value from `SwitchOn`.
  226. //
  227. // For types with a `kind()` accessor that returns a `TypeEnum`,
  228. // this gets the `TypeEnum<...>::RawTypeEnum` for the case type `CaseT`.
  229. //
  230. // For std::variant<...> this returns the value corresponding to the case type
  231. // from the enum synthesized (in `SwitchOn`) for the types in the variant's
  232. // type list.
  233. template <typename SwitchT, typename CaseFnT>
  234. consteval auto ForCase() -> auto {
  235. using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  236. if constexpr (IsStdVariant<SwitchT>) {
  237. using NoRefCaseT = std::remove_cvref_t<CaseT>;
  238. return CaseValueOfTypeInStdVariant<NoRefCaseT, SwitchT>;
  239. } else {
  240. using KindT = llvm::function_traits<
  241. decltype(&std::remove_cvref_t<SwitchT>::kind)>::result_t;
  242. return static_cast<KindT::RawEnumType>(KindT::template For<CaseT>);
  243. }
  244. }
  245. // Given `CARBON_KIND_SWITCH(value)` and `CARBON_KIND(CaseT name)` this converts
  246. // the `value` to `CaseT`.
  247. //
  248. // For types with a `kind()` accessor this uses `value.As<CaseT>`.
  249. //
  250. // For `std::variant<...>` this uses `std::get<CaseT>(value)`.
  251. template <typename CaseFnT, typename SwitchT>
  252. auto Cast(SwitchT&& kind_switch_value) -> decltype(auto) {
  253. using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  254. if constexpr (IsStdVariant<SwitchT>) {
  255. using NoRefCaseT = std::remove_cvref_t<CaseT>;
  256. return std::get<NoRefCaseT>(std::forward<SwitchT>(kind_switch_value));
  257. } else {
  258. return kind_switch_value.template As<CaseT>();
  259. }
  260. }
  261. #define CARBON_INTERNAL_KIND_MERGE(Prefix, Line) Prefix##Line
  262. #define CARBON_INTERNAL_KIND_LABEL(Line) \
  263. CARBON_INTERNAL_KIND_MERGE(carbon_internal_kind_case_, Line)
  264. // To extract the type from an argument, we wrap it in a lambda and will use
  265. // `function_traits` to extract the type. This supports `typed_param` either
  266. // being `Type name`, or just `Type`.
  267. #define CARBON_KIND_INTERNAL_WRAP_TYPE(typed_param) \
  268. decltype([]([[maybe_unused]] typed_param) {})
  269. // Produces the value for a `case` statement on the type of `typed_param`.
  270. #define CARBON_KIND_INTERNAL_CASE_VALUE(typed_param) \
  271. ::Carbon::Internal::Kind::ForCase< \
  272. decltype(carbon_internal_kind_switch_value), \
  273. CARBON_KIND_INTERNAL_WRAP_TYPE(typed_param)>()
  274. // Produces a declaration using `typed_variable_decl`.
  275. //
  276. // This uses `if` to scope the variable, and provides a dangling `else` in order
  277. // to prevent accidental `else` use. The label allows `:` to follow the macro
  278. // name, making it look more like a typical `case`. We use an unbraced `if` body
  279. // to avoid lint warnings that the `else` is unbraced. Braces are required after
  280. // the `:`, but we don't have a good way to enforce that.
  281. //
  282. // TODO: Replace the `;` with `{}` if
  283. // https://github.com/llvm/llvm-project/issues/194694 is fixed.
  284. #define CARBON_KIND_INTERNAL_DECLARE(typed_variable_decl) \
  285. if (typed_variable_decl = \
  286. ::Carbon::Internal::Kind::Cast<CARBON_KIND_INTERNAL_WRAP_TYPE( \
  287. typed_variable_decl)>( \
  288. std::forward<decltype(carbon_internal_kind_switch_value)>( \
  289. carbon_internal_kind_switch_value)); \
  290. false) \
  291. ; \
  292. else [[maybe_unused]] \
  293. CARBON_INTERNAL_KIND_LABEL(__LINE__)
  294. // Helper for `CARBON_KIND_ANY` that expands the now-suffixed macro.
  295. #define CARBON_KIND_ANY_INTERNAL_WITH_SUFFIX(typed_variable_decl, \
  296. Type_CARBON_KIND_ANY_EXPAND) \
  297. CARBON_KIND_ANY_INTERNAL_IMPL(typed_variable_decl, \
  298. Type_CARBON_KIND_ANY_EXPAND)
  299. // Helper for `CARBON_KIND_ANY` that forms the final case setup. The variadic
  300. // arguments are the expansion of `Type_CARBON_KIND_ANY_EXPAND`, which may
  301. // contain commas.
  302. //
  303. // As a consequence of the macro suffixing along with the required prefix comma
  304. // in `Type_CARBON_KIND_ANY_EXPAND`, input of `Namespace::Type` will have become
  305. // `Namespace::, Type_CARBON_KIND_ANY_EXPAND`, and `DiscardNamespace` exists to
  306. // discard `Namespace::`.
  307. #define CARBON_KIND_ANY_INTERNAL_IMPL(typed_variable_decl, DiscardNamespace, \
  308. ...) \
  309. __VA_ARGS__: \
  310. CARBON_KIND_INTERNAL_DECLARE(typed_variable_decl)
  311. } // namespace Carbon::Internal::Kind
  312. #endif // CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_