diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -272,7 +272,7 @@ ------------------------------------------------- ----------------- ``__cpp_lib_smart_ptr_for_overwrite`` *unimplemented* ------------------------------------------------- ----------------- - ``__cpp_lib_source_location`` *unimplemented* + ``__cpp_lib_source_location`` ``201907L`` ------------------------------------------------- ----------------- ``__cpp_lib_span`` ``202002L`` ------------------------------------------------- ----------------- diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -294,7 +294,7 @@ "`3390 `__","``make_move_iterator()``\ cannot be used to construct a ``move_iterator``\ for a move-only iterator","Prague","|Complete|","14.0","|ranges|" "`3393 `__","Missing/incorrect feature test macro for coroutines","Prague","|Complete|","14.0" "`3395 `__","Definition for three-way comparison needs to be updated (US 152)","Prague","","","|spaceship|" -"`3396 `__","Clarify point of reference for ``source_location::current()``\ (DE 169)","Prague","","" +"`3396 `__","Clarify point of reference for ``source_location::current()``\ (DE 169)","Prague","|Nothing To Do|","15.0" "`3397 `__","``ranges::basic_istream_view::iterator``\ should not provide ``iterator_category``\ ","Prague","","","|ranges|" "`3398 `__","``tuple_element_t``\ is also wrong for ``const subrange``\ ","Prague","|Complete|","14.0","|ranges|" "`3446 `__","``indirectly_readable_traits``\ ambiguity for types with both ``value_type``\ and ``element_type``\ ","November virtual meeting","|Complete|","13.0" diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -112,7 +112,7 @@ "`P1065R2 `__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0" "`P1135R6 `__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0" "`P1207R4 `__","LWG","Movability of Single-pass Iterators","Cologne","","" -"`P1208R6 `__","LWG","Adopt source_location for C++20","Cologne","","" +"`P1208R6 `__","LWG","Adopt source_location for C++20","Cologne","|Complete|","15.0" "`P1355R2 `__","LWG","Exposing a narrow contract for ceil2","Cologne","|Complete|","9.0" "`P1361R2 `__","LWG","Integration of chrono with text formatting","Cologne","","" "`P1423R3 `__","LWG","char8_t backward compatibility remediation","Cologne","|In Progress|","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -519,6 +519,7 @@ set setjmp.h shared_mutex + source_location span sstream stack diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -863,6 +863,10 @@ header "shared_mutex" export version } + module source_location { + header "source_location" + export * + } module span { header "span" export ranges.__ranges.enable_borrowed_range 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,79 @@ +// -*- 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 <__config> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && __has_builtin(__builtin_source_location) + +class source_location { + // The names source_location::__impl, _M_file_name, _M_function_name, _M_line, and _M_column + // are hard-coded in the compiler and must not be changed here. + struct __impl { + const char* _M_file_name; + const char* _M_function_name; + unsigned _M_line; + unsigned _M_column; + }; + const __impl* __ptr_ = nullptr; + // GCC returns the type 'const void*' from the builtin, while clang returns + // `const __impl*`. Per C++ [expr.const], casts from void* are not permitted + // in constant evaluation, so we don't want to use `void*` as the argument + // type unless the builtin returned that, anyhow, and the invalid cast is + // unavoidable. + using __bsl_ty = decltype(__builtin_source_location()); + +public: + // The defaulted __ptr argument is necessary so that the builtin is evaluated + // in the context of the caller. An explicit value should never be provided. + static consteval source_location current(__bsl_ty __ptr = __builtin_source_location()) noexcept { + source_location __sl; + __sl.__ptr_ = static_cast(__ptr); + return __sl; + } + _LIBCPP_HIDE_FROM_ABI constexpr source_location() noexcept = default; + + _LIBCPP_HIDE_FROM_ABI constexpr uint_least32_t line() const noexcept { return __ptr_ ? __ptr_->_M_line : 0; } + _LIBCPP_HIDE_FROM_ABI constexpr uint_least32_t column() const noexcept { return __ptr_ ? __ptr_->_M_column : 0; } + _LIBCPP_HIDE_FROM_ABI constexpr const char* file_name() const noexcept { return __ptr_ ? __ptr_->_M_file_name : ""; } + _LIBCPP_HIDE_FROM_ABI constexpr const char* function_name() const noexcept { + return __ptr_ ? __ptr_->_M_function_name : ""; + } +}; + +#endif // _LIBCPP_STD_VER > 17 && __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/libcxx/clang_tidy.sh.cpp b/libcxx/test/libcxx/clang_tidy.sh.cpp --- a/libcxx/test/libcxx/clang_tidy.sh.cpp +++ b/libcxx/test/libcxx/clang_tidy.sh.cpp @@ -159,6 +159,7 @@ #ifndef _LIBCPP_HAS_NO_THREADS # include #endif +#include #include #ifndef _LIBCPP_HAS_NO_LOCALIZATION # include diff --git a/libcxx/test/libcxx/double_include.sh.cpp b/libcxx/test/libcxx/double_include.sh.cpp --- a/libcxx/test/libcxx/double_include.sh.cpp +++ b/libcxx/test/libcxx/double_include.sh.cpp @@ -160,6 +160,7 @@ #ifndef _LIBCPP_HAS_NO_THREADS # include #endif +#include #include #ifndef _LIBCPP_HAS_NO_LOCALIZATION # include diff --git a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp --- a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp +++ b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp @@ -246,6 +246,8 @@ # include TEST_MACROS(); #endif +#include +TEST_MACROS(); #include TEST_MACROS(); #ifndef _LIBCPP_HAS_NO_LOCALIZATION diff --git a/libcxx/test/libcxx/nasty_macros.compile.pass.cpp b/libcxx/test/libcxx/nasty_macros.compile.pass.cpp --- a/libcxx/test/libcxx/nasty_macros.compile.pass.cpp +++ b/libcxx/test/libcxx/nasty_macros.compile.pass.cpp @@ -270,6 +270,7 @@ #ifndef _LIBCPP_HAS_NO_THREADS # include #endif +#include #include #ifndef _LIBCPP_HAS_NO_LOCALIZATION # include diff --git a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp --- a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp +++ b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp @@ -155,6 +155,7 @@ #ifndef _LIBCPP_HAS_NO_THREADS # include #endif +#include #include #ifndef _LIBCPP_HAS_NO_LOCALIZATION # include diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/source_location.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/source_location.version.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/source_location.version.pass.cpp @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_feature_test_macro_components.py +// and should not be edited manually. +// +// clang-format off + +// + +// Test the feature test macros defined by + +/* Constant Value + __cpp_lib_source_location 201907L [C++20] +*/ + +#include +#include "test_macros.h" + +#if TEST_STD_VER < 14 + +# ifdef __cpp_lib_source_location +# error "__cpp_lib_source_location should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 14 + +# ifdef __cpp_lib_source_location +# error "__cpp_lib_source_location should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 17 + +# ifdef __cpp_lib_source_location +# error "__cpp_lib_source_location should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 20 + +# if __has_builtin(__builtin_source_location) +# ifndef __cpp_lib_source_location +# error "__cpp_lib_source_location should be defined in c++20" +# endif +# if __cpp_lib_source_location != 201907L +# error "__cpp_lib_source_location should have the value 201907L in c++20" +# endif +# else +# ifdef __cpp_lib_source_location +# error "__cpp_lib_source_location should not be defined when __has_builtin(__builtin_source_location) is not defined!" +# endif +# endif + +#elif TEST_STD_VER > 20 + +# if __has_builtin(__builtin_source_location) +# ifndef __cpp_lib_source_location +# error "__cpp_lib_source_location should be defined in c++2b" +# endif +# if __cpp_lib_source_location != 201907L +# error "__cpp_lib_source_location should have the value 201907L in c++2b" +# endif +# else +# ifdef __cpp_lib_source_location +# error "__cpp_lib_source_location should not be defined when __has_builtin(__builtin_source_location) is not defined!" +# endif +# endif + +#endif // TEST_STD_VER > 20 + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -3403,16 +3403,16 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) +# if __has_builtin(__builtin_source_location) # ifndef __cpp_lib_source_location # error "__cpp_lib_source_location should be defined in c++20" # endif # if __cpp_lib_source_location != 201907L # error "__cpp_lib_source_location should have the value 201907L in c++20" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_source_location -# error "__cpp_lib_source_location should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_source_location should not be defined when __has_builtin(__builtin_source_location) is not defined!" # endif # endif @@ -4788,16 +4788,16 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) +# if __has_builtin(__builtin_source_location) # ifndef __cpp_lib_source_location # error "__cpp_lib_source_location should be defined in c++2b" # endif # if __cpp_lib_source_location != 201907L # error "__cpp_lib_source_location should have the value 201907L in c++2b" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_source_location -# error "__cpp_lib_source_location should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_source_location should not be defined when __has_builtin(__builtin_source_location) is not defined!" # endif # endif 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,85 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: clang-12, clang-13, clang-14 +// UNSUPPORTED: apple-clang-12, apple-clang-13, apple-clang-14 + +#include + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_move_constructible_v, "support.srcloc.cons (1.1)"); +static_assert(std::is_nothrow_move_assignable_v, "support.srcloc.cons (1.2)"); +static_assert(std::is_nothrow_swappable_v, "support.srcloc.cons (1.3)"); + +ASSERT_NOEXCEPT(std::source_location()); +ASSERT_NOEXCEPT(std::source_location::current()); + +// 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'); + +ASSERT_NOEXCEPT(empty.line()); +ASSERT_NOEXCEPT(empty.column()); +ASSERT_NOEXCEPT(empty.file_name()); +ASSERT_NOEXCEPT(empty.function_name()); +std::same_as auto line = empty.line(); +std::same_as auto column = empty.column(); +std::same_as auto file = empty.file_name(); +std::same_as auto function = empty.function_name(); + +// 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() > 0); +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(int, char**) { + auto local = +#line 2000 + std::source_location::current(); + assert(!strcmp(local.file_name(), "ss")); + assert(strstr(local.function_name(), "main")); + assert(local.line() == 2000); + assert(local.column() > 0); + + // 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()); + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -637,7 +637,8 @@ "name": "__cpp_lib_source_location", "values": { "c++20": 201907 }, "headers": ["source_location"], - "unimplemented": True, + "test_suite_guard": "__has_builtin(__builtin_source_location)", + "libcxx_guard": "__has_builtin(__builtin_source_location)", }, { "name": "__cpp_lib_span", "values": { "c++20": 202002 },