| // Copyright 2023 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: nullability.h |
| // ----------------------------------------------------------------------------- |
| // |
| // This header file defines a set of "templated annotations" for designating the |
| // expected nullability of pointers. These annotations allow you to designate |
| // pointers in one of three classification states: |
| // |
| // * "Non-null" (for pointers annotated `Nonnull<T>`), indicating that it is |
| // invalid for the given pointer to ever be null. |
| // * "Nullable" (for pointers annotated `Nullable<T>`), indicating that it is |
| // valid for the given pointer to be null. |
| // * "Unknown" (for pointers annotated `NullabilityUnknown<T>`), indicating |
| // that the given pointer has not been yet classified as either nullable or |
| // non-null. This is the default state of unannotated pointers. |
| // |
| // NOTE: unannotated pointers implicitly bear the annotation |
| // `NullabilityUnknown<T>`; you should rarely, if ever, see this annotation used |
| // in the codebase explicitly. |
| // |
| // ----------------------------------------------------------------------------- |
| // Nullability and Contracts |
| // ----------------------------------------------------------------------------- |
| // |
| // These nullability annotations allow you to more clearly specify contracts on |
| // software components by narrowing the *preconditions*, *postconditions*, and |
| // *invariants* of pointer state(s) in any given interface. It then depends on |
| // context who is responsible for fulfilling the annotation's requirements. |
| // |
| // For example, a function may receive a pointer argument. Designating that |
| // pointer argument as "non-null" tightens the precondition of the contract of |
| // that function. It is then the responsibility of anyone calling such a |
| // function to ensure that the passed pointer is not null. |
| // |
| // Similarly, a function may have a pointer as a return value. Designating that |
| // return value as "non-null" tightens the postcondition of the contract of that |
| // function. In this case, however, it is the responsibility of the function |
| // itself to ensure that the returned pointer is not null. |
| // |
| // Clearly defining these contracts allows providers (and consumers) of such |
| // pointers to have more confidence in their null state. If a function declares |
| // a return value as "non-null", for example, the caller should not need to |
| // check whether the returned value is `nullptr`; it can simply assume the |
| // pointer is valid. |
| // |
| // Of course most interfaces already have expectations on the nullability state |
| // of pointers, and these expectations are, in effect, a contract; often, |
| // however, those contracts are either poorly or partially specified, assumed, |
| // or misunderstood. These nullability annotations are designed to allow you to |
| // formalize those contracts within the codebase. |
| // |
| // ----------------------------------------------------------------------------- |
| // Using Nullability Annotations |
| // ----------------------------------------------------------------------------- |
| // |
| // It is important to note that these annotations are not distinct strong |
| // *types*. They are alias templates defined to be equal to the underlying |
| // pointer type. A pointer annotated `Nonnull<T*>`, for example, is simply a |
| // pointer of type `T*`. Each annotation acts as a form of documentation about |
| // the contract for the given pointer. Each annotation requires providers or |
| // consumers of these pointers across API boundaries to take appropriate steps |
| // when setting or using these pointers: |
| // |
| // * "Non-null" pointers should never be null. It is the responsibility of the |
| // provider of this pointer to ensure that the pointer may never be set to |
| // null. Consumers of such pointers can treat such pointers as non-null. |
| // * "Nullable" pointers may or may not be null. Consumers of such pointers |
| // should precede any usage of that pointer (e.g. a dereference operation) |
| // with a a `nullptr` check. |
| // * "Unknown" pointers may be either "non-null" or "nullable" but have not been |
| // definitively determined to be in either classification state. Providers of |
| // such pointers across API boundaries should determine -- over time -- to |
| // annotate the pointer in either of the above two states. Consumers of such |
| // pointers across an API boundary should continue to treat such pointers as |
| // they currently do. |
| // |
| // Example: |
| // |
| // // PaySalary() requires the passed pointer to an `Employee` to be non-null. |
| // void PaySalary(absl::Nonnull<Employee *> e) { |
| // pay(e->salary); // OK to dereference |
| // } |
| // |
| // // CompleteTransaction() guarantees the returned pointer to an `Account` to |
| // // be non-null. |
| // absl::Nonnull<Account *> balance CompleteTransaction(double fee) { |
| // ... |
| // } |
| // |
| // // Note that specifying a nullability annotation does not prevent someone |
| // // from violating the contract: |
| // |
| // Nullable<Employee *> find(Map& employees, std::string_view name); |
| // |
| // void g(Map& employees) { |
| // Employee *e = find(employees, "Pat"); |
| // // `e` can now be null. |
| // PaySalary(e); // Violates contract, but compiles! |
| // } |
| // |
| // Nullability annotations, in other words, are useful for defining and |
| // narrowing contracts; *enforcement* of those contracts depends on use and any |
| // additional (static or dynamic analysis) tooling. |
| // |
| // NOTE: The "unknown" annotation state indicates that a pointer's contract has |
| // not yet been positively identified. The unknown state therefore acts as a |
| // form of documentation of your technical debt, and a codebase that adopts |
| // nullability annotations should aspire to annotate every pointer as either |
| // "non-null" or "nullable". |
| // |
| // ----------------------------------------------------------------------------- |
| // Applicability of Nullability Annotations |
| // ----------------------------------------------------------------------------- |
| // |
| // By default, nullability annotations are applicable to raw and smart |
| // pointers. User-defined types can indicate compatibility with nullability |
| // annotations by providing an `absl_nullability_compatible` nested type. 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; |
| // ... |
| // }; |
| // |
| // DISCLAIMER: |
| // =========================================================================== |
| // These nullability annotations are primarily a human readable signal about the |
| // intended contract of the pointer. They are not *types* and do not currently |
| // provide any correctness guarantees. For example, a pointer annotated as |
| // `Nonnull<T*>` is *not guaranteed* to be non-null, and the compiler won't |
| // alert or prevent assignment of a `Nullable<T*>` to a `Nonnull<T*>`. |
| // =========================================================================== |
| #ifndef ABSL_BASE_NULLABILITY_H_ |
| #define ABSL_BASE_NULLABILITY_H_ |
| |
| #include "absl/base/config.h" |
| #include "absl/base/internal/nullability_impl.h" |
| |
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
| |
| // absl::Nonnull |
| // |
| // The indicated pointer is never null. It is the responsibility of the provider |
| // of this pointer across an API boundary to ensure that the pointer is never be |
| // set to null. Consumers of this pointer across an API boundary may safely |
| // dereference the pointer. |
| // |
| // Example: |
| // |
| // // `employee` is designated as not null. |
| // void PaySalary(absl::Nonnull<Employee *> employee) { |
| // pay(*employee); // OK to dereference |
| // } |
| template <typename T> |
| using Nonnull = nullability_internal::NonnullImpl<T>; |
| |
| // absl::Nullable |
| // |
| // The indicated pointer may, by design, be either null or non-null. Consumers |
| // of this pointer across an API boundary should perform a `nullptr` check |
| // before performing any operation using the pointer. |
| // |
| // Example: |
| // |
| // // `employee` may be null. |
| // void PaySalary(absl::Nullable<Employee *> employee) { |
| // if (employee != nullptr) { |
| // Pay(*employee); // OK to dereference |
| // } |
| // } |
| template <typename T> |
| using Nullable = nullability_internal::NullableImpl<T>; |
| |
| // absl::NullabilityUnknown (default) |
| // |
| // The indicated pointer has not yet been determined to be definitively |
| // "non-null" or "nullable." Providers of such pointers across API boundaries |
| // should, over time, annotate such pointers as either "non-null" or "nullable." |
| // Consumers of these pointers across an API boundary should treat such pointers |
| // with the same caution they treat currently unannotated pointers. Most |
| // existing code will have "unknown" pointers, which should eventually be |
| // migrated into one of the above two nullability states: `Nonnull<T>` or |
| // `Nullable<T>`. |
| // |
| // NOTE: Because this annotation is the global default state, pointers without |
| // any annotation are assumed to have "unknown" semantics. This assumption is |
| // designed to minimize churn and reduce clutter within the codebase. |
| // |
| // Example: |
| // |
| // // `employee`s nullability state is unknown. |
| // void PaySalary(absl::NullabilityUnknown<Employee *> employee) { |
| // Pay(*employee); // Potentially dangerous. API provider should investigate. |
| // } |
| // |
| // Note that a pointer without an annotation, by default, is assumed to have the |
| // annotation `NullabilityUnknown`. |
| // |
| // // `employee`s nullability state is unknown. |
| // void PaySalary(Employee* employee) { |
| // Pay(*employee); // Potentially dangerous. API provider should investigate. |
| // } |
| template <typename T> |
| using NullabilityUnknown = nullability_internal::NullabilityUnknownImpl<T>; |
| |
| ABSL_NAMESPACE_END |
| } // namespace absl |
| |
| #endif // ABSL_BASE_NULLABILITY_H_ |