Skip to content

Commit 73d456c

Browse files
committed
[clang] Implement CWG2398 provisional TTP matching to class templates
This solves some ambuguity introduced in P0522 regarding how template template parameters are partially ordered, and should reduce the negative impact of enabling `-frelaxed-template-template-args` by default. When performing template argument deduction, we extend the provisional wording introduced in #89807 so it also covers deduction of class templates. Given the following example: ```C++ template <class T1, class T2 = float> struct A; template <class T3> struct B; template <template <class T4> class TT1, class T5> struct B<TT1<T5>>; // #1 template <class T6, class T7> struct B<A<T6, T7>>; // #2 template struct B<A<int>>; ``` Prior to P0522, `#2` was picked. Afterwards, this became ambiguous. This patch restores the pre-P0522 behavior, `#2` is picked again. This has the beneficial side effect of making the following code valid: ```C++ template<class T, class U> struct A {}; A<int, float> v; template<template<class> class TT> void f(TT<int>); // OK: TT picks 'float' as the default argument for the second parameter. void g() { f(v); } ``` --- Since this changes provisional implementation of CWG2398 which has not been released yet, and already contains a changelog entry, we don't provide a changelog entry here.
1 parent 229cb63 commit 73d456c

File tree

4 files changed

+53
-36
lines changed

4 files changed

