[libc-commits] [libc] 7d06f59 - [libc] Extend optional to support non trivially destructible objects

Mikhail R. Gadelha via libc-commits libc-commits at lists.llvm.org
Mon Aug 14 15:33:06 PDT 2023


Author: Mikhail R. Gadelha
Date: 2023-08-14T19:32:34-03:00
New Revision: 7d06f59b60e6eaebd125dad351a20108f057940b

URL: https://github.com/llvm/llvm-project/commit/7d06f59b60e6eaebd125dad351a20108f057940b
DIFF: https://github.com/llvm/llvm-project/commit/7d06f59b60e6eaebd125dad351a20108f057940b.diff

LOG: [libc] Extend optional to support non trivially destructible objects

This patch moves the storage from inside the libc's optional class to
its own set of class, so we can support non-trivially destructible
objects.

These new classes check if the class is or isn't non trivially
destructible and instantiate the correct base class, i.e., we explicitly
call the destructor if an object is not trivially destructible.

The motivation is to support cpp::optional<UInt<128>> (used by
UInt<T>::div), which is used when a platform does not support native
int128_t types (e.g., riscv32).

The code here is a trimmed-down version of llvm::optional.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D150211

Added: 
    

Modified: 
    libc/src/__support/CPP/optional.h
    libc/src/__support/CPP/type_traits.h

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/CPP/optional.h b/libc/src/__support/CPP/optional.h
index a1eca2c860697a..bbf9188087c1b3 100644
--- a/libc/src/__support/CPP/optional.h
+++ b/libc/src/__support/CPP/optional.h
@@ -36,85 +36,93 @@ LIBC_INLINE_VAR constexpr in_place_t in_place{};
 // several assumptions that the underlying type is trivially constructable,
 // copyable, or movable.
 template <typename T> class optional {
-  template <typename U> class OptionalStorage {
+  template <typename U, bool = !is_trivially_destructible<U>::value>
+  struct OptionalStorage {
     union {
       char empty;
       U stored_value;
     };
-    bool in_use;
 
-  public:
-    LIBC_INLINE ~OptionalStorage() { reset(); }
-
-    LIBC_INLINE constexpr OptionalStorage() : empty(), in_use(false) {}
+    LIBC_INLINE ~OptionalStorage() { stored_value.~U(); }
+    LIBC_INLINE constexpr OptionalStorage() : empty() {}
 
     template <typename... Args>
     LIBC_INLINE constexpr explicit OptionalStorage(in_place_t, Args &&...args)
-        : stored_value(forward<Args>(args)...), in_use(true) {}
+        : stored_value(forward<Args>(args)...) {}
+  };
 
-    LIBC_INLINE void reset() {
-      if (in_use)
-        stored_value.~U();
-      in_use = false;
-    }
+  template <typename U> struct OptionalStorage<U, false> {
+    union {
+      char empty;
+      U stored_value;
+    };
 
-    LIBC_INLINE constexpr bool has_value() const { return in_use; }
+    // The only 
diff erence is that this class doesn't have a destructor.
+    LIBC_INLINE constexpr OptionalStorage() : empty() {}
 
-    LIBC_INLINE U &value() & { return stored_value; }
-    LIBC_INLINE constexpr U const &value() const & { return stored_value; }
-    LIBC_INLINE U &&value() && { return move(stored_value); }
+    template <typename... Args>
+    LIBC_INLINE constexpr explicit OptionalStorage(in_place_t, Args &&...args)
+        : stored_value(forward<Args>(args)...) {}
   };
 
   OptionalStorage<T> storage;
+  bool in_use = false;
 
 public:
   LIBC_INLINE constexpr optional() = default;
   LIBC_INLINE constexpr optional(nullopt_t) {}
 
-  LIBC_INLINE constexpr optional(const T &t) : storage(in_place, t) {}
+  LIBC_INLINE constexpr optional(const T &t)
+      : storage(in_place, t), in_use(true) {}
   LIBC_INLINE constexpr optional(const optional &) = default;
 
-  LIBC_INLINE constexpr optional(T &&t) : storage(in_place, move(t)) {}
+  LIBC_INLINE constexpr optional(T &&t)
+      : storage(in_place, move(t)), in_use(true) {}
   LIBC_INLINE constexpr optional(optional &&O) = default;
 
   template <typename... ArgTypes>
   LIBC_INLINE constexpr optional(in_place_t, ArgTypes &&...Args)
-      : storage(in_place, forward<ArgTypes>(Args)...) {}
+      : storage(in_place, forward<ArgTypes>(Args)...), in_use(true) {}
 
-  LIBC_INLINE optional &operator=(T &&t) {
+  LIBC_INLINE constexpr optional &operator=(T &&t) {
     storage = move(t);
     return *this;
   }
-  LIBC_INLINE optional &operator=(optional &&) = default;
-
-  LIBC_INLINE static constexpr optional create(const T *t) {
-    return t ? optional(*t) : optional();
-  }
+  LIBC_INLINE constexpr optional &operator=(optional &&) = default;
 
-  LIBC_INLINE optional &operator=(const T &t) {
+  LIBC_INLINE constexpr optional &operator=(const T &t) {
     storage = t;
     return *this;
   }
-  LIBC_INLINE optional &operator=(const optional &) = default;
+  LIBC_INLINE constexpr optional &operator=(const optional &) = default;
 
-  LIBC_INLINE void reset() { storage.reset(); }
+  LIBC_INLINE constexpr void reset() {
+    if (in_use)
+      storage.~OptionalStorage();
+    in_use = false;
+  }
 
-  LIBC_INLINE constexpr const T &value() const & { return storage.value(); }
-  LIBC_INLINE T &value() & { return storage.value(); }
+  LIBC_INLINE constexpr const T &value() const & {
+    return storage.stored_value;
+  }
 
-  LIBC_INLINE constexpr explicit operator bool() const { return has_value(); }
-  LIBC_INLINE constexpr bool has_value() const { return storage.has_value(); }
-  LIBC_INLINE constexpr const T *operator->() const { return &storage.value(); }
-  LIBC_INLINE T *operator->() { return &storage.value(); }
-  LIBC_INLINE constexpr const T &operator*() const & { return value(); }
-  LIBC_INLINE T &operator*() & { return value(); }
+  LIBC_INLINE constexpr T &value() & { return storage.stored_value; }
 
-  template <typename U> LIBC_INLINE constexpr T value_or(U &&value) const & {
-    return has_value() ? value() : forward<U>(value);
+  LIBC_INLINE constexpr explicit operator bool() const { return in_use; }
+  LIBC_INLINE constexpr bool has_value() const { return in_use; }
+  LIBC_INLINE constexpr const T *operator->() const {
+    return &storage.stored_value;
+  }
+  LIBC_INLINE constexpr T *operator->() { return &storage.stored_value; }
+  LIBC_INLINE constexpr const T &operator*() const & {
+    return storage.stored_value;
   }
+  LIBC_INLINE constexpr T &operator*() & { return storage.stored_value; }
 
-  LIBC_INLINE T &&value() && { return move(storage.value()); }
-  LIBC_INLINE T &&operator*() && { return move(storage.value()); }
+  LIBC_INLINE constexpr T &&value() && { return move(storage.stored_value); }
+  LIBC_INLINE constexpr T &&operator*() && {
+    return move(storage.stored_value);
+  }
 };
 
 } // namespace cpp

diff  --git a/libc/src/__support/CPP/type_traits.h b/libc/src/__support/CPP/type_traits.h
index 316b84c5bbf095..826a8e35f28c8b 100644
--- a/libc/src/__support/CPP/type_traits.h
+++ b/libc/src/__support/CPP/type_traits.h
@@ -214,6 +214,10 @@ constexpr bool
                      details::void_t<decltype(details::convertible_to_helper<T>(
                          declval<F>()))>> = true;
 
+template <typename T>
+struct is_trivially_destructible
+    : public integral_constant<bool, __is_trivially_destructible(T)> {};
+
 } // namespace cpp
 } // namespace __llvm_libc
 


        


More information about the libc-commits mailing list