xref: /aosp_15_r20/external/tensorflow/tensorflow/core/platform/statusor_internals.h (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #ifndef TENSORFLOW_CORE_PLATFORM_STATUSOR_INTERNALS_H_
17 #define TENSORFLOW_CORE_PLATFORM_STATUSOR_INTERNALS_H_
18 
19 #include "tensorflow/core/platform/macros.h"
20 #include "tensorflow/core/platform/status.h"
21 
22 namespace tensorflow {
23 namespace internal_statusor {
24 
25 class 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.
35 template <typename T, typename... Args>
PlacementNew(void * p,Args &&...args)36 void 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.
47 template <typename T>
48 class StatusOrData {
49   template <typename U>
50   friend class StatusOrData;
51 
52  public:
53   StatusOrData() = delete;
54 
StatusOrData(const StatusOrData & other)55   StatusOrData(const StatusOrData& other) {
56     if (other.ok()) {
57       MakeValue(other.data_);
58       MakeStatus();
59     } else {
60       MakeStatus(other.status_);
61     }
62   }
63 
StatusOrData(StatusOrData && other)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>
StatusOrData(const StatusOrData<U> & other)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>
StatusOrData(StatusOrData<U> && other)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 
StatusOrData(const T & value)93   explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); }
StatusOrData(T && value)94   explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); }
95 
96   template <typename... Args>
StatusOrData(absl::in_place_t,Args &&...args)97   explicit StatusOrData(absl::in_place_t, Args&&... args)
98       : data_(std::forward<Args>(args)...) {
99     MakeStatus();
100   }
101 
StatusOrData(const Status & status)102   explicit StatusOrData(const Status& status) : status_(status) {
103     EnsureNotOk();
104   }
StatusOrData(Status && status)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 
~StatusOrData()127   ~StatusOrData() {
128     if (ok()) {
129       status_.~Status();
130       data_.~T();
131     } else {
132       status_.~Status();
133     }
134   }
135 
Assign(const T & value)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 
Assign(T && value)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 
Assign(const Status & status)156   void Assign(const Status& status) {
157     Clear();
158     status_ = status;
159     EnsureNotOk();
160   }
161 
Assign(Status && status)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 
ok()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 
Clear()191   void Clear() {
192     if (ok()) data_.~T();
193   }
194 
EnsureOk()195   void EnsureOk() const {
196     if (!ok()) Helper::Crash(status_);
197   }
198 
EnsureNotOk()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>
MakeValue(Arg && 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>
MakeStatus(Args &&...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.
223 template <bool Copy, bool Move>
224 struct 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 
232 template <>
233 struct 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 
241 template <>
242 struct 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 tensorflow
252 
253 #endif  // TENSORFLOW_CORE_PLATFORM_STATUSOR_INTERNALS_H_
254