Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions absl/base/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1161,4 +1161,41 @@ struct AbslInternal_YouForgotToExplicitlyInitializeAField {
#define ABSL_UNSAFE_BUFFERS(...) __VA_ARGS__
#endif

// ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER
//
// Marks a two-parameter constructor or factory function of a container or view
// type that takes a (pointer, size) or (begin, end) pair, such as
// `absl::Span<T>(T* ptr, size_t size)`. When Clang's
// `-Wunsafe-buffer-usage-in-container` diagnostic is enabled, calls to an
// annotated function emit a warning unless the arguments are provably safe,
// e.g. `c.data(), c.size()` or `c.begin(), c.end()` on the same container `c`,
// a constant-size array and its size, or when the size evaluates to zero.
//
// Example:
//
// template <typename T>
// class MySpan {
// public:
// ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER
// MySpan(T* ptr, size_t size);
// };
//
// void F(int* p, size_t n, std::vector<int>& v) {
// MySpan<int> s1(p, n); // Warning
// MySpan<int> s2(v.data(), v.size()); // OK
// }
//
// Unavoidable unsafe calls can be suppressed with `ABSL_UNSAFE_BUFFERS()`.
// Note that, as with `[[clang::unsafe_buffer_usage]]`, Clang does not check the
// body of an annotated function for unsafe buffer usage.
//
// See the upstream documentation:
// https://clang.llvm.org/docs/AttributeReference.html#unsafe-buffer-usage
#if ABSL_HAVE_CPP_ATTRIBUTE(clang::unsafe_buffer_usage_in_container)
#define ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER \
[[clang::unsafe_buffer_usage_in_container]]
#else
#define ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER
#endif

#endif // ABSL_BASE_ATTRIBUTES_H_
15 changes: 15 additions & 0 deletions absl/base/attributes_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "absl/base/attributes.h"

#include <cstddef>
#include <type_traits>

#include "gtest/gtest.h"
Expand Down Expand Up @@ -72,4 +73,18 @@ TEST(Attributes, UnsafeBuffers) {
EXPECT_EQ(buffer[1], 1);
}

TEST(Attributes, UnsafeBufferUsageInContainer) {
struct IntSpan {
ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER
IntSpan(int* p, size_t n) : ptr(p), size(n) {}

int* ptr;
size_t size;
};
int buffer[3] = {0};
IntSpan span(buffer, 3);
EXPECT_EQ(span.ptr, buffer);
EXPECT_EQ(span.size, 3u);
}

} // namespace
21 changes: 12 additions & 9 deletions absl/types/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ class ABSL_ATTRIBUTE_VIEW Span {
static constexpr size_type npos = static_cast<size_type>(-1);

constexpr Span() noexcept : Span(nullptr, 0) {}
ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER
constexpr Span(pointer array ABSL_ATTRIBUTE_LIFETIME_BOUND,
size_type length) noexcept
: ptr_(array), len_(length) {}
Expand Down Expand Up @@ -716,14 +717,15 @@ constexpr bool operator>=(Span<T> a, const U& b) {
// or pass the container directly instead of using `&v[0]` or `&v[v.size()]`.
//
template <int&... ExplicitArgumentBarrier, typename T>
constexpr Span<T> MakeSpan(T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND,
size_t size) noexcept {
ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER constexpr Span<T> MakeSpan(
T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, size_t size) noexcept {
return Span<T>(ptr, size);
}

template <int&... ExplicitArgumentBarrier, typename T>
Span<T> MakeSpan(T* absl_nullable begin ABSL_ATTRIBUTE_LIFETIME_BOUND,
T* absl_nullable end) noexcept {
ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER Span<T> MakeSpan(
T* absl_nullable begin ABSL_ATTRIBUTE_LIFETIME_BOUND,
T* absl_nullable end) noexcept {
absl::base_internal::HardeningAssertLE(begin, end);
return Span<T>(begin, static_cast<size_t>(end - begin));
}
Expand Down Expand Up @@ -776,15 +778,16 @@ constexpr Span<T> MakeSpan(
// ProcessInts(absl::MakeConstSpan(std::vector<int>{ 0, 0, 0 }));
//
template <int&... ExplicitArgumentBarrier, typename T>
constexpr Span<const T> MakeConstSpan(
T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND, size_t size) noexcept {
ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER constexpr Span<const T>
MakeConstSpan(T* absl_nullable ptr ABSL_ATTRIBUTE_LIFETIME_BOUND,
size_t size) noexcept {
return Span<const T>(ptr, size);
}

template <int&... ExplicitArgumentBarrier, typename T>
Span<const T> MakeConstSpan(T* absl_nullable begin
ABSL_ATTRIBUTE_LIFETIME_BOUND,
T* absl_nullable end) noexcept {
ABSL_ATTRIBUTE_UNSAFE_BUFFER_USAGE_IN_CONTAINER Span<const T> MakeConstSpan(
T* absl_nullable begin ABSL_ATTRIBUTE_LIFETIME_BOUND,
T* absl_nullable end) noexcept {
absl::base_internal::HardeningAssertLE(begin, end);
return Span<const T>(begin, end - begin);
}
Expand Down
Loading