On Tue, Mar 11, 2025 at 10:26 PM Jonathan Wakely <[email protected]> wrote:
> LWG 4148 (approved in Wrocław, November 2024) makes it ill-formed to
> dereference a std::unique_ptr if that would return a dangling reference.
>
> That can happen with a custom pointer type and a const-qualified
> element_type, such that std::add_lvalue_reference_t<element_type> is a
> reference-to-const that could bind to a short-lived temporary.
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/unique_ptr.h (unique_ptr::operator*): Add
> static_assert to check for dangling reference, as per LWG 4148.
> * testsuite/20_util/unique_ptr/lwg4148.cc: New test.
> ---
>
> Tested x86_64-linux.
>
LGTM.
In C++26 binding reference to temporary in the return is ill-formed per
language rules,
but it's good QoI to raise these errors in eariel standards.
>
> libstdc++-v3/include/bits/unique_ptr.h | 8 +++++
> .../testsuite/20_util/unique_ptr/lwg4148.cc | 31 +++++++++++++++++++
> 2 files changed, 39 insertions(+)
> create mode 100644 libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc
>
> diff --git a/libstdc++-v3/include/bits/unique_ptr.h
> b/libstdc++-v3/include/bits/unique_ptr.h
> index 746989dfe47..6ae46a93800 100644
> --- a/libstdc++-v3/include/bits/unique_ptr.h
> +++ b/libstdc++-v3/include/bits/unique_ptr.h
> @@ -445,6 +445,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> typename add_lvalue_reference<element_type>::type
> operator*() const noexcept(noexcept(*std::declval<pointer>()))
> {
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__reference_converts_from_temporary)
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4148. unique_ptr::operator* should not allow dangling references
> + using _ResT = typename add_lvalue_reference<element_type>::type;
> + using _DerefT = decltype(*get());
> + static_assert(!__reference_converts_from_temporary(_ResT, _DerefT),
> + "operator* must not return a dangling reference");
> +#endif
> __glibcxx_assert(get() != pointer());
> return *get();
> }
> diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc
> b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc
> new file mode 100644
> index 00000000000..c70d7a60631
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc
> @@ -0,0 +1,31 @@
> +// { dg-do compile { target c++11 } }
> +
> +// LWG 4148. unique_ptr::operator* should not allow dangling references
> +
> +#include <memory>
> +
> +struct pointer
> +{
> + pointer() { }
> + pointer(std::nullptr_t) { }
> + int operator*() const { return 0; }
> + bool operator==(pointer) const { return true; }
> + bool operator==(std::nullptr_t) const { return false; }
> +#ifndef __cpp_lib_three_way_comparison
> + bool operator!=(pointer) const { return false; }
> + bool operator!=(std::nullptr_t) const { return true; }
> +#endif
> +};
> +
> +struct Deleter
> +{
> + using pointer = ::pointer;
> + void operator()(pointer) const { }
> +};
> +
> +std::unique_ptr<const int, Deleter> up;
> +int i = *up; // { dg-error "here" }
> +// { dg-error "dangling reference" "" { target *-*-* } 0 }
> +
> +// { dg-warning "returning reference to temporary" "" { target c++23_down
> } 0 }
> +// { dg-error "returning reference to temporary" "" { target c++26 } 0 }
> --
> 2.48.1
>
>