[libc++] Fix ADL for `make_error_{code,condition}`
authorJonathan Wakely <jwakely@redhat.com>
Fri, 30 Sep 2022 13:22:01 +0000 (09:22 -0400)
committerLouis Dionne <ldionne.2@gmail.com>
Fri, 30 Sep 2022 21:23:45 +0000 (17:23 -0400)
Implement LWG 3629, by making lookup for make_error_code and
make_error_condition only consider names found by ADL. This is achieved
by adding a block scope using-declaration for a function that will be
found by unqualified lookup, preventing unqualified lookup from
continuing to enclosing scopes (the class scope, then enclosing
namespaces). The function named by the using declaration is not
viable, so overload resolution must select a candidate found by ADL.

This fixes https://github.com/llvm/llvm-project/issues/57614

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

libcxx/docs/Status/Cxx2bIssues.csv
libcxx/include/system_error
libcxx/test/std/diagnostics/syserr/syserr.errcode/syserr.errcode.constructors/lwg3629.pass.cpp [new file with mode: 0644]
libcxx/test/std/diagnostics/syserr/syserr.errcode/syserr.errcode.modifiers/lwg3629.pass.cpp [new file with mode: 0644]
libcxx/test/std/diagnostics/syserr/syserr.errcondition/syserr.errcondition.constructors/lwg3629.pass.cpp [new file with mode: 0644]
libcxx/test/std/diagnostics/syserr/syserr.errcondition/syserr.errcondition.modifiers/lwg3629.pass.cpp [new file with mode: 0644]

index 85a93cc..20b20b4 100644 (file)
 "`3721 <https://wg21.link/LWG3721>`__","Allow an ``arg-id`` with a value of zero for ``width`` in ``std-format-spec``","July 2022","|Complete|","16.0","|format|"
 "`3724 <https://wg21.link/LWG3724>`__","``decay-copy`` should be constrained","July 2022","|Complete|","14.0"
 "","","","",""
-"`3645 <https://wg21.link/LWG3645>`__","``resize_and_overwrite`` is overspecified to call its callback with lvalues","Not voted in","|Complete|","14.0",""
+"`3629 <https://wg21.link/LWG3629>`__","``make_error_code`` and ``make_error_condition`` are customization points","Not voted in","|Complete|","16.0",""
 "`3631 <https://wg21.link/LWG3631>`__","``basic_format_arg(T&&)`` should use ``remove_cvref_t<T>`` throughout","Not voted in","|Complete|","15.0",""
+"`3645 <https://wg21.link/LWG3645>`__","``resize_and_overwrite`` is overspecified to call its callback with lvalues","Not voted in","|Complete|","14.0",""
 "","","","",""
index f81165f..9891992 100644 (file)
@@ -256,6 +256,13 @@ public:
 _LIBCPP_FUNC_VIS const error_category& generic_category() _NOEXCEPT;
 _LIBCPP_FUNC_VIS const error_category& system_category() _NOEXCEPT;
 
