Skip to content

Commit ff3f41d

Browse files
authored
[clang] Implement CWG2398 provisional TTP matching to class templates (llvm#92855)
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 llvm#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 f3dc732 commit ff3f41d

File tree

4 files changed

+53
-36
lines changed

4 files changed

+53
-36
lines changed

clang/lib/Sema/SemaTemplate.cpp

+4-1
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

+46-30
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

+3-2
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

-3
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)