Repository: carbon-language/carbon-lang
File: toolchain/base/kind_switch.h (C/C++)

Code before:
//
// For types with a `kind()` accessor that returns a `TypeEnum`,
// this gets the `TypeEnum<...>::RawTypeEnum` for the case type `CaseT`.
//
// For std::variant<...> this returns the value corresponding to the case type
// from the enum synthesized (in `SwitchOn`) for the types in the variant's
// type list.
template <typename SwitchT, typename CaseFnT>
consteval auto ForCase() -> auto {
  using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  if constexpr (IsStdVariant<SwitchT>) {
    using NoRefCaseT = std::remove_cvref_t<CaseT>;
    return CaseValueOfTypeInStdVariant<NoRefCaseT, SwitchT>;
  } else {
    using KindT = llvm::function_traits<
        decltype(&std::remove_cvref_t<SwitchT>::kind)>::result_t;
    return static_cast<KindT::RawEnumType>(KindT::template For<CaseT>);
  }
}

// Given `CARBON_KIND_SWITCH(value)` and `CARBON_KIND(CaseT name)` this converts
// the `value` to `CaseT`.
//
// For types with a `kind()` accessor this uses `As<CaseT>`.
//
// For `std::variant<...>` this uses  `std::get<CaseT>`.
template <typename CaseFnT, typename SwitchT>
auto Cast(SwitchT&& kind_switch_value) -> decltype(auto) {
  using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  if constexpr (IsStdVariant<SwitchT>) {
    using NoRefCaseT = std::remove_cvref_t<CaseT>;
    return std::get<NoRefCaseT>(std::forward<SwitchT>(kind_switch_value));
  } else {
    return kind_switch_value.template As<CaseT>();
  }
}

#define CARBON_INTERNAL_KIND_MERGE(Prefix, Line) Prefix##Line
#define CARBON_INTERNAL_KIND_LABEL(Line) \
  CARBON_INTERNAL_KIND_MERGE(carbon_internal_kind_case_, Line)

}  // namespace Carbon::Internal::Kind

// Produces a switch statement on value.kind().
#define CARBON_KIND_SWITCH(value)                       \
  switch (                                              \
      auto&& carbon_internal_kind_switch_value = value; \
      ::Carbon::Internal::Kind::SwitchOn(carbon_internal_kind_switch_value))

// Produces a case-compatible block of code that also instantiates a local typed
// variable. typed_variable_decl looks like `int i`, with a space.

Review comment (suggestion):
```suggestion
// For types with a `kind()` accessor this uses `value.As<CaseT>`.
//
// For `std::variant<...>` this uses `std::get<CaseT>(value)`.
```
Given that the functions are called in quite different ways I think it's useful to indicate that `As` is called as a member function.

Code after:
//
// For types with a `kind()` accessor that returns a `TypeEnum`,
// this gets the `TypeEnum<...>::RawTypeEnum` for the case type `CaseT`.
//
// For std::variant<...> this returns the value corresponding to the case type
// from the enum synthesized (in `SwitchOn`) for the types in the variant's
// type list.
template <typename SwitchT, typename CaseFnT>
consteval auto ForCase() -> auto {
  using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  if constexpr (IsStdVariant<SwitchT>) {
    using NoRefCaseT = std::remove_cvref_t<CaseT>;
    return CaseValueOfTypeInStdVariant<NoRefCaseT, SwitchT>;
  } else {
    using KindT = llvm::function_traits<
        decltype(&std::remove_cvref_t<SwitchT>::kind)>::result_t;
    return static_cast<KindT::RawEnumType>(KindT::template For<CaseT>);
  }
}

// Given `CARBON_KIND_SWITCH(value)` and `CARBON_KIND(CaseT name)` this converts
// the `value` to `CaseT`.
//
// For types with a `kind()` accessor this uses `value.As<CaseT>`.
//
// For `std::variant<...>` this uses `std::get<CaseT>(value)`.
template <typename CaseFnT, typename SwitchT>
auto Cast(SwitchT&& kind_switch_value) -> decltype(auto) {
  using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  if constexpr (IsStdVariant<SwitchT>) {
    using NoRefCaseT = std::remove_cvref_t<CaseT>;
    return std::get<NoRefCaseT>(std::forward<SwitchT>(kind_switch_value));
  } else {
    return kind_switch_value.template As<CaseT>();
  }
}

#define CARBON_INTERNAL_KIND_MERGE(Prefix, Line) Prefix##Line
#define CARBON_INTERNAL_KIND_LABEL(Line) \
  CARBON_INTERNAL_KIND_MERGE(carbon_internal_kind_case_, Line)

}  // namespace Carbon::Internal::Kind

// Produces a switch statement on value.kind().
#define CARBON_KIND_SWITCH(value)                       \
  switch (                                              \
      auto&& carbon_internal_kind_switch_value = value; \
      ::Carbon::Internal::Kind::SwitchOn(carbon_internal_kind_switch_value))

// Produces a case-compatible block of code that also instantiates a local typed
// variable. typed_variable_decl looks like `int i`, with a space.