llvm/clang/test/CXX/special/class.temporary/p6.cpp

// RUN: %clang_cc1 -std=c++17 %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s --implicit-check-not='call{{.*}}dtor'
// RUN: %clang_cc1 -std=c++23 %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-CXX23,CHECK-CXX23-NEXT,CHECK-CXX23-LABEL

namespace std {
  typedef decltype(sizeof(int)) size_t;

  template <class E>
  struct initializer_list {
    const E *begin;
    size_t   size;
    initializer_list() : begin(nullptr), size(0) {}
  };

  template <typename E>
  struct list {
    list() {}
    ~list() {}
    E *begin();
    E *end();
    const E *begin() const;
    const E *end() const;
  };

  template <typename E>
  struct vector {
    vector() {}
    vector(std::initializer_list<E>) {}
    ~vector() {}
    E *begin();
    E *end();
    const E *begin() const;
    const E *end() const;
  };

  template <typename T>
  struct lock_guard {
    lock_guard(T) {}
    ~lock_guard() {}
  };

  struct mutex {};
} // namespace std

void then();

struct dtor {
  ~dtor();
};

dtor ctor();

auto &&lambda = [a = {ctor()}] {};
// CHECK-LABEL: define
// CHECK: call {{.*}}ctor
// CHECK: call {{.*}}atexit{{.*}}global_array_dtor

// CHECK-LABEL: define{{.*}}global_array_dtor
// CHECK: call {{.*}}dtor

// [lifetime extension occurs if the object was obtained by]
//  -- a temporary materialization conversion
// CHECK-LABEL: ref_binding
void ref_binding() {
  // CHECK: call {{.*}}ctor
  auto &&x = ctor();
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}

//  -- ( expression )
// CHECK-LABEL: parens
void parens() {
  // CHECK: call {{.*}}ctor
  auto &&x = ctor();
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}

//  -- subscripting of an array
// CHECK-LABEL: array_subscript_1
void array_subscript_1() {
  using T = dtor[1];
  // CHECK: call {{.*}}ctor
  auto &&x = T{ctor()}[0];
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}
// CHECK-LABEL: array_subscript_2
void array_subscript_2() {
  using T = dtor[1];
  // CHECK: call {{.*}}ctor
  auto &&x = ((dtor*)T{ctor()})[0];
  // CHECK: call {{.*}}dtor
  // CHECK: call {{.*}}then
  then();
  // CHECK: }
}

struct with_member { dtor d; ~with_member(); };
struct with_ref_member { dtor &&d; ~with_ref_member(); };

//  -- a class member access using the . operator [...]
// CHECK-LABEL: member_access_1
void member_access_1() {
  // CHECK: call {{.*}}ctor
  auto &&x = with_member{ctor()}.d;
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}with_member
  // CHECK: }
}
// CHECK-LABEL: member_access_2
void member_access_2() {
  // CHECK: call {{.*}}ctor
  auto &&x = with_ref_member{ctor()}.d;
  // CHECK: call {{.*}}with_ref_member
  // CHECK: call {{.*}}dtor
  // CHECK: call {{.*}}then
  then();
  // CHECK: }
}
// CHECK-LABEL: member_access_3
void member_access_3() {
  // CHECK: call {{.*}}ctor
  auto &&x = (&(const with_member&)with_member{ctor()})->d;
  // CHECK: call {{.*}}with_member
  // CHECK: call {{.*}}then
  then();
  // CHECK: }
}

//  -- a pointer-to-member operation using the .* operator [...]
// CHECK-LABEL: member_ptr_access_1
void member_ptr_access_1() {
  // CHECK: call {{.*}}ctor
  auto &&x = with_member{ctor()}.*&with_member::d;
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}with_member
  // CHECK: }
}
// CHECK-LABEL: member_ptr_access_2
void member_ptr_access_2() {
  // CHECK: call {{.*}}ctor
  auto &&x = (&(const with_member&)with_member{ctor()})->*&with_member::d;
  // CHECK: call {{.*}}with_member
  // CHECK: call {{.*}}then
  then();
  // CHECK: }
}

//  -- a [named] cast [...]
// CHECK-LABEL: static_cast
void test_static_cast() {
  // CHECK: call {{.*}}ctor
  auto &&x = static_cast<dtor&&>(ctor());
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}
// CHECK-LABEL: const_cast
void test_const_cast() {
  // CHECK: call {{.*}}ctor
  auto &&x = const_cast<dtor&&>(ctor());
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}
// CHECK-LABEL: reinterpret_cast
void test_reinterpret_cast() {
  // CHECK: call {{.*}}ctor
  auto &&x = reinterpret_cast<dtor&&>(static_cast<dtor&&>(ctor()));
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}
// CHECK-LABEL: dynamic_cast
void test_dynamic_cast() {
  // CHECK: call {{.*}}ctor
  auto &&x = dynamic_cast<dtor&&>(ctor());
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}

