llvm/clang/test/AST/ByteCode/cxx20.cpp

// RUN: %clang_cc1 -fcxx-exceptions -fexperimental-new-constant-interpreter -std=c++20 -verify=both,expected -fcxx-exceptions %s
// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=both,ref -fcxx-exceptions %s

void test_alignas_operand() {
  alignas(8) char dummy;
  static_assert(__alignof(dummy) == 8);
}

constexpr int getMinus5() {
  int a = 10;
  a = -5;
  int *p = &a;
  return *p;
}
static_assert(getMinus5() == -5, "");

constexpr int assign() {
  int m = 10;
  int k = 12;

  m = (k = 20);

  return m;
}
static_assert(assign() == 20, "");


constexpr int pointerAssign() {
  int m = 10;
  int *p = &m;

  *p = 12; // modifies m

  return m;
}
static_assert(pointerAssign() == 12, "");

constexpr int pointerDeref() {
  int m = 12;
  int *p = &m;

  return *p;
}
static_assert(pointerDeref() == 12, "");

constexpr int pointerAssign2() {
  int m = 10;
  int *p = &m;
  int **pp = &p;

  **pp = 12;

  int v = **pp;

  return v;
}
static_assert(pointerAssign2() == 12, "");

constexpr int unInitLocal() {
  int a;
  return a; // both-note {{read of uninitialized object}}
}
static_assert(unInitLocal() == 0, ""); // both-error {{not an integral constant expression}} \
                                       // both-note {{in call to 'unInitLocal()'}}

constexpr int initializedLocal() {
  int a;
  a = 20;
  return a;
}
static_assert(initializedLocal() == 20);

constexpr int initializedLocal2() {
  int a[2];
  return *a; // both-note {{read of uninitialized object is not allowed in a constant expression}}
}
static_assert(initializedLocal2() == 20); // both-error {{not an integral constant expression}} \
                                          // both-note {{in call to}}


struct Int { int a; };
constexpr int initializedLocal3() {
  Int i;
  return i.a; // both-note {{read of uninitialized object is not allowed in a constant expression}}
}
static_assert(initializedLocal3() == 20); // both-error {{not an integral constant expression}} \
                                          // both-note {{in call to}}



#if 0
// FIXME: This code should be rejected because we pass an uninitialized value
//   as a function parameter.
constexpr int inc(int a) { return a + 1; }
constexpr int f() {
    int i;
    return inc(i);
}
static_assert(f());
#endif

/// Distinct literals have distinct addresses.
/// see https://github.com/llvm/llvm-project/issues/58754
constexpr auto foo(const char *p) { return p; }
constexpr auto p1 = "test1";
constexpr auto p2 = "test2";

constexpr bool b1 = foo(p1) == foo(p1);
static_assert(b1);

constexpr bool b2 = foo(p1) == foo(p2);
static_assert(!b2);

constexpr auto name1() { return "name1"; }
constexpr auto name2() { return "name2"; }

constexpr auto b3 = name1() == name1(); // ref-error {{must be initialized by a constant expression}} \
                                        // ref-note {{comparison of addresses of literals}}
constexpr auto b4 = name1() == name2();
static_assert(!b4);

namespace UninitializedFields {
  class A {
  public:
    int a; // both-note 4{{subobject declared here}}
    constexpr A() {}
  };
  constexpr A a; // both-error {{must be initialized by a constant expression}} \
                 // both-note {{subobject 'a' is not initialized}}
  constexpr A aarr[2]; // both-error {{must be initialized by a constant expression}} \
                       // both-note {{subobject 'a' is not initialized}}
  class F {
    public:
      int f; // both-note 3{{subobject declared here}}

      constexpr F() {}
      constexpr F(bool b) {
        if (b)
          f = 42;
      }
  };

  constexpr F foo[2] = {true}; // both-error {{must be initialized by a constant expression}} \
                               // both-note {{subobject 'f' is not initialized}}
  constexpr F foo2[3] = {true, false, true}; // both-error {{must be initialized by a constant expression}} \
                                             // both-note {{subobject 'f' is not initialized}}
  constexpr F foo3[3] = {true, true, F()}; // both-error {{must be initialized by a constant expression}} \
                                           // both-note {{subobject 'f' is not initialized}}



  class Base {
  public:
    bool b;
    int a; // both-note {{subobject declared here}}
    constexpr Base() : b(true) {}
  };

  class Derived : public Base {
  public:
    constexpr Derived() : Base() {}   };

  constexpr Derived D; // both-error {{must be initialized by a constant expression}} \
                       // both-note {{subobject 'a' is not initialized}}

  class C2 {
  public:
    A a;
    constexpr C2() {}   };
  constexpr C2 c2; // both-error {{must be initialized by a constant expression}} \
                   // both-note {{subobject 'a' is not initialized}}

  class C3 {
  public:
    A a[2];
    constexpr C3() {}
  };
  constexpr C3 c3; // both-error {{must be initialized by a constant expression}} \
                   // both-note {{subobject 'a' is not initialized}}

  class C4 {
  public:
    bool B[2][3]; // both-note {{subobject declared here}}
    constexpr C4(){}
  };
  constexpr C4 c4; // both-error {{must be initialized by a constant expression}} \
                   // both-note {{subobject 'B' is not initialized}}
};

namespace ConstThis {
  class Foo {
    const int T = 12; // both-note {{declared const here}}
    int a;
  public:
    constexpr Foo() {
      this->a = 10;
      T = 13; // both-error {{cannot assign to non-static data member 'T' with const-qualified type}}
    }
  };
  constexpr Foo F; // both-error {{must be initialized by a constant expression}}


  class FooDtor {
    int a;
  public:
    constexpr FooDtor() {
      this->a = 10;
    }
    constexpr ~FooDtor() {
      this->a = 12;
    }
  };

  constexpr int foo() {
    const FooDtor f;
    return 0;
  }
  static_assert(foo() == 0);

  template <bool Good>
  struct ctor_test {
    int a = 0;

    constexpr ctor_test() {
      if (Good)
        a = 10;
      int local = 100 / a; // both-note {{division by zero}}
    }
  };

  template <bool Good>
  struct dtor_test {
    int a = 0;

    constexpr dtor_test() = default;
    constexpr ~dtor_test() {
      if (Good)
        a = 10;
      int local = 100 / a; // both-note {{division by zero}}
    }
  };

  constexpr ctor_test<true> good_ctor;
  constexpr dtor_test<true> good_dtor;

  constexpr ctor_test<false> bad_ctor; // both-error {{must be initialized by a constant expression}} \
                                       // both-note {{in call to}}
  constexpr dtor_test<false> bad_dtor; // both-error {{must have constant destruction}} \
                                       // both-note {{in call to}}
};

namespace BaseInit {
  struct Base {
    int a;
  };

  struct Intermediate : Base {
    int b;
  };

  struct Final : Intermediate {
    int c;

    constexpr Final(int a, int b, int c) : c(c) {}
  };

  static_assert(Final{1, 2, 3}.c == 3, ""); // OK
  static_assert(Final{1, 2, 3}.a == 0, ""); // both-error {{not an integral constant expression}} \
                                            // both-note {{read of uninitialized object}}


  struct Mixin  {
    int b;

    constexpr Mixin() = default;
    constexpr Mixin(int b) : b(b) {}
  };

  struct Final2 : Base, Mixin {
    int c;

    constexpr Final2(int a, int b, int c) : Mixin(b), c(c) {}
    constexpr Final2(int a, int b, int c, bool) : c(c) {}
  };

  static_assert(Final2{1, 2, 3}.c == 3, ""); // OK
  static_assert(Final2{1, 2, 3}.b == 2, ""); // OK
  static_assert(Final2{1, 2, 3}.a == 0, ""); // both-error {{not an integral constant expression}} \
                                             // both-note {{read of uninitialized object}}


  struct Mixin3  {
    int b;
  };

  struct Final3 : Base, Mixin3 {
    int c;

    constexpr Final3(int a, int b, int c) : c(c) { this->b = b; }
    constexpr Final3(int a, int b, int c, bool) : c(c) {}
  };

  static_assert(Final3{1, 2, 3}.c == 3, ""); // OK
  static_assert(Final3{1, 2, 3}.b == 2, ""); // OK
  static_assert(Final3{1, 2, 3}.a == 0, ""); // both-error {{not an integral constant expression}} \
                                             // both-note {{read of uninitialized object}}
};

namespace Destructors {

  class Inc final {
  public:
    int &I;
    constexpr Inc(int &I) : I(I) {}
    constexpr ~Inc() {
      I++;
    }
  };

  class Dec final {
  public:
    int &I;
    constexpr Dec(int &I) : I(I) {}
    constexpr ~Dec() {
      I--;
    }
  };



