Skip to content

[libc++] Enable assertions unconditionally during constant evaluation #107453

Open
@MitalAshok

Description

@MitalAshok

Undefined behaviour when calling a library function is unspecified ([expr.const]p(6.1)). However, it is strange that whether it is detected depends on if libc++ is built with assertions or not.

Consider https://godbolt.org/z/Kx6ez5no5:

#include <vector>

constexpr int f() {
  std::vector<int> v;
  v.reserve(1);
  int& i = v.front();  // Library UB
  v.push_back(4);
  return i;
}

static_assert(f() == 4);

This doesn't compile with assertions:

<source>:11:15: error: static assertion expression is not an integral constant expression
   11 | static_assert(f() == 4);
      |               ^~~~~~~~
/opt/compiler-explorer/clang-assertions-trunk-20240904/bin/../include/c++/v1/vector:652:5: note: subexpression not valid in a constant expression
  652 |     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "front() called on an empty vector");
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-assertions-trunk-20240904/bin/../include/c++/v1/__assert:66:71: note: expanded from macro '_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS'
   66 | #  define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message)    _LIBCPP_ASSERT(expression, message)
      |                                                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-assertions-trunk-20240904/bin/../include/c++/v1/__assert:23:10: note: expanded from macro '_LIBCPP_ASSERT'
   23 |        : _LIBCPP_ASSERTION_HANDLER(__FILE__ ":" _LIBCPP_TOSTRING(__LINE__) ": assertion " _LIBCPP_TOSTRING(            \
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   24 |              expression) " failed: " message "\n"))
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-assertions-trunk-20240904/bin/../include/c++/v1/__assertion_handler:33:50: note: expanded from macro '_LIBCPP_ASSERTION_HANDLER'
   33 | #      define _LIBCPP_ASSERTION_HANDLER(message) __builtin_verbose_trap("libc++", message)
      |                                                  ^~~~~~~~~~~~~~~~~~~~~~
<source>:6:12: note: in call to 'v.front()'
    6 |   int& i = v.front();  // Library UB
      |            ^~~~~~~~~
<source>:11:15: note: in call to 'f()'
   11 | static_assert(f() == 4);
      |               ^~~

But does compile if assertions are disabled.

I believe libstdc++ does something like this, so this fails even without assertions enabled.

This can be done with __libcpp_is_constant_evaluated/__builtin_is_constant_evaluated. This also removes the need for [[maybe_unused]] for variables used just for assertions.

The downside is the compile time would to increase on non-debug builds, where LIBCPP_ASSERT(x) goes from (void) 0 to is_constant_evaluated() && !(x) ? assert_handler(...) : (void) 0. I haven't measured the actual impact, but I don't expect it to be much.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImproving things as opposed to bug fixing, e.g. new or missing featurehardeningIssues related to the hardening effortlibc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions