// Copyright 2022 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: log/internal/check_op.h
// -----------------------------------------------------------------------------
//
// This file declares helpers routines and macros used to implement `CHECK`
// macros.

#ifndef ABSL_LOG_INTERNAL_CHECK_OP_H_
#define ABSL_LOG_INTERNAL_CHECK_OP_H_

#include <stdint.h>

#include <cstddef>
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>

#include "absl/base/attributes.h"
#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/nullability.h"
#include "absl/base/optimization.h"
#include "absl/log/internal/nullguard.h"
#include "absl/log/internal/nullstream.h"
#include "absl/log/internal/strip.h"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/has_ostream_operator.h"
#include "absl/strings/string_view.h"

// `ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL` wraps string literals that
// should be stripped when `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`.
#ifdef ABSL_MIN_LOG_LEVEL
#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal)                \
  (::absl::LogSeverity::kFatal >=                                      \
           static_cast<::absl::LogSeverityAtLeast>(ABSL_MIN_LOG_LEVEL) \
       ? (literal)                                                     \
       : "")
#else
#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) (literal)
#endif

#ifdef NDEBUG
// `NDEBUG` is defined, so `DCHECK_EQ(x, y)` and so on do nothing.  However, we
// still want the compiler to parse `x` and `y`, because we don't want to lose
// potentially useful errors and warnings.
#define ABSL_LOG_INTERNAL_DCHECK_NOP(x, y)   \
  while (false && ((void)(x), (void)(y), 0)) \
  ::absl::log_internal::NullStream().InternalStream()
#endif

#define ABSL_LOG_INTERNAL_CHECK_OP(name, op, val1, val1_text, val2, val2_text) \
  while (const char* absl_nullable absl_log_internal_check_op_result           \
         [[maybe_unused]] = ::absl::log_internal::name##Impl(                  \
             ::absl::log_internal::GetReferenceableValue(val1),                \
             ::absl::log_internal::GetReferenceableValue(val2),                \
             ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val1_text " " #op          \
                                                              " " val2_text))) \
    ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true)                         \
  ABSL_LOG_INTERNAL_CHECK(::absl::implicit_cast<const char* absl_nonnull>(     \
                              absl_log_internal_check_op_result))              \
      .InternalStream()
#define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val1_text, val2,        \
                                    val2_text)                              \
  while (const char* absl_nullable absl_log_internal_qcheck_op_result =     \
             ::absl::log_internal::name##Impl(                              \
                 ::absl::log_internal::GetReferenceableValue(val1),         \
                 ::absl::log_internal::GetReferenceableValue(val2),         \
                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(                    \
                     val1_text " " #op " " val2_text)))                     \
    ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true)                     \
  ABSL_LOG_INTERNAL_QCHECK(::absl::implicit_cast<const char* absl_nonnull>( \
                               absl_log_internal_qcheck_op_result))         \
      .InternalStream()
#define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s1_text, s2,     \
                                      s2_text)                                 \
  while (const char* absl_nullable absl_log_internal_check_strop_result =      \
             ::absl::log_internal::Check##func##expected##Impl(                \
                 (s1), (s2),                                                   \
                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op        \
                                                                " " s2_text))) \
    ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true)                         \
  ABSL_LOG_INTERNAL_CHECK(::absl::implicit_cast<const char* absl_nonnull>(     \
                              absl_log_internal_check_strop_result))           \
      .InternalStream()
#define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s1_text, s2,    \
                                       s2_text)                                \
  while (const char* absl_nullable absl_log_internal_qcheck_strop_result =     \
             ::absl::log_internal::Check##func##expected##Impl(                \
                 (s1), (s2),                                                   \
                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op        \
                                                                " " s2_text))) \
    ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true)                        \
  ABSL_LOG_INTERNAL_QCHECK(::absl::implicit_cast<const char* absl_nonnull>(    \
                               absl_log_internal_qcheck_strop_result))         \
      .InternalStream()

