// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG

struct A {
  ~A();
};

void test_temporary_dtor() {
  A();
}

// CIR: cir.func {{.*}} @_Z19test_temporary_dtorv()
// CIR:   %[[ALLOCA:.*]] = cir.alloca !rec_A, !cir.ptr<!rec_A>, ["agg.tmp.ensured"]
// CIR:   cir.call @_ZN1AD1Ev(%[[ALLOCA]]) nothrow : (!cir.ptr<!rec_A>) -> ()

// LLVM: define dso_local void @_Z19test_temporary_dtorv(){{.*}}
// LLVM:   %[[ALLOCA:.*]] = alloca %struct.A, i64 1, align 1
// LLVM:   call void @_ZN1AD1Ev(ptr %[[ALLOCA]])

// OGCG: define dso_local void @_Z19test_temporary_dtorv()
// OGCG:   %[[ALLOCA:.*]] = alloca %struct.A, align 1
// OGCG:   call void @_ZN1AD1Ev(ptr {{.*}} %[[ALLOCA]])

struct B {
  int n;
  B(int n) : n(n) {}
  ~B() {}
};

bool make_temp(const B &) { return false; }
bool test_temp_or() { return make_temp(1) || make_temp(2); }

// CIR: cir.func{{.*}} @_Z12test_temp_orv()
// CIR:   cir.scope {
// CIR:     %[[REF_TMP0:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp0"]
// CIR:     %[[ONE:.*]] = cir.const #cir.int<1>
// CIR:     cir.call @_ZN1BC2Ei(%[[REF_TMP0]], %[[ONE]])
// CIR:     %[[MAKE_TEMP0:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP0]])
// CIR:     %[[TERNARY:.*]] = cir.ternary(%[[MAKE_TEMP0]], true {
// CIR:       %[[TRUE:.*]] = cir.const #true
// CIR:       cir.yield %[[TRUE]] : !cir.bool
// CIR:     }, false {
// CIR:       %[[REF_TMP1:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp1"]
// CIR:       %[[TWO:.*]] = cir.const #cir.int<2>
// CIR:       cir.call @_ZN1BC2Ei(%[[REF_TMP1]], %[[TWO]])
// CIR:       %[[MAKE_TEMP1:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP1]])
// CIR:       cir.call @_ZN1BD2Ev(%[[REF_TMP1]])
// CIR:       cir.yield %[[MAKE_TEMP1]] : !cir.bool
// CIR:     })
// CIR:     cir.store{{.*}} %[[TERNARY]], %[[RETVAL:.*]]
// CIR:     cir.call @_ZN1BD2Ev(%[[REF_TMP0]])
// CIR:   }

// LLVM: define{{.*}} i1 @_Z12test_temp_orv(){{.*}} {
// LLVM:   %[[REF_TMP0:.*]] = alloca %struct.B
// LLVM:   %[[REF_TMP1:.*]] = alloca %struct.B
// LLVM:   br label %[[LOR_BEGIN:.*]]
// LLVM: [[LOR_BEGIN]]:
// LLVM:   call void @_ZN1BC2Ei(ptr %[[REF_TMP0]], i32 1)
// LLVM:   %[[MAKE_TEMP0:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP0]])
// LLVM:   br i1 %[[MAKE_TEMP0]], label %[[LHS_TRUE_BLOCK:.*]], label %[[LHS_FALSE_BLOCK:.*]]
// LLVM: [[LHS_TRUE_BLOCK]]:
// LLVM:   br label %[[RESULT_BLOCK:.*]]
// LLVM: [[LHS_FALSE_BLOCK]]:
// LLVM:   call void @_ZN1BC2Ei(ptr %[[REF_TMP1]], i32 2)
// LLVM:   %[[MAKE_TEMP1:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP1]])
// LLVM:   call void @_ZN1BD2Ev(ptr %[[REF_TMP1]])
// LLVM:   br label %[[RESULT_BLOCK]]
// LLVM: [[RESULT_BLOCK]]:
// LLVM:   %[[RESULT:.*]] = phi i1 [ %[[MAKE_TEMP1]], %[[LHS_FALSE_BLOCK]] ], [ true, %[[LHS_TRUE_BLOCK]] ]
// LLVM:   br label %[[LOR_END:.*]]
// LLVM: [[LOR_END]]:
// LLVM:   call void @_ZN1BD2Ev(ptr %[[REF_TMP0]])

