// RUN: %clang_cc1 -std=c++20 -fblocks -Wno-return-stack-address -triple x86_64-unknown-unknown-gnu -emit-llvm -O1 -fexperimental-new-pass-manager -o - %s | FileCheck %s

struct X {
    X();
    X(const X&);
    X(X&&);
};

#define L(A, B, C) void l##A() {    \
  auto t = []<class T = X>() -> C { \
    T t;                            \
    return B;                       \
  }();                              \
}

// CHECK-LABEL: define{{.*}} void @_Z2l1v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
L(1, t, X);

// CHECK-LABEL: define{{.*}} void @_Z2l2v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
L(2, t, X&);

// CHECK-LABEL: define{{.*}} void @_Z2l3v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
L(3, t, T);

// CHECK-LABEL: define{{.*}} void @_Z2l4v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
L(4, t, T&);

// CHECK-LABEL: define{{.*}} void @_Z2l5v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
L(5, t, auto);

// CHECK-LABEL: define{{.*}} void @_Z2l6v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
L(6, t, auto&);

// CHECK-LABEL: define{{.*}} void @_Z2l7v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
L(7, t, decltype(auto));

// CHECK-LABEL: define{{.*}} void @_Z2l8v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
L(8, (t), decltype(auto));

#undef L

#define F(A, B, C) template<class T = X> static inline auto tf##A() -> C { \
    T t;                                                                   \
    return B;                                                              \
}                                                                          \
void f##A() { auto t = tf##A(); }                                          \

// CHECK-LABEL: define{{.*}} void @_Z2f1v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
F(1, t, X);

// CHECK-LABEL: define{{.*}} void @_Z2f2v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
F(2, t, X&);

// CHECK-LABEL: define{{.*}} void @_Z2f3v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
F(3, t, T);

// CHECK-LABEL: define{{.*}} void @_Z2f4v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
F(4, t, T&);

// CHECK-LABEL: define{{.*}} void @_Z2f5v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
F(5, t, auto);

// CHECK-LABEL: define{{.*}} void @_Z2f6v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
F(6, t, auto&);

// CHECK-LABEL: define{{.*}} void @_Z2f7v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
F(7, t, decltype(auto));

// CHECK-LABEL: define{{.*}} void @_Z2f8v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
F(8, (t), decltype(auto));

#undef F

#define B(A, B) void b##A() { \
  auto t = []<class T = X>() { return ^ B () { \
      T t;                                     \
      return t;                                \
  }; }()();                                    \
}

//B(1, X); // Uncomment this line at your own peril ;)

// CHECK-LABEL: define{{.*}} void @_Z2b2v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
B(2, X&);

// CHECK-LABEL: define{{.*}} void @_Z2b3v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
B(3, T);

// CHECK-LABEL: define{{.*}} void @_Z2b4v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call {{.*}} @_ZN1XC1ERKS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
B(4, T&);

// CHECK-LABEL: define{{.*}} void @_Z2b5v
// CHECK:       call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT:  call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  call void @llvm.lifetime.end
// CHECK-NEXT:  ret void
B(5, );

#undef B
