[libcxx-commits] [libcxx] dadbe88 - [libc++] Fix std::to_address(array).

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 7 10:56:44 PDT 2021


Author: Arthur O'Dwyer
Date: 2021-09-07T13:56:25-04:00
New Revision: dadbe88a1387e7b1fe4417fa4b64234ffc83c878

URL: https://github.com/llvm/llvm-project/commit/dadbe88a1387e7b1fe4417fa4b64234ffc83c878
DIFF: https://github.com/llvm/llvm-project/commit/dadbe88a1387e7b1fe4417fa4b64234ffc83c878.diff

LOG: [libc++] Fix std::to_address(array).

There were basically two bugs here:

When C++20 `to_address` is called on `int arr[10]`, then `const _Ptr&` becomes
a reference to a const array, and then we dispatch to `__to_address<const int(&)[10]>`,
which, oops, gives us a `const int*` result instead of an `int*` result.
Solution: We need to provide the two standard-specified overloads of
`std::to_address` in exactly the same way that we provide two overloads
of `__to_address`.

When `__to_address` is called on a pointer type, `__to_address(const _Ptr&)`
is disabled so we successfully avoid trying to instantiate pointer_traits of
that pointer type. But when it's called on an array type, it's not disabled
for array types, so we go ahead and instantiate pointer_traits<int[10]>,
which goes boom. Solution: We need to disable `__to_address(const _Ptr&)`
for both pointer and array types. Also disable it for function types,
so that they get the nice error message; and put a test on it.

Differential Revision: https://reviews.llvm.org/D109331

Added: 
    libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_on_funcptr.verify.cpp
    libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_on_function.verify.cpp
    libcxx/test/std/utilities/memory/pointer.conversion/to_address_on_funcptr.verify.cpp
    libcxx/test/std/utilities/memory/pointer.conversion/to_address_on_function.verify.cpp

Modified: 
    libcxx/include/__memory/pointer_traits.h
    libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address.pass.cpp
    libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index 9e9a7a38abc41..c1a4975232b07 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -173,7 +173,9 @@ _Tp* __to_address(_Tp* __p) _NOEXCEPT {
 }
 
 // enable_if is needed here to avoid instantiating checks for fancy pointers on raw pointers
-template <class _Pointer, class = _EnableIf<!is_pointer<_Pointer>::value> >
+template <class _Pointer, class = _EnableIf<
+    !is_pointer<_Pointer>::value && !is_array<_Pointer>::value && !is_function<_Pointer>::value
+> >
 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
 typename decay<decltype(__to_address_helper<_Pointer>::__call(declval<const _Pointer&>()))>::type
 __to_address(const _Pointer& __p) _NOEXCEPT {
@@ -199,6 +201,12 @@ struct __to_address_helper<_Pointer, decltype((void)pointer_traits<_Pointer>::to
 };
 
 #if _LIBCPP_STD_VER > 17
+template <class _Tp>
+inline _LIBCPP_INLINE_VISIBILITY constexpr
+auto to_address(_Tp *__p) noexcept {
+    return _VSTD::__to_address(__p);
+}
+
 template <class _Pointer>
 inline _LIBCPP_INLINE_VISIBILITY constexpr
 auto to_address(const _Pointer& __p) noexcept {

diff  --git a/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address.pass.cpp b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address.pass.cpp
index edc51c3d7a5dc..bd2faa1f1acf6 100644
--- a/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address.pass.cpp
+++ b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address.pass.cpp
@@ -137,6 +137,14 @@ TEST_CONSTEXPR_CXX14 bool test() {
     assert(std::__to_address(p8b) == p8_nil);
     ASSERT_SAME_TYPE(decltype(std::__to_address(p8b)), decltype(p8_nil));
 
+    int p9[2] = {};
+    assert(std::__to_address(p9) == p9);
+    ASSERT_SAME_TYPE(decltype(std::__to_address(p9)), int*);
+
+    const int p10[2] = {};
+    assert(std::__to_address(p10) == p10);
+    ASSERT_SAME_TYPE(decltype(std::__to_address(p10)), const int*);
+
     return true;
 }
 

diff  --git a/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_on_funcptr.verify.cpp b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_on_funcptr.verify.cpp
new file mode 100644
index 0000000000000..02b1676055e7b
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_on_funcptr.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// template <class T> constexpr T* __to_address(T* p) noexcept;
+//     Mandates: T is not a function type.
+
+#include <memory>
+
+int (*pf)();
+
+void test() {
+    (void)std::__to_address(pf); // expected-error@*:* {{is a function type}}
+}

diff  --git a/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_on_function.verify.cpp b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_on_function.verify.cpp
new file mode 100644
index 0000000000000..b8825a181d37a
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_on_function.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// template <class T> constexpr T* __to_address(T* p) noexcept;
+//     Mandates: T is not a function type.
+
+#include <memory>
+
+int f();
+
+void test() {
+    (void)std::__to_address(f); // expected-error@*:* {{is a function type}}
+}

diff  --git a/libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp b/libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp
index 0a2bdf11559ee..b92700143aa6a 100644
--- a/libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp
@@ -139,6 +139,18 @@ constexpr bool test() {
     assert(std::to_address(p8b) == p8_nil);
     ASSERT_SAME_TYPE(decltype(std::to_address(p8b)), decltype(p8_nil));
 
+    int p9[2] = {};
+    assert(std::to_address(p9) == p9);
+    ASSERT_SAME_TYPE(decltype(std::to_address(p9)), int*);
+
+    const int p10[2] = {};
+    assert(std::to_address(p10) == p10);
+    ASSERT_SAME_TYPE(decltype(std::to_address(p10)), const int*);
+
+    int (*p11)() = nullptr;
+    assert(std::to_address(&p11) == &p11);
+    ASSERT_SAME_TYPE(decltype(std::to_address(&p11)), int(**)());
+
     return true;
 }
 

diff  --git a/libcxx/test/std/utilities/memory/pointer.conversion/to_address_on_funcptr.verify.cpp b/libcxx/test/std/utilities/memory/pointer.conversion/to_address_on_funcptr.verify.cpp
new file mode 100644
index 0000000000000..4f4e05ce8c2a0
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/pointer.conversion/to_address_on_funcptr.verify.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// template <class T> constexpr T* to_address(T* p) noexcept;
+//     Mandates: T is not a function type.
+
+#include <memory>
+
+int (*pf)();
+
+void test() {
+    (void)std::to_address(pf); // expected-error@*:* {{is a function type}}
+}

diff  --git a/libcxx/test/std/utilities/memory/pointer.conversion/to_address_on_function.verify.cpp b/libcxx/test/std/utilities/memory/pointer.conversion/to_address_on_function.verify.cpp
new file mode 100644
index 0000000000000..ee2306012eb6f
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/pointer.conversion/to_address_on_function.verify.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// template <class T> constexpr T* to_address(T* p) noexcept;
+//     Mandates: T is not a function type.
+
+#include <memory>
+
+int f();
+
+void test() {
+    (void)std::to_address(f); // expected-error@*:* {{is a function type}}
+}


        


More information about the libcxx-commits mailing list