AK: Don't implicitly convert Optional<T&> to Optional<T>

C++ will jovially select the implicit conversion operator, even if it's
complete bogus, such as for unknown-size types or non-destructible
types. Therefore, all such conversions (which incur a copy) must
(unfortunately) be explicit so that non-copyable types continue to work.

NOTE: We make an exception for trivially copyable types, since they
are, well, trivially copyable.

Co-authored-by: kleines Filmröllchen <filmroellchen@serenityos.org>
This commit is contained in:
Jonne Ransijn 2024-11-25 13:23:31 +01:00 committed by Ali Mohammad Pur
commit d7596a0a61
Notes: github-actions[bot] 2024-12-04 00:59:23 +00:00
22 changed files with 118 additions and 40 deletions

View file

@ -13,6 +13,39 @@
#include <AK/String.h>
#include <AK/Vector.h>
class NonCopyable {
AK_MAKE_NONCOPYABLE(NonCopyable);
AK_MAKE_DEFAULT_MOVABLE(NonCopyable);
public:
NonCopyable() { }
~NonCopyable() = default;
int x { 13 };
};
class NonTriviallyCopyable {
AK_MAKE_DEFAULT_COPYABLE(NonTriviallyCopyable);
AK_MAKE_DEFAULT_MOVABLE(NonTriviallyCopyable);
public:
NonTriviallyCopyable() = default;
~NonTriviallyCopyable() = default;
ByteString x { "13" };
};
class TriviallyCopyable {
AK_MAKE_DEFAULT_COPYABLE(TriviallyCopyable);
AK_MAKE_DEFAULT_MOVABLE(TriviallyCopyable);
public:
TriviallyCopyable() = default;
~TriviallyCopyable() = default;
int x { 13 };
};
TEST_CASE(basic_optional)
{
Optional<int> x;
@ -39,23 +72,12 @@ TEST_CASE(move_optional)
TEST_CASE(optional_rvalue_ref_qualified_getters)
{
struct DontCopyMe {
DontCopyMe() { }
~DontCopyMe() = default;
DontCopyMe(DontCopyMe&&) = default;
DontCopyMe& operator=(DontCopyMe&&) = default;
DontCopyMe(DontCopyMe const&) = delete;
DontCopyMe& operator=(DontCopyMe const&) = delete;
int x { 13 };
};
auto make_an_optional = []() -> Optional<DontCopyMe> {
return DontCopyMe {};
auto make_an_optional = []() -> Optional<NonCopyable> {
return NonCopyable {};
};
EXPECT_EQ(make_an_optional().value().x, 13);
EXPECT_EQ(make_an_optional().value_or(DontCopyMe {}).x, 13);
EXPECT_EQ(make_an_optional().value_or(NonCopyable {}).x, 13);
}
TEST_CASE(optional_leak_1)
@ -299,6 +321,47 @@ static_assert(CheckAssignments<Optional<int const&>, int const&>::allowed);
static_assert(CheckAssignments<Optional<int const&>, int&&>::allowed); // Lifetime extension
static_assert(CheckAssignments<Optional<int const&>, int const&&>::allowed); // Lifetime extension
static_assert(CheckAssignments<Optional<NonTriviallyCopyable const&>, NonTriviallyCopyable&>::allowed);
static_assert(CheckAssignments<Optional<NonTriviallyCopyable const&>, NonTriviallyCopyable const&>::allowed);
static_assert(CheckAssignments<Optional<NonTriviallyCopyable const&>, NonTriviallyCopyable&&>::allowed); // Lifetime extension
static_assert(CheckAssignments<Optional<NonTriviallyCopyable const&>, NonTriviallyCopyable const&&>::allowed); // Lifetime extension
static_assert(CheckAssignments<Optional<TriviallyCopyable const&>, TriviallyCopyable>::allowed);
static_assert(CheckAssignments<Optional<TriviallyCopyable const&>, TriviallyCopyable const&>::allowed);
static_assert(CheckAssignments<Optional<TriviallyCopyable const&>, Optional<TriviallyCopyable>>::allowed);
static_assert(CheckAssignments<Optional<TriviallyCopyable const&>, Optional<TriviallyCopyable const&>>::allowed);
static_assert(CheckAssignments<Optional<TriviallyCopyable>, Optional<TriviallyCopyable const&>>::allowed);
static_assert(CheckAssignments<Optional<NonTriviallyCopyable const&>, NonTriviallyCopyable>::allowed);
static_assert(CheckAssignments<Optional<NonTriviallyCopyable const&>, NonTriviallyCopyable const&>::allowed);
static_assert(CheckAssignments<Optional<NonTriviallyCopyable const&>, Optional<NonTriviallyCopyable>>::allowed);
static_assert(CheckAssignments<Optional<NonTriviallyCopyable const&>, Optional<NonTriviallyCopyable const&>>::allowed);
static_assert(!CheckAssignments<Optional<NonTriviallyCopyable>, Optional<NonTriviallyCopyable const&>>::allowed);
TEST_CASE(nontrivially_copyable_assignment)
{
{
TriviallyCopyable x {};
Optional<TriviallyCopyable const&> y = x;
Optional<TriviallyCopyable> z = y; // Can copy implicitly
EXPECT_EQ(z->x, 13);
}
{
NonTriviallyCopyable x {};
Optional<NonTriviallyCopyable const&> y = x;
Optional<NonTriviallyCopyable> z = y.copy(); // Have to copy explicitly
EXPECT_EQ(z->x, "13");
}
{
NonTriviallyCopyable x {};
Optional<NonTriviallyCopyable const&> y = x;
Optional<NonTriviallyCopyable> z = Optional<NonTriviallyCopyable>(y); // Explicit copy constructor is still defined
EXPECT_EQ(z->x, "13");
}
}
TEST_CASE(string_specialization)
{
EXPECT_EQ(sizeof(Optional<String>), sizeof(String));