// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2022, The Eigen authors. // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef EIGEN_CORE_UTIL_ASSERT_H #define EIGEN_CORE_UTIL_ASSERT_H // Eigen custom assert function. // // The combination of Eigen's relative includes and cassert's `assert` function // (or any usage of the __FILE__ macro) can lead to ODR issues: // a header included using different relative paths in two different TUs will // have two different token-for-token definitions, since __FILE__ is expanded // as an in-line string with different values. Normally this would be // harmless - the linker would just choose one definition. However, it breaks // with C++20 modules when functions in different modules have different // definitions. // // To get around this, we need to use __builtin_FILE() when available, which is // considered a single token, and thus satisfies the ODR. // Only define eigen_plain_assert if we are debugging, and either // - we are not compiling for GPU, or // - gpu debugging is enabled. #if !defined(EIGEN_NO_DEBUG) && (!defined(EIGEN_GPU_COMPILE_PHASE) || !defined(EIGEN_NO_DEBUG_GPU)) #include <cassert> #ifndef EIGEN_USE_CUSTOM_PLAIN_ASSERT // Disable new custom asserts by default for now. #define EIGEN_USE_CUSTOM_PLAIN_ASSERT … #endif #if EIGEN_USE_CUSTOM_PLAIN_ASSERT #ifndef EIGEN_HAS_BUILTIN_FILE // Clang can check if __builtin_FILE() is supported. // GCC > 5, MSVC 2019 14.26 (1926) all have __builtin_FILE(). // // For NVCC, it's more complicated. Through trial-and-error: // - nvcc+gcc supports __builtin_FILE() on host, and on device after CUDA 11. // - nvcc+msvc supports __builtin_FILE() only after CUDA 11. #if (EIGEN_HAS_BUILTIN(__builtin_FILE) && (EIGEN_COMP_CLANG || !defined(EIGEN_CUDA_ARCH))) || \ (EIGEN_GNUC_STRICT_AT_LEAST(5, 0, 0) && (EIGEN_COMP_NVCC >= 110000 || !defined(EIGEN_CUDA_ARCH))) || \ (EIGEN_COMP_MSVC >= 1926 && (!EIGEN_COMP_NVCC || EIGEN_COMP_NVCC >= 110000)) #define EIGEN_HAS_BUILTIN_FILE … #else #define EIGEN_HAS_BUILTIN_FILE … #endif #endif // EIGEN_HAS_BUILTIN_FILE #if EIGEN_HAS_BUILTIN_FILE #define EIGEN_BUILTIN_FILE … #define EIGEN_BUILTIN_LINE … #else // Default (potentially unsafe) values. #define EIGEN_BUILTIN_FILE … #define EIGEN_BUILTIN_LINE … #endif // Use __PRETTY_FUNCTION__ when available, since it is more descriptive, as // __builtin_FUNCTION() only returns the undecorated function name. // This should still be okay ODR-wise since it is a compiler-specific fixed // value. Mixing compilers will likely lead to ODR violations anyways. #if EIGEN_COMP_MSVC #define EIGEN_BUILTIN_FUNCTION … #elif EIGEN_COMP_GNUC #define EIGEN_BUILTIN_FUNCTION … #else #define EIGEN_BUILTIN_FUNCTION … #endif namespace Eigen { namespace internal { // Generic default assert handler. template <typename EnableIf = void, typename... EmptyArgs> struct assert_handler_impl { EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE static inline void run(const char* expression, const char* file, unsigned line, const char* function) { #ifdef EIGEN_GPU_COMPILE_PHASE // GPU device code doesn't allow stderr or abort, so use printf and raise an // illegal instruction exception to trigger a kernel failure. #ifndef EIGEN_NO_IO printf("Assertion failed at %s:%u in %s: %s\n", file == nullptr ? "<file>" : file, line, function == nullptr ? "<function>" : function, expression); #endif __trap(); #else // EIGEN_GPU_COMPILE_PHASE // Print to stderr and abort, as specified in <cassert>. #ifndef EIGEN_NO_IO fprintf(stderr, "Assertion failed at %s:%u in %s: %s\n", file == nullptr ? "<file>" : file, line, function == nullptr ? "<function>" : function, expression); #endif std::abort(); #endif // EIGEN_GPU_COMPILE_PHASE } }; // Use POSIX __assert_fail handler when available. // // This allows us to integrate with systems that have custom handlers. // // NOTE: this handler is not always available on all POSIX systems (otherwise // we could simply test for __unix__ or similar). The handler function name // seems to depend on the specific toolchain implementation, and differs between // compilers, platforms, OSes, etc. Hence, we detect support via SFINAE. template <typename... EmptyArgs> struct assert_handler_impl<void_t<decltype(__assert_fail((const char*)nullptr, // expression (const char*)nullptr, // file 0, // line (const char*)nullptr, // function std::declval<EmptyArgs>()... // Empty substitution required // for SFINAE. ))>, EmptyArgs...> { EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE static inline void run(const char* expression, const char* file, unsigned line, const char* function) { // GCC requires this call to be dependent on the template parameters. __assert_fail(expression, file, line, function, std::declval<EmptyArgs>()...); } }; EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE inline void __assert_handler(const char* expression, const char* file, unsigned line, const char* function) { assert_handler_impl<>::run(expression, file, line, function); } } // namespace internal } // namespace Eigen #define eigen_plain_assert … #else // EIGEN_USE_CUSTOM_PLAIN_ASSERT // Use regular assert. #define eigen_plain_assert(condition) … #endif // EIGEN_USE_CUSTOM_PLAIN_ASSERT #else // EIGEN_NO_DEBUG #define eigen_plain_assert … #endif // EIGEN_NO_DEBUG #endif // EIGEN_CORE_UTIL_ASSERT_H