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 /// As a default, return the given value.
115 [[nodiscard]] ResultT Default(ResultT defaultResult) {
116 if (result)
117 return std::move(*result);
118 return defaultResult;
119 }
120
121 /// Declare default as unreachable, making sure that all cases were handled.
122 [[nodiscard]] ResultT DefaultUnreachable(
123 const char *message = "Fell off the end of a type-switch") {
124 if (result)
125 return std::move(*result);
126 llvm_unreachable(message);
127 }
128
129 [[nodiscard]] operator ResultT() { return DefaultUnreachable(); }
130
131private:
132 /// The pointer to the result of this switch statement, once known,
133 /// null before that.
134 std::optional<ResultT> result;
135};
136
137/// Specialization of TypeSwitch for void returning callables.
138template <typename T>
139class TypeSwitch<T, void>
140 : public detail::TypeSwitchBase<TypeSwitch<T, void>, T> {
141public:
143 using BaseT::BaseT;
144 using BaseT::Case;
145 TypeSwitch(TypeSwitch &&other) = default;
146
147 /// Add a case on the given type.
148 template <typename CaseT, typename CallableT>
149 TypeSwitch<T, void> &Case(CallableT &&caseFn) {
150 if (foundMatch)
151 return *this;
152
153 // Check to see if any of the types apply to 'value'.
154 if (auto caseValue = BaseT::template castValue<CaseT>(this->value)) {
155 caseFn(caseValue);
156 foundMatch = true;
157 }
158 return *this;
159 }
160
161 /// As a default, invoke the given callable within the root value.
162 template <typename CallableT> void Default(CallableT &&defaultFn) {
163 if (!foundMatch)
164 defaultFn(this->value);
165 }
166
167 /// Declare default as unreachable, making sure that all cases were handled.
169 const char *message = "Fell off the end of a type-switch") {
170 if (!foundMatch)
171 llvm_unreachable(message);
172 }
173
174private:
175 /// A flag detailing if we have already found a match.
176 bool foundMatch = false;
177};
178} // end namespace llvm
179
180#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:162
TypeSwitch(TypeSwitch &&other)=default
TypeSwitch< T, void > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition TypeSwitch.h:149
detail::TypeSwitchBase< TypeSwitch< T, void >, T > BaseT
Definition TypeSwitch.h:142
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:168
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:115
TypeSwitch(TypeSwitch &&other)=default
TypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition TypeSwitch.h:97
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:122
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.
These are wrappers over isa* function that allow them to be used in generic algorithms such as llvm:a...
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:644
This class provides various trait information about a callable object.
Definition STLExtras.h:67