LLVM 22.0.0git
TypeSwitch.h
Go to the documentation of this file.
1//===- TypeSwitch.h - Switch functionality for RTTI casting -*- C++ -*-----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file implements the TypeSwitch template, which mimics a switch()
11/// statement whose cases are type names.
12///
13//===-----------------------------------------------------------------------===/
14
15#ifndef LLVM_ADT_TYPESWITCH_H
16#define LLVM_ADT_TYPESWITCH_H
17
18#include "llvm/ADT/STLExtras.h"
21#include <optional>
22
23namespace llvm {
24namespace detail {
25
26template <typename DerivedT, typename T> class TypeSwitchBase {
27public:
30 ~TypeSwitchBase() = default;
31
32 /// TypeSwitchBase is not copyable.
33 TypeSwitchBase(const TypeSwitchBase &) = delete;
34 void operator=(const TypeSwitchBase &) = delete;
35 void operator=(TypeSwitchBase &&other) = delete;
36
37 /// Invoke a case on the derived class with multiple case types.
38 template <typename CaseT, typename CaseT2, typename... CaseTs,
39 typename CallableT>
40 // This is marked always_inline and nodebug so it doesn't show up in stack
41 // traces at -O0 (or other optimization levels). Large TypeSwitch's are
42 // common, are equivalent to a switch, and don't add any value to stack
43 // traces.
45 Case(CallableT &&caseFn) {
46 DerivedT &derived = static_cast<DerivedT &>(*this);
47 return derived.template Case<CaseT>(caseFn)
48 .template Case<CaseT2, CaseTs...>(caseFn);
49 }
50
51 /// Invoke a case on the derived class, inferring the type of the Case from
52 /// the first input of the given callable.
53 /// Note: This inference rules for this overload are very simple: strip
54 /// pointers and references.
55 template <typename CallableT> DerivedT &Case(CallableT &&caseFn) {
57 using CaseT = std::remove_cv_t<std::remove_pointer_t<
58 std::remove_reference_t<typename Traits::template arg_t<0>>>>;
59
60 DerivedT &derived = static_cast<DerivedT &>(*this);
61 return derived.template Case<CaseT>(std::forward<CallableT>(caseFn));
62 }
63
64protected:
65 /// Attempt to dyn_cast the given `value` to `CastT`.
66 template <typename CastT, typename ValueT>
67 static decltype(auto) castValue(ValueT &&value) {
68 return dyn_cast<CastT>(value);
69 }
70
71 /// The root value we are switching on.
72 const T value;
73};
74} // end namespace detail
75
76/// This class implements a switch-like dispatch statement for a value of 'T'
77/// using dyn_cast functionality. Each `Case<T>` takes a callable to be invoked
78/// if the root value isa<T>, the callable is invoked with the result of
79/// dyn_cast<T>() as a parameter.
80///
81/// Example:
82/// Operation *op = ...;
83/// LogicalResult result = TypeSwitch<Operation *, LogicalResult>(op)
84/// .Case<ConstantOp>([](ConstantOp op) { ... })
85/// .Default([](Operation *op) { ... });
86///
87template <typename T, typename ResultT = void>
88class TypeSwitch : public detail::TypeSwitchBase<TypeSwitch<T, ResultT>, T> {
89public:
91 using BaseT::BaseT;
92 using BaseT::Case;
93 TypeSwitch(TypeSwitch &&other) = default;
94
95 /// Add a case on the given type.
96 template <typename CaseT, typename CallableT>
97 TypeSwitch<T, ResultT> &Case(CallableT &&caseFn) {
98 if (result)
99 return *this;
100
101 // Check to see if CaseT applies to 'value'.
102 if (auto caseValue = BaseT::template castValue<CaseT>(this->value))
103 result.emplace(caseFn(caseValue));
104 return *this;
105 }
106
107 /// As a default, invoke the given callable within the root value.
108 template <typename CallableT>
109 [[nodiscard]] ResultT Default(CallableT &&defaultFn) {
110 if (result)
111 return std::move(*result);
112 return defaultFn(this->value);
113 }
114
115 /// As a default, return the given value.
116 [[nodiscard]] ResultT Default(ResultT defaultResult) {
117 if (result)
118 return std::move(*result);
119 return defaultResult;
120 }
121
122 /// Default for pointer-like results types that accept `nullptr`.
123 template <typename ArgT = ResultT,
124 typename =
125 std::enable_if_t<std::is_constructible_v<ArgT, std::nullptr_t>>>
126 [[nodiscard]] ResultT Default(std::nullptr_t) {
127 return Default(ResultT(nullptr));
128 }
129
130 /// Default for optional results types that accept `std::nullopt`.
131 template <typename ArgT = ResultT,
132 typename =
133 std::enable_if_t<std::is_constructible_v<ArgT, std::nullopt_t>>>
134 [[nodiscard]] ResultT Default(std::nullopt_t) {
135 return Default(ResultT(std::nullopt));
136 }
137
138 /// Declare default as unreachable, making sure that all cases were handled.
139 [[nodiscard]] ResultT DefaultUnreachable(
140 const char *message = "Fell off the end of a type-switch") {
141 if (result)
142 return std::move(*result);
143 llvm_unreachable(message);
144 }
145
146 [[nodiscard]] operator ResultT() { return DefaultUnreachable(); }
147
148private:
149 /// The pointer to the result of this switch statement, once known,
150 /// null before that.
151 std::optional<ResultT> result;
152};
153
154/// Specialization of TypeSwitch for void returning callables.
155template <typename T>
156class TypeSwitch<T, void>
157 : public detail::TypeSwitchBase<TypeSwitch<T, void>, T> {
158public:
160 using BaseT::BaseT;
161 using BaseT::Case;
162 TypeSwitch(TypeSwitch &&other) = default;
163
164 /// Add a case on the given type.
165 template <typename CaseT, typename CallableT>
166 TypeSwitch<T, void> &Case(CallableT &&caseFn) {
167 if (foundMatch)
168 return *this;
169
170 // Check to see if any of the types apply to 'value'.
171 if (auto caseValue = BaseT::template castValue<CaseT>(this->value)) {
172 caseFn(caseValue);
173 foundMatch = true;
174 }
175 return *this;
176 }
177
178 /// As a default, invoke the given callable within the root value.
179 template <typename CallableT> void Default(CallableT &&defaultFn) {
180 if (!foundMatch)
181 defaultFn(this->value);
182 }
183
184 /// Declare default as unreachable, making sure that all cases were handled.
186 const char *message = "Fell off the end of a type-switch") {
187 if (!foundMatch)
188 llvm_unreachable(message);
189 }
190
191private:
192 /// A flag detailing if we have already found a match.
193 bool foundMatch = false;
194};
195} // end namespace llvm
196
197#endif // LLVM_ADT_TYPESWITCH_H
#define LLVM_ATTRIBUTE_ALWAYS_INLINE
LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do so, mark a method "always...
Definition Compiler.h:356
#define LLVM_ATTRIBUTE_NODEBUG
LLVM_ATTRIBUTE_NO_DEBUG - On compilers where we have a directive to do so, mark a method "no debug" b...
Definition Compiler.h:365
#define T
This file contains some templates that are useful if you are working with the STL at all.
void Default(CallableT &&defaultFn)
As a default, invoke the given callable within the root value.
Definition TypeSwitch.h:179
TypeSwitch(TypeSwitch &&other)=default
TypeSwitch< T, void > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition TypeSwitch.h:166
detail::TypeSwitchBase< TypeSwitch< T, void >, T > BaseT
Definition TypeSwitch.h:159
void DefaultUnreachable(const char *message="Fell off the end of a type-switch")
Declare default as unreachable, making sure that all cases were handled.
Definition TypeSwitch.h:185
detail::TypeSwitchBase< TypeSwitch< T, ResultT >, T > BaseT
Definition TypeSwitch.h:90
ResultT Default(CallableT &&defaultFn)
As a default, invoke the given callable within the root value.
Definition TypeSwitch.h:109
ResultT Default(ResultT defaultResult)
As a default, return the given value.
Definition TypeSwitch.h:116
TypeSwitch(TypeSwitch &&other)=default
TypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition TypeSwitch.h:97
ResultT Default(std::nullptr_t)
Default for pointer-like results types that accept nullptr.
Definition TypeSwitch.h:126
ResultT DefaultUnreachable(const char *message="Fell off the end of a type-switch")
Declare default as unreachable, making sure that all cases were handled.
Definition TypeSwitch.h:139
ResultT Default(std::nullopt_t)
Default for optional results types that accept std::nullopt.
Definition TypeSwitch.h:134
DerivedT & Case(CallableT &&caseFn)
Invoke a case on the derived class, inferring the type of the Case from the first input of the given ...
Definition TypeSwitch.h:55
void operator=(TypeSwitchBase &&other)=delete
void operator=(const TypeSwitchBase &)=delete
TypeSwitchBase(TypeSwitchBase &&other)
Definition TypeSwitch.h:29
static decltype(auto) castValue(ValueT &&value)
Attempt to dyn_cast the given value to CastT.
Definition TypeSwitch.h:67
TypeSwitchBase(const TypeSwitchBase &)=delete
TypeSwitchBase is not copyable.
TypeSwitchBase(const T &value)
Definition TypeSwitch.h:28
LLVM_ATTRIBUTE_ALWAYS_INLINE LLVM_ATTRIBUTE_NODEBUG DerivedT & Case(CallableT &&caseFn)
Invoke a case on the derived class with multiple case types.
Definition TypeSwitch.h:45
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
A self-contained host- and target-independent arbitrary-precision floating-point software implementat...
Definition ADL.h:123
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
@ Default
The result values are uniform if and only if all operands are uniform.
Definition Uniformity.h:20
This class provides various trait information about a callable object.
Definition STLExtras.h:67