+namespace __adl_only {
+    // Those cause ADL to trigger but they are not viable candidates,
+    // so they are never actually selected.
+    void make_error_condition() = delete;
+    void make_error_code() = delete;
+} // namespace __adl_only
+
 class _LIBCPP_TYPE_VIS error_condition
 {
     int __val_;
@@ -273,7 +280,10 @@ public:
         error_condition(_Ep __e,
               typename enable_if<is_error_condition_enum<_Ep>::value>::type* = nullptr
                                                                      ) _NOEXCEPT
-            {*this = make_error_condition(__e);}
+            {
+                using __adl_only::make_error_condition;
+                *this = make_error_condition(__e);
+            }
 
     _LIBCPP_INLINE_VISIBILITY
     void assign(int __val, const error_category& __cat) _NOEXCEPT
@@ -290,7 +300,11 @@ public:
             error_condition&
         >::type
         operator=(_Ep __e) _NOEXCEPT
-            {*this = make_error_condition(__e); return *this;}
+            {
+                using __adl_only::make_error_condition;
+                *this = make_error_condition(__e);
+                return *this;
+            }
 
     _LIBCPP_INLINE_VISIBILITY
     void clear() _NOEXCEPT
@@ -336,7 +350,10 @@ public:
         error_code(_Ep __e,
                    typename enable_if<is_error_code_enum<_Ep>::value>::type* = nullptr
                                                                      ) _NOEXCEPT
-            {*this = make_error_code(__e);}
+            {
+                using __adl_only::make_error_code;
+                *this = make_error_code(__e);
+            }
 
     _LIBCPP_INLINE_VISIBILITY
     void assign(int __val, const error_category& __cat) _NOEXCEPT
@@ -353,7 +370,11 @@ public:
             error_code&
         >::type
         operator=(_Ep __e) _NOEXCEPT
-            {*this = make_error_code(__e); return *this;}
+            {
+                using __adl_only::make_error_code;
+                *this = make_error_code(__e);
+                return *this;
+            }
 
     _LIBCPP_INLINE_VISIBILITY
     void clear() _NOEXCEPT
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.errcode/syserr.errcode.constructors/lwg3629.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.errcode/syserr.errcode.constructors/lwg3629.pass.cpp
new file mode 100644 (file)
index 0000000..edf1541
--- /dev/null
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <system_error>
+
+// class error_code
+
+// template <ErrorCodeEnum E> error_code(E e);
+
+// Regression test for https://github.com/llvm/llvm-project/issues/57614
+
+int make_error_code; // It's important that this comes before <system_error>
+
+#include <system_error>
+#include <cassert>
+#include <type_traits>
+
+namespace user {
+  enum Err {};
+
+  std::error_code make_error_code(Err) { return std::error_code(42, std::generic_category()); }
+}
+
+namespace std {
+  template <>
+  struct is_error_code_enum<user::Err> : true_type {};
+}
+
+int main(int, char**) {
+  std::error_code e((user::Err()));
+  assert(e.value() == 42);
+  assert(e.category() == std::generic_category());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.errcode/syserr.errcode.modifiers/lwg3629.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.errcode/syserr.errcode.modifiers/lwg3629.pass.cpp
new file mode 100644 (file)
index 0000000..715d8c7
--- /dev/null
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <system_error>
+
+// class error_code
+
+// template <ErrorCodeEnum E> error_code& operator=(E e);
+
+// Regression test for https://github.com/llvm/llvm-project/issues/57614
+
+int make_error_code; // It's important that this comes before <system_error>
+
+#include <system_error>
+#include <cassert>
+#include <type_traits>
+
+namespace user {
+  enum Err {};
+
+  std::error_code make_error_code(Err) { return std::error_code(42, std::generic_category()); }
+}
+
+namespace std {
+  template <>
+  struct is_error_code_enum<user::Err> : true_type {};
+}
+
+int main(int, char**) {
+  std::error_code e;
+  e = user::Err();
+  assert(e.value() == 42);
+  assert(e.category() == std::generic_category());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.errcondition/syserr.errcondition.constructors/lwg3629.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.errcondition/syserr.errcondition.constructors/lwg3629.pass.cpp
new file mode 100644 (file)
index 0000000..3a7dfba
--- /dev/null
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <system_error>
+
+// class error_condition
+
+// template <ErrorCodeEnum E> error_condition(E e);
+
+// Regression test for https://github.com/llvm/llvm-project/issues/57614
+
+int make_error_condition; // It's important that this comes before <system_error>
+
+#include <system_error>
+#include <cassert>
+#include <type_traits>
+
+namespace user {
+  enum Err {};
+
+  std::error_condition make_error_condition(Err) { return std::error_condition(42, std::generic_category()); }
+}
+
+namespace std {
+  template <>
+  struct is_error_condition_enum<user::Err> : true_type {};
+}
+
+int main(int, char**) {
+  std::error_condition e((user::Err()));
+  assert(e.value() == 42);
+  assert(e.category() == std::generic_category());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.errcondition/syserr.errcondition.modifiers/lwg3629.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.errcondition/syserr.errcondition.modifiers/lwg3629.pass.cpp
new file mode 100644 (file)
index 0000000..95b6f6e
--- /dev/null
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <system_error>
+
+// class error_condition
+
+// template <ErrorCodeEnum E> error_condition& operator=(E e);
+
+// Regression test for https://github.com/llvm/llvm-project/issues/57614
+
+int make_error_condition; // It's important that this comes before <system_error>
+
+#include <system_error>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+namespace user {
+  enum Err {};
+
+  std::error_condition make_error_condition(Err) { return std::error_condition(42, std::generic_category()); }
+}
+
+namespace std {
+  template <>
+  struct is_error_condition_enum<user::Err> : true_type {};
+}
+
+int main(int, char**) {
+  std::error_condition e;
+  e = user::Err();
+  assert(e.value() == 42);
+  assert(e.category() == std::generic_category());
+  return 0;
+}