// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify %s
// RUN: %clang_cc1 -std=c++14 -verify=ref %s
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify=expected-cpp20 %s
// RUN: %clang_cc1 -std=c++20 -verify=ref %s

namespace WhileLoop {
  constexpr int f() {
    int i = 0;
    while(false) {
      i = i + 1;
    }
    return i;
  }
  static_assert(f() == 0, "");


  constexpr int f2() {
    int i = 0;
    while(i != 5) {
      i = i + 1;
    }
    return i;
  }
  static_assert(f2() == 5, "");

  constexpr int f3() {
    int i = 0;
    while(true) {
      i = i + 1;

      if (i == 5)
        break;
    }
    return i;
  }
  static_assert(f3() == 5, "");

  constexpr int f4() {
    int i = 0;
    while(i != 5) {

      i = i + 1;
      continue;
      i = i - 1;
    }
    return i;
  }
  static_assert(f4() == 5, "");


  constexpr int f5(bool b) {
    int i = 0;

    while(true) {
      if (!b) {
        if (i == 5)
          break;
      }

      if (b) {
        while (i != 10) {
          i = i + 1;
          if (i == 8)
            break;

          continue;
        }
      }

      if (b)
        break;

      i = i + 1;
      continue;
    }

    return i;
  }
  static_assert(f5(true) == 8, "");
  static_assert(f5(false) == 5, "");

#if 0
  /// FIXME: This is an infinite loop, which should
  ///   be rejected.
  constexpr int f6() {
    while(true);
  }
#endif
};

namespace DoWhileLoop {

  constexpr int f() {
    int i = 0;
    do {
      i = i + 1;
    } while(false);
    return i;
  }
  static_assert(f() == 1, "");

  constexpr int f2() {
    int i = 0;
    do {
      i = i + 1;
    } while(i != 5);
    return i;
  }
  static_assert(f2() == 5, "");


  constexpr int f3() {
    int i = 0;
    do {
      i = i + 1;
      if (i == 5)
        break;
    } while(true);
    return i;
  }
  static_assert(f3() == 5, "");

  constexpr int f4() {
    int i = 0;
    do {
      i = i + 1;
      continue;
      i = i - 1;
    } while(i != 5);
    return i;
  }
  static_assert(f4() == 5, "");

  constexpr int f5(bool b) {
    int i = 0;

    do {
      if (!b) {
        if (i == 5)
          break;
      }

      if (b) {
        do {
          i = i + 1;
          if (i == 8)
            break;

          continue;
        } while (i != 10);
      }

      if (b)
        break;

      i = i + 1;
      continue;
    } while(true);

    return i;
  }
  static_assert(f5(true) == 8, "");
  static_assert(f5(false) == 5, "");

#if __cplusplus >= 202002L
  constexpr int f6() {
    int i;
    do {
      i = 5;
      break;
    } while (true);
    return i;
  }
  static_assert(f6() == 5, "");
#endif

#if 0
  /// FIXME: This is an infinite loop, which should
  ///   be rejected.
  constexpr int f7() {
    while(true);
  }
#endif
};

namespace ForLoop {
  constexpr int f() {
    int i = 0;
    for (;false;) {
      i = i + 1;
    }
    return i;
  }
  static_assert(f() == 0, "");

  constexpr int f2() {
    int m = 0;
    for (int i = 0; i < 10; i = i + 1){
      m = i;
    }
    return m;
  }
  static_assert(f2() == 9, "");

  constexpr int f3() {
    int i = 0;
    for (; i != 5; i = i + 1);
    return i;
  }
  static_assert(f3() == 5, "");

  constexpr int f4() {
    int i = 0;
    for (;;) {
      i = i + 1;

      if (i == 5)
        break;
    }
    return i;
  }
  static_assert(f4() == 5, "");

  constexpr int f5() {
    int i = 0;
    for (;i != 5;) {
      i = i + 1;
      continue;
      i = i - 1;
    }
    return i;
  }
  static_assert(f5() == 5, "");

  constexpr int f6(bool b) {
    int i = 0;

    for (;true;) {
      if (!b) {
        if (i == 5)
          break;
      }

      if (b) {
        for (; i != 10; i = i + 1) {
          if (i == 8)
            break;
          continue;
        }
      }

      if (b)
        break;

      i = i + 1;
      continue;
    }

    return i;
  }
  static_assert(f6(true) == 8, "");
  static_assert(f6(false) == 5, "");

#if 0
  /// FIXME: This is an infinite loop, which should
  ///   be rejected.
  constexpr int f6() {
    for(;;);
  }
#endif

};

namespace RangeForLoop {
  constexpr int localArray() {
    int a[] = {1,2,3,4};
    int s = 0;
    for(int i : a) {
      s += i;
    }
    return s;
  }
  static_assert(localArray() == 10, "");

  constexpr int localArray2() {
    int a[] = {1,2,3,4};
    int s = 0;
    for(const int &i : a) {
      s += i;
    }
    return s;
  }
  static_assert(localArray2() == 10, "");

  constexpr int nested() {
    int s = 0;
    for (const int i : (int[]){1,2,3,4}) {
      int a[] = {i, i};
      for(int m : a) {
        s += m;
      }
    }
    return s;
  }
  static_assert(nested() == 20, "");

  constexpr int withBreak() {
    int s = 0;
    for (const int &i: (bool[]){false, true}) {
      if (i)
        break;
      s++;
    }
    return s;
  }
  static_assert(withBreak() == 1, "");

  constexpr void NoBody() {
    for (const int &i: (bool[]){false, true}); // expected-warning {{empty body}} \
                                               // expected-note {{semicolon on a separate line}} \
                                               // expected-cpp20-warning {{empty body}} \
                                               // expected-cpp20-note {{semicolon on a separate line}} \
                                               // ref-warning {{empty body}} \
                                               // ref-note {{semicolon on a separate line}}
  }
}

namespace Scopes {
  constexpr int foo() {
    int n = 0;
    {
      int m = 12;
    for (int i = 0;i < 10;++i) {

      {
        int a  = 10;
        {
          int b = 20;
          {
            int c = 30;
            continue;
          }
        }
      }
    }
    ++m;
    n = m;
    }

    ++n;
    return n;
  }
  static_assert(foo() == 14, "");
}
