Add absl::SourceLocation PiperOrigin-RevId: 881550268 Change-Id: I1add4438cecc44f41f01c0570c69bb85f706ecfc
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index cbeb96c..842c6b3 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -454,6 +454,7 @@ "types/internal/span.h" "types/optional.h" "types/optional_ref.h" + "types/source_location.h" "types/span.h" "types/variant.h" "utility/utility.h"
diff --git a/CMakeLists.txt b/CMakeLists.txt index a31c6a9..3583e3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -227,7 +227,7 @@ # Handle features that require at least C++20. if (ABSL_INTERNAL_AT_LEAST_CXX20) - foreach(FEATURE "ORDERING") + foreach(FEATURE "ORDERING" "SOURCE_LOCATION") string(REPLACE "#define ABSL_OPTION_USE_STD_${FEATURE} 2" "#define ABSL_OPTION_USE_STD_${FEATURE} 1"
diff --git a/absl/base/config.h b/absl/base/config.h index 82e8009..b707654 100644 --- a/absl/base/config.h +++ b/absl/base/config.h
@@ -531,6 +531,42 @@ #define ABSL_HAVE_STD_VARIANT 1 #define ABSL_USES_STD_VARIANT 1 +// ABSL_HAVE_STD_SOURCE_LOCATION +// +// Checks whether C++20 std::source_location is available. +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION +#error "ABSL_HAVE_STD_SOURCE_LOCATION cannot be directly set." +#elif (defined(__cpp_lib_source_location) && \ + __cpp_lib_source_location >= 201907L) || \ + (defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L) +#ifdef __has_include +#if __has_include(<source_location>) +#define ABSL_HAVE_STD_SOURCE_LOCATION 1 +#endif +#else +// No __has_include support, so just assume C++ language version is correct. +#define ABSL_HAVE_STD_SOURCE_LOCATION 1 +#endif +#endif + +// ABSL_USES_STD_SOURCE_LOCATION +// +// Indicates whether absl::SourceLocation is an alias for std::source_location. +#if !defined(ABSL_OPTION_USE_STD_SOURCE_LOCATION) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_SOURCE_LOCATION == 0 || \ + (ABSL_OPTION_USE_STD_SOURCE_LOCATION == 2 && \ + !defined(ABSL_HAVE_STD_SOURCE_LOCATION)) +#undef ABSL_USES_STD_SOURCE_LOCATION +#elif ABSL_OPTION_USE_STD_SOURCE_LOCATION == 1 || \ + (ABSL_OPTION_USE_STD_SOURCE_LOCATION == 2 && \ + defined(ABSL_HAVE_STD_SOURCE_LOCATION)) +#define ABSL_USES_STD_SOURCE_LOCATION 1 +#else +#error options.h is misconfigured. +#endif + // ABSL_HAVE_STD_ORDERING // // Checks whether C++20 std::{partial,weak,strong}_ordering are available. @@ -799,6 +835,14 @@ #define ABSL_INTERNAL_HAVE_ARM_NEON 1 #endif +#if ABSL_HAVE_BUILTIN(__builtin_LINE) && ABSL_HAVE_BUILTIN(__builtin_FILE) +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#elif defined(__GNUC__) && !defined(__clang__) && 5 <= __GNUC__ && __GNUC__ < 10 +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#elif defined(_MSC_VER) && _MSC_VER >= 1926 +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#endif + // ABSL_HAVE_CONSTANT_EVALUATED is used for compile-time detection of // constant evaluation support through `absl::is_constant_evaluated`. #ifdef ABSL_HAVE_CONSTANT_EVALUATED
diff --git a/absl/base/options.h b/absl/base/options.h index 6f48e75..cfacc6e 100644 --- a/absl/base/options.h +++ b/absl/base/options.h
@@ -73,6 +73,34 @@ // Type Compatibility Options // ----------------------------------------------------------------------------- +// ABSL_OPTION_USE_STD_SOURCE_LOCATION +// +// This option controls whether absl::SourceLocation is implemented as an alias +// to the std::source_location type, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++17 +// support, and is expected to run on every toolchain we support, and to +// properly capture source location information on every toolchain that supports +// the necessary built-ins (such as `__builtin_LINE`). +// +// A value of 1 means to use aliases. This requires that all code using Abseil +// is built in C++20 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if working std::source_location types are available. +// This option is useful when you are building your program from source. It +// should not be used otherwise -- for example, if you are distributing Abseil +// in a binary package manager -- since in mode 2, they will name different +// types, with different mangled names and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// the source location type is an alias of std::source_location type, use the +// feature macro ABSL_USES_STD_SOURCE_LOCATION. +// +#define ABSL_OPTION_USE_STD_SOURCE_LOCATION 2 + // ABSL_OPTION_USE_STD_ORDERING // // This option controls whether absl::{partial,weak,strong}_ordering are
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index a52c358..19709a5 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel
@@ -51,6 +51,32 @@ ) cc_library( + name = "source_location", + hdrs = ["source_location.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + "//absl/base:nullability", + ], +) + +cc_test( + name = "source_location_test", + size = "small", + srcs = ["source_location_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":source_location", + "//absl/base:config", + "//absl/strings:string_view", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_library( name = "span", srcs = [ "internal/span.h",
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index 75dd07d..f69a1ea 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt
@@ -129,6 +129,32 @@ absl_cc_library( NAME + source_location + HDRS + "source_location.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::nullability + PUBLIC +) + +absl_cc_test( + NAME + source_location_test + SRCS + "source_location_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::source_location + GTest::gmock_main +) + +absl_cc_library( + NAME compare HDRS "compare.h"
diff --git a/absl/types/source_location.h b/absl/types/source_location.h new file mode 100644 index 0000000..6e14836 --- /dev/null +++ b/absl/types/source_location.h
@@ -0,0 +1,172 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: source_location.h +// ----------------------------------------------------------------------------- +// +// absl::SourceLocation provides source-code location info for C++17 and later. +// +// Critically, it is a **view type** (like std::string_view). Unlike +// std::source_location, it is not permanently valid and must not outlive its +// source. Using an invalid location is an error and may result in warnings +// or crashes. +// +// Additionally, it does not guarantee the retention of all caller information +// (e.g. column or function name) and may e.g. return unspecified values for +// performance reasons. +// +// To define a function that has access to the source location of the +// callsite, define it with a parameter of type `absl::SourceLocation`. The +// caller can then invoke the function, passing +// `absl::SourceLocation::current()` as the argument. +// +// If at all possible, make the `absl::SourceLocation` parameter be the +// function's last parameter. That way, when `std::source_location` is +// available, you will be able to switch to it, and give the parameter a default +// argument of `std::source_location::current()`. Users will then be able to +// omit that argument, and the default will automatically capture the location +// of the callsite. + +#ifndef ABSL_TYPES_SOURCE_LOCATION_H_ +#define ABSL_TYPES_SOURCE_LOCATION_H_ + +#include <cstdint> +#include <type_traits> + +#include "absl/base/config.h" +#include "absl/base/nullability.h" + +// This needs to come after absl/base/config.h, which is responsible for +// defining ABSL_HAVE_STD_SOURCE_LOCATION +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION +#include <source_location> // NOLINT(build/c++20) +#endif + +// For OSS release, whether to alias to std::source_location is configurable via +// config.h/options.h, similar to std::string_view/variant/etc. +#if defined(ABSL_USES_STD_SOURCE_LOCATION) && \ + defined(ABSL_HAVE_STD_SOURCE_LOCATION) +namespace absl { +ABSL_NAMESPACE_BEGIN +using SourceLocation = std::source_location; +ABSL_NAMESPACE_END +} // namespace absl + +#else // ABSL_HAVE_STD_SOURCE_LOCATION + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// C++17-compatible class representing a specific location in the source code of +// a program. Similar to std::source_location, but with a few key differences +// explained above. +class SourceLocation { + struct PrivateTag { + private: + explicit PrivateTag() = default; + friend class SourceLocation; + }; + + public: + // Avoid this constructor; it populates the object with dummy values. + SourceLocation() = default; + +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION + constexpr SourceLocation( // NOLINT(google-explicit-constructor) + std::source_location loc) + : SourceLocation(loc.line(), loc.file_name()) {} +#endif + +#ifdef ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE + // SourceLocation::current + // + // Creates a `SourceLocation` based on the current source location. Currently, + // it only captures file and line information for efficiency purposes, but + // that is subject to change. APIs that accept a `SourceLocation` as a default + // parameter can use this to capture their caller's locations. + // + // Example: + // + // void TracedAdd(int i, SourceLocation loc = SourceLocation::current()) { + // std::cout << loc.file_name() << ":" << loc.line() << " added " << i; + // ... + // } + // + // void UserCode() { + // TracedAdd(1); + // TracedAdd(2); + // } + static constexpr SourceLocation current( + PrivateTag = PrivateTag{}, std::uint_least32_t line = __builtin_LINE(), + const char* absl_nonnull file_name = __builtin_FILE()) { + return SourceLocation(line, file_name); + } +#else + // Creates a dummy `SourceLocation` of "<source_location>" at line number 1, + // if no `SourceLocation::current()` implementation is available. + static constexpr SourceLocation current() { + return SourceLocation(1, "<source_location>"); + } +#endif + // The line number of the captured source location, or an unspecified value + // if this information is not available. + constexpr std::uint_least32_t line() const noexcept { return line_; } + + // The column number of the captured source location, or an unspecified value + // if this information is not available. + constexpr std::uint_least32_t column() const noexcept { return 0; } + + // The file name of the captured source location, or an unspecified string + // if this information is not available. Guaranteed to never be NULL. + constexpr const char* absl_nonnull file_name() const noexcept { + return file_name_; + } + + // The function name of the captured source location, or an unspecified string + // if this information is not available. Guaranteed to never be NULL. + // + // NOTE: Currently, we deliberately avoid providing the function name, as it + // can bloat binary sizes and is non-critical. This may change in the future. + constexpr const char* absl_nonnull function_name() const noexcept { + return ""; + } + + private: + // `file_name` must outlive all copies of the `absl::SourceLocation` object, + // so in practice it should be a string literal. + constexpr SourceLocation(std::uint_least32_t line, + const char* absl_nonnull file_name) + : line_(line), file_name_(file_name) {} + + // We would use [[maybe_unused]] here, but it doesn't work on all supported + // toolchains at the moment. + friend constexpr int UseUnused() { + static_assert(SourceLocation(0, nullptr).unused_column_ == 0, + "Use the otherwise-unused member."); + return 0; + } + + // "unused" members are present to minimize future changes in the size of this + // type. + std::uint_least32_t line_ = 0; + std::uint_least32_t unused_column_ = 0; + const char* absl_nonnull file_name_ = ""; +}; + +ABSL_NAMESPACE_END +} // namespace absl +#endif // ABSL_HAVE_STD_SOURCE_LOCATION + +#endif // ABSL_TYPES_SOURCE_LOCATION_H_
diff --git a/absl/types/source_location_test.cc b/absl/types/source_location_test.cc new file mode 100644 index 0000000..a312aa9 --- /dev/null +++ b/absl/types/source_location_test.cc
@@ -0,0 +1,139 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/types/source_location.h" + +#include "absl/base/config.h" + +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION +#include <source_location> // NOLINT(build/c++20) +#endif +#include <type_traits> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" + +namespace { + +using ::absl::SourceLocation; +using ::testing::EndsWith; + +TEST(SourceLocationTest, DefaultConstructionWorks) { + static_assert(!std::is_trivially_default_constructible_v<SourceLocation>); + static_assert(std::is_nothrow_default_constructible_v<SourceLocation>); + constexpr SourceLocation loc1 [[maybe_unused]]; + SourceLocation loc2 [[maybe_unused]]{}; + EXPECT_EQ(loc1.line(), loc2.line()); +} + +#ifdef ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE + +TEST(SourceLocationTest, ConstexprMembers) { + constexpr SourceLocation loc1 = absl::SourceLocation::current(); + const SourceLocation loc2 = absl::SourceLocation::current(); + EXPECT_EQ(loc1.line(), loc2.line() - 1); + EXPECT_EQ(absl::string_view(loc1.file_name()), loc2.file_name()); +} + +TEST(SourceLocationTest, ConversionFromStdSourceLocationWorks) { +#ifndef ABSL_HAVE_STD_SOURCE_LOCATION + GTEST_SKIP() << "std::source_location is not available"; +#else + constexpr SourceLocation loc1 = std::source_location::current(); + const std::source_location loc2 = std::source_location::current(); + EXPECT_EQ(loc1.line(), loc2.line() - 1); + EXPECT_EQ(absl::string_view(loc1.file_name()), loc2.file_name()); +#endif +} + +TEST(SourceLocationTest, CopyConstructionWorks) { + constexpr SourceLocation location = absl::SourceLocation::current(); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +TEST(SourceLocationTest, CopyAssignmentWorks) { + SourceLocation location; + location = absl::SourceLocation::current(); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +SourceLocation Echo(const SourceLocation& location) { return location; } + +TEST(SourceLocationTest, ExpectedUsageWorks) { + SourceLocation location = Echo(absl::SourceLocation::current()); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +TEST(SourceLocationTest, CurrentWorks) { + constexpr SourceLocation location = SourceLocation::current(); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +SourceLocation FuncWithDefaultParam( + SourceLocation loc = SourceLocation::current()) { + return loc; +} + +TEST(SourceLocationTest, CurrentWorksAsDefaultParam) { + SourceLocation location = FuncWithDefaultParam(); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +#endif + +template <typename T> +bool TryPassLineAndFile(decltype(T::current(0, ""))*) { + return true; +} +template <typename T> +bool TryPassLineAndFile(decltype(T::current({}, 0, ""))*) { + return true; +} +template <typename T> +bool TryPassLineAndFile(decltype(T::current(typename T::Tag{}, 0, ""))*) { + return true; +} +template <typename T> +bool TryPassLineAndFile(...) { + return false; +} + +TEST(SourceLocationTest, CantPassLineAndFile) { +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION + using StdSourceLocation = std::source_location; +#else + using StdSourceLocation = void; +#endif + if constexpr (!std::is_same_v<absl::SourceLocation, StdSourceLocation>) { + EXPECT_FALSE(TryPassLineAndFile<absl::SourceLocation>(nullptr)); + } +} + +} // namespace
diff --git a/ci/absl_alternate_options.h b/ci/absl_alternate_options.h index d5567f3..7f70aad 100644 --- a/ci/absl_alternate_options.h +++ b/ci/absl_alternate_options.h
@@ -20,6 +20,7 @@ #ifndef ABSL_CI_ABSL_ALTERNATE_OPTIONS_H_ #define ABSL_CI_ABSL_ALTERNATE_OPTIONS_H_ +#define ABSL_OPTION_USE_STD_SOURCE_LOCATION 0 #define ABSL_OPTION_USE_STD_ORDERING 0 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 #define ABSL_OPTION_INLINE_NAMESPACE_NAME ns