Skip to content

Commit 53ae8e5

Browse files
committed
Avoid lifetimebound for 'this'
Unless the return is a reference to a T that is _always_ owned by the class, it causes false positives. We already address avoiding returning references to temporaries through deleting operator&& in this case.
1 parent dd060bd commit 53ae8e5

File tree

4 files changed

+26
-27
lines changed

4 files changed

+26
-27
lines changed

PRINCIPLES.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,23 @@ This library is an experiment and not intended for use. See the
103103
lvalue-reference-qualified (with `&` or `const&`). If they are
104104
`const&`-qualified, then `=delete` the `&&` override to avoid rvalues
105105
returning references to what they own.
106+
* If the inner type is _always_ owned, the method can be annotated
107+
with `sus_lifetimebound`, but avoid it when the inner type may be a
108+
pointer/reference. We rely on deleting the `&&` override to avoid
109+
references to temporaries.
106110
* When the inner type is a **single** template variable, disallow const
107111
on the inner type. The const on the Owning type will apply transitively
108112
to the owned inner type.
109113
* For Reference types:
110-
* Methods that return a `const&` are `const&` qualified without deleting
114+
* Methods that return a `const&` are `const&` qualified _without_ deleting
111115
the `&&` override. Mutable methods, including those that return a `&`
112116
are unqualified or `&&-qualified` (non-const, but not `&`-qualified in
113117
order to allow rvalues).
118+
* Avoid `sus_lifetimebound` on methods that return references, as it
119+
ties the lifetime to the method's class, but the class also only
120+
holds a reference.
121+
* Annotate constructor and method parameters with `sus_lifetimebound`
122+
when the reference will be stored in the class.
114123
* When the inner type is a template variable, allow const on the inner
115124
type. The const on the Owning type is unable to be transferred to the
116125
inner type, i.e. a const `ref<T>` can be copied or moved to a non-const

subspace/choice/choice.h

