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