//  -- [explicit cast notation is defined in terms of the above]
// CHECK-LABEL: c_style_cast
void c_style_cast() {
  // CHECK: call {{.*}}ctor
  auto &&x = (dtor&&)ctor();
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}
// CHECK-LABEL: function_style_cast
void function_style_cast() {
  // CHECK: call {{.*}}ctor
  using R = dtor&&;
  auto &&x = R(ctor());
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}

//  -- a conditional operator
// CHECK-LABEL: conditional
void conditional(bool b) {
  // CHECK: call {{.*}}ctor
  // CHECK: call {{.*}}ctor
  auto &&x = b ? (dtor&&)ctor() : (dtor&&)ctor();
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: call {{.*}}dtor
  // CHECK: }
}

//  -- a comma expression
// CHECK-LABEL: comma
void comma() {
  // CHECK: call {{.*}}ctor
  auto &&x = (true, (dtor&&)ctor());
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}


// This applies recursively: if an object is lifetime-extended and contains a
// reference, the referent is also extended.
// CHECK-LABEL: init_capture_ref
void init_capture_ref() {
  // CHECK: call {{.*}}ctor
  auto x = [&a = (const dtor&)ctor()] {};
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}
// CHECK-LABEL: init_capture_ref_indirect
void init_capture_ref_indirect() {
  // CHECK: call {{.*}}ctor
  auto x = [&a = (const dtor&)ctor()] {};
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}
// CHECK-LABEL: init_capture_init_list
void init_capture_init_list() {
  // CHECK: call {{.*}}ctor
  auto x = [a = {ctor()}] {};
  // CHECK: call {{.*}}then
  then();
  // CHECK: call {{.*}}dtor
  // CHECK: }
}

void check_dr1815() { // dr1815: yes
#if __cplusplus >= 201402L

  struct A {
    int &&r = 0;
    ~A() {}
  };

  struct B {
    A &&a = A{};
    ~B() {}
  };
  B a = {};
  
  // CHECK: call {{.*}}block_scope_begin_function
  extern void block_scope_begin_function();
  extern void block_scope_end_function();
  block_scope_begin_function();
  {
    // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
    // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
    B b = {};
  }
  // CHECK: call {{.*}}block_scope_end_function
  block_scope_end_function();

  // CHECK: call {{.*}}some_other_function
  extern void some_other_function();
  some_other_function();
  // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
  // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
#endif
}