+53
-36
lines changed

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,8 @@ static void SetNestedNameSpecifier(Sema &S, TagDecl *T,
18071807
// Returns the template parameter list with all default template argument
18081808
// information.
18091809
static TemplateParameterList *GetTemplateParameterList(TemplateDecl *TD) {
1810+
if (TD->isImplicit())
1811+
return TD->getTemplateParameters();
18101812
// Make sure we get the template parameter list from the most
18111813
// recent declaration, since that is the only one that is guaranteed to
18121814
// have all the default template argument information.
@@ -1827,7 +1829,8 @@ static TemplateParameterList *GetTemplateParameterList(TemplateDecl *TD) {
18271829
// template <class = void> friend struct C;
18281830
// };
18291831
// template struct S<int>;
1830-
while (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None &&
1832+
while ((D->isImplicit() ||
1833+
D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) &&
18311834
D->getPreviousDecl())
18321835
D = D->getPreviousDecl();
18331836
return cast<TemplateDecl>(D)->getTemplateParameters();

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,8 @@ static NamedDecl *getTemplateParameterWithDefault(Sema &S, NamedDecl *A,
527527
R->setDefaultArgument(
528528
S.Context,
529529
S.getTrivialTemplateArgumentLoc(Default, QualType(), SourceLocation()));
530-
if (R->hasTypeConstraint()) {
531-
auto *C = R->getTypeConstraint();
530+
if (T->hasTypeConstraint()) {
531+
auto *C = T->getTypeConstraint();
532532
R->setTypeConstraint(C->getConceptReference(),
533533
C->getImmediatelyDeclaredConstraint());
534534
}
@@ -583,37 +583,53 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
583583
return TemplateDeductionResult::Success;
584584

585585
auto NewDeduced = DeducedTemplateArgument(Arg);
586-
// Provisional resolution for CWG2398: If Arg is also a template template
587-
// param, and it names a template specialization, then we deduce a
588-
// synthesized template template parameter based on A, but using the TS's
589-
// arguments as defaults.
590-
if (auto *TempArg = dyn_cast_or_null<TemplateTemplateParmDecl>(
591-
Arg.getAsTemplateDecl())) {
586+
// Provisional resolution for CWG2398: If Arg names a template
587+
// specialization, then we deduce a synthesized template template parameter
588+
// based on A, but using the TS's arguments as defaults.
589+
if (DefaultArguments.size() != 0) {
592590
assert(Arg.getKind() == TemplateName::Template);
593-
assert(!TempArg->isExpandedParameterPack());
594-
591+
TemplateDecl *TempArg = Arg.getAsTemplateDecl();
595592
TemplateParameterList *As = TempArg->getTemplateParameters();
596-
if (DefaultArguments.size() != 0) {
597-
assert(DefaultArguments.size() <= As->size());
598-
SmallVector<NamedDecl *, 4> Params(As->size());
599-
for (unsigned I = 0; I < DefaultArguments.size(); ++I)
600-
Params[I] = getTemplateParameterWithDefault(S, As->getParam(I),
601-
DefaultArguments[I]);
602-
for (unsigned I = DefaultArguments.size(); I < As->size(); ++I)
603-
Params[I] = As->getParam(I);
604-
// FIXME: We could unique these, and also the parameters, but we don't
605-
// expect programs to contain a large enough amount of these deductions
606-
// for that to be worthwhile.
607-
auto *TPL = TemplateParameterList::Create(
608-
S.Context, SourceLocation(), SourceLocation(), Params,
609-
SourceLocation(), As->getRequiresClause());
610-
NewDeduced = DeducedTemplateArgument(
611-
TemplateName(TemplateTemplateParmDecl::Create(
612-
S.Context, TempArg->getDeclContext(), SourceLocation(),
613-
TempArg->getDepth(), TempArg->getPosition(),
614-
TempArg->isParameterPack(), TempArg->getIdentifier(),
615-
TempArg->wasDeclaredWithTypename(), TPL)));
593+
assert(DefaultArguments.size() <= As->size());
594+
595+
SmallVector<NamedDecl *, 4> Params(As->size());
596+
for (unsigned I = 0; I < DefaultArguments.size(); ++I)
597+
Params[I] = getTemplateParameterWithDefault(S, As->getParam(I),
598+
DefaultArguments[I]);
599+
for (unsigned I = DefaultArguments.size(); I < As->size(); ++I)
600+
Params[I] = As->getParam(I);
601+
// FIXME: We could unique these, and also the parameters, but we don't
602+
// expect programs to contain a large enough amount of these deductions
603+
// for that to be worthwhile.
604+
auto *TPL = TemplateParameterList::Create(
605+
S.Context, SourceLocation(), SourceLocation(), Params,
606+
SourceLocation(), As->getRequiresClause());
607+
608+
TemplateDecl *TD;
609+
switch (TempArg->getKind()) {
610+
case Decl::TemplateTemplateParm: {
611+
auto *A = cast<TemplateTemplateParmDecl>(TempArg);
612+
assert(!A->isExpandedParameterPack());
613+
TD = TemplateTemplateParmDecl::Create(
614+
S.Context, A->getDeclContext(), SourceLocation(), A->getDepth(),
615+
A->getPosition(), A->isParameterPack(), A->getIdentifier(),
616+
A->wasDeclaredWithTypename(), TPL);
617+
break;
618+
}
619+
case Decl::ClassTemplate: {
620+
auto *A = cast<ClassTemplateDecl>(TempArg);
621+
auto *CT = ClassTemplateDecl::Create(S.Context, A->getDeclContext(),
622+
SourceLocation(), A->getDeclName(),
623+
TPL, A->getTemplatedDecl());
624+
CT->setPreviousDecl(A);
625+
TD = CT;
626+
break;
627+
}
628+
default:
629+
llvm_unreachable("Unexpected Template Kind");
616630
}
631+
TD->setImplicit(true);
632+
NewDeduced = DeducedTemplateArgument(TemplateName(TD));
617633
}
618634

619635
DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context,

clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ namespace StdExample {
2828
{ /* ... */ }
2929

3030
template<template<class> class TT>
31-
void f(TT<int>); // expected-note {{candidate template ignored}}
31+
void f(TT<int>);
3232

3333
template<template<class,class> class TT>
3434
void g(TT<int, Alloc<int>>);
3535

3636
int h() {
37-
f(v); // expected-error {{no matching function for call to 'f'}}
37+
f(v); // OK: TT = vector, Alloc<int> is used as the default argument for the
38+
// second parameter.
3839
g(v); // OK: TT = vector
3940
}
4041

clang/test/SemaTemplate/cwg2398.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,10 @@ namespace class_template {
6565
template <class T3> struct B;
6666

6767
template <template <class T4> class TT1, class T5> struct B<TT1<T5>>;
68-
// new-note@-1 {{partial specialization matches}}
6968

7069
template <class T6, class T7> struct B<A<T6, T7>> {};
71-
// new-note@-1 {{partial specialization matches}}
7270

7371
template struct B<A<int>>;
74-
// new-error@-1 {{ambiguous partial specialization}}
7572
} // namespace class_template
7673

7774
namespace type_pack1 {

0 commit comments

Comments
 (0)