[libc-commits] [libc] [libc] Make cpp::expected::operator bool explicit (PR #208681)
Pavel Labath via libc-commits
libc-commits at lists.llvm.org
Fri Jul 10 03:01:31 PDT 2026
https://github.com/labath created https://github.com/llvm/llvm-project/pull/208681
Without explicit, expected<T, E> implicitly converts to bool in all sorts of unexpected contexts (e.g. `EXPECT_EQ(ErrorOr<int>(47), 1)`), and also deviates from std::expected in C++23.
While in there, add basic unit tests for expected and unexpected (we didn't have any tests for this utility yet), and update the file header.
Assisted by Gemini.
>From 97e62d7d7bbfbd17a5d8725bbefe661bdfa8a247 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Fri, 10 Jul 2026 09:37:09 +0000
Subject: [PATCH] [libc] Make cpp::expected::operator bool explicit
Without explicit, expected<T, E> implicitly converts to bool in all sorts of
unexpected contexts (e.g. `EXPECT_EQ(ErrorOr<int>(47), 1)`), and also deviates
from std::expected in C++23.
While in there, add basic unit tests for expected and unexpected (we didn't have
any tests for this utility yet), and update the file header.
Assisted by Gemini.
---
libc/src/__support/CPP/expected.h | 9 +-
libc/test/src/__support/CPP/CMakeLists.txt | 11 ++
libc/test/src/__support/CPP/expected_test.cpp | 110 ++++++++++++++++++
3 files changed, 128 insertions(+), 2 deletions(-)
create mode 100644 libc/test/src/__support/CPP/expected_test.cpp
diff --git a/libc/src/__support/CPP/expected.h b/libc/src/__support/CPP/expected.h
index 8a93091f0ebfb..d416422ba2b50 100644
--- a/libc/src/__support/CPP/expected.h
+++ b/libc/src/__support/CPP/expected.h
@@ -1,10 +1,15 @@
-//===-- Holds an expected or unexpected value -------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Implementation of C++23-style expected and unexpected utilities.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_EXPECTED_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_EXPECTED_H
@@ -46,7 +51,7 @@ template <class T, class E> class expected {
LIBC_INLINE constexpr const T &value() const { return exp; }
LIBC_INLINE constexpr const E &error() const { return unexp; }
- LIBC_INLINE constexpr operator bool() const { return is_expected; }
+ LIBC_INLINE constexpr explicit operator bool() const { return is_expected; }
LIBC_INLINE constexpr T &operator*() { return exp; }
LIBC_INLINE constexpr const T &operator*() const { return exp; }
diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt
index c9d405710007b..ac0e8289e64bd 100644
--- a/libc/test/src/__support/CPP/CMakeLists.txt
+++ b/libc/test/src/__support/CPP/CMakeLists.txt
@@ -53,6 +53,17 @@ add_libc_test(
libc.src.__support.CPP.cstddef
)
+add_libc_test(
+ expected_test
+ SUITE
+ libc-cpp-utils-tests
+ SRCS
+ expected_test.cpp
+ DEPENDS
+ libc.src.__support.CPP.expected
+ libc.src.__support.CPP.type_traits
+)
+
add_libc_test(
stringview_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..21ff1d6159ff4
--- /dev/null
+++ b/libc/test/src/__support/CPP/expected_test.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Unit tests for LIBC_NAMESPACE::cpp::expected and unexpected.
+///
+//===----------------------------------------------------------------------===//
+
+#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;
+
+TEST(LlvmLibcExpectedTest, Unexpected) {
+ unexpected<int> u1(42);
+ ASSERT_EQ(u1.error(), 42);
+}
+
+TEST(LlvmLibcExpectedTest, ValueConstruction) {
+ expected<int, double> e(123);
+ ASSERT_TRUE(e.has_value());
+ ASSERT_TRUE(static_cast<bool>(e));
+ ASSERT_EQ(e.value(), 123);
+ ASSERT_EQ(*e, 123);
+}
+
+TEST(LlvmLibcExpectedTest, ErrorConstruction) {
+ expected<int, int> e(unexpected(404));
+ ASSERT_FALSE(e.has_value());
+ ASSERT_FALSE(static_cast<bool>(e));
+ ASSERT_EQ(e.error(), 404);
+}
+
+TEST(LlvmLibcExpectedTest, Mutation) {
+ expected<int, int> e(10);
+ ASSERT_EQ(e.value(), 10);
+ e.value() = 20;
+ ASSERT_EQ(e.value(), 20);
+ *e = 30;
+ ASSERT_EQ(*e, 30);
+
+ expected<int, int> u(unexpected<int>(1));
+ ASSERT_EQ(u.error(), 1);
+ u.error() = 2;
+ ASSERT_EQ(u.error(), 2);
+}
+
+TEST(LlvmLibcExpectedTest, ConstAccess) {
+ const expected<int, int> CE(100);
+ ASSERT_TRUE(CE.has_value());
+ ASSERT_TRUE(static_cast<bool>(CE));
+ ASSERT_EQ(CE.value(), 100);
+ ASSERT_EQ(*CE, 100);
+
+ const expected<int, int> CU(unexpected(500));
+ ASSERT_FALSE(CU.has_value());
+ ASSERT_FALSE(static_cast<bool>(CU));
+ ASSERT_EQ(CU.error(), 500);
+}
+
+struct Foo {
+ int x;
+ int get_x() const { return x; }
+ void set_x(int new_x) { x = new_x; }
+};
+
+TEST(LlvmLibcExpectedTest, ArrowOperator) {
+ expected<Foo, int> e(Foo{42});
+ ASSERT_TRUE(e.has_value());
+ ASSERT_EQ(e->x, 42);
+ ASSERT_EQ(e->get_x(), 42);
+ e->set_x(99);
+ ASSERT_EQ(e->x, 99);
+
+ const expected<Foo, int> CE(Foo{123});
+ ASSERT_EQ(CE->x, 123);
+ ASSERT_EQ(CE->get_x(), 123);
+}
+
+constexpr bool test_constexpr_value() {
+ expected<int, int> e(42);
+ if (!e.has_value() || !static_cast<bool>(e))
+ return false;
+ if (e.value() != 42 || *e != 42)
+ return false;
+ return true;
+}
+
+static_assert(test_constexpr_value(), "expected constexpr value check failed");
+
+constexpr bool test_constexpr_error() {
+ expected<int, int> e(unexpected<int>(99));
+ if (e.has_value() || static_cast<bool>(e))
+ return false;
+ if (e.error() != 99)
+ return false;
+ return true;
+}
+
+static_assert(test_constexpr_error(), "expected constexpr error check failed");
+
+static_assert(!LIBC_NAMESPACE::cpp::is_convertible_v<expected<int, long>, bool>,
+ "only explicit conversions allowed");
More information about the libc-commits
mailing list