// Copyright 2026 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: optional_ref.h
// -----------------------------------------------------------------------------
//
// `optional_ref<T>` provides a `std::optional`-like interface around `T*`.
// It is similar to C++26's `std::optional<T&>`, but with slight enhancements,
// such as the fact that it permits construction from rvalues. That is, it
// relaxes the std::reference_constructs_from_temporary constraint. Its intent
// is to make it easier for functions to accept nullable object addresses,
// regardless of whether or not they point to temporaries.
//
// It can be constructed in the following ways:
//   * optional_ref<T> ref;
//   * optional_ref<T> ref = std::nullopt;
//   * T foo; optional_ref<T> ref = foo;
//   * std::optional<T> foo; optional_ref<T> ref = foo;
//   * T* foo = ...; optional_ref<T> ref = foo;
//   * optional_ref<T> foo; optional_ref<const T> ref = foo;
//
// Since it is trivially copyable and destructible, it should be passed by
// value.
//
// Other properties:
//   * Assignment is not allowed. Example:
//       optional_ref<int> ref;
//       // Compile error.
//       ref = 2;
//
//   * operator bool() is intentionally not defined, as it would be error prone
//     for optional_ref<bool>.
//
// Example usage, assuming some type `T` that is expensive to copy:
//   void ProcessT(optional_ref<const T> input) {
//     if (!input.has_value()) {
//       // Handle empty case.
//       return;
//     }
//     const T& val = *input;
//     // Do something with val.
//   }
//
//   ProcessT(std::nullopt);
//   ProcessT(BuildT());

#ifndef ABSL_TYPES_OPTIONAL_REF_H_
#define ABSL_TYPES_OPTIONAL_REF_H_

#include <cstddef>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>

#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/hardening.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h"

namespace absl {
ABSL_NAMESPACE_BEGIN

template <typename T>
class optional_ref {
  template <typename U>
  using EnableIfConvertibleFrom =
      std::enable_if_t<std::is_convertible_v<U*, T*>>;

 public:
  using value_type = T;

  constexpr optional_ref() : ptr_(nullptr) {}
  constexpr optional_ref(  // NOLINT(google-explicit-constructor)
      std::nullopt_t)
      : ptr_(nullptr) {}

  // Constructor given a concrete value.
  constexpr optional_ref(  // NOLINT(google-explicit-constructor)
      T& input ABSL_ATTRIBUTE_LIFETIME_BOUND)
      : ptr_(std::addressof(input)) {}

  // Constructors given an existing std::optional value.
  // Templated on the input optional's type to avoid creating a temporary.
  template <typename U, typename = EnableIfConvertibleFrom<const U>>
  constexpr optional_ref(  // NOLINT(google-explicit-constructor)
      const std::optional<U>& input ABSL_ATTRIBUTE_LIFETIME_BOUND)
      : ptr_(input.has_value() ? std::addressof(*input) : nullptr) {}
  template <typename U, typename = EnableIfConvertibleFrom<U>>
  constexpr optional_ref(  // NOLINT(google-explicit-constructor)
      std::optional<U>& input ABSL_ATTRIBUTE_LIFETIME_BOUND)
      : ptr_(input.has_value() ? std::addressof(*input) : nullptr) {}

  // Constructor given a T*, where nullptr indicates empty/absent.
  constexpr optional_ref(  // NOLINT(google-explicit-constructor)
      T* input ABSL_ATTRIBUTE_LIFETIME_BOUND)
      : ptr_(input) {}

  // Don't allow naked nullptr as input, as this creates confusion in the case
  // of optional_ref<T*>. Use std::nullopt instead to create an empty
  // optional_ref.
  constexpr optional_ref(  // NOLINT(google-explicit-constructor)
      std::nullptr_t) = delete;

  // Copying is allowed.
  optional_ref(const optional_ref<T>&) = default;
  // Assignment is not allowed.
  optional_ref<T>& operator=(const optional_ref<T>&) = delete;

  // Conversion from optional_ref<U> is allowed iff U* is convertible to T*.
  // (Note this also allows non-const to const conversions.)
  template <typename U, typename = EnableIfConvertibleFrom<U>>
  constexpr optional_ref(  // NOLINT(google-explicit-constructor)
      optional_ref<U> input)
      : ptr_(input.as_pointer()) {}

  // Determines whether the `optional_ref` contains a value. Returns `false` if
  // and only if `*this` is empty.
  constexpr bool has_value() const { return ptr_ != nullptr; }

  // Returns a reference to an `optional_ref`s underlying value. The constness
  // and lvalue/rvalue-ness of the `optional_ref` is preserved to the view of
  // the `T` sub-object. Throws the same error as `std::optional`'s `value()`
  // when the `optional_ref` is empty.
  constexpr T& value() const {
    return ABSL_PREDICT_TRUE(ptr_ != nullptr)
               ? *ptr_
               // Replicate the same error logic as in `std::optional`'s
               // `value()`. It either throws an exception or aborts the
               // program. We intentionally ignore the return value of
               // the constructed optional's value as we only need to run
               // the code for error checking.
               : ((void)std::optional<T>().value(), *ptr_);
  }

