Skip to content

Commit c777a2e

Browse files
committed
Add a type that is a pointer which is never null.
1 parent f932830 commit c777a2e

File tree

3 files changed

+237
-0
lines changed

3 files changed

+237
-0
lines changed

mem/nonnull.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "assertions/check.h"
18+
#include "marker/unsafe.h"
19+
#include "mem/addressof.h"
20+
21+
namespace sus::mem {
22+
23+
/// A pointer wrapper which holds a never-null pointer.
24+
template <class T>
25+
struct NonNull {
26+
static constexpr inline NonNull with(T& t) { return NonNull(addressof(t)); }
27+
static constexpr inline NonNull with_ptr(T* t) {
28+
check(t);
29+
return NonNull(t);
30+
}
31+
static constexpr inline NonNull with_ptr_unchecked(
32+
::sus::marker::UnsafeFnMarker, T* t) {
33+
check(t);
34+
return NonNull(*t);
35+
}
36+
37+
constexpr inline const T& as_ref() const { return *ptr_; }
38+
constexpr inline T& as_ref_mut() { return *ptr_; }
39+
40+
constexpr inline const T* as_ptr() const { return ptr_; }
41+
constexpr inline T* as_ptr_mut() { return ptr_; }
42+
43+
private:
44+
explicit constexpr inline NonNull(T* t) : ptr_(t) {}
45+
46+
T* ptr_;
47+
48+
sus_class_nonzero_field(unsafe_fn, NonNull, ptr_);
49+
};
50+
51+
} // namespace sus::mem

option/__private/storage.h

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "mem/layout.h"
18+
#include "mem/replace.h"
19+
#include "mem/take.h"
20+
#include "option/state.h"
21+
22+
namespace sus::option::__private {
23+
24+
using State::None;
25+
using State::Some;
26+
27+
template <class T,
28+
bool HasNonNullField = sus::mem::layout::nonzero_field<T>::has_field>
29+
struct Storage;
30+
31+
// TODO: Determine if we can put the State into the storage of `T`. Probably
32+
// though a user-defined trait for `T`?
33+
//
34+
// TODO: If the compiler provided an extension to get the offset of a reference
35+
// or non-null-annotated pointer inside a type, we could use that to determine a
36+
// place to "store" the liveness bit inside `T`. When we destroy `T`, we'd write
37+
// a `null` to that location, and when `T` is constructed, we know it will write
38+
// a non-`null` there. This is a generalization of what we have done for the
39+
// `T&` type. Something like `__offset_of_nonnull_field(T)`, which would be
40+
// possible to determine at compile time for a fully-defined type `T`.
41+
template <class T>
42+
struct Storage<T, false> final {
43+
constexpr ~Storage()
44+
requires(std::is_trivially_destructible_v<T>)
45+
= default;
46+
constexpr ~Storage()
47+
requires(!std::is_trivially_destructible_v<T>)
48+
{}
49+
50+
constexpr Storage(const Storage&)
51+
requires(std::is_trivially_copy_constructible_v<T>)
52+
= default;
53+
constexpr Storage& operator=(const Storage&)
54+
requires(std::is_trivially_copy_assignable_v<T>)
55+
= default;
56+
constexpr Storage(Storage&&)
57+
requires(std::is_trivially_move_constructible_v<T>)
58+
= default;
59+
constexpr Storage& operator=(Storage&&)
60+
requires(std::is_trivially_move_assignable_v<T>)
61+
= default;
62+
63+
constexpr Storage() {}
64+
constexpr Storage(const std::remove_cvref_t<T>& t) : val_(t), state_(Some) {}
65+
constexpr Storage(std::remove_cvref_t<T>& t) : val_(t), state_(Some) {}
66+
constexpr Storage(std::remove_cvref_t<T>&& t)
67+
: val_(static_cast<T&&>(t)), state_(Some) {}
68+
69+
union {
70+
T val_;
71+
};
72+
State state_ = None;
73+
74+
[[nodiscard]] constexpr inline State state() const { return state_; }
75+
76+
constexpr inline void construct_from_none(T&& t) noexcept {
77+
new (&val_) T(static_cast<T&&>(t));
78+
state_ = Some;
79+
}
80+
81+
[[nodiscard]] constexpr inline void set_some(T&& t) noexcept {
82+
if (state_ == None)
83+
construct_from_none(static_cast<T&&>(t));
84+
else
85+
::sus::mem::replace_and_discard(mref(val_), static_cast<T&&>(t));
86+
state_ = Some;
87+
}
88+
89+
[[nodiscard]] constexpr inline T replace_some(T&& t) noexcept {
90+
return ::sus::mem::replace(mref(val_), static_cast<T&&>(t));
91+
}
92+
93+
[[nodiscard]] constexpr inline T take_and_set_none() noexcept {
94+
state_ = None;
95+
return ::sus::mem::take_and_destruct(unsafe_fn, val_);
96+
}
97+
98+
constexpr inline void set_none() noexcept {
99+
state_ = None;
100+
val_.~T();
101+
}
102+
};
103+
104+
template <class T>
105+
struct Storage<T, true> final {
106+
constexpr ~Storage()
107+
requires(std::is_trivially_destructible_v<T>)
108+
= default;
109+
constexpr ~Storage()
110+
requires(!std::is_trivially_destructible_v<T>)
111+
{}
112+
113+
constexpr Storage(const Storage&)
114+
requires(std::is_trivially_copy_constructible_v<T>)
115+
= default;
116+
constexpr Storage& operator=(const Storage&)
117+
requires(std::is_trivially_copy_assignable_v<T>)
118+
= default;
119+
constexpr Storage(Storage&&)
120+
requires(std::is_trivially_move_constructible_v<T>)
121+
= default;
122+
constexpr Storage& operator=(Storage&&)
123+
requires(std::is_trivially_move_assignable_v<T>)
124+
= default;
125+
126+
constexpr Storage() {
127+
::sus::mem::layout::nonzero_field<T>::set_zero(unsafe_fn, &val_);
128+
}
129+
constexpr Storage(const T& t) : val_(t) {}
130+
constexpr Storage(T&& t) : val_(static_cast<T&&>(t)) {}
131+
132+
union {
133+
T val_;
134+
};
135+
136+
[[nodiscard]] constexpr inline State state() const noexcept {
137+
return ::sus::mem::layout::nonzero_field<T>::is_non_zero(unsafe_fn, &val_)
138+
? Some
139+
: None;
140+
}
141+
142+
inline void construct_from_none(T&& t) noexcept {
143+
new (&val_) T(static_cast<T&&>(t));
144+
}
145+
146+
[[nodiscard]] constexpr inline T take_and_set_none() noexcept {
147+
T t = take_and_destruct(unsafe_fn, mref(val_));
148+
::sus::mem::layout::nonzero_field<T>::set_zero(unsafe_fn, &val_);
149+
return t;
150+
}
151+
152+
constexpr inline void set_none() noexcept {
153+
val_.~T();
154+
::sus::mem::layout::nonzero_field<T>::set_zero(unsafe_fn, &val_);
155+
}
156+
};
157+
158+
} // namespace sus::option::__private

option/state.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
namespace sus::option {
18+
19+
/// The representation of an Option's state, which can either be #None to
20+
/// represent it has no value, or #Some for when it is holding a value.
21+
enum class State : bool {
22+
/// The Option is not holding any value.
23+
None,
24+
/// The Option is holding a value.
25+
Some,
26+
};
27+
28+
}

0 commit comments

Comments
 (0)