kind_switch.h 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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. // This library provides switch-like behaviors for Carbon's kind-based types.
  9. //
  10. // An expected use case is to mix regular switch `case` statements and
  11. // `CARBON_KIND`. However, the `switch` must be defined using
  12. // `CARBON_KIND_SWITCH`. For example:
  13. //
  14. // CARBON_KIND_SWITCH(untyped_inst) {
  15. // case CARBON_KIND(SomeInstType inst): {
  16. // return inst.typed_field;
  17. // }
  18. // case OtherType1::Kind:
  19. // case OtherType2::Kind:
  20. // return value;
  21. // default:
  22. // return default_value;
  23. // }
  24. //
  25. // For compatibility, this requires:
  26. //
  27. // - The type passed to `CARBON_KIND_SWITCH` has `.kind()` to switch on, and
  28. // `.As<CaseT>` for `CARBON_KIND` to cast to.
  29. // - Each type passed to `CARBON_KIND` (`CaseT` above) provides
  30. // `CaseT::Kind`, which is passed to the `case` keyword.
  31. // `CaseT::Kind::RawEnumType` is the type returned by `.kind()`.
  32. //
  33. // Note, this is currently used primarily for Inst in toolchain. When more
  34. // use-cases are added, it would be worth considering whether the API
  35. // requirements should change.
  36. namespace Carbon::Internal::Kind {
  37. // Given `CARBON_KIND_SWITCH(value)` this returns `value.kind()` to switch on.
  38. template <typename SwitchT>
  39. auto SwitchOn(SwitchT&& switch_value) -> auto {
  40. return switch_value.kind();
  41. }
  42. // Given `CARBON_KIND(CaseT name)` this generates `CaseT::Kind`. It explicitly
  43. // returns `KindT` because that may differ from `CaseT::Kind`, and may not be
  44. // copyable.
  45. template <typename SwitchT, typename CaseFnT>
  46. consteval auto ForCase() -> auto {
  47. using KindT = llvm::function_traits<
  48. decltype(&std::remove_cvref_t<SwitchT>::kind)>::result_t;
  49. using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  50. return static_cast<KindT::RawEnumType>(KindT::template For<CaseT>);
  51. }
  52. // Given `CARBON_KIND_SWITCH(value)` and `CARBON_KIND(CaseT name)` this
  53. // generates `value.As<CaseT>()`.
  54. template <typename FnT, typename ValueT>
  55. auto Cast(ValueT&& kind_switch_value) -> auto {
  56. using CaseT = llvm::function_traits<FnT>::template arg_t<0>;
  57. return kind_switch_value.template As<CaseT>();
  58. }
  59. #define CARBON_INTERNAL_KIND_MERGE(Prefix, Line) Prefix##Line
  60. #define CARBON_INTERNAL_KIND_LABEL(Line) \
  61. CARBON_INTERNAL_KIND_MERGE(carbon_internal_kind_case_, Line)
  62. } // namespace Carbon::Internal::Kind
  63. // Produces a switch statement on value.kind().
  64. #define CARBON_KIND_SWITCH(value) \
  65. switch ( \
  66. const auto& carbon_internal_kind_switch_value = value; \
  67. ::Carbon::Internal::Kind::SwitchOn(carbon_internal_kind_switch_value))
  68. // Produces a case-compatible block of code that also instantiates a local typed
  69. // variable. typed_variable_decl looks like `int i`, with a space.
  70. //
  71. // This uses `if` to scope the variable, and provides a dangling `else` in order
  72. // to prevent accidental `else` use. The label allows `:` to follow the macro
  73. // name, making it look more like a typical `case`.
  74. #define CARBON_KIND(typed_variable_decl) \
  75. ::Carbon::Internal::Kind::ForCase< \
  76. decltype(carbon_internal_kind_switch_value), \
  77. decltype([]([[maybe_unused]] typed_variable_decl) {})>() \
  78. : if (typed_variable_decl = ::Carbon::Internal::Kind::Cast< \
  79. decltype([]([[maybe_unused]] typed_variable_decl) {})>( \
  80. carbon_internal_kind_switch_value); \
  81. false) {} \
  82. else [[maybe_unused]] CARBON_INTERNAL_KIND_LABEL(__LINE__)
  83. #endif // CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_