Skip to content

Commit

Permalink
Add nullability attributes to nullability type aliases.
Browse files Browse the repository at this point in the history
This is a followup to the [previous
change](69195d5)
that added the `ABSL_NULLABILITY_COMPATIBLE` attribute macro.

Adding these attributes has the following benefits:

- Clang itself can now diagnose certain nullability errors through the
  `-Wnonnull` and `-Wnullability` warnings.

- The nullability annotations can now also be used on pointers to incomplete
  types, as we have removed the `IsSupportedType` mechanism that used the
  `absl_nullability_compatible` tag to check whether a type is
  nullability-compatible (which only worked for complete types) and instead let
  Clang perform this check through the `ABSL_NULLABILITY_COMPATIBLE` attribute
  (which also works on incomplete types).

PiperOrigin-RevId: 684342145
Change-Id: I94c8affd5be704cb49340058ced177f09ebd83a3
  • Loading branch information
martinboehme authored and copybara-github committed Oct 10, 2024
1 parent 8634e35 commit 485f2be
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 67 deletions.
73 changes: 17 additions & 56 deletions absl/base/internal/nullability_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,80 +26,41 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace nullability_internal {

// `IsNullabilityCompatible` checks whether its first argument is a class
// explicitly tagged as supporting nullability annotations. The tag is the type
// declaration `absl_nullability_compatible`.
template <typename, typename = void>
struct IsNullabilityCompatible : std::false_type {};

template <typename T>
struct IsNullabilityCompatible<
T, absl::void_t<typename T::absl_nullability_compatible>> : std::true_type {
};

template <typename T>
constexpr bool IsSupportedType = IsNullabilityCompatible<T>::value;

template <typename T>
constexpr bool IsSupportedType<T*> = true;

template <typename T, typename U>
constexpr bool IsSupportedType<T U::*> = true;

template <typename T, typename... Deleter>
constexpr bool IsSupportedType<std::unique_ptr<T, Deleter...>> = true;

template <typename T>
constexpr bool IsSupportedType<std::shared_ptr<T>> = true;

template <typename T>
struct EnableNullable {
static_assert(nullability_internal::IsSupportedType<std::remove_cv_t<T>>,
"Template argument must be a raw or supported smart pointer "
"type. See absl/base/nullability.h.");
using type = T;
};

template <typename T>
struct EnableNonnull {
static_assert(nullability_internal::IsSupportedType<std::remove_cv_t<T>>,
"Template argument must be a raw or supported smart pointer "
"type. See absl/base/nullability.h.");
using type = T;
};

template <typename T>
struct EnableNullabilityUnknown {
static_assert(nullability_internal::IsSupportedType<std::remove_cv_t<T>>,
"Template argument must be a raw or supported smart pointer "
"type. See absl/base/nullability.h.");
using type = T;
};

// Note: we do not apply Clang nullability attributes (e.g. _Nullable). These
// only support raw pointers, and conditionally enabling them only for raw
// pointers inhibits template arg deduction. Ideally, they would support all
// pointer-like types.
template <typename T, typename = typename EnableNullable<T>::type>
using NullableImpl
#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate)
[[clang::annotate("Nullable")]]
#endif
// Don't add the _Nullable attribute in Objective-C compiles. Many Objective-C
// projects enable the `-Wnullable-to-nonnull-conversion warning`, which is
// liable to produce false positives.
#if ABSL_HAVE_FEATURE(nullability_on_classes) && !defined(__OBJC__)
= T _Nullable;
#else
= T;
#endif

template <typename T, typename = typename EnableNonnull<T>::type>
template <typename T>
using NonnullImpl
#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate)
[[clang::annotate("Nonnull")]]
#endif
#if ABSL_HAVE_FEATURE(nullability_on_classes) && !defined(__OBJC__)
= T _Nonnull;
#else
= T;
#endif

template <typename T, typename = typename EnableNullabilityUnknown<T>::type>
template <typename T>
using NullabilityUnknownImpl
#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate)
[[clang::annotate("Nullability_Unspecified")]]
#endif
#if ABSL_HAVE_FEATURE(nullability_on_classes) && !defined(__OBJC__)
= T _Null_unspecified;
#else
= T;
#endif

} // namespace nullability_internal
ABSL_NAMESPACE_END
Expand Down
18 changes: 7 additions & 11 deletions absl/base/nullability.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,9 @@
// ...
// };
//
// Note: For the time being, nullability-compatible classes should additionally
// be marked with an `absl_nullability_compatible` nested type (this will soon
// be deprecated). The actual definition of this inner type is not relevant as
// it is used merely as a marker. It is common to use a using declaration of
// `absl_nullability_compatible` set to void.
//
// // Example:
// struct MyPtr {
// using absl_nullability_compatible = void;
// ...
// };
// Note: Compilers that don't support the `nullability_on_classes` feature will
// allow nullability annotations to be applied to any type, not just ones
// marked with `ABSL_NULLABILITY_COMPATIBLE`.
//
// DISCLAIMER:
// ===========================================================================
Expand Down Expand Up @@ -279,6 +271,10 @@ ABSL_NAMESPACE_END
// struct ABSL_NULLABILITY_COMPATIBLE MyPtr {
// ...
// };
//
// Note: Compilers that don't support the `nullability_on_classes` feature will
// allow nullability annotations to be applied to any type, not just ones marked
// with `ABSL_NULLABILITY_COMPATIBLE`.
#if ABSL_HAVE_FEATURE(nullability_on_classes)
#define ABSL_NULLABILITY_COMPATIBLE _Nullable
#else
Expand Down

0 comments on commit 485f2be

Please sign in to comment.