diff --git a/libcxx/include/source_location b/libcxx/include/source_location new file mode 100644 --- /dev/null +++ b/libcxx/include/source_location @@ -0,0 +1,81 @@ +// -*- 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 _LIBCPP_SOURCE_LOCATION +#define _LIBCPP_SOURCE_LOCATION + +/* source_location synopsis + +namespace std { + struct source_location { + static consteval source_location current() noexcept; + constexpr source_location() noexcept; + + constexpr uint_least32_t line() const noexcept; + constexpr uint_least32_t column() const noexcept; + constexpr const char* file_name() const noexcept; + constexpr const char* function_name() const noexcept; + }; +} +*/ + +#include + +// TODO: this include is for uint_least32_t; should we just use +// __UINT_LEAST32_TYPE__ instead of including the header? +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if __cplusplus > 201703L && __has_builtin(__builtin_source_location) + +class source_location { + // Note: the name of the struct '__impl', and the names of its fields, are + // REQUIRED by __builtin_source_location, and cannot be changed. + struct __impl { + const char* _M_file_name; + const char* _M_function_name; + unsigned _M_line; + unsigned _M_column; + }; + const __impl* __ptr_ = nullptr; + +public: + // static_cast is redundant on Clang, but needed on GCC, which returns 'const void*' from the builtin. + static consteval source_location current(const __impl* __ptr = + static_cast(__builtin_source_location()) noexcept { + source_location __sl; + __sl.__ptr_ = __ptr; + return __sl; + } + constexpr source_location() noexcept = default; + + constexpr uint_least32_t line() const noexcept { + return __ptr_ ? __ptr_->_M_line : 0; + } + constexpr uint_least32_t column() const noexcept { + return __ptr_ ? __ptr_->_M_column : 0; + } + constexpr const char *file_name() const noexcept { + return __ptr_ ? __ptr_->_M_file_name : ""; + } + constexpr const char *function_name() const noexcept { + return __ptr_ ? __ptr_->_M_function_name : ""; + } +}; + +#endif // __cplusplus > 201703L && __has_builtin(__builtin_source_location) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_SOURCE_LOCATION diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -358,7 +358,9 @@ # endif # define __cpp_lib_shift 201806L // # define __cpp_lib_smart_ptr_for_overwrite 202002L -// # define __cpp_lib_source_location 201907L +#if __has_builtin(__builtin_source_location) +# define __cpp_lib_source_location 201907L +#endif # define __cpp_lib_span 202002L # define __cpp_lib_ssize 201902L # define __cpp_lib_starts_ends_with 201711L diff --git a/libcxx/test/std/language.support/support.srcloc/general.pass.cpp b/libcxx/test/std/language.support/support.srcloc/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/support.srcloc/general.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +#include +#include "test_macros.h" +#include +#include + +// Only run tests when support is available; it's not yet universally available +// in c++20 compilers. TODO: would it be better to exclude this test with some +// more "UNSUPPORTED" annotations? +#if defined(__cpp_lib_source_location) + +// Note: the standard doesn't strictly require the particular values asserted +// here, but does "suggest" them. Additional tests for details of how the +// implementation of current() chooses which location to report for more complex +// scenarios are in the Clang test-suite, and not replicated here. + +// A default-constructed value. +constexpr std::source_location empty; +static_assert(empty.line() == 0); +static_assert(empty.column() == 0); +static_assert(empty.file_name()[0] == '\0'); +static_assert(empty.function_name()[0] == '\0'); + +// A simple use of current() outside a function. +constexpr std::source_location cur = +# line 1000 "ss" + std::source_location::current(); +static_assert(cur.line() == 1000); +static_assert(cur.column() == 5); +static_assert(cur.file_name()[0] == 's' && cur.file_name()[1] == 's' && cur.file_name()[2] == '\0'); +static_assert(cur.function_name()[0] == '\0'); + +// and inside a function. +int main() { + auto local = +# line 2000 + std::source_location::current(); + assert(!strcmp(local.file_name(), "ss")); + assert(!strcmp(local.function_name(), "int main()")); + assert(local.line() == 2000); + assert(local.column() == 7); + + // Finally, the type should be copy-constructible + auto local2 = cur; + assert(!strcmp(local2.file_name(), cur.file_name())); + assert(!strcmp(local2.function_name(), cur.function_name())); + assert(local2.line() == cur.line()); + assert(local2.column() == cur.column()); + + // and copy-assignable. + local = cur; + assert(!strcmp(local.file_name(), cur.file_name())); + assert(!strcmp(local.function_name(), cur.function_name())); + assert(local.line() == cur.line()); + assert(local.column() == cur.column()); +} + +#else +int main() {} +#endif