[clang] check deduction consistency when partial ordering function templates (#100692)

This makes partial ordering of function templates consistent with other
entities, by implementing [temp.deduct.type]p1 in that case.

Fixes #18291
This commit is contained in:
Matheus Izvekov 2024-08-28 16:53:40 -03:00 committed by GitHub
parent e61d6066e2
commit aa7497a66c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 797 additions and 338 deletions

View File

@ -317,6 +317,8 @@ Bug Fixes to C++ Support
- Clang now properly handles the order of attributes in `extern` blocks. (#GH101990).
- Fixed an assertion failure by preventing null explicit object arguments from being deduced. (#GH102025).
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)
- When performing partial ordering of function templates, clang now checks that
the deduction was consistent. Fixes (#GH18291).
- Fixed an assertion failure about a constraint of a friend function template references to a value with greater
template depth than the friend function template. (#GH98258)
- Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context

View File

@ -13283,6 +13283,10 @@ public:
/// \param AllowDeducedTST Whether a DeducedTemplateSpecializationType is
/// acceptable as the top level type of the result.
///
/// \param IsIncompleteSubstitution If provided, the pointee will be set
/// whenever substitution would perform a replacement with a null or
/// non-existent template argument.
///
/// \returns If the instantiation succeeds, the instantiated
/// type. Otherwise, produces diagnostics and returns a NULL type.
TypeSourceInfo *SubstType(TypeSourceInfo *T,
@ -13292,7 +13296,8 @@ public:
QualType SubstType(QualType T,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity);
SourceLocation Loc, DeclarationName Entity,
bool *IsIncompleteSubstitution = nullptr);
TypeSourceInfo *SubstType(TypeLoc TL,
const MultiLevelTemplateArgumentList &TemplateArgs,

File diff suppressed because it is too large Load Diff

View File

@ -1344,15 +1344,21 @@ namespace {
DeclarationName Entity;
// Whether to evaluate the C++20 constraints or simply substitute into them.
bool EvaluateConstraints = true;
// Whether Substitution was Incomplete, that is, we tried to substitute in
// any user provided template arguments which were null.
bool IsIncomplete = false;
// Whether an incomplete substituion should be treated as an error.
bool BailOutOnIncomplete;
public:
typedef TreeTransform<TemplateInstantiator> inherited;
TemplateInstantiator(Sema &SemaRef,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity)
SourceLocation Loc, DeclarationName Entity,
bool BailOutOnIncomplete = false)
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
Entity(Entity) {}
Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}
void setEvaluateConstraints(bool B) {
EvaluateConstraints = B;
@ -1374,6 +1380,9 @@ namespace {
/// Returns the name of the entity being instantiated, if any.
DeclarationName getBaseEntity() { return Entity; }
/// Returns whether any substitution so far was incomplete.
bool getIsIncomplete() const { return IsIncomplete; }
/// Sets the "base" location and entity when that
/// information is known based on another transformation.
void setBase(SourceLocation Loc, DeclarationName Entity) {
@ -1420,6 +1429,10 @@ namespace {
if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
Result = TemplateArgs(Depth, Index);
TemplateArgs.setArgument(Depth, Index, TemplateArgument());
} else {
IsIncomplete = true;
if (BailOutOnIncomplete)
return TemplateArgument();
}
}
@ -1820,8 +1833,10 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
// template arguments in a function template, but there were some
// arguments left unspecified.
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
TTP->getPosition()))
return D;
TTP->getPosition())) {
IsIncomplete = true;
return BailOutOnIncomplete ? nullptr : D;
}
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
@ -1967,8 +1982,10 @@ TemplateName TemplateInstantiator::TransformTemplateName(
// template arguments in a function template, but there were some
// arguments left unspecified.
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
TTP->getPosition()))
return Name;
TTP->getPosition())) {
IsIncomplete = true;
return BailOutOnIncomplete ? TemplateName() : Name;
}
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
@ -2050,8 +2067,10 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
// template arguments in a function template, but there were some
// arguments left unspecified.
if (!TemplateArgs.hasTemplateArgument(NTTP->getDepth(),
NTTP->getPosition()))
return E;
NTTP->getPosition())) {
IsIncomplete = true;
return BailOutOnIncomplete ? ExprError() : E;
}
TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
@ -2470,6 +2489,10 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
// template arguments in a function template class, but there were some
// arguments left unspecified.
if (!TemplateArgs.hasTemplateArgument(T->getDepth(), T->getIndex())) {
IsIncomplete = true;
if (BailOutOnIncomplete)
return QualType();
TemplateTypeParmTypeLoc NewTL
= TLB.push<TemplateTypeParmTypeLoc>(TL.getType());
NewTL.setNameLoc(TL.getNameLoc());
@ -2841,7 +2864,8 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
/// Deprecated form of the above.
QualType Sema::SubstType(QualType T,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity) {
SourceLocation Loc, DeclarationName Entity,
bool *IsIncompleteSubstitution) {
assert(!CodeSynthesisContexts.empty() &&
"Cannot perform an instantiation without some context on the "
"instantiation stack");
@ -2851,8 +2875,13 @@ QualType Sema::SubstType(QualType T,
if (!T->isInstantiationDependentType() && !T->isVariablyModifiedType())
return T;
TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, Entity);
return Instantiator.TransformType(T);
TemplateInstantiator Instantiator(
*this, TemplateArgs, Loc, Entity,
/*BailOutOnIncomplete=*/IsIncompleteSubstitution != nullptr);
QualType QT = Instantiator.TransformType(T);
if (IsIncompleteSubstitution && Instantiator.getIsIncomplete())
*IsIncompleteSubstitution = true;
return QT;
}
static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) {

View File

@ -8,7 +8,7 @@ void f() {
// The important thing is that we provide OVERLOAD signature in all those cases.
//
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-5):7 %s -o - | FileCheck --check-prefix=CHECK-1 %s
// CHECK-1: OVERLOAD: [#void#]fun(<#T x#>, Args args...)
// CHECK-1: OVERLOAD: [#void#]fun(<#T x#>)
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-7):10 %s -o - | FileCheck --check-prefix=CHECK-2 %s
// CHECK-2: OVERLOAD: [#void#]fun(int x)
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-9):13 %s -o - | FileCheck --check-prefix=CHECK-3 %s

