xref: /llvm-project/flang/include/flang/Common/unwrap.h (revision 7860f970666f46184ad740db48a69882d62e64fc)
1 //===-- include/flang/Common/unwrap.h ---------------------------*- 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 #ifndef FORTRAN_COMMON_UNWRAP_H_
10 #define FORTRAN_COMMON_UNWRAP_H_
11 
12 #include "indirection.h"
13 #include "reference-counted.h"
14 #include "reference.h"
15 #include "variant.h"
16 #include "visit.h"
17 #include <memory>
18 #include <optional>
19 #include <type_traits>
20 
21 // Given a nest of variants, optionals, &/or pointers, Unwrap<>() isolates
22 // a packaged value of a specific type if it is present and returns a pointer
23 // thereto; otherwise, it returns a null pointer.  It's analogous to
24 // std::get_if<>() but it accepts a reference argument and is recursive.
25 // The target type parameter cannot be omitted.
26 //
27 // Be advised: If the target type parameter is not const-qualified, but the
28 // isolated value is const-qualified, the result of Unwrap<> will be a
29 // pointer to a const-qualified value.
30 //
31 // Further: const-qualified alternatives in instances of non-const-qualified
32 // variants will not be returned from Unwrap if the target type is not
33 // const-qualified.
34 //
35 // UnwrapCopy<>() is a variation of Unwrap<>() that returns an optional copy
36 // of the value if one is present with the desired type.
37 
38 namespace Fortran::common {
39 
40 // Utility: Produces "const A" if B is const and A is not already so.
41 template <typename A, typename B>
42 using Constify = std::conditional_t<std::is_const_v<B> && !std::is_const_v<A>,
43     std::add_const_t<A>, A>;
44 
45 // Unwrap's mutually-recursive template functions are packaged in a struct
46 // to avoid a need for prototypes.
47 struct UnwrapperHelper {
48 
49   // Base case
50   template <typename A, typename B>
51   static auto Unwrap(B &x) -> Constify<A, B> * {
52     if constexpr (std::is_same_v<std::decay_t<A>, std::decay_t<B>>) {
53       return &x;
54     } else {
55       return nullptr;
56     }
57   }
58 
59   // Implementations of specializations
60   template <typename A, typename B>
61   static auto Unwrap(B *p) -> Constify<A, B> * {
62     if (p) {
63       return Unwrap<A>(*p);
64     } else {
65       return nullptr;
66     }
67   }
68 
69   template <typename A, typename B>
70   static auto Unwrap(const std::unique_ptr<B> &p) -> Constify<A, B> * {
71     if (p.get()) {
72       return Unwrap<A>(*p);
73     } else {
74       return nullptr;
75     }
76   }
77 
78   template <typename A, typename B>
79   static auto Unwrap(const std::shared_ptr<B> &p) -> Constify<A, B> * {
80     if (p.get()) {
81       return Unwrap<A>(*p);
82     } else {
83       return nullptr;
84     }
85   }
86 
87   template <typename A, typename B>
88   static auto Unwrap(std::optional<B> &x) -> Constify<A, B> * {
89     if (x) {
90       return Unwrap<A>(*x);
91     } else {
92       return nullptr;
93     }
94   }
95 
96   template <typename A, typename B>
97   static auto Unwrap(const std::optional<B> &x) -> Constify<A, B> * {
98     if (x) {
99       return Unwrap<A>(*x);
100     } else {
101       return nullptr;
102     }
103   }
104 
105   template <typename A, typename... Bs>
UnwrapUnwrapperHelper106   static A *Unwrap(std::variant<Bs...> &u) {
107     return common::visit(
108         [](auto &x) -> A * {
109           using Ty = std::decay_t<decltype(Unwrap<A>(x))>;
110           if constexpr (!std::is_const_v<std::remove_pointer_t<Ty>> ||
111               std::is_const_v<A>) {
112             return Unwrap<A>(x);
113           }
114           return nullptr;
115         },
116         u);
117   }
118 
119   template <typename A, typename... Bs>
120   static auto Unwrap(const std::variant<Bs...> &u) -> std::add_const_t<A> * {
121     return common::visit(
122         [](const auto &x) -> std::add_const_t<A> * { return Unwrap<A>(x); }, u);
123   }
124 
125   template <typename A, typename B>
126   static auto Unwrap(const Reference<B> &ref) -> Constify<A, B> * {
127     return Unwrap<A>(*ref);
128   }
129 
130   template <typename A, typename B, bool COPY>
131   static auto Unwrap(const Indirection<B, COPY> &p) -> Constify<A, B> * {
132     return Unwrap<A>(p.value());
133   }
134 
135   template <typename A, typename B>
136   static auto Unwrap(const CountedReference<B> &p) -> Constify<A, B> * {
137     if (p.get()) {
138       return Unwrap<A>(*p);
139     } else {
140       return nullptr;
141     }
142   }
143 };
144 
145 template <typename A, typename B> auto Unwrap(B &x) -> Constify<A, B> * {
146   return UnwrapperHelper::Unwrap<A>(x);
147 }
148 
149 // Returns a copy of a wrapped value, if present, otherwise a vacant optional.
UnwrapCopy(const B & x)150 template <typename A, typename B> std::optional<A> UnwrapCopy(const B &x) {
151   if (const A * p{Unwrap<A>(x)}) {
152     return std::make_optional<A>(*p);
153   } else {
154     return std::nullopt;
155   }
156 }
157 } // namespace Fortran::common
158 #endif // FORTRAN_COMMON_UNWRAP_H_
159