[libc-commits] [libc] [libc] refactor expected to correctly destruct payload (PR #195873)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Tue May 5 08:34:17 PDT 2026
https://github.com/SchrodingerZhu created https://github.com/llvm/llvm-project/pull/195873
None
>From 0eebcbe2d43cce7120e1d81bd10af562c68fe889 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Tue, 5 May 2026 11:33:52 -0400
Subject: [PATCH] [libc] refactor expected to correctly destruct payload
---
libc/src/__support/CPP/CMakeLists.txt | 3 +
libc/src/__support/CPP/expected.h | 93 +++++++++++++++----
libc/test/src/__support/CPP/CMakeLists.txt | 10 ++
libc/test/src/__support/CPP/expected_test.cpp | 65 +++++++++++++
4 files changed, 153 insertions(+), 18 deletions(-)
create mode 100644 libc/test/src/__support/CPP/expected_test.cpp
diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index bdfbc6151c773..f6c9ef5d0b20c 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -197,6 +197,9 @@ add_header_library(
expected
HDRS
expected.h
+ DEPENDS
+ .type_traits
+ .utility
)
add_object_library(
diff --git a/libc/src/__support/CPP/expected.h b/libc/src/__support/CPP/expected.h
index 8a93091f0ebfb..80512b3c4faa7 100644
--- a/libc/src/__support/CPP/expected.h
+++ b/libc/src/__support/CPP/expected.h
@@ -9,6 +9,8 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_EXPECTED_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_EXPECTED_H
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/CPP/utility.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
@@ -21,37 +23,92 @@ template <class T> class unexpected {
T value;
public:
- LIBC_INLINE constexpr explicit unexpected(T value) : value(value) {}
- LIBC_INLINE constexpr T error() { return value; }
+ LIBC_INLINE constexpr explicit unexpected(T value)
+ : value(cpp::move(value)) {}
+
+ LIBC_INLINE constexpr T &error() & { return value; }
+ LIBC_INLINE constexpr const T &error() const & { return value; }
+ LIBC_INLINE constexpr T &&error() && { return cpp::move(value); }
};
template <class T> explicit unexpected(T) -> unexpected<T>;
-template <class T, class E> class expected {
+struct unexpect_t {
+ LIBC_INLINE constexpr explicit unexpect_t() = default;
+};
+
+LIBC_INLINE_VAR constexpr unexpect_t unexpect{};
+
+template <class T, class E,
+ bool =
+ is_trivially_destructible_v<T> && is_trivially_destructible_v<E>>
+struct expected_storage;
+
+// Trivial case: no destructor declared.
+template <class T, class E> struct expected_storage<T, E, true> {
+ union {
+ T val;
+ E err;
+ };
+
+ bool exp;
+
+ template <typename... Args>
+ LIBC_INLINE constexpr explicit expected_storage(in_place_t, Args &&...args)
+ : val(cpp::forward<Args>(args)...), exp(true) {}
+
+ template <typename... Args>
+ LIBC_INLINE constexpr explicit expected_storage(unexpect_t, Args &&...args)
+ : err(cpp::forward<Args>(args)...), exp(false) {}
+};
+
+// Non-trivial case: destructor destroys the active member.
+template <class T, class E> struct expected_storage<T, E, false> {
union {
- T exp;
- E unexp;
+ T val;
+ E err;
};
- bool is_expected;
+
+ bool exp;
+
+ template <typename... Args>
+ LIBC_INLINE constexpr explicit expected_storage(in_place_t, Args &&...args)
+ : val(cpp::forward<Args>(args)...), exp(true) {}
+
+ template <typename... Args>
+ LIBC_INLINE constexpr explicit expected_storage(unexpect_t, Args &&...args)
+ : err(cpp::forward<Args>(args)...), exp(false) {}
+
+ LIBC_INLINE ~expected_storage() {
+ if (exp)
+ val.~T();
+ else
+ err.~E();
+ }
+};
+
+template <class T, class E> class expected : private expected_storage<T, E> {
+ using Base = expected_storage<T, E>;
public:
- LIBC_INLINE constexpr expected(T exp) : exp(exp), is_expected(true) {}
+ LIBC_INLINE constexpr expected(const T &val) : Base(in_place, val) {}
+ LIBC_INLINE constexpr expected(T &&val) : Base(in_place, cpp::move(val)) {}
LIBC_INLINE constexpr expected(unexpected<E> unexp)
- : unexp(unexp.error()), is_expected(false) {}
+ : Base(unexpect, cpp::move(unexp).error()) {}
- LIBC_INLINE constexpr bool has_value() const { return is_expected; }
+ LIBC_INLINE constexpr bool has_value() const { return this->exp; }
- LIBC_INLINE constexpr T &value() { return exp; }
- LIBC_INLINE constexpr E &error() { return unexp; }
- LIBC_INLINE constexpr const T &value() const { return exp; }
- LIBC_INLINE constexpr const E &error() const { return unexp; }
+ LIBC_INLINE constexpr T &value() { return this->val; }
+ LIBC_INLINE constexpr E &error() { return this->err; }
+ LIBC_INLINE constexpr const T &value() const { return this->val; }
+ LIBC_INLINE constexpr const E &error() const { return this->err; }
- LIBC_INLINE constexpr operator bool() const { return is_expected; }
+ LIBC_INLINE constexpr operator bool() const { return this->exp; }
- LIBC_INLINE constexpr T &operator*() { return exp; }
- LIBC_INLINE constexpr const T &operator*() const { return exp; }
- LIBC_INLINE constexpr T *operator->() { return &exp; }
- LIBC_INLINE constexpr const T *operator->() const { return &exp; }
+ LIBC_INLINE constexpr T &operator*() { return this->val; }
+ LIBC_INLINE constexpr const T &operator*() const { return this->val; }
+ LIBC_INLINE constexpr T *operator->() { return &this->val; }
+ LIBC_INLINE constexpr const T *operator->() const { return &this->val; }
};
} // namespace cpp
diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt
index 430cd7b136c69..962c95a733048 100644
--- a/libc/test/src/__support/CPP/CMakeLists.txt
+++ b/libc/test/src/__support/CPP/CMakeLists.txt
@@ -130,6 +130,16 @@ add_libc_test(
libc.src.__support.CPP.optional
)
+add_libc_test(
+ expected_test
+ SUITE
+ libc-cpp-utils-tests
+ SRCS
+ expected_test.cpp
+ DEPENDS
+ libc.src.__support.CPP.expected
+)
+
add_libc_test(
tuple_test
SUITE
diff --git a/libc/test/src/__support/CPP/expected_test.cpp b/libc/test/src/__support/CPP/expected_test.cpp
new file mode 100644
index 0000000000000..c73f6f945892b
--- /dev/null
+++ b/libc/test/src/__support/CPP/expected_test.cpp
@@ -0,0 +1,65 @@
+//===-- Unittests for Expected --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/expected.h"
+#include "src/__support/CPP/type_traits.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::cpp::expected;
+using LIBC_NAMESPACE::cpp::unexpected;
+
+class MoveOnly {
+ int *destroyed;
+
+public:
+ explicit MoveOnly(int *destroyed) : destroyed(destroyed) {}
+
+ MoveOnly(const MoveOnly &) = delete;
+ MoveOnly &operator=(const MoveOnly &) = delete;
+
+ MoveOnly(MoveOnly &&other) : destroyed(other.destroyed) {
+ other.destroyed = nullptr;
+ }
+
+ ~MoveOnly() {
+ if (destroyed)
+ ++*destroyed;
+ }
+};
+
+static_assert(
+ LIBC_NAMESPACE::cpp::is_trivially_destructible_v<expected<int, int>>,
+ "expected should be trivially destructible for trivial payloads");
+static_assert(!LIBC_NAMESPACE::cpp::is_trivially_destructible_v<
+ expected<MoveOnly, int>>,
+ "expected should be nontrivially destructible for nontrivial "
+ "value payloads");
+static_assert(!LIBC_NAMESPACE::cpp::is_trivially_destructible_v<
+ expected<int, MoveOnly>>,
+ "expected should be nontrivially destructible for nontrivial "
+ "error payloads");
+
+TEST(LlvmLibcExpectedTest, DestroysNonTrivialValue) {
+ int destroyed = 0;
+ {
+ expected<MoveOnly, int> value{MoveOnly(&destroyed)};
+ ASSERT_TRUE(value.has_value());
+ ASSERT_TRUE(static_cast<bool>(value));
+ }
+ ASSERT_EQ(destroyed, 1);
+}
+
+TEST(LlvmLibcExpectedTest, DestroysNonTrivialError) {
+ int destroyed = 0;
+ {
+ expected<int, MoveOnly> error{unexpected<MoveOnly>{MoveOnly(&destroyed)}};
+ ASSERT_FALSE(error.has_value());
+ ASSERT_FALSE(static_cast<bool>(error));
+ }
+ ASSERT_EQ(destroyed, 1);
+}
More information about the libc-commits
mailing list