// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=latest | FileCheck %s
// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=19 | FileCheck %s --check-prefix=CLANG19
// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=17 | FileCheck %s --check-prefix=CLANG17
// expected-no-diagnostics

namespace test1 {
template <bool> struct S {};
template <typename> concept C = true;
template <typename T = int> S<C<T>> f0() { return S<C<T>>{}; }
template S<C<int>> f0<>();
// CHECK: @_ZN5test12f0IiEENS_1SIX1CIT_EEEEv(
// CLANG17: @_ZN5test12f0IiEENS_1SIL_ZNS_1CIT_EEEEEv(
}

template <bool> struct S {};
template <typename> concept C = true;
template <typename, typename> concept D = true;

template <typename T = int> S<test1::C<T>> f0a() { return S<C<T>>{}; }
template S<test1::C<int>> f0a<>();
// CHECK: @_Z3f0aIiE1SIXsr5test1E1CIT_EEEv(
// CLANG17: @_Z3f0aIiE1SIL_ZN5test11CIT_EEEEv(

template <typename T = int> S<C<T>> f0() { return S<C<T>>{}; }
template S<C<int>> f0<>();
// CHECK: @_Z2f0IiE1SIX1CIT_EEEv(
// CLANG17: @_Z2f0IiE1SIL_Z1CIT_EEEv(

template<typename T> concept True = true;

namespace test2 {
  // Member-like friends.
  template<typename T> struct A {
    friend void f(...) requires True<T> {}

    template<typename U = void>
    friend void g(...) requires True<T> && True<U> {}

    template<typename U = void>
    friend void h(...) requires True<U> {}

    template<typename U = void> requires True<T> && True<U>
    friend void i(...) {}

    template<typename U = void> requires True<U>
    friend void j(...) {}

    template<True U = void> requires True<T>
    friend void k(...) {}

    template<True U = void>
    friend void l(...) {}
  };

  A<int> ai;

  // CHECK-LABEL: define {{.*}}@{{.*}}test2{{.*}}use
  void use() {
    // CHECK: call {{.*}}@_ZN5test21AIiEF1fEzQ4TrueIT_E(
    // CLANG17: call {{.*}}@_ZN5test21fEz(
    f(ai);
    // CHECK: call {{.*}}@_ZN5test21AIiEF1gIvEEvzQaa4TrueIT_E4TrueITL0__E(
    // CLANG19: call {{.*}}@_ZN5test2F1gIvEEvzQaa4TrueIT_E4TrueITL0__E(
    // CLANG17: call {{.*}}@_ZN5test21gIvEEvz(
    g(ai);
    // CHECK: call {{.*}}@_ZN5test21hIvEEvzQ4TrueITL0__E(
    // CLANG17: call {{.*}}@_ZN5test21hIvEEvz(
    h(ai);
    // CHECK: call {{.*}}@_ZN5test21AIiEF1iIvQaa4TrueIT_E4TrueITL0__EEEvz(
    // CLANG19: call {{.*}}@_ZN5test2F1iIvQaa4TrueIT_E4TrueITL0__EEEvz(
    // CLANG17: call {{.*}}@_ZN5test21iIvEEvz(
    i(ai);
    // CHECK: call {{.*}}@_ZN5test21jIvQ4TrueITL0__EEEvz(
    // CLANG17: call {{.*}}@_ZN5test21jIvEEvz(
    j(ai);
    // CHECK: call {{.*}}@_ZN5test21AIiEF1kITk4TruevQ4TrueIT_EEEvz(
    // CLANG19: call {{.*}}@_ZN5test2F1kITk4TruevQ4TrueIT_EEEvz(
    // CLANG17: call {{.*}}@_ZN5test21kIvEEvz(
    k(ai);
    // CHECK: call {{.*}}@_ZN5test21lITk4TruevEEvz(
    // CLANG17: call {{.*}}@_ZN5test21lIvEEvz(
    l(ai);
  }
}

namespace test3 {
  // Unconstrained auto.
  template<auto> void d() {}
  template void d<0>();
  // CHECK: define {{.*}}@_ZN5test31dITnDaLi0EEEvv(
  // CLANG17: define {{.*}}@_ZN5test31dILi0EEEvv(

