diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst index aecac6692932b..5fde7bfbe9ff0 100644 --- a/libcxx/docs/ReleaseNotes/20.rst +++ b/libcxx/docs/ReleaseNotes/20.rst @@ -53,6 +53,8 @@ Improvements and New Features - The ``_LIBCPP_ENABLE_CXX20_REMOVED_UNCAUGHT_EXCEPTION`` macro has been added to make ``std::uncaught_exception`` available in C++20 and later modes. +- ``_LIBCPP_ASSUME(expression)`` now checks the expression is ``true`` during constant evaluation. + This means that all assertions are now checked regardless of hardening mode in constant expressions. Deprecations and Removals ------------------------- diff --git a/libcxx/include/__assert b/libcxx/include/__assert index 49769fb4d4497..b03c106771793 100644 --- a/libcxx/include/__assert +++ b/libcxx/include/__assert @@ -17,6 +17,15 @@ # pragma GCC system_header #endif +#ifdef _LIBCPP_COMPILER_CLANG_BASED +// TODO: use `_LIBCPP_DIAGNOSTIC_*` macros after #107715 is fixed in all supported clang compilers +# define _LIBCPP_ASSERT_IS_CONSTANT_EVALUATED \ + (_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wconstant-evaluated\"") \ + __builtin_is_constant_evaluated() _Pragma("clang diagnostic pop")) +#else +# define _LIBCPP_ASSERT_IS_CONSTANT_EVALUATED (__builtin_is_constant_evaluated()) +#endif + #define _LIBCPP_ASSERT(expression, message) \ (__builtin_expect(static_cast(expression), 1) \ ? (void)0 \ @@ -27,13 +36,18 @@ // assumptions without a clear optimization intent, disable that to avoid worsening the code generation. // See https://discourse.llvm.org/t/llvm-assume-blocks-optimization/71609 for a discussion. #if 0 && __has_builtin(__builtin_assume) -# define _LIBCPP_ASSUME(expression) \ +# define _LIBCPP_RUNTIME_ASSUME(expression) \ (_LIBCPP_DIAGNOSTIC_PUSH _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wassume") \ __builtin_assume(static_cast(expression)) _LIBCPP_DIAGNOSTIC_POP) #else -# define _LIBCPP_ASSUME(expression) ((void)0) +# define _LIBCPP_RUNTIME_ASSUME(expression) ((void)0) #endif +#define _LIBCPP_ASSUME(expression) \ + (_LIBCPP_ASSERT_IS_CONSTANT_EVALUATED \ + ? static_cast(expression) ? (void)0 : __builtin_unreachable() \ + : _LIBCPP_RUNTIME_ASSUME(expression)) + // clang-format off // Fast hardening mode checks. diff --git a/libcxx/include/experimental/__simd/vec_ext.h b/libcxx/include/experimental/__simd/vec_ext.h index 6c7fb8b09a467..6c4a8d98b1da9 100644 --- a/libcxx/include/experimental/__simd/vec_ext.h +++ b/libcxx/include/experimental/__simd/vec_ext.h @@ -37,7 +37,11 @@ inline constexpr bool is_abi_tag_v> = _Np > 0 && _Np <= template struct __simd_storage<_Tp, simd_abi::__vec_ext<_Np>> { - _Tp __data __attribute__((__vector_size__(std::__bit_ceil((sizeof(_Tp) * _Np))))); + // This doesn't work in GCC if it is directly inside the __vector_size__ attribute because of a call to + // __builtin_is_constant_evaluated. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105233 + static constexpr size_t __n_bytes = std::__bit_ceil(sizeof(_Tp) * _Np); + + _Tp __data __attribute__((__vector_size__(__n_bytes))); _LIBCPP_HIDE_FROM_ABI _Tp __get(size_t __idx) const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__idx < _Np, "Index is out of bounds"); diff --git a/libcxx/test/libcxx/assertions/constant_expression.verify.cpp b/libcxx/test/libcxx/assertions/constant_expression.verify.cpp new file mode 100644 index 0000000000000..a1fd93539c05b --- /dev/null +++ b/libcxx/test/libcxx/assertions/constant_expression.verify.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Make sure that both `_LIBCPP_ASSERT(false, ...)` and `_LIBCPP_ASSUME(false)` +// mean that a constant expression cannot be formed. + +#include <__assert> +#include "test_macros.h" + +// expected-note@*:* 0+ {{expanded from macro}} + +static_assert((_LIBCPP_ASSERT(false, "message"), true), ""); +// expected-error@-1 {{static assertion expression is not an integral constant expression}} + +static_assert((_LIBCPP_ASSUME(false), true), ""); +// expected-error@-1 {{static assertion expression is not an integral constant expression}} + +static_assert(!__builtin_constant_p(_LIBCPP_ASSERT(false, "message")), ""); +static_assert(!__builtin_constant_p(_LIBCPP_ASSUME(false)), ""); diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp index d59864eb87023..33e73d1a4ffa7 100644 --- a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp +++ b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp @@ -108,10 +108,12 @@ constexpr bool test() { return value; }; assert(std::ranges::clamp(3, 2, 4, std::ranges::less{}, projection_function) == 3); + // When assertions are enabled, we call the projection more times #if defined(_LIBCPP_HARDENING_MODE) && \ _LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_EXTENSIVE && \ _LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_DEBUG - assert(counter <= 3); + if (!std::__libcpp_is_constant_evaluated()) + assert(counter <= 3); #endif } diff --git a/libcxx/test/support/nasty_string.h b/libcxx/test/support/nasty_string.h index ea9d83ccf282a..6b3a82f71438f 100644 --- a/libcxx/test/support/nasty_string.h +++ b/libcxx/test/support/nasty_string.h @@ -40,8 +40,8 @@ #ifndef TEST_HAS_NO_NASTY_STRING // Make sure the char-like operations in strings do not depend on the char-like type. struct nasty_char { - template - friend auto operator<=>(T, T) = delete; + template + friend auto operator<=>(T, U) = delete; template friend void operator+(T&&) = delete;