  constexpr int m() {
    int i = 0;
    {
      Inc f1(i);
      Inc f2(i);
      Inc f3(i);
    }
    return i;
  }
  static_assert(m() == 3, "");


  constexpr int C() {
    int i = 0;

    while (i < 10) {
      Inc inc(i);
      continue;
      Dec dec(i);
    }
    return i;
  }
  static_assert(C() == 10, "");


  constexpr int D() {
    int i = 0;

    {
      Inc i1(i);
      {
        Inc i2(i);
        return i;
      }
    }

    return i;
  }
  static_assert(D() == 0, "");

  constexpr int E() {
    int i = 0;

    for(;;) {
      Inc i1(i);
      break;
    }
    return i;
  }
  static_assert(E() == 1, "");


  /// FIXME: This should be rejected, since we call the destructor
  ///   twice. However, GCC doesn't care either.
  constexpr int ManualDtor() {
    int i = 0;
    {
      Inc I(i); // ref-note {{destroying object 'I' whose lifetime has already ended}}
      I.~Inc();
    }
    return i;
  }
  static_assert(ManualDtor() == 1, ""); // expected-error {{static assertion failed}} \
                                        // expected-note {{evaluates to '2 == 1'}} \
                                        // ref-error {{not an integral constant expression}} \
                                        // ref-note {{in call to 'ManualDtor()'}}

  constexpr void doInc(int &i) {
    Inc I(i);
    return;
  }
  constexpr int testInc() {
    int i = 0;
    doInc(i);
    return i;
  }
  static_assert(testInc() == 1, "");
  constexpr void doInc2(int &i) {
    Inc I(i);
    // No return statement.
  }
   constexpr int testInc2() {
    int i = 0;
    doInc2(i);
    return i;
  }
  static_assert(testInc2() == 1, "");


  namespace DtorOrder {
    class A {
      public:
      int &I;
      constexpr A(int &I) : I(I) {}
      constexpr ~A() {
        I = 1337;
      }
    };

    class B : public A {
      public:
      constexpr B(int &I) : A(I) {}
      constexpr ~B() {
        I = 42;
      }
    };

    constexpr int foo() {
      int i = 0;
      {
        B b(i);
      }
      return i;
    }

    static_assert(foo() == 1337);
  }

  class FieldDtor1 {
  public:
    Inc I1;
    Inc I2;
    constexpr FieldDtor1(int &I) : I1(I), I2(I){}
  };

  constexpr int foo2() {
    int i = 0;
    {
      FieldDtor1 FD1(i);
    }
    return i;
  }

  static_assert(foo2() == 2);

  class FieldDtor2 {
  public:
    Inc Incs[3];
    constexpr FieldDtor2(int &I)  : Incs{Inc(I), Inc(I), Inc(I)} {}
  };

  constexpr int foo3() {
    int i = 0;
    {
      FieldDtor2 FD2(i);
    }
    return i;
  }

  static_assert(foo3() == 3);

  struct ArrD {
    int index;
    int *arr;
    int &p;
    constexpr ~ArrD() {
      arr[p] = index;
      ++p;
    }
  };
  constexpr bool ArrayOrder() {
    int order[3] = {0, 0, 0};
    int p = 0;
    {
      ArrD ds[3] = {
        {1, order, p},
        {2, order, p},
        {3, order, p},
      };
      // ds will be destroyed.
    }
    return order[0] == 3 && order[1] == 2 && order[2] == 1;
  }
  static_assert(ArrayOrder());


  // Static members aren't destroyed.
  class Dec2 {
  public:
    int A = 0;
    constexpr ~Dec2() {
      A++;
    }
  };
  class Foo {
  public:
    static constexpr Dec2 a;
    static Dec2 b;
  };
  static_assert(Foo::a.A == 0);
  constexpr bool f() {
    Foo f;
    return true;
  }
  static_assert(Foo::a.A == 0);
  static_assert(f());
  static_assert(Foo::a.A == 0);


  struct NotConstexpr {
    NotConstexpr() {}
    ~NotConstexpr() {}
  };

  struct Outer {
    constexpr Outer() = default;
    constexpr ~Outer();

    constexpr int foo() {
      return 12;
    }

    constexpr int bar()const  {
      return Outer{}.foo();
    }

    static NotConstexpr Val;
  };

  constexpr Outer::~Outer() {}

  constexpr Outer O;
  static_assert(O.bar() == 12);
}

namespace BaseAndFieldInit {
  struct A {
    int a;
  };

  struct B : A {
    int b;
  };

