Files
clang-p2996/clang/test/AST/ast-dump-templates.cpp
David Blaikie 50fdd7df82 Add more test coverage for D77598
Add coverage to demonstrate why including the type of template
parameters is necessary to disambiguate function template
specializations.

Test courtesy of Richard Smith
2021-11-14 21:09:11 -08:00

107 lines
3.0 KiB
C++

// RUN: %clang_cc1 -std=c++1z -ast-print %s > %t
// RUN: FileCheck < %t %s -check-prefix=CHECK1
// RUN: FileCheck < %t %s -check-prefix=CHECK2
// RUN: %clang_cc1 -std=c++1z -ast-dump %s | FileCheck --check-prefix=DUMP %s
template <int X, typename Y, int Z = 5>
struct foo {
int constant;
foo() {}
Y getSum() { return Y(X + Z); }
};
template <int A, typename B>
B bar() {
return B(A);
}
void baz() {
int x = bar<5, int>();
int y = foo<5, int>().getSum();
double z = foo<2, double, 3>().getSum();
}
// Template definition - foo
// CHECK1: template <int X, typename Y, int Z = 5> struct foo {
// CHECK2: template <int X, typename Y, int Z = 5> struct foo {
// Template instantiation - foo
// Since the order of instantiation may vary during runs, run FileCheck twice
// to make sure each instantiation is in the correct spot.
// CHECK1: template<> struct foo<5, int, 5> {
// CHECK2: template<> struct foo<2, double, 3> {
// Template definition - bar
// CHECK1: template <int A, typename B> B bar()
// CHECK2: template <int A, typename B> B bar()
// Template instantiation - bar
// CHECK1: template<> int bar<5, int>()
// CHECK2: template<> int bar<5, int>()
// CHECK1-LABEL: template <typename ...T> struct A {
// CHECK1-NEXT: template <T ...x[3]> struct B {
template <typename ...T> struct A {
template <T ...x[3]> struct B {};
};
// CHECK1-LABEL: template <typename ...T> void f(T ...[3]) {
// CHECK1-NEXT: A<T[3]...> a;
template <typename ...T> void f(T ...[3]) {
A<T[3]...> a;
}
namespace test2 {
void func(int);
void func(float);
template<typename T>
void tmpl() {
func(T());
}
// DUMP: UnresolvedLookupExpr {{.*}} <col:3> '<overloaded function type>' lvalue (ADL) = 'func'
}
namespace test3 {
template<typename T> struct A {};
template<typename T> A(T) -> A<int>;
// CHECK1: template <typename T> A(T) -> A<int>;
}
namespace test4 {
template <unsigned X, auto A>
struct foo {
static void fn();
};
// Prints using an "integral" template argument. Test that this correctly
// includes the type for the auto argument and omits it for the fixed
// type/unsigned argument (see
// TemplateParameterList::shouldIncludeTypeForArgument)
// CHECK1: {{^ }}template<> struct foo<0, 0L> {
// CHECK1: {{^ }}void test(){{ }}{
// CHECK1: {{^ }}foo<0, 0 + 0L>::fn();
void test() {
foo<0, 0 + 0L>::fn();
}
// Prints using an "expression" template argument. This renders based on the way
// the user wrote the arguments (including that + expression) - so it's not
// powered by the shouldIncludeTypeForArgument functionality.
// Not sure if this it's intentional that these two specializations are rendered
// differently in this way.
// CHECK1: {{^ }}template<> struct foo<1, 0 + 0L> {
template struct foo<1, 0 + 0L>;
}
namespace test5 {
template<long> void f() {}
void (*p)() = f<0>;
template<unsigned = 0> void f() {}
void (*q)() = f<>;
// Not perfect - this code in the dump would be ambiguous, but it's the best we
// can do to differentiate these two implicit specializations.
// CHECK1: template<> void f<0L>()
// CHECK1: template<> void f<0U>()
}