Skip to content

[libc++][hardening] Introduce assertion semantics. #149459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

var-const
Copy link
Member

Assertion semantics closely mimic C++26 Contracts evaluation semantics. This
brings our implementation closer in line with C++26 Library Hardening (one
particular benefit is that using the observe semantic makes adopting hardening
easier for projects).

@var-const var-const force-pushed the varconst/hardening-semantics-introduce-2 branch from e4e7c6c to 966773a Compare July 22, 2025 09:51
@var-const var-const marked this pull request as ready for review July 22, 2025 09:52
@var-const var-const requested review from a team as code owners July 22, 2025 09:52
@llvmbot llvmbot added libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. libc++abi libc++abi C++ Runtime Library. Not libc++. github:workflow labels Jul 22, 2025
@var-const var-const requested review from philnik777 and ldionne July 22, 2025 09:52
@llvmbot
Copy link
Member

llvmbot commented Jul 22, 2025

@llvm/pr-subscribers-libcxxabi
@llvm/pr-subscribers-github-workflow

@llvm/pr-subscribers-libcxx

Author: Konstantin Varlamov (var-const)

Changes

Assertion semantics closely mimic C++26 Contracts evaluation semantics. This
brings our implementation closer in line with C++26 Library Hardening (one
particular benefit is that using the observe semantic makes adopting hardening
easier for projects).


Patch is 56.36 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/149459.diff

34 Files Affected:

  • (modified) .github/workflows/libcxx-build-and-test.yaml (+1)
  • (added) libcxx/cmake/caches/Generic-hardening-mode-extensive-observe-semantic.cmake (+2)
  • (modified) libcxx/docs/Hardening.rst (+61)
  • (modified) libcxx/docs/ReleaseNotes/21.rst (+5)
  • (modified) libcxx/include/CMakeLists.txt (+1)
  • (modified) libcxx/include/__config (+35)
  • (added) libcxx/include/__log_hardening_failure (+40)
  • (modified) libcxx/include/module.modulemap.in (+3)
  • (modified) libcxx/src/CMakeLists.txt (+1)
  • (added) libcxx/src/experimental/log_hardening_failure.cpp (+54)
  • (added) libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp (+27)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp (+3-3)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp (+3-3)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp (+4-1)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.conversion.pass.cpp (+3-3)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp (+2-1)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp (+12-9)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp (+4-4)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp (+2-2)
  • (modified) libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp (+4)
  • (modified) libcxx/test/libcxx/thread/thread.barrier/assert.arrive.pass.cpp (+2)
  • (modified) libcxx/test/libcxx/thread/thread.latch/assert.arrive_and_wait.pass.cpp (+2)
  • (modified) libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp (+5-4)
  • (modified) libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp (+1-1)
  • (modified) libcxx/test/support/check_assertion.h (+119-18)
  • (modified) libcxx/test/support/test.support/test_check_assertion.pass.cpp (+14-6)
  • (modified) libcxx/utils/ci/run-buildbot (+6)
  • (modified) libcxx/utils/libcxx/test/params.py (+20)
  • (modified) libcxx/vendor/llvm/default_assertion_handler.in (+31-4)
  • (modified) libcxxabi/src/demangle/DemangleConfig.h (+8)
diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml
index ec937de02ca1a..41a2aad1da236 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-build-and-test.yaml
@@ -128,6 +128,7 @@ jobs:
           'generic-abi-unstable',
           'generic-hardening-mode-debug',
           'generic-hardening-mode-extensive',
+          'generic-hardening-mode-extensive-observe-semantic',
           'generic-hardening-mode-fast',
           'generic-hardening-mode-fast-with-abi-breaks',
           'generic-merged',
diff --git a/libcxx/cmake/caches/Generic-hardening-mode-extensive-observe-semantic.cmake b/libcxx/cmake/caches/Generic-hardening-mode-extensive-observe-semantic.cmake
new file mode 100644
index 0000000000000..c843c02977a87
--- /dev/null
+++ b/libcxx/cmake/caches/Generic-hardening-mode-extensive-observe-semantic.cmake
@@ -0,0 +1,2 @@
+set(LIBCXX_HARDENING_MODE "extensive" CACHE STRING "")
+set(LIBCXX_TEST_PARAMS "assertion_semantic=observe" CACHE STRING "")
diff --git a/libcxx/docs/Hardening.rst b/libcxx/docs/Hardening.rst
index 17808841bd9ec..0aecac309acd9 100644
--- a/libcxx/docs/Hardening.rst
+++ b/libcxx/docs/Hardening.rst
@@ -39,6 +39,8 @@ modes are:
 
    Enabling hardening has no impact on the ABI.
 