// This one is tricky:
// * We must evaluate `val` exactly once, yet we need to do two things with it:
//   evaluate `.ok()` and (sometimes) `.ToString()`.
// * `val` might be an `absl::Status` or some `absl::StatusOr<T>`.
// * `val` might be e.g. `ATemporary().GetStatus()`, which may return a
//   reference to a member of `ATemporary` that is only valid until the end of
//   the full expression.
// * We don't want this file to depend on `absl::Status` `#include`s or linkage,
//   nor do we want to move the definition to status and introduce a dependency
//   in the other direction.  We can be assured that callers must already have a
//   `Status` and the necessary `#include`s and linkage.
// * Callsites should be small and fast (at least when `val.ok()`): one branch,
//   minimal stack footprint.
//   * In particular, the string concat stuff should be out-of-line and emitted
//     in only one TU to save linker input size
// * We want the `val.ok()` check inline so static analyzers and optimizers can
//   see it.
// * As usual, no braces so we can stream into the expansion with `operator<<`.
// * Also as usual, it must expand to a single (partial) statement with no
//   ambiguous-else problems.
// * When stripped by `ABSL_MIN_LOG_LEVEL`, we must discard the `<expr> is OK`
//   string literal and abort without doing any streaming.  We don't need to
//   strip the call to stringify the non-ok `Status` as long as we don't log it;
//   dropping the `Status`'s message text is out of scope.
#define ABSL_LOG_INTERNAL_CHECK_OK(val, val_text)                         \
  for (::std::pair<const ::absl::Status* absl_nonnull,                    \
                   const char* absl_nonnull>                              \
           absl_log_internal_check_ok_goo;                                \
       absl_log_internal_check_ok_goo.first =                             \
           ::absl::log_internal::AsStatus(val),                           \
       absl_log_internal_check_ok_goo.second =                            \
           ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok())  \
               ? "" /* Don't use nullptr, to keep the annotation happy */ \
               : ::absl::status_internal::MakeCheckFailString(            \
                     absl_log_internal_check_ok_goo.first,                \
                     ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text      \
                                                            " is OK")),   \
       !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());)   \
    ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true)                    \
  ABSL_LOG_INTERNAL_CHECK(absl_log_internal_check_ok_goo.second)          \
      .InternalStream()
#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text)                        \
  for (::std::pair<const ::absl::Status* absl_nonnull,                    \
                   const char* absl_nonnull>                              \
           absl_log_internal_qcheck_ok_goo;                               \
       absl_log_internal_qcheck_ok_goo.first =                            \
           ::absl::log_internal::AsStatus(val),                           \
       absl_log_internal_qcheck_ok_goo.second =                           \
           ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok()) \
               ? "" /* Don't use nullptr, to keep the annotation happy */ \
               : ::absl::status_internal::MakeCheckFailString(            \
                     absl_log_internal_qcheck_ok_goo.first,               \
                     ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text      \
                                                            " is OK")),   \
       !ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok());)  \
    ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true)                   \
  ABSL_LOG_INTERNAL_QCHECK(absl_log_internal_qcheck_ok_goo.second)        \
      .InternalStream()

namespace absl {
ABSL_NAMESPACE_BEGIN

class Status;
template <typename T>
class StatusOr;

namespace status_internal {
ABSL_ATTRIBUTE_PURE_FUNCTION const char* absl_nonnull MakeCheckFailString(
    const absl::Status* absl_nonnull status, const char* absl_nonnull prefix);
}  // namespace status_internal

namespace log_internal {

// Convert a Status or a StatusOr to its underlying status value.
//
// (This implementation does not require a dep on absl::Status to work.)
inline const absl::Status* absl_nonnull AsStatus(const absl::Status& s) {
  return &s;
}
template <typename T>
const absl::Status* absl_nonnull AsStatus(const absl::StatusOr<T>& s) {
  return &s.status();
}

// A helper class for formatting `expr (V1 vs. V2)` in a `CHECK_XX` statement.
// See `MakeCheckOpString` for sample usage.
class CheckOpMessageBuilder final {
 public:
  // Inserts `exprtext` and ` (` to the stream.
  explicit CheckOpMessageBuilder(const char* absl_nonnull exprtext);
  ~CheckOpMessageBuilder() = default;
  // For inserting the first variable.
  std::ostream& ForVar1() { return stream_; }
  // For inserting the second variable (adds an intermediate ` vs. `).
  std::ostream& ForVar2();
  // Get the result (inserts the closing `)`).
  const char* absl_nonnull NewString();

 private:
  std::ostringstream stream_;
};

// This formats a value for a failing `CHECK_XX` statement.  Ordinarily, it uses
// the definition for `operator<<`, with a few special cases below.
template <typename T>
inline void MakeCheckOpValueString(std::ostream& os, const T& v) {
  os << log_internal::NullGuard<T>::Guard(v);
}

// Overloads for char types provide readable values for unprintable characters.
void MakeCheckOpValueString(std::ostream& os, char v);
void MakeCheckOpValueString(std::ostream& os, signed char v);
void MakeCheckOpValueString(std::ostream& os, unsigned char v);
void MakeCheckOpValueString(std::ostream& os, const void* absl_nullable p);

// A wrapper for types that have no operator<<.
struct UnprintableWrapper {
  template <typename T>
  explicit UnprintableWrapper(const T&) {}

