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

Code before:

template <size_t I, typename T, typename... Ts>
struct IndexOfTypeValue<I, T, TypePack<T, Ts...>> {
  static constexpr size_t Index = I;
};

template <size_t I, typename T, typename U, typename... Ts>
struct IndexOfTypeValue<I, T, TypePack<U, Ts...>> {
  static constexpr size_t Index =
      IndexOfTypeValue<I + 1, T, TypePack<Ts...>>::Index;
};

template <size_t I, typename T, typename TypePack>
static constexpr size_t IndexOfType = IndexOfTypeValue<I, T, TypePack>::Index;

// Given `CARBON_KIND(CaseT name)` this generates `CaseT::Kind`. It explicitly
// returns `KindT` because that may differ from `CaseT::Kind`, and may not be
// copyable.
template <typename SwitchT, typename CaseFnT>
consteval auto ForCase() -> auto {
  using CaseT = llvm::function_traits<CaseFnT>::template arg_t<0>;
  if constexpr (IsStdVariant<SwitchT>) {
    return IndexOfType<0, CaseT, StdVariantTypePack<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
// generates `value.As<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>) {
    return std::get<CaseT>(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 (                                                   \

Review comment (suggestion):
After some testing, it looks like this trick doesn't actually work: Clang diagnoses if the *last* element of the enum doesn't have a case, but doesn't warn if lower values are not handled.

I think if we want to make this work we'll need to declare a bunch of enum types, one for each number of variant elements we want to support, up to some fixed bound. Eg:
```suggestion
template <typename T0, typename T1>
struct StdVariantEnumValue<std::variant<T0, T1>> {
  enum [[clang::enum_extensibility(open)]] EnumType{
      VariantType0,
      VariantType1,
  };
  using Type = EnumType;
};
template <typename T0, typename T1, typename T2>
struct StdVariantEnumValue<std::variant<T0, T1, T2>> {
  enum [[clang::enum_extensibility(open)]] EnumType{
      VariantType0,
      VariantType1,
      VariantType2,
  };
  using Type = EnumType;
};
template <typename T0, typename T1, typename T2, typename T3>
struct StdVariantEnumValue<std::variant<T0, T1, T2, T3>> {
  enum [[clang::enum_extensibility(open)]] EnumType{
      VariantType0,
      VariantType1,
      VariantType2,
      VariantType3,
  };
  using Type = EnumType;
};
// ...
```

Code after:
     * it means a type in the std::variant<...> type list does not have a      \
     * matching case statement.                                                \
     */                                                                        \
    enum class EnumType {                                                      \
      CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES(__VA_ARGS__)                    \
    };                                                                         \
    /* A mapping from a single type in the std::variant<...> type list to a    \
     * value in the EnumType. This value is only used in the case a type is    \
     * queried which is not part of the type list, and ValidCaseType is used   \
     * to produce a diagnostic explaining the situation.  */                   \
    template <typename Tn>                                                     \
    static constexpr EnumType EnumValue = ValidCaseType<                       \
        Tn, std::variant<CARBON_INTERNAL_KIND_IDENTIFIERS(__VA_ARGS__)>>();    \
    /**/                                                                       \
    CARBON_FOR_EACH(CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME,                    \
                    CARBON_FOR_EACH_SEMI, __VA_ARGS__);                        \
  }

template <typename... Ts>
struct StdVariantTypeMap<std::variant<Ts...>> {
  // The number here should match the number of arguments in the largest
  // `CARBON_INTERNAL_KIND_TYPE_MAP` invocation below.
  static_assert(sizeof...(Ts) <= 12,
                "CARBON_KIND_SWITCH supports std::variant with up to 12 types. "
                "Add more if needed.");
};

// Generate StdVariantTypeMap specializations for each number of types in the
// std::variant<...> type list.
CARBON_INTERNAL_KIND_TYPE_MAP(0);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);

#undef CARBON_INTERNAL_KIND_IDENTIFIER
#undef CARBON_INTERNAL_KIND_IDENTIFIERS
#undef CARBON_INTERNAL_KIND_TYPENAME
#undef CARBON_INTERNAL_KIND_TYPENAMES
#undef CARBON_INTERNAL_KIND_ENUM_NAME
#undef CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES
#undef CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME
#undef CARBON_INTERNAL_KIND_TYPE_MAP