// OGCG: define {{.*}} i1 @_Z12test_temp_orv()
// OGCG: [[ENTRY:.*]]:
// OGCG:   %[[RETVAL:.*]] = alloca i1
// OGCG:   %[[REF_TMP0:.*]] = alloca %struct.B
// OGCG:   %[[REF_TMP1:.*]] = alloca %struct.B
// OGCG:   %[[CLEANUP_COND:.*]] = alloca i1
// OGCG:   call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP0]], i32 {{.*}} 1)
// OGCG:   %[[MAKE_TEMP0:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP0]])
// OGCG:   store i1 false, ptr %cleanup.cond
// OGCG:   br i1 %[[MAKE_TEMP0]], label %[[LOR_END:.*]], label %[[LOR_RHS:.*]]
// OGCG: [[LOR_RHS]]:
// OGCG:   call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP1]], i32 {{.*}} 2)
// OGCG:   store i1 true, ptr %[[CLEANUP_COND]]
// OGCG:   %[[MAKE_TEMP1:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP1]])
// OGCG:   br label %[[LOR_END]]
// OGCG: [[LOR_END]]:
// OGCG:    %[[PHI:.*]] = phi i1 [ true, %[[ENTRY]] ], [ %[[MAKE_TEMP1]], %[[LOR_RHS]] ]
// OGCG:   store i1 %[[PHI]], ptr %[[RETVAL]]
// OGCG:   %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]]
// OGCG:   br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]]
// OGCG: [[CLEANUP_ACTION]]:
// OGCG:   call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP1]])
// OGCG:   br label %[[CLEANUP_DONE]]
// OGCG: [[CLEANUP_DONE]]:
// OGCG:   call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]])

bool test_temp_and() { return make_temp(1) && make_temp(2); }

// CIR: cir.func{{.*}} @_Z13test_temp_andv()
// CIR:   cir.scope {
// CIR:     %[[REF_TMP0:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp0"]
// CIR:     %[[ONE:.*]] = cir.const #cir.int<1>
// CIR:     cir.call @_ZN1BC2Ei(%[[REF_TMP0]], %[[ONE]])
// CIR:     %[[MAKE_TEMP0:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP0]])
// CIR:     %[[TERNARY:.*]] = cir.ternary(%[[MAKE_TEMP0]], true {
// CIR:       %[[REF_TMP1:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp1"]
// CIR:       %[[TWO:.*]] = cir.const #cir.int<2>
// CIR:       cir.call @_ZN1BC2Ei(%[[REF_TMP1]], %[[TWO]])
// CIR:       %[[MAKE_TEMP1:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP1]])
// CIR:       cir.call @_ZN1BD2Ev(%[[REF_TMP1]])
// CIR:       cir.yield %[[MAKE_TEMP1]] : !cir.bool
// CIR:     }, false {
// CIR:       %[[FALSE:.*]] = cir.const #false
// CIR:       cir.yield %[[FALSE]] : !cir.bool
// CIR:     })
// CIR:     cir.store{{.*}} %[[TERNARY]], %[[RETVAL:.*]]
// CIR:     cir.call @_ZN1BD2Ev(%[[REF_TMP0]])
// CIR:   }