  friend std::ostream& operator<<(std::ostream& os, UnprintableWrapper);
};

namespace detect_specialization {

// MakeCheckOpString is being specialized for every T and U pair that is being
// passed to the CHECK_op macros. However, there is a lot of redundancy in these
// specializations that creates unnecessary library and binary bloat.
// The number of instantiations tends to be O(n^2) because we have two
// independent inputs. This technique works by reducing `n`.
//
// Most user-defined types being passed to CHECK_op end up being printed as a
// builtin type. For example, enums tend to be implicitly converted to its
// underlying type when calling operator<<, and pointers are printed with the
// `const void*` overload.
// To reduce the number of instantiations we coerce these values before calling
// MakeCheckOpString instead of inside it.
//
// To detect if this coercion is needed, we duplicate all the relevant
// operator<< overloads as specified in the standard, just in a different
// namespace. If the call to `stream << value` becomes ambiguous, it means that
// one of these overloads is the one selected by overload resolution. We then
// do overload resolution again just with our overload set to see which one gets
// selected. That tells us which type to coerce to.
// If the augmented call was not ambiguous, it means that none of these were
// selected and we can't coerce the input.
//
// As a secondary step to reduce code duplication, we promote integral types to
// their 64-bit variant. This does not change the printed value, but reduces the
// number of instantiations even further. Promoting an integer is very cheap at
// the call site.
int64_t operator<<(std::ostream&, short value);           // NOLINT
int64_t operator<<(std::ostream&, unsigned short value);  // NOLINT
int64_t operator<<(std::ostream&, int value);
int64_t operator<<(std::ostream&, unsigned int value);
int64_t operator<<(std::ostream&, long value);                 // NOLINT
uint64_t operator<<(std::ostream&, unsigned long value);       // NOLINT
int64_t operator<<(std::ostream&, long long value);            // NOLINT
uint64_t operator<<(std::ostream&, unsigned long long value);  // NOLINT
float operator<<(std::ostream&, float value);
double operator<<(std::ostream&, double value);
long double operator<<(std::ostream&, long double value);
bool operator<<(std::ostream&, bool value);
const void* absl_nullable operator<<(std::ostream&,
                                     const void* absl_nullable value);
const void* absl_nullable operator<<(std::ostream&, std::nullptr_t);

// These `char` overloads are specified like this in the standard, so we have to
// write them exactly the same to ensure the call is ambiguous.
// If we wrote it in a different way (eg taking std::ostream instead of the
// template) then one call might have a higher rank than the other and it would
// not be ambiguous.
template <typename Traits>
char operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
signed char operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
unsigned char operator<<(std::basic_ostream<char, Traits>&, unsigned char);
template <typename Traits>
const char* absl_nonnull operator<<(std::basic_ostream<char, Traits>&,
                                    const char* absl_nonnull);
template <typename Traits>
const signed char* absl_nonnull operator<<(std::basic_ostream<char, Traits>&,
                                           const signed char* absl_nonnull);
template <typename Traits>
const unsigned char* absl_nonnull operator<<(std::basic_ostream<char, Traits>&,
                                             const unsigned char* absl_nonnull);

// This overload triggers when the call is not ambiguous.
// It means that T is being printed with some overload not on this list.
// We keep the value as `const T&`.
template <typename T, typename = decltype(std::declval<std::ostream&>()
                                          << std::declval<const T&>())>
const T& Detect(int);

// This overload triggers when the call is ambiguous.
// It means that T is either one from this list or printed as one from this
// list. Eg an unscoped enum that decays to `int` for printing.
// We ask the overload set to give us the type we want to convert it to.
template <typename T>
decltype(detect_specialization::operator<<(
    std::declval<std::ostream&>(), std::declval<const T&>())) Detect(char);

// A sink for AbslStringify which redirects everything to a std::ostream.
class StringifySink {
 public:
  explicit StringifySink(std::ostream& os ABSL_ATTRIBUTE_LIFETIME_BOUND);

  void Append(absl::string_view text);
  void Append(size_t length, char ch);
  friend void AbslFormatFlush(StringifySink* absl_nonnull sink,
                              absl::string_view text);

 private:
  std::ostream& os_;
};

// Wraps a type implementing AbslStringify, and implements operator<<.
template <typename T>
class StringifyToStreamWrapper {
 public:
  explicit StringifyToStreamWrapper(const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND)
      : v_(v) {}

  friend std::ostream& operator<<(std::ostream& os,
                                  const StringifyToStreamWrapper& wrapper) {
    StringifySink sink(os);
    AbslStringify(sink, wrapper.v_);
    return os;
  }

