[libcxx-commits] [libcxx] 17c05a4 - [libc++] Introduce a compile-time mechanism to override __libcpp_verbose_abort

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jan 24 18:39:27 PST 2023


Author: Louis Dionne
Date: 2023-01-24T21:39:14-05:00
New Revision: 17c05a44d9f0ab08342c7310bd0e22114e932882

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

LOG: [libc++] Introduce a compile-time mechanism to override __libcpp_verbose_abort

This changes the mechanism for verbose termination (again!) to make it
support compile-time customization in addition to link-time customization,
which is important for users who need fine-grained control over what code
gets generated around sites that call the verbose termination handler.

This concern had been raised to me both privately by prospecting users
and in https://llvm.org/D140944, so I think it is clearly worth fixing.

We still support _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED for
a limited time since the same functionality can be achieved by overriding
the _LIBCPP_VERBOSE_ABORT macro.

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

Added: 
    libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp
    libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
    libcxx/test/libcxx/assertions/deprecated-link-time-custom-handler.pass.cpp

Modified: 
    libcxx/docs/ReleaseNotes.rst
    libcxx/docs/UsingLibcxx.rst
    libcxx/include/__assert
    libcxx/include/__availability
    libcxx/include/__verbose_abort
    libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp

Removed: 
    libcxx/test/libcxx/assertions/customize_verbose_abort.backdeployment.pass.cpp
    libcxx/test/libcxx/assertions/customize_verbose_abort.pass.cpp


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index f22e076684d49..060ab387fe5f6 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -140,6 +140,10 @@ Upcoming Deprecations and Removals
   and such a base template is bound to be incorrect for some types, which could currently cause unexpected
   behavior while going undetected.
 
+- The ``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED`` macro will not be honored anymore in LLVM 18.
+  Please see the updated documentation about the safe libc++ mode and in particular the ``_LIBCPP_VERBOSE_ABORT``
+  macro for details.
+
 API Changes
 -----------
 - The comparison operators on ``thread::id`` are now defined as free-standing

diff  --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index 6fac013bbd715..3b64adb5f5a73 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -172,12 +172,19 @@ library provides a default function that prints an error message and calls ``std
 that this function is provided by the static or shared library, so it is only available when deploying
 to a platform where the compiled library is sufficiently recent. On older platforms, the program will
 terminate in an unspecified unsuccessful manner, but the quality of diagnostics won't be great.
-However, users can also override that function with their own, which can be useful to either provide
-custom behavior or when deploying to an older platform where the default function isn't available.
-
-Replacing the default verbose termination function is done by defining the
-``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED`` macro in all translation units of your program
-and defining the following function in exactly one translation unit:
+However, users can also override that mechanism at two 
diff erent levels. First, the mechanism can be
+overriden at compile-time by defining the ``_LIBCPP_VERBOSE_ABORT(format, args...)`` variadic macro.
+When that macro is defined, it will be called with a format string as the first argument, followed by
+a series of arguments to format using printf-style formatting. Compile-time customization may be
+interesting to get precise control over code generation, however it is also inconvenient to use in
+some cases. Indeed, compile-time customization of the verbose termination function requires that all
+translation units be compiled with a consistent definition for ``_LIBCPP_VERBOSE_ABORT`` to avoid ODR
+violations, which can add complexity in the build system of users.
+
+Otherwise, if compile-time customization is not necessary, link-time customization of the handler is also
+possible, similarly to how replacing ``operator new`` works. This mechanism trades off fine-grained control
+over the call site where the termination is initiated in exchange for more ergonomics. Link-time customization
+is done by simply defining the following function in exactly one translation unit of your program:
 
 .. code-block:: cpp
 