+.. _notes-for-users:
+
 Notes for users
 ---------------
 
@@ -72,6 +74,11 @@ to control the level by passing **one** of the following options to the compiler
    pre-built components. Most libc++ code is header-based, so a user-provided
    value for ``_LIBCPP_HARDENING_MODE`` will be mostly respected.
 
+In some cases, users might want to override the assertion semantic used by the
+library.
+This can be done similarly to setting the hardening mode; please refer to the
+:ref:`relevant section <assertion-semantics>`.
+
 Notes for vendors
 -----------------
 
@@ -260,6 +267,60 @@ output. This is less secure and increases the size of the binary (among other
 things, it has to store the error message strings) but makes the failure easier
 to debug. It also allows testing the error messages in our test suite.
 
+This default behavior can be customized by users via :ref:`assertion semantics
+<assertion-semantics>`; it can also be completely overridden by vendors by
+providing a :ref:`custom assertion failure handler
+<override-assertion-handler>`.
+
+.. _assertion-semantics:
+
+Assertion semantics
+-------------------
+
+What happens when an assertion fails depends on the assertion semantic being
+used. Four assertion semantics are available, based on C++26 Contracts
+evaluation semantics:
+
+- ``ignore`` evaluates the assertion but has no effect if it fails (note that it
+  differs from the Contracts ``ignore`` semantic which would not evaluate
+  the assertion at all);
+- ``observe`` logs an error (indicating, if possible on the platform, that the
+  error is fatal) but continues execution;
+- ``quick-enforce`` terminates the program as fast as possible via a trap
+  instruction. It is the default semantic for the production modes (``fast`` and
+  ``extensive``);
+- ``enforce`` logs an error and then terminates the program. It is the default
+  semantic for the ``debug`` mode.
+
+Notes:
+
+- Continuing execution after a hardening check fails results in undefined
+  behavior; the ``observe`` semantic is meant to make adopting hardening easier
+  but should not be used outside of the adoption period;
+- C++26 wording for Library Hardening precludes a conforming Hardened
+  implementation from using the Contracts ``ignore`` semantic when evaluating
+  hardened preconditions in the Library. Libc++ allows using this semantic for
+  hardened preconditions, but please be aware that using ``ignore`` does not
+  produce a conforming "Hardened" implementation, unlike the other semantics
+  above.
+
+The default assertion semantics are as follows:
+
+- ``fast``: ``quick-enforce``;
+- ``extensive``: ``quick-enforce``;
+- ``debug``: ``enforce``.
+
+The default assertion semantics can be overridden by passing **one** of the
+following options to the compiler:
+
+- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_IGNORE``
+- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_OBSERVE``
+- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE``
+- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE``
+
+All the :ref:`same notes <notes-for-users>` apply to setting this macro as for
+setting ``_LIBCPP_HARDENING_MODE``.
+
 .. _override-assertion-handler:
 
 Overriding the assertion failure handler
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index 6f18b61284f49..0bb73a1c0196d 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -88,6 +88,11 @@ Improvements and New Features
 
 - ``ctype::tolower`` and ``ctype::toupper`` have been optimized, resulting in a 2x performance improvement.
 
