[libcxx-commits] [libcxx] [libcxx] implement LWG4148: unique_ptr::operator* should not allow dangling references (PR #128213)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Sat Aug 8 06:35:49 PDT 2026


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/128213

>From 4d4b9748fa8f91114ba74defa01b0b329a0758e2 Mon Sep 17 00:00:00 2001
From: Mohamed Atef <mohamedatef1698 at gmail.com>
Date: Fri, 21 Feb 2025 20:13:33 +0200
Subject: [PATCH 1/3] [libcxx] implement LWG4148: unique_ptr::operator* should
 not allow dangling references

---
 libcxx/docs/Status/Cxx2cIssues.csv            |  2 +-
 libcxx/include/__memory/unique_ptr.h          |  8 ++++++
 .../test/libcxx/memory/unique_ptr.verify.cpp  | 27 +++++++++++++++++++
 3 files changed, 36 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/libcxx/memory/unique_ptr.verify.cpp

diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index d7f75380cb93e..24672ca88e23f 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -103,7 +103,7 @@
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
 "`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
-"`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
+"`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","|Complete|","22","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
 "`LWG4154 <https://wg21.link/LWG4154>`__","The Mandates for ``std::packaged_task``'s constructor from a callable entity should consider decaying","2024-11 (Wrocław)","","","`#118364 <https://github.com/llvm/llvm-project/issues/118364>`__",""
 "`LWG4157 <https://wg21.link/LWG4157>`__","The resolution of LWG3465 was damaged by P2167R3","2024-11 (Wrocław)","|Complete|","20","`#118365 <https://github.com/llvm/llvm-project/issues/118365>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 6a4ec0a466ba7..874f9220babe7 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -260,6 +260,14 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __add_lvalue_reference_t<_Tp> operator*() const
       _NOEXCEPT_(_NOEXCEPT_(*std::declval<pointer>())) {
+    // TODO(LLVM-21): Remove this workaround
+#if __has_builtin(__reference_converts_from_temporary) ||                                                              \
+    (defined(_LIBCPP_CLANG_VER) &&                                                                                     \
+     ((!defined(__ANDROID__) && _LIBCPP_CLANG_VER >= 1901) || (defined(__ANDROID__) && _LIBCPP_CLANG_VER >= 2000)))
+    static_assert(
+        !__reference_converts_from_temporary(__add_lvalue_reference_t<_Tp>, decltype(*std::declval<pointer>())),
+        "Reference type _Tp must not convert from a temporary object");
+#endif
     return *__ptr_;
   }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer operator->() const _NOEXCEPT { return __ptr_; }
diff --git a/libcxx/test/libcxx/memory/unique_ptr.verify.cpp b/libcxx/test/libcxx/memory/unique_ptr.verify.cpp
new file mode 100644
index 0000000000000..6bd3d8818431f
--- /dev/null
+++ b/libcxx/test/libcxx/memory/unique_ptr.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++11
+
+// <memory>
+
+#include <memory>
+
+struct deleter {
+  using pointer = long*;
+  void operator()(pointer) const {}
+};
+
+int main(int, char**) {
+  long l = 0;
+  std::unique_ptr<const int, deleter> p(&l);
+  // expected-error-re@*:* {{static assertion failed{{.*}}'!__reference_converts_from_temporary(const int &, long &)': Reference type _Tp must not convert from a temporary object}}
+  // expected-error@*:*{{returning reference to local temporary object}}
+  int i = *p; // expected-note {{requested here}}
+  return 0;
+}

>From 11b26a37be3e23ab8da418412c1c6de876556f58 Mon Sep 17 00:00:00 2001
From: Mohamed Atef <mohamedatef1698 at gmail.com>
Date: Wed, 6 May 2026 01:29:54 +0300
Subject: [PATCH 2/3] Address comments

---
 libcxx/include/__memory/unique_ptr.h                      | 8 ++------
 .../__type_traits/reference_converts_from_temporary.h     | 3 +++
 libcxx/test/libcxx/memory/unique_ptr.verify.cpp           | 5 ++---
 3 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 874f9220babe7..32441b9828013 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -42,6 +42,7 @@
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/reference_converts_from_temporary.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
@@ -260,14 +261,9 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __add_lvalue_reference_t<_Tp> operator*() const
       _NOEXCEPT_(_NOEXCEPT_(*std::declval<pointer>())) {
-    // TODO(LLVM-21): Remove this workaround
-#if __has_builtin(__reference_converts_from_temporary) ||                                                              \
-    (defined(_LIBCPP_CLANG_VER) &&                                                                                     \
-     ((!defined(__ANDROID__) && _LIBCPP_CLANG_VER >= 1901) || (defined(__ANDROID__) && _LIBCPP_CLANG_VER >= 2000)))
     static_assert(
-        !__reference_converts_from_temporary(__add_lvalue_reference_t<_Tp>, decltype(*std::declval<pointer>())),
+        !__reference_converts_from_temporary_v<__add_lvalue_reference_t<_Tp>, decltype(*std::declval<pointer>())>,
         "Reference type _Tp must not convert from a temporary object");
-#endif
     return *__ptr_;
   }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer operator->() const _NOEXCEPT { return __ptr_; }
diff --git a/libcxx/include/__type_traits/reference_converts_from_temporary.h b/libcxx/include/__type_traits/reference_converts_from_temporary.h
index 9c51225e53b8e..bbc62ee40c930 100644
--- a/libcxx/include/__type_traits/reference_converts_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_converts_from_temporary.h
@@ -30,6 +30,9 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_converts_from_tempora
 
 #endif
 
+template <class _Tp, class _Up>
+inline const bool __reference_converts_from_temporary_v = __reference_converts_from_temporary(_Tp, _Up);
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_REFERENCE_CONVERTS_FROM_TEMPORARY_H
diff --git a/libcxx/test/libcxx/memory/unique_ptr.verify.cpp b/libcxx/test/libcxx/memory/unique_ptr.verify.cpp
index 6bd3d8818431f..e9c36a643e91f 100644
--- a/libcxx/test/libcxx/memory/unique_ptr.verify.cpp
+++ b/libcxx/test/libcxx/memory/unique_ptr.verify.cpp
@@ -17,11 +17,10 @@ struct deleter {
   void operator()(pointer) const {}
 };
 
-int main(int, char**) {
+void test() {
   long l = 0;
   std::unique_ptr<const int, deleter> p(&l);
   // expected-error-re@*:* {{static assertion failed{{.*}}'!__reference_converts_from_temporary(const int &, long &)': Reference type _Tp must not convert from a temporary object}}
   // expected-error@*:*{{returning reference to local temporary object}}
-  int i = *p; // expected-note {{requested here}}
-  return 0;
+  std::ignore = *p; // expected-note {{requested here}}
 }

>From 59f426b12fe0fb704df085f852cbc43c1f2b5416 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Sat, 8 Aug 2026 16:35:40 +0300
Subject: [PATCH 3/3] Apply suggestions from code review

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/docs/Status/Cxx2cIssues.csv              | 2 +-
 libcxx/include/__memory/unique_ptr.h            | 2 +-
 libcxx/test/libcxx/memory/unique_ptr.verify.cpp | 6 +++---
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 24672ca88e23f..0cb4a7a640133 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -103,7 +103,7 @@
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
 "`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
-"`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","|Complete|","22","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
+"`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","|Complete|","24","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
 "`LWG4154 <https://wg21.link/LWG4154>`__","The Mandates for ``std::packaged_task``'s constructor from a callable entity should consider decaying","2024-11 (Wrocław)","","","`#118364 <https://github.com/llvm/llvm-project/issues/118364>`__",""
 "`LWG4157 <https://wg21.link/LWG4157>`__","The resolution of LWG3465 was damaged by P2167R3","2024-11 (Wrocław)","|Complete|","20","`#118365 <https://github.com/llvm/llvm-project/issues/118365>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 32441b9828013..1b24249287c4f 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -263,7 +263,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
       _NOEXCEPT_(_NOEXCEPT_(*std::declval<pointer>())) {
     static_assert(
         !__reference_converts_from_temporary_v<__add_lvalue_reference_t<_Tp>, decltype(*std::declval<pointer>())>,
-        "Reference type _Tp must not convert from a temporary object");
+        "the returned reference must not bind to a temporary object");
     return *__ptr_;
   }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer operator->() const _NOEXCEPT { return __ptr_; }
diff --git a/libcxx/test/libcxx/memory/unique_ptr.verify.cpp b/libcxx/test/libcxx/memory/unique_ptr.verify.cpp
index e9c36a643e91f..7f6541c101ddd 100644
--- a/libcxx/test/libcxx/memory/unique_ptr.verify.cpp
+++ b/libcxx/test/libcxx/memory/unique_ptr.verify.cpp
@@ -20,7 +20,7 @@ struct deleter {
 void test() {
   long l = 0;
   std::unique_ptr<const int, deleter> p(&l);
-  // expected-error-re@*:* {{static assertion failed{{.*}}'!__reference_converts_from_temporary(const int &, long &)': Reference type _Tp must not convert from a temporary object}}
-  // expected-error@*:*{{returning reference to local temporary object}}
-  std::ignore = *p; // expected-note {{requested here}}
+// expected-error-re@*:* {{static assertion failed{{.*}}the returned reference must not bind to a temporary object}}
+  // expected-error@*:* 0-1{{returning reference to local temporary object}}
+  (void)*p; // expected-note {{requested here}}
 }



More information about the libcxx-commits mailing list