diff  --git a/libcxx/include/__assert b/libcxx/include/__assert
index afe7304d0308f..9c0cd1b1192ab 100644
--- a/libcxx/include/__assert
+++ b/libcxx/include/__assert
@@ -41,7 +41,7 @@
 # define _LIBCPP_ASSERT(expression, message)                                        \
     (__builtin_expect(static_cast<bool>(expression), 1) ?                           \
       (void)0 :                                                                     \
-      ::std::__libcpp_verbose_abort("%s:%d: assertion %s failed: %s", __FILE__, __LINE__, #expression, message))
+      _LIBCPP_VERBOSE_ABORT("%s:%d: assertion %s failed: %s", __FILE__, __LINE__, #expression, message))
 #elif !defined(_LIBCPP_ASSERTIONS_DISABLE_ASSUME) && __has_builtin(__builtin_assume)
 # define _LIBCPP_ASSERT(expression, message)                                        \
     (_LIBCPP_DIAGNOSTIC_PUSH                                                        \

diff  --git a/libcxx/include/__availability b/libcxx/include/__availability
index 72ff663334d36..6dfca3fa8b1ab 100644
--- a/libcxx/include/__availability
+++ b/libcxx/include/__availability
@@ -156,20 +156,9 @@
 #   define _LIBCPP_AVAILABILITY_FORMAT
 // #   define _LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format
 
-    // This controls whether the default verbose termination function is
-    // provided by the library.
-    //
-    // Note that when users provide their own custom function, it doesn't
-    // matter whether the dylib provides a default function, and the
-    // availability markup can actually give a false positive diagnostic
-    // (it will think that no function is provided, when in reality the
-    // user has provided their own).
-    //
-    // Users can pass -D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED
-    // to the compiler to tell the library not to define its own verbose abort.
-    // Note that defining this macro but failing to define a custom function
-    // will lead to a load-time error on back-deployment targets, so it should
-    // be avoided.
+    // This controls whether the library claims to provide a default verbose
+    // termination function, and consequently whether the headers will try
+    // to use it when the mechanism isn't overriden at compile-time.
 // #   define _LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY
 
 #elif defined(__APPLE__)

diff  --git a/libcxx/include/__verbose_abort b/libcxx/include/__verbose_abort
index 3559c5231c312..a16d75d5ac4c3 100644
--- a/libcxx/include/__verbose_abort
+++ b/libcxx/include/__verbose_abort
@@ -17,32 +17,41 @@
 #  pragma GCC system_header
 #endif
 
-// Provide a default implementation of __libcpp_verbose_abort if we know that neither the built
-// library nor the user is providing one. Otherwise, just declare it and use the one from the
-// built library or the one provided by the user.
-//
-// We can't provide a great implementation because it needs to be pretty much
-// dependency-free (this is included everywhere else in the library).
-#if defined(_LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY) && !defined(_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED)
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-_LIBCPP_NORETURN _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) _LIBCPP_HIDE_FROM_ABI inline
-void __libcpp_verbose_abort(const char *, ...) {
-  __builtin_abort();
-}
-
-_LIBCPP_END_NAMESPACE_STD
-
-#else
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+// This function should never be called directly from the code -- it should only be called through
+// the _LIBCPP_VERBOSE_ABORT macro.
 _LIBCPP_NORETURN _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2)
 void __libcpp_verbose_abort(const char *__format, ...);
 
-_LIBCPP_END_NAMESPACE_STD
+// _LIBCPP_VERBOSE_ABORT(format, args...)
+//
+// This macro is used to abort the program abnormally while providing additional diagnostic information.
+//
+// The first argument is a printf-style format string, and the remaining arguments are values to format
+// into the format-string. This macro can be customized by users to provide fine-grained control over
+// how verbose termination is triggered.
+//
+// If the user does not supply their own version of the _LIBCPP_VERBOSE_ABORT macro, we pick the default
+// behavior based on whether we know the built library we're running against provides support for the
+// verbose termination handler or not. If it does, we call it. If it doesn't, we call __builtin_abort to
+// make sure that the program terminates but without taking any complex dependencies in this header.
+#if !defined(_LIBCPP_VERBOSE_ABORT)
+
+// Support _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED until LLVM 18, but tell people
+// to move to customizing _LIBCPP_VERBOSE_ABORT instead.
+#  if defined(_LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY) && defined(_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED)
+#    undef _LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY
+#    warning _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED is deprecated, please customize _LIBCPP_VERBOSE_ABORT instead
+#  endif
+
+#  if defined(_LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY)
+#    define _LIBCPP_VERBOSE_ABORT(...) __builtin_abort()
+#  else
+#    define _LIBCPP_VERBOSE_ABORT(...) ::std::__libcpp_verbose_abort(__VA_ARGS__)
+#  endif
+#endif // !defined(_LIBCPP_VERBOSE_ABORT)
 
-#endif
+_LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___VERBOSE_ABORT

diff  --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp
new file mode 100644
index 0000000000000..1ea6c362971b8
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// This compile-time customization requires cross-file macros, which doesn't work with modules.
+// UNSUPPORTED: modules-build
+
+// Make sure that we can customize the verbose termination function at compile-time by
+// defining _LIBCPP_VERBOSE_ABORT ourselves. Note that this does not have any
+// deployment target requirements.
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 -D_LIBCPP_VERBOSE_ABORT(...)=my_abort(__VA_ARGS__)
+
+#include <cstdlib>
+
+void my_abort(char const*, ...) {
+  std::exit(EXIT_SUCCESS);
+}
+
+int main(int, char**) {
+  _LIBCPP_ASSERT(false, "message");
+  return EXIT_FAILURE;
+}

diff  --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
similarity index 91%
rename from libcxx/test/libcxx/assertions/customize_verbose_abort.pass.cpp
rename to libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
index 3ee9b7803b709..c0422dc1b1433 100644
--- a/libcxx/test/libcxx/assertions/customize_verbose_abort.pass.cpp
+++ b/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Test that we can set a custom verbose termination function.
+// Test that we can set a custom verbose termination function at link-time.
 
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
 

diff  --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.backdeployment.pass.cpp b/libcxx/test/libcxx/assertions/deprecated-link-time-custom-handler.pass.cpp
similarity index 66%
rename from libcxx/test/libcxx/assertions/customize_verbose_abort.backdeployment.pass.cpp
rename to libcxx/test/libcxx/assertions/deprecated-link-time-custom-handler.pass.cpp
index 15962671a4690..b699a3d0f8266 100644
--- a/libcxx/test/libcxx/assertions/customize_verbose_abort.backdeployment.pass.cpp
+++ b/libcxx/test/libcxx/assertions/deprecated-link-time-custom-handler.pass.cpp
@@ -6,14 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that we can enable assertions when we back-deploy to older platforms
-// if we define _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED.
-//
-// Note that this test isn't really 
diff erent from customize_verbose_abort.pass.cpp when
-// run outside of back-deployment scenarios, but we always want to run this test.
+// Make sure that we still support _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED for folks
+// who customize the verbose termination function at link-time in back-deployment environments.
 
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 -D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED
 
+// We emit a #warning about the deprecation of this setting, so make sure we don't turn that into an error.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-error
+
 #include <cstdlib>
 
 void std::__libcpp_verbose_abort(char const*, ...) {

diff  --git a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp
index 7ca6d91b29ab5..80f3f254c68f1 100644
--- a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp
+++ b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp
@@ -6,7 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Test that all public C++ headers define the verbose termination function.
+// Test that all public C++ headers define the verbose termination function, which
+// is required for users to be able to include any public header and then override
+// the function using a strong definition.
 
 // The system-provided <uchar.h> seems to be broken on AIX, which trips up this test.
 // XFAIL: LIBCXX-AIX-FIXME


        


More information about the libcxx-commits mailing list