[libc] Make cpp::expected::operator bool explicit (#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.
This commit is contained in:
parent
fd17f88189
commit
11112ed9b7
3 changed files with 128 additions and 2 deletions
|
|
@ -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 @@ public:
|
|||
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; }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
110
libc/test/src/__support/CPP/expected_test.cpp
Normal file
110
libc/test/src/__support/CPP/expected_test.cpp
Normal file
|
|
@ -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");
|
||||
Loading…
Reference in a new issue