  template<decltype(auto)> void e() {}
  template void e<0>();
  // CHECK: define {{.*}}@_ZN5test31eITnDcLi0EEEvv(
  // CLANG17: define {{.*}}@_ZN5test31eILi0EEEvv(

  // Constrained auto.
  template<C auto> void f() {}
  template void f<0>();
  // CHECK: define {{.*}}@_ZN5test31fITnDk1CLi0EEEvv(
  // CLANG17: define {{.*}}@_ZN5test31fILi0EEEvv(

  template<D<int> auto> void g() {}
  template void g<0>();
  // CHECK: define {{.*}}@_ZN5test31gITnDk1DIiELi0EEEvv(
  // CLANG17: define {{.*}}@_ZN5test31gILi0EEEvv(

  template<typename T, D<T> auto> void h() {}
  template void h<int, 0>();
  // CHECK: define {{.*}}@_ZN5test31hIiTnDk1DIT_ELi0EEEvv(
  // CLANG17: define {{.*}}@_ZN5test31hIiLi0EEEvv(

  template<typename T> void i(decltype(new C auto(T()))) {}
  template void i<int>(int*);
  // CHECK: define {{.*}}@_ZN5test31iIiEEvDTnw_Dk1CpicvT__EEE(
  // CLANG17: define {{.*}}@_ZN5test31iIiEEvDTnw_DapicvT__EEE(

  template<typename T> void j(decltype(new C decltype(auto)(T()))) {}
  template void j<int>(int*);
  // CHECK: define {{.*}}@_ZN5test31jIiEEvDTnw_DK1CpicvT__EEE(
  // CLANG17: define {{.*}}@_ZN5test31jIiEEvDTnw_DcpicvT__EEE(
}

namespace test4 {
  // Constrained type parameters.
  template<C> void f() {}
  template void f<int>();
  // CHECK: define {{.*}}@_ZN5test41fITk1CiEEvv(
  // CLANG17: define {{.*}}@_ZN5test41fIiEEvv(

  template<D<int>> void g() {}
  template void g<int>();
  // CHECK: define {{.*}}@_ZN5test41gITk1DIiEiEEvv(
  // CLANG17: define {{.*}}@_ZN5test41gIiEEvv(
}

namespace test5 {
  // Exact-match vs non-exact-match template template parameters.
  template<typename T, T V> struct X {};
  template<typename T, T V> requires C<T> struct Y {};
  template<C T, T V> struct Z {};

  template<template<typename T, T> typename> void f() {}
  // CHECK: define {{.*}}@_ZN5test51fINS_1XEEEvv(
  template void f<X>();
  // CHECK: define {{.*}}@_ZN5test51fITtTyTnTL0__ENS_1YEEEvv(
  template void f<Y>();
  // CHECK: define {{.*}}@_ZN5test51fITtTyTnTL0__ENS_1ZEEEvv(
  template void f<Z>();

  template<template<typename T, T> requires C<T> typename> void g() {}
  // CHECK: define {{.*}}@_ZN5test51gITtTyTnTL0__Q1CIS1_EENS_1XEEEvv(
  template void g<X>();
  // CHECK: define {{.*}}@_ZN5test51gINS_1YEEEvv(
  template void g<Y>();
  // CHECK: define {{.*}}@_ZN5test51gITtTyTnTL0__Q1CIS1_EENS_1ZEEEvv(
  template void g<Z>();

  template<template<C T, T> typename> void h() {}
  // CHECK: define {{.*}}@_ZN5test51hITtTk1CTnTL0__ENS_1XEEEvv(
  template void h<X>();
  // CHECK: define {{.*}}@_ZN5test51hITtTk1CTnTL0__ENS_1YEEEvv(
  template void h<Y>();
  // CHECK: define {{.*}}@_ZN5test51hINS_1ZEEEvv(
  template void h<Z>();

  // Packs must match the first argument.
  template<template<C T, T> typename...> void i() {}
  // CHECK: define {{.*}}@_ZN5test51iITpTtTk1CTnTL0__EJNS_1XENS_1YENS_1ZEEEEvv(
  template void i<X, Y, Z>();
  // CHECK: define {{.*}}@_ZN5test51iITpTtTk1CTnTL0__EJNS_1YENS_1ZENS_1XEEEEvv(
  template void i<Y, Z, X>();
  // CHECK: define {{.*}}@_ZN5test51iIJNS_1ZENS_1XENS_1YEEEEvv(
  template void i<Z, X, Y>();

