Skip to content

Commit 9d0ae90

Browse files
committed
Support hiding the Option<T> flag inside the T object.
The Fn, FnMut, and FnOnce types indicate that their enum state is never 0, and the Option<Fn> is thus the same size as a Fn. We add a NonNull<T> which is a never-null T*, and which indicates that its pointer field is never zero as a result. Then we implement Option<T&> on top of NonNull<T>.
1 parent c777a2e commit 9d0ae90

File tree

10 files changed

+269
-248
lines changed

10 files changed

+269
-248
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ add_library(subspace STATIC
5555
"mem/forward.h"
5656
"mem/move.h"
5757
"mem/mref.h"
58+
"mem/nonnull.h"
5859
"mem/pass.h"
5960
"mem/relocate.h"
6061
"mem/replace.h"
@@ -75,7 +76,9 @@ add_library(subspace STATIC
7576
"num/num_concepts.h"
7677
"num/unsigned_integer.h"
7778
"option/__private/is_option_type.h"
79+
"option/__private/storage.h"
7880
"option/option.h"
81+
"option/state.h"
7982
"result/__private/is_result_type.h"
8083
"result/result.h"
8184
"tuple/__private/storage.h"

fn/fn_defn.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
#pragma once
1616

1717
#include "fn/callable.h"
18-
#include "mem/relocate.h"
1918
#include "mem/forward.h"
19+
#include "mem/layout.h"
20+
#include "mem/relocate.h"
21+
#include "num/types.h"
2022

2123
namespace sus::fn {
2224

@@ -50,9 +52,9 @@ enum StorageConstructionFnType { StorageConstructionFn };
5052
/// heap-allocated storage.
5153
enum FnType {
5254
/// Holds a function pointer or captureless lambda.
53-
FnPointer,
55+
FnPointer = 1,
5456
/// Holds the type-erased output of sus_bind() in a heap allocation.
55-
Storage,
57+
Storage = 2,
5658
};
5759

5860
} // namespace __private
@@ -208,6 +210,7 @@ class [[sus_trivial_abi]] FnOnce<R(CallArgs...)> {
208210

209211
private:
210212
sus_class_trivial_relocatable(unsafe_fn);
213+
sus_class_nonzero_field(unsafe_fn, FnOnce, type_);
211214
};
212215

213216
/// A closure that erases the type of the internal callable object (lambda). A

fn/fn_unittest.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "mem/forward.h"
2121
#include "mem/move.h"
2222
#include "mem/replace.h"
23+
#include "option/option.h"
2324
#include "third_party/googletest/googletest/include/gtest/gtest.h"
2425

2526
namespace {
@@ -67,6 +68,13 @@ SubClass* s_s_function(SubClass* b) { return b; }
6768

6869
// clang-format off
6970

71+
// Fn types all have a non-zero field.
72+
static_assert(sus::mem::layout::nonzero_field<FnOnce<void()>>::has_field);
73+
static_assert(sus::mem::layout::nonzero_field<FnMut<void()>>::has_field);
74+
static_assert(sus::mem::layout::nonzero_field<Fn<void()>>::has_field);
75+
// Which allows them to not require a flag in Option.
76+
static_assert(sizeof(sus::Option<FnOnce<void()>>) == sizeof(FnOnce<void()>));
77+
7078
// Closures can not be copied, as their storage is uniquely owned.
7179
static_assert(!std::is_copy_constructible_v<FnOnce<void()>>);
7280
static_assert(!std::is_copy_assignable_v<FnOnce<void()>>);

mem/layout.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ struct layout_nonzero_tag final {
3232
return true;
3333
};
3434

35-
static constexpr bool is_non_zero(const T* t) {
35+
static constexpr bool is_non_zero(const T* t)
36+
{
3637
return t->SusUnsafeNonZeroIsNonZero();
3738
};
38-
static constexpr void set_zero(T* t) { t->SusUnsafeNonZeroSetZero(); };
39+
static constexpr void set_zero(T* t)
40+
{
41+
t->SusUnsafeNonZeroSetZero();
42+
};
3943
};
4044

4145
} // namespace __private
@@ -46,10 +50,14 @@ struct nonzero_field {
4650
__private::layout_nonzero_tag<T>::has_field(0);
4751

4852
static constexpr bool is_non_zero(::sus::marker::UnsafeFnMarker,
49-
const T* t) noexcept {
53+
const T* t) noexcept
54+
requires(has_field)
55+
{
5056
return __private::layout_nonzero_tag<T>::is_non_zero(t);
5157
}
52-
static constexpr void set_zero(::sus::marker::UnsafeFnMarker, T* t) noexcept {
58+
static constexpr void set_zero(::sus::marker::UnsafeFnMarker, T* t) noexcept
59+
requires(has_field)
60+
{
5361
__private::layout_nonzero_tag<T>::set_zero(t);
5462
}
5563
};

mem/take.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,13 @@ constexpr T take_and_destruct(::sus::marker::UnsafeFnMarker,
4646
return taken;
4747
}
4848

49+
template <class T>
50+
requires std::is_move_constructible_v<T>
51+
constexpr T take_and_destruct(::sus::marker::UnsafeFnMarker,
52+
T& t) noexcept {
53+
T taken(static_cast<T&&>(t));
54+
t.~T();
55+
return taken;
56+
}
57+
4958
} // namespace sus::mem

option/__private/storage.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ struct Storage<T, false> final {
7878
state_ = Some;
7979
}
8080

81-
[[nodiscard]] constexpr inline void set_some(T&& t) noexcept {
81+
constexpr inline void set_some(T&& t) noexcept {
8282
if (state_ == None)
8383
construct_from_none(static_cast<T&&>(t));
8484
else
@@ -143,6 +143,17 @@ struct Storage<T, true> final {
143143
new (&val_) T(static_cast<T&&>(t));
144144
}
145145

146+
constexpr inline void set_some(T&& t) noexcept {
147+
if (state() == None)
148+
construct_from_none(static_cast<T&&>(t));
149+
else
150+
::sus::mem::replace_and_discard(mref(val_), static_cast<T&&>(t));
151+
}
152+
153+
[[nodiscard]] constexpr inline T replace_some(T&& t) noexcept {
154+
return ::sus::mem::replace(mref(val_), static_cast<T&&>(t));
155+
}
156+
146157
[[nodiscard]] constexpr inline T take_and_set_none() noexcept {
147158
T t = take_and_destruct(unsafe_fn, mref(val_));
148159
::sus::mem::layout::nonzero_field<T>::set_zero(unsafe_fn, &val_);

0 commit comments

Comments
 (0)