// LLVM: define{{.*}} i1 @_Z13test_temp_andv(){{.*}} {
// LLVM:   %[[REF_TMP0:.*]] = alloca %struct.B
// LLVM:   %[[REF_TMP1:.*]] = alloca %struct.B
// LLVM:   br label %[[LAND_BEGIN:.*]]
// LLVM: [[LAND_BEGIN]]:
// LLVM:   call void @_ZN1BC2Ei(ptr %[[REF_TMP0]], i32 1)
// LLVM:   %[[MAKE_TEMP0:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP0]])
// LLVM:   br i1 %[[MAKE_TEMP0]], label %[[LHS_TRUE_BLOCK:.*]], label %[[LHS_FALSE_BLOCK:.*]]
// LLVM: [[LHS_TRUE_BLOCK]]:
// LLVM:   call void @_ZN1BC2Ei(ptr %[[REF_TMP1]], i32 2)
// LLVM:   %[[MAKE_TEMP1:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP1]])
// LLVM:   call void @_ZN1BD2Ev(ptr %[[REF_TMP1]])
// LLVM:   br label %[[RESULT_BLOCK:.*]]
// LLVM: [[LHS_FALSE_BLOCK]]:
// LLVM:   br label %[[RESULT_BLOCK]]
// LLVM: [[RESULT_BLOCK]]:
// LLVM:   %[[RESULT:.*]] = phi i1 [ false, %[[LHS_FALSE_BLOCK]] ], [ %[[MAKE_TEMP1]], %[[LHS_TRUE_BLOCK]] ]
// LLVM:   br label %[[LAND_END:.*]]
// LLVM: [[LAND_END]]:
// LLVM:   call void @_ZN1BD2Ev(ptr %[[REF_TMP0]])

// OGCG: define {{.*}} i1 @_Z13test_temp_andv()
// OGCG: [[ENTRY:.*]]:
// OGCG:   %[[RETVAL:.*]] = alloca i1
// OGCG:   %[[REF_TMP0:.*]] = alloca %struct.B
// OGCG:   %[[REF_TMP1:.*]] = alloca %struct.B
// OGCG:   %[[CLEANUP_COND:.*]] = alloca i1
// OGCG:   call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP0]], i32 {{.*}} 1)
// OGCG:   %[[MAKE_TEMP0:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP0]])
// OGCG:   store i1 false, ptr %cleanup.cond
// OGCG:   br i1 %[[MAKE_TEMP0]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]]
// OGCG: [[LAND_RHS]]:
// OGCG:   call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP1]], i32 {{.*}} 2)
// OGCG:   store i1 true, ptr %[[CLEANUP_COND]]
// OGCG:   %[[MAKE_TEMP1:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP1]])
// OGCG:   br label %[[LAND_END]]
// OGCG: [[LAND_END]]:
// OGCG:   %[[PHI:.*]] = phi i1 [ false, %[[ENTRY]] ], [ %[[MAKE_TEMP1]], %[[LAND_RHS]] ]
// OGCG:   store i1 %[[PHI]], ptr %[[RETVAL]]
// OGCG:   %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]]
// OGCG:   br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]]
// OGCG: [[CLEANUP_ACTION]]:
// OGCG:   call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP1]])
// OGCG:   br label %[[CLEANUP_DONE]]
// OGCG: [[CLEANUP_DONE]]:
// OGCG:   call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]])

struct C {
  ~C();
};

struct D {
  int n;
  C c;
  ~D() {}
};

// CIR: cir.func {{.*}} @_ZN1DD2Ev
// CIR:   %[[C:.*]] = cir.get_member %{{.*}}[1] {name = "c"}
// CIR:   cir.call @_ZN1CD1Ev(%[[C]])

// LLVM: define {{.*}} void @_ZN1DD2Ev
// LLVM:   %[[C:.*]] = getelementptr %struct.D, ptr %{{.*}}, i32 0, i32 1
// LLVM:   call void @_ZN1CD1Ev(ptr %[[C]])

// This destructor is defined after the calling function in OGCG.

void test_nested_dtor() {
  D d;
}

// CIR: cir.func{{.*}} @_Z16test_nested_dtorv()
// CIR:   cir.call @_ZN1DD2Ev(%{{.*}})

// LLVM: define {{.*}} void @_Z16test_nested_dtorv(){{.*}}
// LLVM:   call void @_ZN1DD2Ev(ptr %{{.*}})

// OGCG: define {{.*}} void @_Z16test_nested_dtorv()
// OGCG:   call void @_ZN1DD2Ev(ptr {{.*}} %{{.*}})