namespace P2718R0 {
namespace basic {
template <typename E> using T2 = std::list<E>;
template <typename E> const T2<E> &f1_temp(const T2<E> &t)  { return t; }
template <typename E> const T2<E> &f2_temp(T2<E> t)         { return t; }
template <typename E> T2<E> g_temp()                        { return T2<E>{}; }

template <typename E>
void foo_dependent_context1() {
  // CHECK-CXX23: void @_ZN7P2718R05basic22foo_dependent_context1IiEEvv()
  // CHECK-CXX23: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  for (auto e : f1_temp(g_temp<E>())) {}  // OK, lifetime of return value of g() extended
}

template <typename E>
void foo_dependent_context2() {
  // CHECK-CXX23: void @_ZN7P2718R05basic22foo_dependent_context2IiEEvv()
  // CHECK-CXX23-NEXT: entry:
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R05basic6g_tempIiEESt4listIT_Ev(
  // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R05basic7f2_tempIiEERKSt4listIT_ES4_(
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  // CHECK-CXX23: call {{.*}} @_ZNKSt4listIiE5beginEv(
  // CHECK-CXX23: call {{.*}} @_ZNKSt4listIiE3endEv(
  for (auto e : f2_temp(g_temp<E>())) {}  // undefined behavior
}

template void foo_dependent_context1<int>();
template void foo_dependent_context2<int>();
} // namespace basic

namespace discard_value_expression {
template <typename T>
void f_dependent_context1() {
  std::vector<T> v = { 42, 17, 13 };
  std::mutex m;
  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context1IiEEvv()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
  for (T x : std::lock_guard<std::mutex>(m), v)  // lock released in C++ 2023
    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
}

template <typename T>
void f_dependent_context2() {
  std::vector<T> v = { 42, 17, 13 };
  std::mutex m;
  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context2IiEEvv()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
  for (T x : (void)std::lock_guard<std::mutex>(m), v)  // lock released in C++ 2023
    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
}

template <typename T>
void f_dependent_context3() {
  std::vector<T> v = { 42, 17, 13 };
  std::mutex m;
  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context3IiEEvv()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
  for (T x : static_cast<void>(std::lock_guard<std::mutex>(m)), v)  // lock released in C++ 2023
    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
}

template void f_dependent_context1<int>();
template void f_dependent_context2<int>();
template void f_dependent_context3<int>();
} // namespace discard_value_expression

namespace member_call {
template <typename T>
struct ListWrapper {
  std::list<T> list;
  ListWrapper() {}
  ~ListWrapper() {}
  const T *begin() const { return list.begin(); }
  const T *end() const { return list.end(); }
  ListWrapper& r() { return *this; }
  ListWrapper g() { return ListWrapper(); }
};

template <typename E>
ListWrapper<E> g_temp() { return ListWrapper<E>{}; }

template <typename T>
void member_call_dependent_context() {
  // CHECK-CXX23: void @_ZN7P2718R011member_call29member_call_dependent_contextIiEEvv()
  // CHECK-CXX23-LABEL: for.cond.cleanup: 
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  for (auto e : g_temp<T>().r().g().r().g().r().g()) {}
}

template void member_call_dependent_context<int>();
} // namespace member_call

namespace default_arg {
template <typename T>
struct DefaultArg {
  DefaultArg() {}
  DefaultArg(int) {}
  ~DefaultArg() {}
};

template <typename T>
struct C2 : public std::list<T> {
  C2() {}
  C2(int, const C2 &, const DefaultArg<T> &Default = DefaultArg<T>{}) {}
};

template <typename T>
std::list<T> temp_foo(const std::list<T>&, const DefaultArg<T> &Default = DefaultArg<T>{}) {
  return std::list<T>{};
}

template <typename T>
void default_arg_dependent_context1() {
  // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context1IiEEvv()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  for (auto e : temp_foo(std::list<T>{})) {}
}

template <typename T>
void default_arg_dependent_context2() {
  // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context2IiEEvv()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  for (auto e : temp_foo(temp_foo(std::list<T>{}))) {}
}

template <typename T>
void default_arg_dependent_context3() {
  // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context3IiEEvv()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(

  for (auto e : C2<T>(0, C2<T>(0, C2<T>(0, C2<T>())))) {}
}

template void default_arg_dependent_context1<int>();
template void default_arg_dependent_context2<int>();
template void default_arg_dependent_context3<int>();
} // namespace default_arg

namespace basic {
using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t)        { return t; }
T g()                   { return T{}; }

void foo1() {
  // CHECK-CXX23: void @_ZN7P2718R05basic4foo1Ev()
  // CHECK-CXX23: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  for (auto e : f1(g())) {}  // OK, lifetime of return value of g() extended
}

void foo2() {
  // CHECK-CXX23: void @_ZN7P2718R05basic4foo2Ev()
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R05basic1gEv(
  // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R05basic2f2ESt4listIiE(
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  for (auto e : f2(g())) {}  // undefined behavior
}
} // namespace basic

namespace discard_value_expression {
void f1() {
  std::vector<int> v = { 42, 17, 13 };
  std::mutex m;
  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f1Ev()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev( 
  for (int x : std::lock_guard<std::mutex>(m), v)  // lock released in C++ 2023
    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
}

void f2() {
  std::vector<int> v = { 42, 17, 13 };
  std::mutex m;
  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f2Ev()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
  for (int x : (void)std::lock_guard<std::mutex>(m), v)  // lock released in C++ 2023
    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
}

void f3() {
  std::vector<int> v = { 42, 17, 13 };
  std::mutex m;
  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f3Ev()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
  for (int x : static_cast<void>(std::lock_guard<std::mutex>(m)), v)  // lock released in C++ 2023
    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
}
} // namespace discard_value_expression

namespace member_call {
using A = ListWrapper<int>;

A g() { return A(); }
const A &f1(const A &t) { return t; }

void member_call() {
  // CHECK-CXX23: void @_ZN7P2718R011member_call11member_callEv()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  for (auto e : g().r().g().r().g().r().g()) {}
}
} // namespace member_call

namespace default_arg {
using A = std::list<int>;
using DefaultA = DefaultArg<int>;
struct C : public A {
  C() {}
  C(int, const C &, const DefaultA & = DefaultA()) {}
};

A foo(const A&, const DefaultA &Default = DefaultA()) {
  return A();
}

int (&some_func(const A & = A{}))[3];

void default_arg1() {
  // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg1Ev()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  for (auto e : some_func()) {}
}

void default_arg2() {
  // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg2Ev()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
  for (auto e : some_func(foo(foo(A())))) {}
}

void default_arg3() {
  // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg3Ev()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
  for (auto e : C(0, C(0, C(0, C())))) {}
}
} // namespace default_arg
} // namespace P2718R0