+6-8
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ class Choice<__private::TypeList<Ts...>, Tags...> final {
352352
/// passed as the template parameter.
353353
template <TagsType V>
354354
requires(__private::ValueIsNotVoid<StorageTypeOfTag<V>>)
355-
constexpr inline decltype(auto) as() const& noexcept sus_lifetimebound {
355+
constexpr inline decltype(auto) as() const& noexcept {
356356
::sus::check(index_ == index<V>);
357357
return __private::find_choice_storage<index<V>>(storage_).as();
358358
}
@@ -376,7 +376,7 @@ class Choice<__private::TypeList<Ts...>, Tags...> final {
376376
/// passed as the template parameter.
377377
template <TagsType V>
378378
requires(__private::ValueIsNotVoid<StorageTypeOfTag<V>>)
379-
constexpr inline decltype(auto) as_mut() & noexcept sus_lifetimebound {
379+
constexpr inline decltype(auto) as_mut() & noexcept {
380380
::sus::check(index_ == index<V>);
381381
return __private::find_choice_storage_mut<index<V>>(storage_).as_mut();
382382
}
@@ -411,8 +411,7 @@ class Choice<__private::TypeList<Ts...>, Tags...> final {
411411
/// active member.
412412
template <TagsType V>
413413
requires(__private::ValueIsNotVoid<StorageTypeOfTag<V>>)
414-
constexpr inline Option<AccessTypeOfTagConst<V>> get() const& noexcept
415-
sus_lifetimebound {
414+
constexpr inline Option<AccessTypeOfTagConst<V>> get() const& noexcept {
416415
if (index_ != index<V>) return ::sus::none();
417416
return ::sus::some(__private::find_choice_storage<index<V>>(storage_).as());
418417
}
@@ -432,8 +431,7 @@ class Choice<__private::TypeList<Ts...>, Tags...> final {
432431
/// active member.
433432
template <TagsType V>
434433
requires(__private::ValueIsNotVoid<StorageTypeOfTag<V>>)
435-
constexpr inline Option<AccessTypeOfTagMut<V>> get_mut() & noexcept
436-
sus_lifetimebound {
434+
constexpr inline Option<AccessTypeOfTagMut<V>> get_mut() & noexcept {
437435
if (index_ != index<V>) return ::sus::none();
438436
return ::sus::some(
439437
__private::find_choice_storage_mut<index<V>>(storage_).as_mut());
@@ -469,7 +467,7 @@ class Choice<__private::TypeList<Ts...>, Tags...> final {
469467
template <TagsType V>
470468
requires(__private::ValueIsNotVoid<StorageTypeOfTag<V>>)
471469
constexpr inline decltype(auto) get_unchecked(
472-
::sus::marker::UnsafeFnMarker) const& noexcept sus_lifetimebound {
470+
::sus::marker::UnsafeFnMarker) const& noexcept {
473471
return __private::find_choice_storage<index<V>>(storage_).as();
474472
}
475473
// If the storage is a value type, it can't be accessed by reference in an
@@ -494,7 +492,7 @@ class Choice<__private::TypeList<Ts...>, Tags...> final {
494492
template <TagsType V>
495493
requires(__private::ValueIsNotVoid<StorageTypeOfTag<V>>)
496494
constexpr inline decltype(auto) get_unchecked_mut(
497-
::sus::marker::UnsafeFnMarker) & noexcept sus_lifetimebound {
495+
::sus::marker::UnsafeFnMarker) & noexcept {
498496
return __private::find_choice_storage_mut<index<V>>(storage_).as_mut();
499497
}
500498

subspace/option/option.h

+8-16
Original file line numberDiff line numberDiff line change
@@ -385,14 +385,12 @@ class Option final {
385385
/// reuse, making variable names bad.
386386
/// * It's expected due to std::optional and general container-of-one things
387387
/// to provide access through operator* and operator->.
388-
constexpr const std::remove_reference_t<T>& operator*() const& noexcept
389-
sus_lifetimebound {
388+
constexpr const std::remove_reference_t<T>& operator*() const& noexcept {
390389
::sus::check(t_.state() == Some);
391390
return t_.val();
392391
}
393392
constexpr const std::remove_reference_t<T>* operator*() && noexcept = delete;
394-
constexpr std::remove_reference_t<T>& operator*() & noexcept
395-
sus_lifetimebound {
393+
constexpr std::remove_reference_t<T>& operator*() & noexcept {
396394
::sus::check(t_.state() == Some);
397395
return t_.val_mut();
398396
}
@@ -422,15 +420,13 @@ class Option final {
422420
/// reuse, making variable names bad.
423421
/// * It's expected due to std::optional and general container-of-one things
424422
/// to provide access through operator* and operator->.
425-
constexpr const std::remove_reference_t<T>* operator->() const& noexcept
426-
sus_lifetimebound {
423+
constexpr const std::remove_reference_t<T>* operator->() const& noexcept {
427424
::sus::check(t_.state() == Some);
428425
return ::sus::mem::addressof(
429426
static_cast<const std::remove_reference_t<T>&>(t_.val()));
430427
}
431428
constexpr const std::remove_reference_t<T>* operator->() && noexcept;
432-
constexpr std::remove_reference_t<T>* operator->() & noexcept
433-
sus_lifetimebound {
429+
constexpr std::remove_reference_t<T>* operator->() & noexcept {
434430
::sus::check(t_.state() == Some);
435431
return ::sus::mem::addressof(
436432
static_cast<std::remove_reference_t<T>&>(t_.val_mut()));
@@ -839,8 +835,7 @@ class Option final {
839835

840836
/// Returns an Option<const T&> from this Option<T>, that either holds #None
841837
/// or a reference to the value in this Option.
842-
constexpr Option<const std::remove_reference_t<T>&> as_ref() const& noexcept
843-
sus_lifetimebound {
838+
constexpr Option<const std::remove_reference_t<T>&> as_ref() const& noexcept {
844839
if (t_.state() == None)
845840
return Option<const std::remove_reference_t<T>&>::none();
846841
else
@@ -860,7 +855,7 @@ class Option final {
860855

861856
/// Returns an Option<T&> from this Option<T>, that either holds #None or a
862857
/// reference to the value in this Option.
863-
constexpr Option<T&> as_mut() & noexcept sus_lifetimebound {
858+
constexpr Option<T&> as_mut() & noexcept {
864859
if (t_.state() == None)
865860
return Option<T&>::none();
866861
else
@@ -878,8 +873,7 @@ class Option final {
878873
return Option<T&>(t_.take_and_set_none());
879874
}
880875

881-
constexpr Once<const std::remove_reference_t<T>&> iter() const& noexcept
882-
sus_lifetimebound {
876+
constexpr Once<const std::remove_reference_t<T>&> iter() const& noexcept {
883877
return Once<const std::remove_reference_t<T>&>::with(as_ref());
884878
}
885879
constexpr Once<const std::remove_reference_t<T>&> iter() && noexcept
@@ -889,9 +883,7 @@ class Option final {
889883
::sus::move(*this).as_ref());
890884
}
891885

892-
constexpr Once<T&> iter_mut() & noexcept sus_lifetimebound {
893-
return Once<T&>::with(as_mut());
894-
}
886+
constexpr Once<T&> iter_mut() & noexcept { return Once<T&>::with(as_mut()); }
895887
constexpr Once<T&> iter_mut() && noexcept
896888
requires(std::is_reference_v<T>)
897889
{

subspace/result/result.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ class [[nodiscard]] Result final {
539539
mref(storage_.err_));
540540
}
541541

542-
constexpr Once<const T&> iter() const& noexcept sus_lifetimebound {
542+
constexpr Once<const T&> iter() const& noexcept {
543543
::sus::check(state_ != __private::ResultState::IsMoved);
544544
if (state_ == __private::ResultState::IsOk)
545545
return Once<const T&>::with(Option<const T&>::some(storage_.ok_));
@@ -548,7 +548,7 @@ class [[nodiscard]] Result final {
548548
}
549549
Once<const T&> iter() const&& = delete;
550550

551-
constexpr Once<T&> iter_mut() & noexcept sus_lifetimebound {
551+
constexpr Once<T&> iter_mut() & noexcept {
552552
::sus::check(state_ != __private::ResultState::IsMoved);
553553
if (state_ == __private::ResultState::IsOk)
554554
return Once<T&>::with(Option<T&>::some(mref(storage_.ok_)));

0 commit comments

Comments
 (0)