 private:
  const T& v_;
};

// This overload triggers when T implements AbslStringify.
// StringifyToStreamWrapper is used to allow MakeCheckOpString to use
// operator<<.
template <typename T>
std::enable_if_t<HasAbslStringify<T>::value,
                 StringifyToStreamWrapper<T>>
Detect(...);  // Ellipsis has lowest preference when int passed.

// This overload triggers when T is neither possible to print nor an enum.
template <typename T>
std::enable_if_t<std::negation_v<std::disjunction<
                     std::is_convertible<T, int>, std::is_enum<T>,
                     std::is_pointer<T>, std::is_same<T, std::nullptr_t>,
                     HasOstreamOperator<T>, HasAbslStringify<T>>>,
                 UnprintableWrapper>
Detect(...);

// Equivalent to the updated std::underlying_type from C++20, which is no
// longer undefined behavior for non-enum types.
template <typename T, typename EnableT = void>
struct UnderlyingType {};

template <typename T>
struct UnderlyingType<T, std::enable_if_t<std::is_enum_v<T>>> {
  using type = std::underlying_type_t<T>;
};
template <typename T>
using UnderlyingTypeT = typename UnderlyingType<T>::type;

// This overload triggers when T is a scoped enum that has not defined an output
// stream operator (operator<<) or AbslStringify. It causes the enum value to be
// converted to a type that can be streamed. For consistency with other enums, a
// scoped enum backed by a bool or char is converted to its underlying type, and
// one backed by another integer is converted to (u)int64_t.
template <typename T>
std::enable_if_t<
    std::conjunction_v<std::is_enum<T>,
                       std::negation<std::is_convertible<T, int>>,
                       std::negation<HasOstreamOperator<T>>,
                       std::negation<HasAbslStringify<T>>>,
    std::conditional_t<std::is_same_v<UnderlyingTypeT<T>, bool> ||
                           std::is_same_v<UnderlyingTypeT<T>, char> ||
                           std::is_same_v<UnderlyingTypeT<T>, signed char> ||
                           std::is_same_v<UnderlyingTypeT<T>, unsigned char>,
                       UnderlyingTypeT<T>,
                       std::conditional_t<std::is_signed_v<UnderlyingTypeT<T>>,
                                          int64_t, uint64_t>>>
Detect(...);

template <typename T>
using Detected = decltype(Detect<T>(0));
}  // namespace detect_specialization

// If the comparison will happen as pointers, decay `char*` arguments to `void*`
// when printing them. There is no evidence that they are a NULL terminated
// C-String so printing them as such could lead to UB, and more importantly we
// compared pointers so showing the pointers is a better result.
template <typename T>
constexpr bool IsCharStarOrVoidStar() {
  if constexpr (std::is_reference_v<T>) {
    return IsCharStarOrVoidStar<std::remove_reference_t<T>>();
  } else if constexpr (std::is_array_v<T>) {
    return IsCharStarOrVoidStar<std::decay_t<T>>();
  } else {
    using U = std::remove_const_t<std::remove_pointer_t<T>>;
    return std::is_pointer_v<T> &&
        (std::is_same_v<char, U> || std::is_same_v<unsigned char, U> ||
         std::is_same_v<signed char, U> || std::is_void_v<U>);
  }
}

template <typename T1, typename T2,
          typename U1 = detect_specialization::Detected<T1>,
          typename U2 = detect_specialization::Detected<T2>>
using CheckOpStreamType =
    std::conditional_t<IsCharStarOrVoidStar<U1>() && IsCharStarOrVoidStar<U2>(),
                       const void*, U1>;

// Build the error message string.  Specify no inlining for code size.
template <typename T1, typename T2>
ABSL_ATTRIBUTE_RETURNS_NONNULL const char* absl_nonnull MakeCheckOpString(
    T1 v1, T2 v2, const char* absl_nonnull exprtext) ABSL_ATTRIBUTE_NOINLINE;

template <typename T1, typename T2>
const char* absl_nonnull MakeCheckOpString(T1 v1, T2 v2,
                                           const char* absl_nonnull exprtext) {
  if constexpr (std::is_same_v<CheckOpStreamType<T1, T2>, UnprintableWrapper> &&
                std::is_same_v<CheckOpStreamType<T2, T1>, UnprintableWrapper>) {
    // No sense printing " (UNPRINTABLE vs. UNPRINTABLE)"
    return exprtext;
  } else {
    CheckOpMessageBuilder comb(exprtext);
    MakeCheckOpValueString(comb.ForVar1(), v1);
    MakeCheckOpValueString(comb.ForVar2(), v2);
    return comb.NewString();
  }
}

// Add a few commonly used instantiations as extern to reduce size of objects
// files.
#define ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(x) \
  extern template const char* absl_nonnull MakeCheckOpString(   \
      x, x, const char* absl_nonnull)
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(bool);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(int64_t);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(uint64_t);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(float);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(double);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(char);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(unsigned char);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const std::string&);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const absl::string_view&);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const char* absl_nonnull);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(
    const signed char* absl_nonnull);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(
    const unsigned char* absl_nonnull);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void* absl_nonnull);
#undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN

// `ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT` skips formatting the Check_OP result
// string iff `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`, instead returning an empty
// string.
#ifdef ABSL_MIN_LOG_LEVEL
#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \
  ((::absl::LogSeverity::kFatal >=                                       \
    static_cast<::absl::LogSeverityAtLeast>(ABSL_MIN_LOG_LEVEL))         \
       ? MakeCheckOpString<U1, U2>(v1, v2, exprtext)                     \
       : "")
#else
#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \
  MakeCheckOpString<U1, U2>(v1, v2, exprtext)
#endif

// Helper functions for `ABSL_LOG_INTERNAL_CHECK_OP` macro family.  The
// `(int, int)` override works around the issue that the compiler will not
// instantiate the template version of the function on values of unnamed enum
// type.
#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL(name, op)                          \
  template <typename T1, typename T2>                                      \
  inline constexpr const char* absl_nullable name##Impl(                   \
      const T1& v1, const T2& v2, const char* absl_nonnull exprtext) {     \
    using U1 = CheckOpStreamType<T1, T2>;                                  \
    using U2 = CheckOpStreamType<T2, T1>;                                  \
    return ABSL_PREDICT_TRUE(v1 op v2)                                     \
               ? nullptr                                                   \
               : ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, U1(v1),    \
                                                        U2(v2), exprtext); \
  }                                                                        \
  inline constexpr const char* absl_nullable name##Impl(                   \
      int v1, int v2, const char* absl_nonnull exprtext) {                 \
    return name##Impl<int, int>(v1, v2, exprtext);                         \
  }

ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_EQ, ==)
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_NE, !=)
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LE, <=)
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LT, <)
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GE, >=)
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GT, >)
#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT
#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL

const char* absl_nullable CheckstrcmptrueImpl(
    const char* absl_nullable s1, const char* absl_nullable s2,
    const char* absl_nonnull exprtext);
const char* absl_nullable CheckstrcmpfalseImpl(
    const char* absl_nullable s1, const char* absl_nullable s2,
    const char* absl_nonnull exprtext);
const char* absl_nullable CheckstrcasecmptrueImpl(
    const char* absl_nullable s1, const char* absl_nullable s2,
    const char* absl_nonnull exprtext);
const char* absl_nullable CheckstrcasecmpfalseImpl(
    const char* absl_nullable s1, const char* absl_nullable s2,
    const char* absl_nonnull exprtext);

// `CHECK_EQ` and friends want to pass their arguments by reference, however
// this winds up exposing lots of cases where people have defined and
// initialized static const data members but never declared them (i.e. in a .cc
// file), meaning they are not referenceable.  This function avoids that problem
// for integers (the most common cases) by overloading for every primitive
// integer type, even the ones we discourage, and returning them by value.
// NOLINTBEGIN(runtime/int)
// NOLINTBEGIN(google-runtime-int)
template <typename T>
inline constexpr const T& GetReferenceableValue(const T& t) {
  return t;
}
inline constexpr char GetReferenceableValue(char t) { return t; }
inline constexpr unsigned char GetReferenceableValue(unsigned char t) {
  return t;
}
inline constexpr signed char GetReferenceableValue(signed char t) { return t; }
inline constexpr short GetReferenceableValue(short t) { return t; }
inline constexpr unsigned short GetReferenceableValue(unsigned short t) {
  return t;
}
inline constexpr int GetReferenceableValue(int t) { return t; }
inline constexpr unsigned int GetReferenceableValue(unsigned int t) {
  return t;
}
inline constexpr long GetReferenceableValue(long t) { return t; }
inline constexpr unsigned long GetReferenceableValue(unsigned long t) {
  return t;
}
inline constexpr long long GetReferenceableValue(long long t) { return t; }
inline constexpr unsigned long long GetReferenceableValue(
    unsigned long long t) {
  return t;
}
// NOLINTEND(google-runtime-int)
// NOLINTEND(runtime/int)

}  // namespace log_internal
ABSL_NAMESPACE_END
}  // namespace absl

#endif  // ABSL_LOG_INTERNAL_CHECK_OP_H_
