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