  struct C : B {
    int c;
  };

  constexpr C c = {1,2,3};
  static_assert(c.a == 1 && c.b == 2 && c.c == 3);
}

namespace ImplicitFunction {
  struct A {
    int a; // ref-note {{subobject declared here}}
  };

  constexpr int callMe() {
   A a;
   A b{12};

   /// The operator= call here will fail and the diagnostics should be fine.
   b = a; // ref-note {{subobject 'a' is not initialized}} \
          // expected-note {{read of uninitialized object}} \
          // both-note {{in call to}}

   return 1;
  }
  static_assert(callMe() == 1, ""); // both-error {{not an integral constant expression}} \
                                    // both-note {{in call to 'callMe()'}}
}

namespace std {
  class strong_ordering {
  public:
    int n;
    static const strong_ordering less, equal, greater;
    constexpr bool operator==(int n) const noexcept { return this->n == n;}
    constexpr bool operator!=(int n) const noexcept { return this->n != n;}
  };
  constexpr strong_ordering strong_ordering::less = {-1};
  constexpr strong_ordering strong_ordering::equal = {0};
  constexpr strong_ordering strong_ordering::greater = {1};

  class partial_ordering {
  public:
    long n;
    static const partial_ordering less, equal, greater, equivalent, unordered;
    constexpr bool operator==(long n) const noexcept { return this->n == n;}
    constexpr bool operator!=(long n) const noexcept { return this->n != n;}
  };
  constexpr partial_ordering partial_ordering::less = {-1};
  constexpr partial_ordering partial_ordering::equal = {0};
  constexpr partial_ordering partial_ordering::greater = {1};
  constexpr partial_ordering partial_ordering::equivalent = {0};
  constexpr partial_ordering partial_ordering::unordered = {-127};
} // namespace std

namespace ThreeWayCmp {
  static_assert(1 <=> 2 == -1, "");
  static_assert(1 <=> 1 == 0, "");
  static_assert(2 <=> 1 == 1, "");
  static_assert(1.0 <=> 2.f == -1, "");
  static_assert(1.0 <=> 1.0 == 0, "");
  static_assert(2.0 <=> 1.0 == 1, "");
  constexpr int k = (1 <=> 1, 0); // both-warning {{comparison result unused}}
  static_assert(k== 0, "");

  /// Pointers.
  constexpr int a[] = {1,2,3};
  constexpr int b[] = {1,2,3};
  constexpr const int *pa1 = &a[1];
  constexpr const int *pa2 = &a[2];
  constexpr const int *pb1 = &b[1];
  static_assert(pa1 <=> pb1 != 0, ""); // both-error {{not an integral constant expression}} \
                                       // both-note {{has unspecified value}}
  static_assert(pa1 <=> pa1 == 0, "");
  static_assert(pa1 <=> pa2 == -1, "");
  static_assert(pa2 <=> pa1 == 1, "");
}

namespace ConstexprArrayInitLoopExprDestructors
{
  struct Highlander {
      int *p = 0;
      constexpr Highlander() {}
      constexpr void set(int *p) { this->p = p; ++*p; if (*p != 1) throw "there can be only one"; }
      constexpr ~Highlander() { --*p; }
  };

  struct X {
      int *p;
      constexpr X(int *p) : p(p) {}
      constexpr X(const X &x, Highlander &&h = Highlander()) : p(x.p) {
          h.set(p);
      }
  };

  constexpr int f() {
      int n = 0;
      X x[3] = {&n, &n, &n};
      auto [a, b, c] = x;
      return n;
  }

  static_assert(f() == 0);
}

namespace NonPrimitiveOpaqueValue
{
  struct X {
    int x;
    constexpr operator bool() const { return x != 0; }
  };

  constexpr int ternary() { return X(0) ?: X(0); }

  static_assert(!ternary(), "");
}

namespace TryCatch {
  constexpr int foo() {
    int a = 10;
    try {
      ++a;
    } catch(int m) {
      --a;
    }
    return a;
  }
  static_assert(foo() == 11);
}

namespace IgnoredConstantExpr {
  consteval int immediate() { return 0;}
  struct ReferenceToNestedMembers {
    int m;
    int a = ((void)immediate(), m);
    int b = ((void)immediate(), this->m);
  };
  struct ReferenceToNestedMembersTest {
    void* m = nullptr;
    ReferenceToNestedMembers j{0};
  } test_reference_to_nested_members;
}

