(root)/
gcc-13.2.0/
gcc/
testsuite/
g++.dg/
cpp23/
attr-assume3.C
// P1774R8 - Portable assumptions
// { dg-do run { target c++11 } }

namespace std
{
  constexpr bool
  isfinite (float x)
  { return __builtin_isfinite (x); }

  constexpr bool
  isfinite (double x)
  { return __builtin_isfinite (x); }

  constexpr bool
  isfinite (long double x)
  { return __builtin_isfinite (x); }

  constexpr float
  sqrt (float x)
  { return __builtin_sqrtf (x); }

  constexpr double
  sqrt (double x)
  { return __builtin_sqrt (x); }

  constexpr long double
  sqrt (long double x)
  { return __builtin_sqrtl (x); }

  extern "C" void
  abort ();
}

constexpr int
f1 (int i)
{
#if __cpp_constexpr >= 201603L
  auto f = [=] { [[__assume__ (i == 0)]]; };
  return sizeof (f);
#else
  return sizeof (int);
#endif
}

void
f2 ()
{
  static_assert (f1 (0) >= sizeof (int), "");
}

int
f3 (int i)
{
  [[gnu::assume (i == 42)]];
  return i;
}

int
f4 (int i)
{
  __attribute__ ((assume (++i == 44)));
  return i;
}

int a;
int *volatile c;

bool
f5 ()
{
  ++a;
  return true;
}

constexpr int
f6 ()
{
#if __cpp_constexpr >= 201304L
  [[__assume__ (f5 ())]];
#endif
  return 1;
}

template <int ...args>
bool
f7 ()
{
#if __cpp_fold_expressions >= 201411L
  [[__gnu__::__assume__ (((args >= 0) && ...))]];
  return ((args >= 0) && ...);
#else
  return true;
#endif
}

bool
f8 (double x)
{
  [[gnu::assume (std::isfinite (x) && x >= 0.0)]];
  return std::isfinite (std::sqrt (x));
}

double
f9 (double x)
{
  __attribute__((assume (std::isfinite (std::sqrt (x)))));
  return std::sqrt (x);
}

template <typename T, T N>
T
f10 (T x)
{
  [[__assume__ (x == N)]];
  return x;
}

int
f11 (int x)
{
  [[gnu::assume (x == 93 ? true : throw 1)]];
  return x;
}

constexpr int
f12 (int x)
{
#if __cpp_constexpr >= 201304L
  __attribute__((assume (++x == 43)));
#endif
  return x;
}

static_assert (f12 (42) == 42, "");

struct S
{
  operator bool () { return true; }
};

int
f13 ()
{
  S s;
  [[__gnu__::__assume__ (s)]];
  return 0;
}

template <typename T>
int
f14 ()
{
  T t;
  __attribute__((assume (t)));
  return 0;
}

int
main ()
{
  int b = 42;
  double d = 42.0, e = 43.0;
  c = &b;
  [[__assume__ (f5 ())]];
  if (a)
    std::abort ();
  [[gnu::assume (++b == 43)]];
  if (b != 42 || *c != 42)
    std::abort ();
  static_assert (f6 () == 1, "");
  if (f6 () != 1)
    std::abort ();
  if (a)
    std::abort ();
  if (!f7 <0> () || !f7 <1, 2, 3, 4> ())
    std::abort ();
  __attribute__((assume (d < e)));
  if (f10 <int, 45> (45) != 45
      || f10 <long long, 128LL> (128LL) != 128LL
#if __cpp_nontype_template_args >= 201911L
      || f10 <long double, -42.0L> (-42.0L) != -42.0L
#endif
      || false)
    std::abort ();
  int i = 90, j = 91, k = 92;
  [[__assume__ (i == 90), gnu::assume (j <= 91)]]
#if __cplusplus >= 201703L
  [[using gnu:assume (k >= 92)]]
#else
  [[gnu::assume (k >= 92)]]
#endif
  ;
  __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92)));
  if (f11 (93) != 93)
    std::abort ();
  if (f14 <S> () != 0)
    std::abort ();
}