  // Returns the value iff *this has a value, otherwise returns `default_value`.
  template <typename U>
  constexpr T value_or(U&& default_value) const {
    // Instantiate std::optional<T>::value_or(U) to trigger its static_asserts.
    if (false) {
      // We use `std::add_const_t` here since just using `const` makes MSVC
      // complain about the syntax.
      (void)std::add_const_t<std::optional<T>>{}.value_or(
          std::forward<U>(default_value));
    }
    return ptr_ != nullptr ? *ptr_
                           : static_cast<T>(std::forward<U>(default_value));
  }

  // Accesses the underlying `T` value of an `optional_ref`. If the
  // `optional_ref` is empty, behavior is undefined.
  constexpr T& operator*() const {
    absl::base_internal::HardeningAssertNonNull(ptr_);
    return *ptr_;
  }
  constexpr T* operator->() const {
    absl::base_internal::HardeningAssertNonNull(ptr_);
    return ptr_;
  }

  // Convenience function to represent the `optional_ref` as a `T*` pointer.
  constexpr T* as_pointer() const { return ptr_; }
  // Convenience function to represent the `optional_ref` as an `optional`,
  // which incurs a copy when the `optional_ref` is non-empty. The template type
  // allows for implicit type conversion; example:
  //   optional_ref<std::string> a = ...;
  //   std::optional<std::string_view> b = a.as_optional<std::string_view>();
  template <typename U = std::decay_t<T>>
  constexpr std::optional<U> as_optional() const {
    if (ptr_ == nullptr) return std::nullopt;
    return *ptr_;
  }

 private:
  T* const ptr_;

  // T constraint checks.  You can't have an optional of nullopt_t or
  // in_place_t.
  static_assert(!std::is_same_v<std::nullopt_t, std::remove_cv_t<T>>,
                "optional_ref<nullopt_t> is not allowed.");
  static_assert(!std::is_same_v<std::in_place_t, std::remove_cv_t<T>>,
                "optional_ref<in_place_t> is not allowed.");
};

// Template type deduction guides:

template <typename T>
optional_ref(const T&) -> optional_ref<const T>;
template <typename T>
optional_ref(T&) -> optional_ref<T>;

template <typename T>
optional_ref(const std::optional<T>&) -> optional_ref<const T>;
template <typename T>
optional_ref(std::optional<T>&) -> optional_ref<T>;

template <typename T>
optional_ref(T*) -> optional_ref<T>;

namespace optional_ref_internal {

// This is a C++-11 compatible version of std::equality_comparable_with that
// only requires `t == u` is a valid boolean expression.
//
// We still need this for a couple reasons:
// -  As of 2026-02-13, Abseil supports C++17.
//  - Even for targets that are built with the default toolchain, using
//    std::equality_comparable_with gives us an error due to mutual recursion
//    between its definition and our definition of operator==.
//
template <typename T, typename U>
using enable_if_equality_comparable_t = std::enable_if_t<std::is_convertible_v<
    decltype(std::declval<T>() == std::declval<U>()), bool>>;

}  // namespace optional_ref_internal

// Compare an optional referenced value to std::nullopt.

template <typename T>
constexpr bool operator==(optional_ref<T> a, std::nullopt_t) {
  return !a.has_value();
}
template <typename T>
constexpr bool operator==(std::nullopt_t, optional_ref<T> b) {
  return !b.has_value();
}
template <typename T>
constexpr bool operator!=(optional_ref<T> a, std::nullopt_t) {
  return a.has_value();
}
template <typename T>
constexpr bool operator!=(std::nullopt_t, optional_ref<T> b) {
  return b.has_value();
}

// Compare two optional referenced values. Note, this does not test that the
// contained `ptr_`s are equal. If the caller wants "shallow" reference equality
// semantics, they should use `as_pointer()` explicitly.

template <typename T, typename U>
constexpr bool operator==(optional_ref<T> a, optional_ref<U> b) {
  return a.has_value() ? *a == b : !b.has_value();
}

// Compare an optional referenced value to a non-optional value.

template <
    typename T, typename U,
    typename = optional_ref_internal::enable_if_equality_comparable_t<T, U>>
constexpr bool operator==(const T& a, optional_ref<U> b) {
  return b.has_value() && a == *b;
}
template <
    typename T, typename U,
    typename = optional_ref_internal::enable_if_equality_comparable_t<T, U>>
constexpr bool operator==(optional_ref<T> a, const U& b) {
  return b == a;
}

// Inequality operators, as above.

template <typename T, typename U>
constexpr bool operator!=(optional_ref<T> a, optional_ref<U> b) {
  return !(a == b);
}
template <
    typename T, typename U,
    typename = optional_ref_internal::enable_if_equality_comparable_t<T, U>>
constexpr bool operator!=(optional_ref<T> a, const U& b) {
  return !(a == b);
}
template <
    typename T, typename U,
    typename = optional_ref_internal::enable_if_equality_comparable_t<T, U>>
constexpr bool operator!=(const T& a, optional_ref<U> b) {
  return !(a == b);
}

ABSL_NAMESPACE_END
}  // namespace absl

#endif  // ABSL_TYPES_OPTIONAL_REF_H_