namespace RewrittenBinaryOperators {
  template <class T, T Val>
  struct Conv {
    constexpr operator T() const { return Val; }
    operator T() { return Val; }
  };

  struct X {
    constexpr const Conv<int, -1> operator<=>(X) { return {}; }
  };
  static_assert(X() < X(), "");
}

namespace GH61417 {
struct A {
  unsigned x : 1;
  unsigned   : 0;
  unsigned y : 1;

  constexpr A() : x(0), y(0) {}
  bool operator==(const A& rhs) const noexcept = default;
};

void f1() {
  constexpr A a, b;
  constexpr bool c = (a == b); // no diagnostic, we should not be comparing the
                               // unnamed bit-field which is indeterminate
}

void f2() {
    A a, b;
    bool c = (a == b); // no diagnostic nor crash during codegen attempting to
                       // access info for unnamed bit-field
}
}

namespace FailingDestructor {
  struct D {
    int n;
    bool can_destroy;

    constexpr ~D() {
      if (!can_destroy)
        throw "oh no";
    }
  };
  template<D d>
  void f() {} // both-note {{invalid explicitly-specified argument}}

  void g() {
    f<D{0, false}>(); // both-error {{no matching function}}
  }
}


void overflowInSwitchCase(int n) {
  switch (n) {
  case (int)(float)1e300: // both-error {{constant expression}} \
                          // both-note {{value +Inf is outside the range of representable values of type 'int'}}
    break;
  }
}

namespace APValues {
  int g;
  struct A { union { int n, m; }; int *p; int A::*q; char buffer[32]; };
  template<A a> constexpr const A &get = a;
  constexpr const A &v = get<A{}>;
  constexpr const A &w = get<A{1, &g, &A::n, "hello"}>;
}

namespace self_referencing {
  struct S {
    S* ptr = nullptr;
    constexpr S(int i) : ptr(this) {
      if (this == ptr && i)
        ptr = nullptr;
    }
    constexpr ~S() {}
  };

  void test() {
    S s(1);
  }
}

namespace GH64949 {
  struct f {
    int g; // both-note {{subobject declared here}}
    constexpr ~f() {}
  };

  class h {
  public:
    consteval h(char *) {}
    f i;
  };

  void test() { h{nullptr}; } // both-error {{call to consteval function 'GH64949::h::h' is not a constant expression}} \
                              // both-note {{subobject 'g' is not initialized}} \
                              // both-warning {{expression result unused}}
}

/// This used to cause an assertion failure inside EvaluationResult::checkFullyInitialized.
namespace CheckingNullPtrForInitialization {
  struct X {
    consteval operator const char *() const {
      return nullptr;
    }
  };
  const char *f() {
    constexpr X x;
    return x;
  }
}

namespace VariadicCallOperator {
  class F {
  public:
    constexpr void operator()(int a, int b, ...) {}
  };
  constexpr int foo() {
    F f;

    f(1,2, 3);
    return 1;
  }
  constexpr int A = foo();
}

namespace DefinitionLoc {

  struct NonConstexprCopy {
    constexpr NonConstexprCopy() = default;
    NonConstexprCopy(const NonConstexprCopy &);
    constexpr NonConstexprCopy(NonConstexprCopy &&) = default;

    int n = 42;
  };

  NonConstexprCopy::NonConstexprCopy(const NonConstexprCopy &) = default; // both-note {{here}}

  constexpr NonConstexprCopy ncc1 = NonConstexprCopy(NonConstexprCopy());
  constexpr NonConstexprCopy ncc2 = ncc1; // both-error {{constant expression}} \
                                          // both-note {{non-constexpr constructor}}
}

namespace VirtDtor {
  class B {
  public:
    constexpr B(char *p) : p(p) {}
    virtual constexpr ~B() {
      *p = 'B';
      ++p;
    }

    char *p;
  };

  class C : public B {
  public:
    constexpr C(char *p) : B(p) {}
    virtual constexpr ~C() override {
      *p = 'C';
      ++p;
    }
  };

  union U {
    constexpr U(char *p) : c(p) {}
    constexpr ~U() {}

    C c;
  };

  constexpr int test(char a, char b) {
    char buff[2] = {};
    U u(buff);

    /// U is a union, so it won't call the destructor of its fields.
    /// We do this manually here. Explicitly calling ~C() here should
    /// also call the destructor of the base classes however.
    u.c.~C();

    return buff[0] == a && buff[1] == b;
  }

  static_assert(test('C', 'B'));
}