+- Hardening now supports assertion semantics that allow customizing how a hardening assertion failure is handled. The
+  four available semantics, modeled on C++26 Contracts, are ``ignore``, ``observe``, ``quick-enforce`` and ``enforce``.
+  The ``observe`` semantic is intended to make it easier to adopt Hardening in production but should not be used outside
+  of this scenario. Please refer to the :ref:`Hardening documentation <hardening>` for details.
+
 Deprecations and Removals
 -------------------------
 
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 25b567df2dd33..4a6a61b640a48 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -535,6 +535,7 @@ set(files
   __locale_dir/time.h
   __locale_dir/wbuffer_convert.h
   __locale_dir/wstring_convert.h
+  __log_hardening_failure
   __math/abs.h
   __math/copysign.h
   __math/error_functions.h
diff --git a/libcxx/include/__config b/libcxx/include/__config
index ee06abfba7a08..3a6ae886bf904 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -147,6 +147,40 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \
 _LIBCPP_HARDENING_MODE_DEBUG
 #  endif
 
+// Hardening assertion semantics generally mirror the evaluation semantics of C++26 Contracts:
+// - `ignore` evaluates the assertion but doesn't do anything if it fails (note that it differs from the Contracts
+//   `ignore` semantic which wouldn't evaluate the assertion at all);
+// - `observe` logs an error (indicating, if possible, that the error is fatal) and continues execution;
+// - `quick-enforce` terminates the program as fast as possible (via trapping);
+// - `enforce` logs an error and then terminates the program.
+//
+// Notes:
+// - Continuing execution after a hardening check fails results in undefined behavior; the `observe` semantic is meant
+//   to make adopting hardening easier but should not be used outside of this scenario;
+// - C++26 wording for Library Hardening precludes a conforming Hardened implementation from using the Contracts
+//   `ignore` semantic when evaluating hardened preconditions in the Library. Libc++ allows using this semantic for
+//   hardened preconditions, however, be aware that using `ignore` does not produce a conforming "Hardened"
+//   implementation, unlike the other semantics above.
+// clang-format off
+#  define _LIBCPP_ASSERTION_SEMANTIC_IGNORE        (1 << 1)
+#  define _LIBCPP_ASSERTION_SEMANTIC_OBSERVE       (1 << 2)
+#  define _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE (1 << 3)
+#  define _LIBCPP_ASSERTION_SEMANTIC_ENFORCE       (1 << 4)
+// clang-format on
+
+// Allow users to define an arbitrary assertion semantic; otherwise, use the default mapping from modes to semantics.
+// The default is for production-capable modes to use `quick-enforce` (i.e., trap) and for the `debug` mode to use
+// `enforce` (i.e., log and abort).
+#  ifndef _LIBCPP_ASSERTION_SEMANTIC
+
+#    if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#      define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
+#    else
+#      define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
+#    endif
+
+#  endif // _LIBCPP_ASSERTION_SEMANTIC
+
 // } HARDENING
 
 #  define _LIBCPP_TOSTRING2(x) #x
@@ -207,6 +241,7 @@ _LIBCPP_HARDENING_MODE_DEBUG
 #  define _LIBCPP_HAS_EXPERIMENTAL_PSTL _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #  define _LIBCPP_HAS_EXPERIMENTAL_TZDB _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #  define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
+#  define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 
 #  if defined(__MVS__)
 #    include <features.h> // for __NATIVE_ASCII_F
diff --git a/libcxx/include/__log_hardening_failure b/libcxx/include/__log_hardening_failure
new file mode 100644
index 0000000000000..76da1153d78b2
--- /dev/null
+++ b/libcxx/include/__log_hardening_failure
@@ -0,0 +1,40 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___LOG_HARDENING_FAILURE
+#define _LIBCPP___LOG_HARDENING_FAILURE
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// This function should never be called directly from the code -- it should only be called through the
+// `_LIBCPP_LOG_HARDENING_FAILURE` macro.
+_LIBCPP_EXPORTED_FROM_ABI void __log_hardening_failure(const char* __message) _NOEXCEPT;
+
+// _LIBCPP_LOG_HARDENING_FAILURE(message)
+//
+// This macro is used to log an error without terminating the program (as is the case for hardening failures if the
+// `observe` assertion semantic is used).
+
+#  if !defined(_LIBCPP_LOG_HARDENING_FAILURE)
+#    define _LIBCPP_LOG_HARDENING_FAILURE(__message) ::std::__log_hardening_failure(__message)
+#  endif // !defined(_LIBCPP_LOG_HARDENING_FAILURE)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC
+
+#endif // _LIBCPP___LOG_HARDENING_FAILURE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 78607f2c1301d..9ee964c0069f4 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2353,6 +2353,9 @@ module std [system] {
     header "__std_mbstate_t.h"
     export *
   }
+  module log_hardening_failure {
+    header "__log_hardening_failure"
+  }
   module verbose_abort {
     header "__verbose_abort"
   }
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 97fe57a5f24f8..f59fe0e08fccb 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -309,6 +309,7 @@ add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS})
 # Build the experimental static library
 set(LIBCXX_EXPERIMENTAL_SOURCES
   experimental/keep.cpp
+  experimental/log_hardening_failure.cpp
   )
 
 if (LIBCXX_PSTL_BACKEND STREQUAL "libdispatch")
