1/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14==============================================================================*/
15
16#ifndef TENSORFLOW_TSL_PLATFORM_STATUSOR_INTERNALS_H_
17#define TENSORFLOW_TSL_PLATFORM_STATUSOR_INTERNALS_H_
18
19#include "tensorflow/tsl/platform/macros.h"
20#include "tensorflow/tsl/platform/status.h"
21
22namespace tsl {
23namespace internal_statusor {
24
25class Helper {
26 public:
27 // Move type-agnostic error handling to the .cc.
28 static void HandleInvalidStatusCtorArg(Status*);
29 TF_ATTRIBUTE_NORETURN static void Crash(const Status& status);
30};
31
32// Construct an instance of T in `p` through placement new, passing Args... to
33// the constructor.
34// This abstraction is here mostly for the gcc performance fix.
35template <typename T, typename... Args>
36void PlacementNew(void* p, Args&&... args) {
37#if defined(__GNUC__) && !defined(__clang__)
38 // Teach gcc that 'p' cannot be null, fixing code size issues.
39 if (p == nullptr) __builtin_unreachable();
40#endif
41 new (p) T(std::forward<Args>(args)...);
42}
43
44// Helper base class to hold the data and all operations.
45// We move all this to a base class to allow mixing with the appropriate
46// TraitsBase specialization.
47template <typename T>
48class StatusOrData {
49 template <typename U>
50 friend class StatusOrData;
51
52 public:
53 StatusOrData() = delete;
54
55 StatusOrData(const StatusOrData& other) {
56 if (other.ok()) {
57 MakeValue(other.data_);
58 MakeStatus();
59 } else {
60 MakeStatus(other.status_);
61 }
62 }
63
64 StatusOrData(StatusOrData&& other) noexcept {
65 if (other.ok()) {
66 MakeValue(std::move(other.data_));
67 MakeStatus();
68 } else {
69 MakeStatus(other.status_);
70 }
71 }
72
73 template <typename U>
74 StatusOrData(const StatusOrData<U>& other) {
75 if (other.ok()) {
76 MakeValue(other.data_);
77 MakeStatus();
78 } else {
79 MakeStatus(other.status_);
80 }
81 }
82
83 template <typename U>
84 StatusOrData(StatusOrData<U>&& other) {
85 if (other.ok()) {
86 MakeValue(std::move(other.data_));
87 MakeStatus();
88 } else {
89 MakeStatus(other.status_);
90 }
91 }
92
93 explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); }
94 explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); }
95
96 template <typename... Args>
97 explicit StatusOrData(absl::in_place_t, Args&&... args)
98 : data_(std::forward<Args>(args)...) {
99 MakeStatus();
100 }
101
102 explicit StatusOrData(const Status& status) : status_(status) {
103 EnsureNotOk();
104 }
105 explicit StatusOrData(Status&& status) : status_(std::move(status)) {
106 EnsureNotOk();
107 }
108
109 StatusOrData& operator=(const StatusOrData& other) {
110 if (this == &other) return *this;
111 if (other.ok())
112 Assign(other.data_);
113 else
114 Assign(other.status_);
115 return *this;
116 }
117
118 StatusOrData& operator=(StatusOrData&& other) {
119 if (this == &other) return *this;
120 if (other.ok())
121 Assign(std::move(other.data_));
122 else
123 Assign(std::move(other.status_));
124 return *this;
125 }
126
127 ~StatusOrData() {
128 if (ok()) {
129 status_.~Status();
130 data_.~T();
131 } else {
132 status_.~Status();
133 }
134 }
135
136 void Assign(const T& value) {
137 if (ok()) {
138 data_.~T();
139 MakeValue(value);
140 } else {
141 MakeValue(value);
142 status_ = OkStatus();
143 }
144 }
145
146 void Assign(T&& value) {
147 if (ok()) {
148 data_.~T();
149 MakeValue(std::move(value));
150 } else {
151 MakeValue(std::move(value));
152 status_ = OkStatus();
153 }
154 }
155
156 void Assign(const Status& status) {
157 Clear();
158 status_ = status;
159 EnsureNotOk();
160 }
161
162 void Assign(Status&& status) {
163 Clear();
164 // Note that we copy instead of moving the status here so that
165 // status.~StatusOrData() can call ok() without invoking UB.
166 status_ = status;
167 EnsureNotOk();
168 }
169
170 bool ok() const { return status_.ok(); }
171
172 protected:
173 // status_ will always be active after the constructor.
174 // We make it a union to be able to initialize exactly how we need without
175 // waste.
176 // Eg. in the copy constructor we use the default constructor of Status in
177 // the ok() path to avoid an extra Ref call.
178 union {
179 Status status_;
180 };
181
182 // data_ is active iff status_.ok()==true
183 struct Dummy {};
184 union {
185 // When T is const, we need some non-const object we can cast to void* for
186 // the placement new. dummy_ is that object.
187 Dummy dummy_;
188 T data_;
189 };
190
191 void Clear() {
192 if (ok()) data_.~T();
193 }
194
195 void EnsureOk() const {
196 if (!ok()) Helper::Crash(status_);
197 }
198
199 void EnsureNotOk() {
200 if (ok()) Helper::HandleInvalidStatusCtorArg(&status_);
201 }
202
203 // Construct the value (ie. data_) through placement new with the passed
204 // argument.
205 template <typename Arg>
206 void MakeValue(Arg&& arg) {
207 internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg));
208 }
209
210 // Construct the status (ie. status_) through placement new with the passed
211 // argument.
212 template <typename... Args>
213 void MakeStatus(Args&&... args) {
214 internal_statusor::PlacementNew<Status>(&status_,
215 std::forward<Args>(args)...);
216 }
217};
218
219// Helper base class to allow implicitly deleted constructors and assignment
220// operations in StatusOr.
221// TraitsBase will explicitly delete what it can't support and StatusOr will
222// inherit that behavior implicitly.
223template <bool Copy, bool Move>
224struct TraitsBase {
225 TraitsBase() = default;
226 TraitsBase(const TraitsBase&) = default;
227 TraitsBase(TraitsBase&&) = default;
228 TraitsBase& operator=(const TraitsBase&) = default;
229 TraitsBase& operator=(TraitsBase&&) = default;
230};
231
232template <>
233struct TraitsBase<false, true> {
234 TraitsBase() = default;
235 TraitsBase(const TraitsBase&) = delete;
236 TraitsBase(TraitsBase&&) = default;
237 TraitsBase& operator=(const TraitsBase&) = delete;
238 TraitsBase& operator=(TraitsBase&&) = default;
239};
240
241template <>
242struct TraitsBase<false, false> {
243 TraitsBase() = default;
244 TraitsBase(const TraitsBase&) = delete;
245 TraitsBase(TraitsBase&&) = delete;
246 TraitsBase& operator=(const TraitsBase&) = delete;
247 TraitsBase& operator=(TraitsBase&&) = delete;
248};
249
250} // namespace internal_statusor
251} // namespace tsl
252
253#endif // TENSORFLOW_TSL_PLATFORM_STATUSOR_INTERNALS_H_
254