diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt index a119850cd808e..1423b6713fd35 100644 --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -66,6 +66,19 @@ if (NOT "${LIBCXX_HARDENING_MODE}" IN_LIST LIBCXX_SUPPORTED_HARDENING_MODES) message(FATAL_ERROR "Unsupported hardening mode: '${LIBCXX_HARDENING_MODE}'. Supported values are ${LIBCXX_SUPPORTED_HARDENING_MODES}.") endif() +set(LIBCXX_SUPPORTED_ASSERTION_SEMANTICS hardening_dependent ignore observe quick_enforce enforce) +set(LIBCXX_ASSERTION_SEMANTIC "hardening_dependent" CACHE STRING + "Specify the default assertion semantic to use. This semantic will be used + inside the compiled library and will be the default when compiling user code. + Note that users can override this setting in their own code. This does not + affect the ABI. Supported values are ${LIBCXX_SUPPORTED_ASSERTION_SEMANTICS}. + `hardening_dependent` is a special value that instructs the library to select + the assertion semantic based on the hardening mode in effect.") + +if (NOT "${LIBCXX_ASSERTION_SEMANTIC}" IN_LIST LIBCXX_SUPPORTED_ASSERTION_SEMANTICS) + message(FATAL_ERROR + "Unsupported assertion semantic: '${LIBCXX_ASSERTION_SEMANTIC}'. Supported values are ${LIBCXX_SUPPORTED_ASSERTION_SEMANTICS}.") +endif() set(LIBCXX_ASSERTION_HANDLER_FILE "vendor/llvm/default_assertion_handler.in" CACHE STRING @@ -763,6 +776,17 @@ elseif (LIBCXX_HARDENING_MODE STREQUAL "extensive") elseif (LIBCXX_HARDENING_MODE STREQUAL "debug") config_define(8 _LIBCPP_HARDENING_MODE_DEFAULT) endif() +if (LIBCXX_ASSERTION_SEMANTIC STREQUAL "hardening_dependent") + config_define(2 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT) +elseif (LIBCXX_ASSERTION_SEMANTIC STREQUAL "ignore") + config_define(4 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT) +elseif (LIBCXX_ASSERTION_SEMANTIC STREQUAL "observe") + config_define(8 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT) +elseif (LIBCXX_ASSERTION_SEMANTIC STREQUAL "quick_enforce") + config_define(16 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT) +elseif (LIBCXX_ASSERTION_SEMANTIC STREQUAL "enforce") + config_define(32 _LIBCPP_ASSERTION_SEMANTIC_DEFAULT) +endif() if (LIBCXX_PSTL_BACKEND STREQUAL "serial") config_define(1 _LIBCPP_PSTL_BACKEND_SERIAL) diff --git a/libcxx/docs/Hardening.rst b/libcxx/docs/Hardening.rst index 1cdb3605c38ab..1360518379aef 100644 --- a/libcxx/docs/Hardening.rst +++ b/libcxx/docs/Hardening.rst @@ -328,6 +328,20 @@ following options to the compiler: All the :ref:`same notes ` apply to setting this macro as for setting ``_LIBCPP_HARDENING_MODE``. +Notes for vendors +----------------- + +Similarly to hardening modes, vendors can set the default assertion semantic by +providing ``LIBCXX_ASSERTION_SEMANTIC`` as a configuration option, with the +possible values of ``hardening_dependent``, ``ignore``, ``observe``, +``quick_enforce`` and ``enforce``. The default value is ``hardening_dependent`` +which is a special value that instructs the library to select the semantic based +on the hardening mode in effect (the mapping is described in +:ref:`the main section on assertion semantics `). + +This option controls both the assertion semantic that the precompiled library is +built with and the default assertion semantic that users will build with. + .. _override-assertion-handler: Overriding the assertion failure handler @@ -447,6 +461,13 @@ The first character of an ABI tag encodes the hardening mode: - ``d`` -- [d]ebug mode; - ``n`` -- [n]one mode. +The second character of an ABI tag encodes the assertion semantic: + +- ``i`` -- [i]gnore semantic; +- ``o`` -- [o]bserve semantic; +- ``q`` -- [q]uick-enforce semantic; +- ``e`` -- [e]nforce semantic. + Hardened containers status ========================== diff --git a/libcxx/include/__config b/libcxx/include/__config index e907961446201..d45f101861e2d 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -291,6 +291,16 @@ typedef __char32_t char32_t; # define _LIBCPP_HARDENING_SIG n // "none" # endif +# if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE +# define _LIBCPP_ASSERTION_SEMANTIC_SIG o +# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE +# define _LIBCPP_ASSERTION_SEMANTIC_SIG q +# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE +# define _LIBCPP_ASSERTION_SEMANTIC_SIG e +# else +# define _LIBCPP_ASSERTION_SEMANTIC_SIG i // `ignore` +# endif + # if !_LIBCPP_HAS_EXCEPTIONS # define _LIBCPP_EXCEPTIONS_SIG n # else @@ -298,7 +308,9 @@ typedef __char32_t char32_t; # endif # define _LIBCPP_ODR_SIGNATURE \ - _LIBCPP_CONCAT(_LIBCPP_CONCAT(_LIBCPP_HARDENING_SIG, _LIBCPP_EXCEPTIONS_SIG), _LIBCPP_VERSION) + _LIBCPP_CONCAT( \ + _LIBCPP_CONCAT(_LIBCPP_CONCAT(_LIBCPP_HARDENING_SIG, _LIBCPP_ASSERTION_SEMANTIC_SIG), _LIBCPP_EXCEPTIONS_SIG), \ + _LIBCPP_VERSION) // This macro marks a symbol as being hidden from libc++'s ABI. This is achieved // on two levels: diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in index b68c0c8258366..6dcca1849a96c 100644 --- a/libcxx/include/__config_site.in +++ b/libcxx/include/__config_site.in @@ -40,6 +40,7 @@ // Hardening. #cmakedefine _LIBCPP_HARDENING_MODE_DEFAULT @_LIBCPP_HARDENING_MODE_DEFAULT@ +#cmakedefine _LIBCPP_ASSERTION_SEMANTIC_DEFAULT @_LIBCPP_ASSERTION_SEMANTIC_DEFAULT@ // __USE_MINGW_ANSI_STDIO gets redefined on MinGW #ifdef __clang__ diff --git a/libcxx/include/__configuration/hardening.h b/libcxx/include/__configuration/hardening.h index bc2a8d078fa77..5723f5a65e1bf 100644 --- a/libcxx/include/__configuration/hardening.h +++ b/libcxx/include/__configuration/hardening.h @@ -135,13 +135,26 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \ _LIBCPP_HARDENING_MODE_DEBUG #endif -// Hardening assertion semantics generally mirror the evaluation semantics of C++26 Contracts: +// The library provides the macro `_LIBCPP_ASSERTION_SEMANTIC` for configuring the assertion semantic used by hardening; +// it can be set to one of the following values: +// +// - `_LIBCPP_ASSERTION_SEMANTIC_IGNORE`; +// - `_LIBCPP_ASSERTION_SEMANTIC_OBSERVE`; +// - `_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE`; +// - `_LIBCPP_ASSERTION_SEMANTIC_ENFORCE`. +// +// libc++ 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. // +// Additionally, a special `hardening-dependent` value selects the assertion semantic based on the hardening mode in +// effect: the production-capable modes (`fast` and `extensive`) map to `quick_enforce` and the `debug` mode maps to +// `enforce`. The `hardening-dependent` semantic cannot be selected explicitly, it is only used when no assertion +// semantic is provided by the user _and_ the library's default semantic is configured to be dependent on hardening. +// // 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; @@ -150,32 +163,53 @@ _LIBCPP_HARDENING_MODE_DEBUG // 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) +# define _LIBCPP_ASSERTION_SEMANTIC_HARDENING_DEPENDENT (1 << 1) +# define _LIBCPP_ASSERTION_SEMANTIC_IGNORE (1 << 2) +# define _LIBCPP_ASSERTION_SEMANTIC_OBSERVE (1 << 3) +# define _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE (1 << 4) +# define _LIBCPP_ASSERTION_SEMANTIC_ENFORCE (1 << 5) // 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 - -#else - +// If the user attempts to configure the assertion semantic, check that it is allowed in the current environment. +#if defined(_LIBCPP_ASSERTION_SEMANTIC) # if !_LIBCPP_HAS_EXPERIMENTAL_LIBRARY # error "Assertion semantics are an experimental feature." # endif # if defined(_LIBCPP_CXX03_LANG) # error "Assertion semantics are not available in the C++03 mode." # endif +#endif // defined(_LIBCPP_ASSERTION_SEMANTIC) + +// User-provided semantic takes top priority -- don't override if set. +#ifndef _LIBCPP_ASSERTION_SEMANTIC -#endif // _LIBCPP_ASSERTION_SEMANTIC +# ifndef _LIBCPP_ASSERTION_SEMANTIC_DEFAULT +# error _LIBCPP_ASSERTION_SEMANTIC_DEFAULT is not defined. This definition should be set at configuration time in \ +the `__config_site` header, please make sure your installation of libc++ is not broken. +# endif + +# if _LIBCPP_ASSERTION_SEMANTIC_DEFAULT != _LIBCPP_ASSERTION_SEMANTIC_HARDENING_DEPENDENT +# define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_DEFAULT +# else +# 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_DEFAULT != _LIBCPP_ASSERTION_SEMANTIC_HARDENING_DEPENDENT + +#endif // #ifndef _LIBCPP_ASSERTION_SEMANTIC + +// Finally, validate the selected semantic (in case the user tries setting it to an incorrect value): +#if _LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_IGNORE && \ + _LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_OBSERVE && \ + _LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE && \ + _LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_ENFORCE +# error _LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: \ +_LIBCPP_ASSERTION_SEMANTIC_IGNORE, \ +_LIBCPP_ASSERTION_SEMANTIC_OBSERVE, \ +_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, \ +_LIBCPP_ASSERTION_SEMANTIC_ENFORCE +#endif #endif // _LIBCPP___CONFIGURATION_HARDENING_H diff --git a/libcxx/test/extensions/libcxx/odr_signature.assertion_semantics.sh.cpp b/libcxx/test/extensions/libcxx/odr_signature.assertion_semantics.sh.cpp new file mode 100644 index 0000000000000..8ec1ec824c13c --- /dev/null +++ b/libcxx/test/extensions/libcxx/odr_signature.assertion_semantics.sh.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// ABI tags have no effect in MSVC mode. +// XFAIL: msvc + +// Assertion semantics are not supported in C++03 mode and currently are experimental. +// UNSUPPORTED: c++03, libcpp-has-no-experimental-hardening-observe-semantic + +// Test that we encode the assertion semantic in an ABI tag to avoid ODR violations when linking TUs that have different +// values for it. + +// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DTU1 -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_IGNORE -o %t.tu1.o +// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DTU2 -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_OBSERVE -o %t.tu2.o +// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DTU3 -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE -o %t.tu3.o +// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DTU4 -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE -o %t.tu4.o +// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DMAIN -o %t.main.o +// RUN: %{cxx} %t.tu1.o %t.tu2.o %t.tu3.o %t.tu4.o %t.main.o %{flags} %{link_flags} -o %t.exe +// RUN: %{exec} %t.exe + +#include "test_macros.h" + +// `ignore` assertion semantic. +#ifdef TU1 +# include <__config> +_LIBCPP_HIDE_FROM_ABI TEST_NOINLINE inline int f() { return 1; } +int tu1() { return f(); } +#endif // TU1 + +// `observe` assertion semantic. +#ifdef TU2 +# include <__config> +_LIBCPP_HIDE_FROM_ABI TEST_NOINLINE inline int f() { return 2; } +int tu2() { return f(); } +#endif // TU2 + +// `quick-enforce` assertion semantic. +#ifdef TU3 +# include <__config> +_LIBCPP_HIDE_FROM_ABI TEST_NOINLINE inline int f() { return 3; } +int tu3() { return f(); } +#endif // TU3 + +// `enforce` assertion semantic. +#ifdef TU4 +# include <__config> +_LIBCPP_HIDE_FROM_ABI TEST_NOINLINE inline int f() { return 4; } +int tu4() { return f(); } +#endif // TU4 + +#ifdef MAIN +# include + +int tu1(); +int tu2(); +int tu3(); +int tu4(); + +int main(int, char**) { + assert(tu1() == 1); + assert(tu2() == 2); + assert(tu3() == 3); + assert(tu4() == 4); + return 0; +} +#endif // MAIN diff --git a/libcxx/test/libcxx/assertions/semantics/assertion_semantic_incorrect_value.sh.cpp b/libcxx/test/libcxx/assertions/semantics/assertion_semantic_incorrect_value.sh.cpp new file mode 100644 index 0000000000000..d7c1e26491ba9 --- /dev/null +++ b/libcxx/test/libcxx/assertions/semantics/assertion_semantic_incorrect_value.sh.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 test verifies that setting the assertion semantic to a value that's not part of the predefined constants +// triggers a compile-time error. + +// Modules build produces a different error ("Could not build module 'std'"). +// UNSUPPORTED: clang-modules-build +// UNSUPPORTED: c++03, libcpp-has-no-experimental-hardening-observe-semantic +// REQUIRES: verify-support + +// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=42 +// `hardening-dependent` cannot be set as the semantic (it's only an indicator to use hardening-related logic to pick +// the final semantic). +// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_HARDENING_DEPENDENT +// Make sure that common cases of misuse produce readable errors. We deliberately disallow setting the assertion +// semantic as if it were a boolean flag. +// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=0 +// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=1 +// RUN: %{verify} -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC + +#include + +// expected-error@*:* {{_LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: _LIBCPP_ASSERTION_SEMANTIC_IGNORE, _LIBCPP_ASSERTION_SEMANTIC_OBSERVE, _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, _LIBCPP_ASSERTION_SEMANTIC_ENFORCE}} diff --git a/libcxx/test/libcxx/assertions/semantics/override_with_enforce_semantic.pass.cpp b/libcxx/test/libcxx/assertions/semantics/override_with_enforce_semantic.pass.cpp new file mode 100644 index 0000000000000..056864e1aea71 --- /dev/null +++ b/libcxx/test/libcxx/assertions/semantics/override_with_enforce_semantic.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 test ensures that we can override the assertion semantic used by any checked hardening mode with `enforce` on +// a per-TU basis (this is valid for the `debug` mode as well, though a no-op). + +// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support. +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, no-localization +// UNSUPPORTED: libcpp-hardening-mode=none, libcpp-has-no-experimental-hardening-observe-semantic +// The ability to set a custom abort message is required to compare the assertion message. +// XFAIL: availability-verbose_abort-missing +// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE + +#include +#include "check_assertion.h" + +int main(int, char**) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire"); + TEST_LIBCPP_ASSERT_FAILURE([] { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Should fire and log a message"); }(), + "Should fire and log a message"); + + return 0; +} diff --git a/libcxx/test/libcxx/assertions/semantics/override_with_ignore_semantic.pass.cpp b/libcxx/test/libcxx/assertions/semantics/override_with_ignore_semantic.pass.cpp new file mode 100644 index 0000000000000..b8c9028fe2e5c --- /dev/null +++ b/libcxx/test/libcxx/assertions/semantics/override_with_ignore_semantic.pass.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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 test ensures that we can override the assertion semantic used by any hardening mode with `ignore` on a per-TU +// basis (this is valid for the `none` mode as well, though a no-op). + +// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support. +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, no-localization +// UNSUPPORTED: libcpp-has-no-experimental-hardening-observe-semantic +// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_IGNORE + +#include +#include "check_assertion.h" + +int main(int, char**) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire"); + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Also should not fire"); + + return 0; +} diff --git a/libcxx/test/libcxx/assertions/semantics/override_with_observe_semantic.pass.cpp b/libcxx/test/libcxx/assertions/semantics/override_with_observe_semantic.pass.cpp new file mode 100644 index 0000000000000..a14c44f5a8e73 --- /dev/null +++ b/libcxx/test/libcxx/assertions/semantics/override_with_observe_semantic.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 test ensures that we can override the assertion semantic used by any checked hardening mode with `observe` on +// a per-TU basis. + +// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support. +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, no-localization +// UNSUPPORTED: libcpp-hardening-mode=none, libcpp-has-no-experimental-hardening-observe-semantic +// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_OBSERVE + +#include +#include "check_assertion.h" + +int main(int, char**) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire"); + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Also should not fire"); + // TODO(hardening): check that a message is logged. + + return 0; +} diff --git a/libcxx/test/libcxx/assertions/semantics/override_with_quick_enforce_semantic.pass.cpp b/libcxx/test/libcxx/assertions/semantics/override_with_quick_enforce_semantic.pass.cpp new file mode 100644 index 0000000000000..be5038c4bb4ff --- /dev/null +++ b/libcxx/test/libcxx/assertions/semantics/override_with_quick_enforce_semantic.pass.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 test ensures that we can override the assertion semantic used by any checked hardening mode with `quick-enforce` +// on a per-TU basis (this is valid for the `fast` and `extensive` modes as well, though a no-op). + +// `check_assertion.h` is only available starting from C++11 and requires Unix headers and regex support. +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, no-localization +// UNSUPPORTED: libcpp-hardening-mode=none, libcpp-has-no-experimental-hardening-observe-semantic +// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_ASSERTION_SEMANTIC -D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE + +#include +#include "check_assertion.h" + +int main(int, char**) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(true, "Should not fire"); + TEST_LIBCPP_ASSERT_FAILURE( + [] { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(false, "Should fire without logging a message"); }(), + "The message should not matter"); + + return 0; +} diff --git a/libcxx/test/support/test.support/test_check_assertion.pass.cpp b/libcxx/test/support/test.support/test_check_assertion.pass.cpp index 78e47b32cdd2b..9d356ef30a501 100644 --- a/libcxx/test/support/test.support/test_check_assertion.pass.cpp +++ b/libcxx/test/support/test.support/test_check_assertion.pass.cpp @@ -21,11 +21,11 @@ template bool TestDeathTest( Outcome expected_outcome, DeathCause expected_cause, const char* stmt, Func&& func, const Matcher& matcher) { auto get_matcher = [&] { -#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG - return matcher; -#else +#if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE (void)matcher; return MakeAnyMatcher(); +#else + return matcher; #endif }; @@ -69,7 +69,7 @@ bool TestDeathTest( // clang-format on -#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG +#if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE DeathCause assertion_death_cause = DeathCause::VerboseAbort; #else DeathCause assertion_death_cause = DeathCause::Trap; @@ -99,7 +99,7 @@ int main(int, char**) { // Success -- assertion failure with a specific matcher. TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, good_matcher, fail_assert()); -# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG +# if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE // Failure -- error message doesn't match. TEST_DEATH_TEST_MATCHES(Outcome::UnexpectedErrorMessage, assertion_death_cause, bad_matcher, fail_assert()); # endif