View File

@ -409,7 +409,8 @@ struct Bar2Template : public BarTemplates {
// CHECK-CC22-NEXT: Objective-C interface
// RUN: c-index-test -code-completion-at=%s:64:10 %s | FileCheck -check-prefix=CHECK-CC23 %s
// CHECK-CC23: OverloadCandidate:{ResultType void}{Text foo_12}{LeftParen (}{CurrentParameter int}{Comma , }{Placeholder int}{RightParen )} (1)
// CHECK-CC23: OverloadCandidate:{ResultType void}{Text foo_12}{LeftParen (}{CurrentParameter T}{Comma , }{Placeholder T}{RightParen )} (1)
// CHECK-CC23: Completion contexts:
// CHECK-CC23-NEXT: Any type
// CHECK-CC23-NEXT: Any value
@ -702,7 +703,7 @@ struct Bar2Template : public BarTemplates {
// CHECK-CC46-NEXT: Objective-C interface
// RUN: c-index-test -code-completion-at=%s:84:12 %s | FileCheck -check-prefix=CHECK-CC47 %s
// CHECK-CC47: OverloadCandidate:{ResultType void}{Text foo_12}{LeftParen (}{CurrentParameter int}{Comma , }{Placeholder int}{RightParen )} (1)
// CHECK-CC47: OverloadCandidate:{ResultType void}{Text foo_12}{LeftParen (}{CurrentParameter T}{Comma , }{Placeholder T}{RightParen )} (1)
// CHECK-CC47: Completion contexts:
// CHECK-CC47-NEXT: Any type
// CHECK-CC47-NEXT: Any value

View File

@ -1012,6 +1012,49 @@ static_assert(__is_same(X<B{0}>, X<B{0}>));
static_assert(!__is_same(X<B{0}>, X<B{1}>));
} // namespace defaulted_compare
namespace static_overloaded_operator {
struct A {
template<auto N>
static void operator()(const char (&)[N]);
void operator()(this auto &&, auto &&);
void implicit_this() {
operator()("123");
}
};
struct B {
template<auto N>
void operator()(this auto &&, const char (&)[N]);
static void operator()(auto &&);
void implicit_this() {
operator()("123");
}
};
struct C {
template<auto N>
static void operator[](const char (&)[N]);
void operator[](this auto &&, auto &&);
void implicit_this() {
operator[]("123");
}
};
struct D {
template<auto N>
void operator[](this auto &&, const char (&)[N]);
static void operator[](auto &&);
void implicit_this() {
operator[]("123");
}
};
} // namespace static_overloaded_operator
namespace GH102025 {
struct Foo {
template <class T>

View File

@ -0,0 +1,32 @@
// RUN: %clang_cc1 -std=c++23 -verify %s
namespace t1 {
template<bool> struct enable_if { typedef void type; };
template <class T> class Foo {};
template <class X> constexpr bool check() { return true; }
template <class X, class Enable = void> struct Bar {};
template<class X> void func(Bar<X, typename enable_if<check<X>()>::type>) {}
// expected-note@-1 {{candidate function}}
template<class T> void func(Bar<Foo<T>>) {}
// expected-note@-1 {{candidate function}}
void g() {
func(Bar<Foo<int>>()); // expected-error {{call to 'func' is ambiguous}}
}
} // namespace t1
namespace t2 {
template <bool> struct enable_if;
template <> struct enable_if<true> {
typedef int type;
};
struct pair {
template <int = 0> pair(int);
template <class _U2, enable_if<__is_constructible(int &, _U2)>::type = 0>
pair(_U2 &&);
};
int test_test_i;
void test() { pair{test_test_i}; }
} // namespace t2

View File

@ -74,6 +74,20 @@ namespace class_template {
// new-error@-1 {{ambiguous partial specialization}}
} // namespace class_template
namespace class_template_func {
template <class T1, class T2 = float> struct A {};
template <template <class T4> class TT1, class T5> void f(TT1<T5>);
// new-note@-1 {{candidate function}}
template <class T6, class T7> void f(A<T6, T7>) {};
// new-note@-1 {{candidate function}}
void g() {
f(A<int>()); // new-error {{call to 'f' is ambiguous}}
}
} // namespace class_template_func
namespace type_pack1 {
template<class T2> struct A;
template<template<class ...T3s> class TT1, class T4> struct A<TT1<T4>> ;

View File

@ -458,17 +458,13 @@ namespace dependent_nested_partial_specialization {
namespace nondependent_default_arg_ordering {
int n, m;
template<typename A, A B = &n> struct X {};
template<typename A> void f(X<A>); // expected-note {{candidate}}
template<typename A> void f(X<A, &m>); // expected-note {{candidate}}
template<typename A, A B> void f(X<A, B>); // expected-note 2{{candidate}}
template<typename A> void f(X<A>);
template<typename A> void f(X<A, &m>);
template<typename A, A B> void f(X<A, B>);
template<template<typename U, U> class T, typename A, int *B> void f(T<A, B>);
void g() {
// FIXME: The first and second function templates above should be
// considered more specialized than the third, but during partial
// ordering we fail to check that we actually deduced template arguments
// that make the deduced A identical to A.
X<int *, &n> x; f(x); // expected-error {{ambiguous}}
X<int *, &m> y; f(y); // expected-error {{ambiguous}}
X<int *, &n> x; f(x);
X<int *, &m> y; f(y);
}
}

View File

@ -69,11 +69,10 @@ namespace deduce_noexcept {
void noexcept_function() noexcept;
void throwing_function();
template<typename T, bool B> float &deduce_function(T(*)() noexcept(B)); // expected-note {{candidate}}
template<typename T> int &deduce_function(T(*)() noexcept); // expected-note {{candidate}}
template<typename T, bool B> float &deduce_function(T(*)() noexcept(B));
template<typename T> int &deduce_function(T(*)() noexcept);
void test_function_deduction() {
// FIXME: This should probably unambiguously select the second overload.
int &r = deduce_function(noexcept_function); // expected-error {{ambiguous}}
int &r = deduce_function(noexcept_function);
float &s = deduce_function(throwing_function);
}

View File

@ -170,6 +170,30 @@ template <bool d = true, class = typename b<d>::c> void a() { a(); }
template <bool = true> void d(int = 0) { d(); }
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+a$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
@ -225,41 +249,41 @@ void e() {
}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}}
// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}}
template <template<typename> class>
@ -275,59 +299,59 @@ void foo() {
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}}
// CHECK: {{^kind:[ ]+PriorTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:265:35'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
// CHECK: {{^poi:[ ]+''$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}}
// CHECK: {{^kind:[ ]+PriorTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:265:35'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
// CHECK: {{^poi:[ ]+''$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'d<C>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'d<C>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:171:29'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:171:29'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}}