// OGCG: define {{.*}} void @_ZN1DD2Ev
// OGCG:   %[[C:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i64 4
// OGCG:   call void @_ZN1CD1Ev(ptr {{.*}} %[[C]])

struct E {
  ~E();
};

struct F : public E {
  int n;
  ~F() {}
};

// CIR: cir.func {{.*}} @_ZN1FD2Ev
// CIR:   %[[BASE_E:.*]] = cir.base_class_addr %{{.*}} : !cir.ptr<!rec_F> nonnull [0] -> !cir.ptr<!rec_E>
// CIR:   cir.call @_ZN1ED2Ev(%[[BASE_E]]) nothrow : (!cir.ptr<!rec_E>) -> ()

// Because E is at offset 0 in F, there is no getelementptr needed.

// LLVM: define {{.*}} void @_ZN1FD2Ev
// LLVM:   call void @_ZN1ED2Ev(ptr %{{.*}})

// This destructor is defined after the calling function in OGCG.

void test_base_dtor_call() {
  F f;
}

// CIR: cir.func {{.*}} @_Z19test_base_dtor_callv()
//   cir.call @_ZN1FD2Ev(%{{.*}}) nothrow : (!cir.ptr<!rec_F>) -> ()

// LLVM: define {{.*}} void @_Z19test_base_dtor_callv(){{.*}}
// LLVM:   call void @_ZN1FD2Ev(ptr %{{.*}})

// OGCG: define {{.*}} void @_Z19test_base_dtor_callv()
// OGCG:   call void @_ZN1FD2Ev(ptr {{.*}} %{{.*}})

// OGCG: define {{.*}} void @_ZN1FD2Ev
// OGCG:   call void @_ZN1ED2Ev(ptr {{.*}} %{{.*}})

struct VirtualBase {
  ~VirtualBase();
};

struct Derived : virtual VirtualBase {
  ~Derived() {}
};

void test_base_dtor_call_virtual_base() {
  Derived d;
}

// Derived D2 (base) destructor -- does not call VirtualBase destructor

// CIR:     cir.func {{.*}} @_ZN7DerivedD2Ev
// CIR-NOT:   cir.call{{.*}} @_ZN11VirtualBaseD2Ev
// CIR:       cir.return

// LLVM:     define {{.*}} void @_ZN7DerivedD2Ev
// LLVM-NOT:   call{{.*}} @_ZN11VirtualBaseD2Ev
// LLVM:       ret

// Derived D1 (complete) destructor -- does call VirtualBase destructor

// CIR: cir.func {{.*}} @_ZN7DerivedD1Ev
// CIR:   %[[THIS:.*]] = cir.load %{{.*}}
// CIR:   %[[VTT:.*]] = cir.vtt.address_point @_ZTT7Derived, offset = 0 -> !cir.ptr<!cir.ptr<!void>>
// CIR:   cir.call @_ZN7DerivedD2Ev(%[[THIS]], %[[VTT]])
// CIR:   %[[VIRTUAL_BASE:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_VirtualBase>
// CIR:   cir.call @_ZN11VirtualBaseD2Ev(%[[VIRTUAL_BASE]])

// LLVM: define {{.*}} void @_ZN7DerivedD1Ev
// LLVM:   call void @_ZN7DerivedD2Ev(ptr %{{.*}}, ptr @_ZTT7Derived)
// LLVM:   call void @_ZN11VirtualBaseD2Ev(ptr %{{.*}})

// OGCG emits these destructors in reverse order

// OGCG: define {{.*}} void @_ZN7DerivedD1Ev
// OGCG:   call void @_ZN7DerivedD2Ev(ptr {{.*}} %{{.*}}, ptr {{.*}} @_ZTT7Derived)
// OGCG:   call void @_ZN11VirtualBaseD2Ev(ptr {{.*}} %{{.*}})

// OGCG:     define {{.*}} void @_ZN7DerivedD2Ev
// OGCG-NOT:   call{{.*}} @_ZN11VirtualBaseD2Ev
// OGCG:       ret