diff --git a/libcxx/src/experimental/log_hardening_failure.cpp b/libcxx/src/experimental/log_hardening_failure.cpp
new file mode 100644
index 0000000000000..274175c1acaa8
--- /dev/null
+++ b/libcxx/src/experimental/log_hardening_failure.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__log_hardening_failure>
+#include <cstdio>
+
+#ifdef __BIONIC__
+#  include <syslog.h>
+extern "C" void android_set_abort_message(const char* msg);
+#endif // __BIONIC__
+
+#if defined(__APPLE__) && __has_include(<os/reason_private.h>)
+#  include <TargetConditionals.h>
+#  include <os/reason_private.h>
+#endif
+
+#if _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+void __log_hardening_failure(const char* message) noexcept {
+  // Always log the message to `stderr` in case the platform-specific system calls fail.
+  std::fputs(message, stderr);
+
+  // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
+#  if defined(__APPLE__) && __has_include(<os/reason_private.h>) && !TARGET_OS_SIMULATOR
+  os_fault_with_payload(
+      /*reason_namespace=*/OS_REASON_SECURITY,
+      /*reason_code=*/0,
+      /*payload=*/nullptr,
+      /*payload_size=*/0,
+      /*reason_string=*/message,
+      /*reason_flags=*/0);
+
+#  elif defined(__BIONIC__)
+  // Show error in tombstone.
+  android_set_abort_message(message);
+
+  // Show error in logcat. The latter two arguments are ignored on Android.
+  openlog("libc++", 0, 0);
+  syslog(LOG_CRIT, "%s", message);
+  closelog();
+#  endif
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC
diff --git a/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp b/libcxx/test/libcxx/assertions/log_hardening_failure.pass.cpp
new file mode 100644
index 0000000000000..d13b1a0a9c13a
--- /dev/null
+++ b/libcxx/test/libcxx/assertions/log_hardening_failure.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
+//
+//===----------------------------------------------------------------------===//
+
+// Basic smoke test for `__log_hardening_failure`.
+//
+// UNSUPPORTED: libcpp-has-no-experimental-hardening-observe-semantic
+// XFAIL: availability-log_hardening_failure-missing
+// UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME
+
+#include <__log_hardening_failure>
+
+#include "test_macros.h"
+
+ASSERT_NOEXCEPT(std::__log_hardening_failure(""));
+
+int main(int, char**) {
+  std::__log_hardening_failure("Some message");
+  // It's difficult to properly test platform-specific logging behavior of the function; just make sure it exists and
+  // can be called at runtime.
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
index 90cb0c84a063b..1c9829837ae62 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
@@ -43,17 +43,17 @@ int main(int, char**) {
   }
   // mismatch of static extent
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<int, D, 5> e1(std::array{1000, 3}); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<int, D, 5> e1(std::array{1000, 3}); }()),
                                "extents construction: mismatch of provided arguments with static extents.");
   }
   // value out of range
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<signed char, D, 5> e1(std::array{1000, 5}); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<signed char, D, 5> e1(std::array{1000, 5}); }()),
                                "extents ctor: arguments must be representable as index_type and nonnegative");
   }
   // negative value
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<signed char, D, 5> e1(std::array{-1, 5}); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<signed char, D, 5> e1(std::array{-1, 5}); }()),
                                "extents ctor: arguments must be representable as index_type and nonnegative");
   }
   return 0;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
index 37e79aabf8532..17bab03b922e6 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
@@ -45,17 +45,17 @@ int main(int, char**) {
   }
   // mismatch of static extent
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<int, D, 5> e1(1000, 3); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<int, D, 5> e1(1000, 3); }()),
                                "extents construction: mismatch of provided arguments with static extents.");
   }
   // value out of range
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<signed char, D, 5> e1(1000, 5); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<signed char, D, 5> e1(1000, 5); }()),
                                "extents ctor: arguments must be representable as index_type and nonnegative");
   }
   // negative value
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<signed char, D, 5> e1(-1, 5); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<signed char, D, 5> e1(-1, 5); }()),
                                "extents ctor: arguments must be representable as index_type and nonnegative");
   }
   return 0;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
index 7b6616f19d724..c67529671fcf7 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
@@ -44,7 +44,7 @@ int main(int, char**) {
   {
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_left::mapping<std::extents<signed char, D>> m(
+          [[maybe_unused]] std::layout_left::mapping<std::extents<signed char, D>> m(
               std::layout_left::mapping<std::extents<int, D>>(std::extents<int, D>(500)));
         }()),
         "extents ctor: arguments must be representable as index_type and nonnegative");
@@ -55,7 +55,7 @@ int main(int, char**) {
     [[maybe_unused]] std::extents<signed char, D, 5> e(arg_exts);
     // but the product is not, so we can't use it for layout_left
     TEST_LIBCPP_ASSERT_FAILURE(
-        ([=] { std::layout_left::mapping<std::extents<signed char, D, 5>> m(arg); }()),
+        ([=] { [[maybe_unused]] std::layout_left::mapping<std::extents<signed char, D, 5>> m(arg); }()),
         "layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type.");
   }
   return 0;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
