On Wed, 12 Mar 2025 at 07:38, Tomasz Kaminski <tkami...@redhat.com> wrote: > > > > On Tue, Mar 11, 2025 at 10:26 PM Jonathan Wakely <jwak...@redhat.com> 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.
Yes, the new test expects a -Wreturn-local-addr warning for <= C++23 and an error for C++26, but I think having the static_assert consistently is good. Especially because the C++26 error can be disabled by -Wno-return-local-addr With the static_assert, unique_ptr::operator* will still be ill-formed even if the required compiler diagnostic has been disabled. >> >> >> 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 >>