Index: libc/config/linux/api.td =================================================================== --- libc/config/linux/api.td +++ libc/config/linux/api.td @@ -18,6 +18,35 @@ }]; } +def AssertMacro : MacroDef<"assert"> { + let Defn = [{ + #undef assert + + #ifdef NDEBUG + #define assert(e) (void)0 + #else + + #ifdef __cplusplus + extern "C" + #endif + _Noreturn void __assert_fail(const char *, const char *, unsigned, const char *); + + #define assert(e) \ + ((e) ? (void)0 : __assert_fail(#e, __FILE__, __LINE__, __PRETTY_FUNCTION__)) + + #endif + }]; +} + +def StaticAssertMacro : MacroDef<"static_assert"> { + let Defn = [{ + #ifndef __cplusplus + #undef static_assert + #define static_assert _Static_assert + #endif + }]; +} + def NullMacro : MacroDef<"NULL"> { let Defn = [{ #define __need_NULL @@ -35,6 +64,13 @@ }]; } +def AssertAPI : PublicAPI<"assert.h"> { + let Macros = [ + AssertMacro, + StaticAssertMacro, + ]; +} + def MathAPI : PublicAPI<"math.h"> { let Functions = [ "acos", Index: libc/include/CMakeLists.txt =================================================================== --- libc/include/CMakeLists.txt +++ libc/include/CMakeLists.txt @@ -27,6 +27,14 @@ llvm_libc_common_h ) +add_gen_header( + assert_h + DEF_FILE assert.h.def + GEN_HDR assert.h + DEPENDS + llvm_libc_common_h +) + add_gen_header( string_h DEF_FILE string.h.def Index: libc/include/assert.h.def =================================================================== --- /dev/null +++ libc/include/assert.h.def @@ -0,0 +1,14 @@ +//===---------------- C standard library header assert.h ------------------===// +// +// 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 <__llvm-libc-common.h> + +// This file may be usefully included multiple times to change assert()'s +// definition based on NDEBUG. + +%%public_api() Index: libc/lib/CMakeLists.txt =================================================================== --- libc/lib/CMakeLists.txt +++ libc/lib/CMakeLists.txt @@ -2,6 +2,9 @@ add_entrypoint_library( llvmlibc DEPENDS + # assert.h entrypoints + __assert_fail + # errno.h entrypoints __errno_location Index: libc/spec/stdc.td =================================================================== --- libc/spec/stdc.td +++ libc/spec/stdc.td @@ -16,6 +16,17 @@ PtrType IntPtr = PtrType; + HeaderSpec Assert = HeaderSpec< + "assert.h", + [ + Macro<"static_assert">, + Macro<"assert">, + ], + [], // Types + [], // Enumerations + [] + >; + HeaderSpec String = HeaderSpec< "string.h", [ @@ -285,6 +296,7 @@ >; let Headers = [ + Assert, Errno, Math, String, Index: libc/src/CMakeLists.txt =================================================================== --- libc/src/CMakeLists.txt +++ libc/src/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(assert) add_subdirectory(errno) add_subdirectory(math) add_subdirectory(signal) Index: libc/src/assert/CMakeLists.txt =================================================================== --- /dev/null +++ libc/src/assert/CMakeLists.txt @@ -0,0 +1,13 @@ +add_entrypoint_object( + __assert_fail + SRCS + __assert_fail.cpp + HDRS + assert.h + DEPENDS + abort + # These two dependencies are temporary and should be replaced by fprintf + # later. + sys_syscall_h + linux_syscall_h +) Index: libc/src/assert/__assert_fail.cpp =================================================================== --- /dev/null +++ libc/src/assert/__assert_fail.cpp @@ -0,0 +1,38 @@ +//===----------------- Implementation of __assert_fail --------------------===// +// +// 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 "src/assert/assert.h" +#include "src/stdlib/abort.h" + +// These includes are temporary. +#include "config/linux/syscall.h" // For internal syscall function. +#include "include/sys/syscall.h" // For syscall numbers. + +namespace __llvm_libc { + +// This is just a temporary solution to make assert available to internal +// llvm libc code. In the future writeToStderr will not exist and __assert_fail +// will call fprintf(stderr, ...). +static void writeToStderr(const char *s) { + size_t length = 0; + for (const char *curr = s; *curr; ++curr, ++length); + __llvm_libc::syscall(SYS_write, 2, s, length); +} + +void LLVM_LIBC_ENTRYPOINT(__assert_fail)(const char *assertion, const char *file, + unsigned line, const char *function) { + writeToStderr(file); + writeToStderr(": Assertion failed: '"); + writeToStderr(assertion); + writeToStderr("' in function: '"); + writeToStderr(function); + writeToStderr("'\n"); + __llvm_libc::abort(); +} + +} // namespace __llvm_libc Index: libc/src/assert/assert.h =================================================================== --- /dev/null +++ libc/src/assert/assert.h @@ -0,0 +1,31 @@ +//===-------------------- Internal header for assert ----------*- 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 LLVM_LIBC_SRC_ASSERT_ASSERT_H +#define LLVM_LIBC_SRC_ASSERT_ASSERT_H + +#include + +namespace __llvm_libc { + +[[noreturn]] void __assert_fail(const char *assertion, const char *file, unsigned line, + const char *function); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_ASSERT_ASSERT_H + +#undef assert + +#ifdef NDEBUG +#define assert(e) (void)0 +#else +#define assert(e) \ + ((e) ? (void)0 : \ + __llvm_libc::__assert_fail(#e, __FILE__, __LINE__, __PRETTY_FUNCTION__)) +#endif Index: libc/test/src/CMakeLists.txt =================================================================== --- libc/test/src/CMakeLists.txt +++ libc/test/src/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(assert) add_subdirectory(errno) add_subdirectory(signal) add_subdirectory(stdlib) Index: libc/test/src/assert/CMakeLists.txt =================================================================== --- /dev/null +++ libc/test/src/assert/CMakeLists.txt @@ -0,0 +1,15 @@ +add_libc_testsuite(libc_assert_unittests) + +add_libc_unittest( + assert_test + SUITE + libc_assert_unittests + SRCS + assert_test.cpp + DEPENDS + __assert_fail + # These are necessary for now because dependencies are not properly added. + abort + raise + _Exit +) Index: libc/test/src/assert/assert_test.cpp =================================================================== --- /dev/null +++ libc/test/src/assert/assert_test.cpp @@ -0,0 +1,32 @@ +//===---------------------- Unittests for assert --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#undef NDEBUG +#include "src/assert/assert.h" +#include "utils/UnitTest/Test.h" + +extern "C" int close(int); + +TEST(Assert, Enabled) { + // -1 matches against any signal, which is necessary for now until + // __llvm_libc::abort() unblocks SIGABRT. Close standard error for the + // child process so we don't print the assertion failure message. + EXPECT_DEATH( + [] { + close(2); + assert(0); + }, + -1); +} + +#define NDEBUG +#include "src/assert/assert.h" + +TEST(Assert, Disabled) { + EXPECT_EXITS([] { assert(0); }, 0); +}