index 7c96f8ec9353f..45f07f1ed5c06 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
@@ -31,7 +31,10 @@ int main(int, char**) {
   {
     // the extents are representable but the product is not, so we can't use it for layout_left
     TEST_LIBCPP_ASSERT_FAILURE(
-        ([=] { std::layout_left::mapping<std::extents<signed char, D, 5>> m(std::extents<signed char, D, 5>(100)); }()),
+        ([=] {
+          [[maybe_unused]] std::layout_left::mapping<std::extents<signed char, D, 5>> m(
+              std::extents<signed char, D, 5>(100));
+        }()),
         "layout_left::mapping extents ctor: product of extents must be representable as index_type.");
   }
   return 0;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp
index e578bac2103b0..04a6c59d265e1 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp
@@ -39,14 +39,14 @@ int main(int, char**) {
   }
   // mismatch of static extent
   {
-    TEST_LIBCPP_...
[truncated]

@var-const var-const force-pushed the varconst/hardening-semantics-introduce-2 branch from 966773a to a33241a Compare July 22, 2025 09:55
Comment on lines 270 to 289
This default behavior can be customized by users via :ref:`assertion semantics
<assertion-semantics>`; it can also be completely overridden by vendors by
providing a :ref:`custom assertion failure handler
<override-assertion-handler>`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This default behavior can be customized by users via :ref:`assertion semantics
<assertion-semantics>`; it can also be completely overridden by vendors by
providing a :ref:`custom assertion failure handler
<override-assertion-handler>`.
Experimentally, this default behavior can be customized by users via :ref:`assertion semantics
<assertion-semantics>`; it can also be completely overridden by vendors by
providing a :ref:`custom assertion failure handler
<override-assertion-handler>`.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a note and a warning to make it stand out (and make it uniform throughout), let me know what you think.

@ldionne ldionne added this to the LLVM 21.x Release milestone Jul 23, 2025
@github-project-automation github-project-automation bot moved this to Needs Triage in LLVM Release Status Jul 23, 2025
@tru tru moved this from Needs Triage to Needs Fix in LLVM Release Status Jul 24, 2025
Assertion semantics closely mimic C++26 Contracts evaluation semantics.
This brings our implementation closer in line with C++26 Library
Hardening (one particular benefit is that using the `observe` semantic
makes adopting hardening easier for users).
@var-const var-const force-pushed the varconst/hardening-semantics-introduce-2 branch from a33241a to e5b7a3a Compare July 24, 2025 17:43
Comment on lines +87 to +88
to the :ref:`relevant section <assertion-semantics>`. Note that this feature is
currently experimental.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
to the :ref:`relevant section <assertion-semantics>`. Note that this feature is
currently experimental.
to the :ref:`relevant section <assertion-semantics>`.

I think it's more future-proof not to duplicate this information.


Assertion semantics are not available in the C++03 mode.

Experimentally, this default behavior can be customized by users via
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Experimentally, this default behavior can be customized by users via
This default behavior can be customized by users via

Comment on lines +278 to +284
.. warning::

Assertion semantics are currently an experimental feature.

.. note::

Assertion semantics are not available in the C++03 mode.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably remove these two warnings. They are redundant with the ones immediately below under the Assertion semantics section.

Comment on lines +77 to +83
.. warning::

Assertion semantics are currently an experimental feature.

.. note::

Assertion semantics are not available in the C++03 mode.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think it makes sense to only leave a warning + note in the main section, since these other bits of text link to the main section about assertion semantics.

# else
# if !_LIBCPP_HAS_EXPERIMENTAL_LIBRARY
# error "Assertion semantics are an experimental feature."
# endif // ! _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# endif // ! _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
# endif

I think this is more readable given the small size of the block

@@ -24,14 +25,40 @@
# pragma GCC system_header
#endif

#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
#if defined(_LIBCPP_CXX03_LANG) && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

We can do this, since we've included __config at this point.

# endif // ! _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
# if defined(_LIBCPP_CXX03_LANG)
# error "Assertion semantics are not available in the C++03 mode."
# endif // defined(_LIBCPP_CXX03_LANG)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# endif // defined(_LIBCPP_CXX03_LANG)
# endif

# error "Assertion semantics are an experimental feature."
# endif // ! _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
# if defined(_LIBCPP_CXX03_LANG)
# error "Assertion semantics are not available in the C++03 mode."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add a check in __cxx03/__config that does:

#if defined(_LIBCPP_ASSERTION_SEMANTIC)
#  error "Assertion semantics are not available in the C++03 mode."
#endif

Otherwise we'd be #erroring out in C++03 mode, but we'd be silently ignoring any user-provided _LIBCPP_ASSERTION_SEMANTIC in frozen-C++03 mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
github:workflow libc++abi libc++abi C++ Runtime Library. Not libc++. libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
Status: Needs Fix
Development

Successfully merging this pull request may close these issues.

3 participants