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