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"
22#include <optional>
23
24namespace llvm {
25namespace detail {
26
27template <typename DerivedT, typename T> class TypeSwitchBase {
28public:
31 ~TypeSwitchBase() = default;
32
33 /// TypeSwitchBase is not copyable.
34 TypeSwitchBase(const TypeSwitchBase &) = delete;
35 void operator=(const TypeSwitchBase &) = delete;
36 void operator=(TypeSwitchBase &&other) = delete;
37
38 /// Invoke a case on the derived class with multiple case types.
39 template <typename CaseT, typename CaseT2, typename... CaseTs,
40 typename CallableT>
41 // This is marked always_inline and nodebug so it doesn't show up in stack
42 // traces at -O0 (or other optimization levels). Large TypeSwitch's are
43 // common, are equivalent to a switch, and don't add any value to stack
44 // traces.
46 Case(CallableT &&caseFn) {
47 DerivedT &derived = static_cast<DerivedT &>(*this);
48 return derived.template Case<CaseT>(caseFn)
49 .template Case<CaseT2, CaseTs...>(caseFn);
50 }
51
52 /// Invoke a case on the derived class, inferring the type of the Case from
53 /// the first input of the given callable.
54 /// Note: This inference rules for this overload are very simple: strip
55 /// pointers and references.
56 template <typename CallableT> DerivedT &Case(CallableT &&caseFn) {
58 using CaseT = std::remove_cv_t<std::remove_pointer_t<
59 std::remove_reference_t<typename Traits::template arg_t<0>>>>;
60
61 DerivedT &derived = static_cast<DerivedT &>(*this);
62 return derived.template Case<CaseT>(std::forward<CallableT>(caseFn));
63 }
64
65protected:
66 /// Attempt to dyn_cast the given `value` to `CastT`.
67 template <typename CastT, typename ValueT>
68 static decltype(auto) castValue(ValueT &&value) {
69 return dyn_cast<CastT>(value);
70 }
71
72 /// The root value we are switching on.
73 const T value;
74};
75} // end namespace detail
76
77/// This class implements a switch-like dispatch statement for a value of 'T'
78/// using dyn_cast functionality. Each `Case<T>` takes a callable to be invoked
79/// if the root value isa<T>, the callable is invoked with the result of
80/// dyn_cast<T>() as a parameter.
81///
82/// Example:
83/// Operation *op = ...;
84/// LogicalResult result = TypeSwitch<Operation *, LogicalResult>(op)
85/// .Case<ConstantOp>([](ConstantOp op) { ... })
86/// .Default([](Operation *op) { ... });
87///
88template <typename T, typename ResultT = void>
89class TypeSwitch : public detail::TypeSwitchBase<TypeSwitch<T, ResultT>, T> {
90public:
92 using BaseT::BaseT;
93 using BaseT::Case;
94 TypeSwitch(TypeSwitch &&other) = default;
95
96 /// Add a case on the given type.
97 template <typename CaseT, typename CallableT>
98 TypeSwitch<T, ResultT> &Case(CallableT &&caseFn) {
99 if (result)
100 return *this;
101
102 // Check to see if CaseT applies to 'value'.
103 if (auto caseValue = BaseT::template castValue<CaseT>(this->value))
104 result.emplace(caseFn(caseValue));
105 return *this;
106 }
107
108 /// As a default, invoke the given callable within the root value.
109 template <typename CallableT>
110 [[nodiscard]] ResultT Default(CallableT &&defaultFn) {
111 if (result)
112 return std::move(*result);
113 return defaultFn(this->value);
114 }
115
116 /// As a default, return the given value.
117 [[nodiscard]] ResultT Default(ResultT defaultResult) {
118 if (result)
119 return std::move(*result);
120 return defaultResult;
121 }
122
123 /// Default for pointer-like results types that accept `nullptr`.
124 template <typename ArgT = ResultT,
125 typename =
126 std::enable_if_t<std::is_constructible_v<ArgT, std::nullptr_t>>>
127 [[nodiscard]] ResultT Default(std::nullptr_t) {
128 return Default(ResultT(nullptr));
129 }
130
131 /// Default for optional results types that accept `std::nullopt`.
132 template <typename ArgT = ResultT,
133 typename =
134 std::enable_if_t<std::is_constructible_v<ArgT, std::nullopt_t>>>
135 [[nodiscard]] ResultT Default(std::nullopt_t) {
136 return Default(ResultT(std::nullopt));
137 }
138
139 /// Default for result types constructible from `LogicalResult` (e.g.,
140 /// `FailureOr<T>`).
141 template <typename ArgT = ResultT,
142 typename =
143 std::enable_if_t<std::is_constructible_v<ArgT, LogicalResult> &&
144 !std::is_same_v<ArgT, LogicalResult>>>
145 [[nodiscard]] ResultT Default(LogicalResult result) {
146 return Default(ResultT(result));
147 }
148
149 /// Declare default as unreachable, making sure that all cases were handled.
150 [[nodiscard]] ResultT DefaultUnreachable(
151 const char *message = "Fell off the end of a type-switch") {
152 if (result)
153 return std::move(*result);
154 llvm_unreachable(message);
155 }
156
157 [[nodiscard]] operator ResultT() { return DefaultUnreachable(); }
158
159private:
160 /// The pointer to the result of this switch statement, once known,
161 /// null before that.
162 std::optional<ResultT> result;
163};
164
165/// Specialization of TypeSwitch for void returning callables.
166template <typename T>
167class TypeSwitch<T, void>
168 : public detail::TypeSwitchBase<TypeSwitch<T, void>, T> {
169public:
171 using BaseT::BaseT;
172 using BaseT::Case;
173 TypeSwitch(TypeSwitch &&other) = default;
174
175 /// Add a case on the given type.
176 template <typename CaseT, typename CallableT>
177 TypeSwitch<T, void> &Case(CallableT &&caseFn) {
178 if (foundMatch)
179 return *this;
180
181 // Check to see if any of the types apply to 'value'.
182 if (auto caseValue = BaseT::template castValue<CaseT>(this->value)) {
183 caseFn(caseValue);
184 foundMatch = true;
185 }
186 return *this;
187 }
188
189 /// As a default, invoke the given callable within the root value.
190 template <typename CallableT> void Default(CallableT &&defaultFn) {
191 if (!foundMatch)
192 defaultFn(this->value);
193 }
194
195 /// Declare default as unreachable, making sure that all cases were handled.
197 const char *message = "Fell off the end of a type-switch") {
198 if (!foundMatch)
199 llvm_unreachable(message);
200 }
201
202private:
203 /// A flag detailing if we have already found a match.
204 bool foundMatch = false;
205};
206} // end namespace llvm
207
208#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:190
TypeSwitch(TypeSwitch &&other)=default
TypeSwitch< T, void > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition TypeSwitch.h:177
detail::TypeSwitchBase< TypeSwitch< T, void >, T > BaseT
Definition TypeSwitch.h:170
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:196
detail::TypeSwitchBase< TypeSwitch< T, ResultT >, T > BaseT
Definition TypeSwitch.h:91
ResultT Default(CallableT &&defaultFn)
As a default, invoke the given callable within the root value.
Definition TypeSwitch.h:110
ResultT Default(ResultT defaultResult)
As a default, return the given value.
Definition TypeSwitch.h:117
TypeSwitch(TypeSwitch &&other)=default
TypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition TypeSwitch.h:98
ResultT Default(std::nullptr_t)
Default for pointer-like results types that accept nullptr.
Definition TypeSwitch.h:127
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:150
ResultT Default(LogicalResult result)
Default for result types constructible from LogicalResult (e.g., FailureOr<T>).
Definition TypeSwitch.h:145
ResultT Default(std::nullopt_t)
Default for optional results types that accept std::nullopt.
Definition TypeSwitch.h:135
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:56
void operator=(TypeSwitchBase &&other)=delete
void operator=(const TypeSwitchBase &)=delete
TypeSwitchBase(TypeSwitchBase &&other)
Definition TypeSwitch.h:30
static decltype(auto) castValue(ValueT &&value)
Attempt to dyn_cast the given value to CastT.
Definition TypeSwitch.h:68
TypeSwitchBase(const TypeSwitchBase &)=delete
TypeSwitchBase is not copyable.
TypeSwitchBase(const T &value)
Definition TypeSwitch.h:29
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:46
#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.
Definition Types.h:26
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 represents an efficient way to signal success or failure.
This class provides various trait information about a callable object.
Definition STLExtras.h:67