  template<typename ...T> struct A {};
  template<typename, typename> struct B {};

  template<template<typename ...> typename> void p() {}
  // CHECK: define {{.*}}@_ZN5test51pINS_1AEEEvv(
  // CLANG17: define {{.*}}@_ZN5test51pINS_1AEEEvv(
  template void p<A>();
  // CHECK: define {{.*}}@_ZN5test51pITtTpTyENS_1BEEEvv(
  // CLANG17: define {{.*}}@_ZN5test51pINS_1BEEEvv(
  template void p<B>();

  template<template<typename, typename> typename> void q() {}
  // CHECK: define {{.*}}@_ZN5test51qITtTyTyENS_1AEEEvv(
  // CLANG17: define {{.*}}@_ZN5test51qINS_1AEEEvv(
  template void q<A>();
  // CHECK: define {{.*}}@_ZN5test51qINS_1BEEEvv(
  // CLANG17: define {{.*}}@_ZN5test51qINS_1BEEEvv(
  template void q<B>();
}

namespace test6 {
  // Abbreviated function templates.
  void f(C auto) {}
  // CHECK: define {{.*}}@_ZN5test61fITk1CiEEvT_(
  // CLANG17: define {{.*}}@_ZN5test61fIiEEvT_(
  template void f(int);

  template<typename T>
  void g(D<T> auto) {}
  // CHECK: define {{.*}}@_ZN5test61gIiTk1DIT_EiEEvT0_(
  // CLANG17: define {{.*}}@_ZN5test61gIiiEEvT0_(
  template void g<int>(int);
}

namespace test7 {
  // Constrained lambdas.
  template<typename T> void f() {
    // Ensure that requires-clauses affect lambda numbering.
    // CHECK-LABEL: define {{.*}}@_ZN5test71fIiEEvv(
    // CHECK: call {{.*}}@_ZZN5test71fIiEEvvENKUlTyQaa1CIT_E1CITL0__ET0_E_clIiiEEDaS3_Q1CIDtfp_EE(
    ([]<typename U> requires C<T> && C<U> (auto x) requires C<decltype(x)> {}).template operator()<int>(0);
    // CHECK: call {{.*}}@_ZZN5test71fIiEEvvENKUlTyQaa1CIT_E1CITL0__ET0_E0_clIiiEEDaS3_Qaa1CIDtfp_EELb1E(
    ([]<typename U> requires C<T> && C<U> (auto x) requires C<decltype(x)> && true {}).template operator()<int>(0);
    // CHECK: call {{.*}}@_ZZN5test71fIiEEvvENKUlTyQaa1CIT_E1CITL0__ET0_E1_clIiiEEDaS3_Q1CIDtfp_EE(
    ([]<typename U> requires C<T> && C<U> (auto x) requires C<decltype(x)> {}).template operator()<int>(0);
    // CHECK: call {{.*}}@_ZZN5test71fIiEEvvENKUlTyT0_E_clIiiEEDaS1_(
    ([]<typename U> (auto x){}).template operator()<int>(0);
  }
  template void f<int>();
}

namespace gh67244 {
  template<typename T, typename ...Ts> constexpr bool B = true;
  template<typename T, typename ...Ts> concept C = B<T, Ts...>;
  template<C<int, float> T> void f(T) {}
  // CHECK: define {{.*}} @_ZN7gh672441fITkNS_1CIifEEiEEvT_(
  template void f(int);
}

namespace gh67356 {
  template<typename, typename T> concept C = true;
  template<typename T> void f(T t, C<decltype(t)> auto) {}
  // CHECK: define {{.*}} @_ZN7gh673561fIiTkNS_1CIDtfL0p_EEEiEEvT_T0_(
  template void f(int, int);

  // Note, we use `fL0p` not `fp` above because:
  template<typename T> void g(T t, C<auto (T u) -> decltype(f(t, u))> auto) {}
  // CHECK: define {{.*}} @_ZN7gh673561gIiTkNS_1CIFDTcl1ffL0p_fp_EET_EEEiEEvS3_T0_(
  template void g(int, int);
}
