// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_ #define CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_ #include "llvm/ADT/STLExtras.h" // This library provides switch-like behaviors for Carbon's kind-based types. // // An expected use case is to mix regular switch `case` statements and // `CARBON_KIND`. However, the `switch` must be defined using // `CARBON_KIND_SWITCH`. For example: // // CARBON_KIND_SWITCH(untyped_inst) { // case CARBON_KIND(SomeInstType inst): { // return inst.typed_field; // } // case OtherType1::Kind: // case OtherType2::Kind: // return value; // default: // return default_value; // } // // For compatibility, this requires: // // - The type passed to `CARBON_KIND_SWITCH` has `.kind()` to switch on, and // `.As` for `CARBON_KIND` to cast to. // - Each type passed to `CARBON_KIND` (`CaseT` above) provides // `CaseT::Kind`, which is passed to the `case` keyword. // `CaseT::Kind::RawEnumType` is the type returned by `.kind()`. // // Note, this is currently used primarily for Inst in toolchain. When more // use-cases are added, it would be worth considering whether the API // requirements should change. namespace Carbon::Internal::Kind { // Given `CARBON_KIND_SWITCH(value)` this handles calling `value.kind()`. template auto SwitchOn(T&& switch_value) -> auto { return switch_value.kind(); } // 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 consteval auto ForCase() -> auto { using ArgT = llvm::function_traits::template arg_t<0>; return static_cast(ArgT::Kind); } // Given `CARBON_KIND_SWITCH(value)` and `CARBON_KIND(CaseT name)` this // generates `value.As()`. template auto Cast(ValueT&& kind_switch_value) -> auto { using CaseT = llvm::function_traits::template arg_t<0>; return kind_switch_value.template As(); } #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 ( \ const 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. // // This uses `if` to scope the variable, and provides a dangling `else` in order // to prevent accidental `else` use. The label allows `:` to follow the macro // name, making it look more like a typical `case`. #define CARBON_KIND(typed_variable_decl) \ ::Carbon::Internal::Kind::ForCase< \ decltype([]([[maybe_unused]] typed_variable_decl) {})>() \ : if (typed_variable_decl = ::Carbon::Internal::Kind::Cast< \ decltype([]([[maybe_unused]] typed_variable_decl) {})>( \ carbon_internal_kind_switch_value); \ false) {} \ else [[maybe_unused]] CARBON_INTERNAL_KIND_LABEL(__LINE__) #endif // CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_