[llvm-branch-commits] [clang] [clang] Implement CWG2398 provisional TTP matching to class templates (PR #92855)
Matheus Izvekov via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon May 20 21:33:07 PDT 2024
https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/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 https://github.com/llvm/llvm-project/pull/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.
>From de5b6aa7f6c071440b909327ed7a21fad6c2317e Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Mon, 20 May 2024 01:15:03 -0300
Subject: [PATCH] [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 https://github.com/llvm/llvm-project/pull/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.
---
clang/lib/Sema/SemaTemplate.cpp | 5 +-
clang/lib/Sema/SemaTemplateDeduction.cpp | 72 +++++++++++--------
.../CXX/temp/temp.decls/temp.alias/p2.cpp | 5 +-
clang/test/SemaTemplate/cwg2398.cpp | 3 -
4 files changed, 51 insertions(+), 34 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 6af35ac8911bb..b7479cbcdba23 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1807,6 +1807,8 @@ static void SetNestedNameSpecifier(Sema &S, TagDecl *T,
// Returns the template parameter list with all default template argument
// information.
static TemplateParameterList *GetTemplateParameterList(TemplateDecl *TD) {
+ if (TD->isImplicit())
+ return TD->getTemplateParameters();
// Make sure we get the template parameter list from the most
// recent declaration, since that is the only one that is guaranteed to
// have all the default template argument information.
@@ -1827,7 +1829,8 @@ static TemplateParameterList *GetTemplateParameterList(TemplateDecl *TD) {
// template <class = void> friend struct C;
// };
// template struct S<int>;
- while (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None &&
+ while ((D->isImplicit() ||
+ D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) &&
D->getPreviousDecl())
D = D->getPreviousDecl();
return cast<TemplateDecl>(D)->getTemplateParameters();
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index f16a07e1a1b34..791e44658bd96 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -583,37 +583,53 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
return TemplateDeductionResult::Success;
auto NewDeduced = DeducedTemplateArgument(Arg);
- // Provisional resolution for CWG2398: If Arg is also a template template
- // param, and it names a template specialization, then we deduce a
- // synthesized template template parameter based on A, but using the TS's
- // arguments as defaults.
- if (auto *TempArg = dyn_cast_or_null<TemplateTemplateParmDecl>(
- Arg.getAsTemplateDecl())) {
+ // Provisional resolution for CWG2398: If Arg names a template
+ // specialization, then we deduce a synthesized template template parameter
+ // based on A, but using the TS's arguments as defaults.
+ if (DefaultArguments.size() != 0) {
assert(Arg.getKind() == TemplateName::Template);
- assert(!TempArg->isExpandedParameterPack());
-
+ TemplateDecl *TempArg = Arg.getAsTemplateDecl();
TemplateParameterList *As = TempArg->getTemplateParameters();
- if (DefaultArguments.size() != 0) {
- assert(DefaultArguments.size() <= As->size());
- SmallVector<NamedDecl *, 4> Params(As->size());
- for (unsigned I = 0; I < DefaultArguments.size(); ++I)
- Params[I] = getTemplateParameterWithDefault(S, As->getParam(I),
- DefaultArguments[I]);
- for (unsigned I = DefaultArguments.size(); I < As->size(); ++I)
- Params[I] = As->getParam(I);
- // FIXME: We could unique these, and also the parameters, but we don't
- // expect programs to contain a large enough amount of these deductions
- // for that to be worthwhile.
- auto *TPL = TemplateParameterList::Create(
- S.Context, SourceLocation(), SourceLocation(), Params,
- SourceLocation(), As->getRequiresClause());
- NewDeduced = DeducedTemplateArgument(
- TemplateName(TemplateTemplateParmDecl::Create(
- S.Context, TempArg->getDeclContext(), SourceLocation(),
- TempArg->getDepth(), TempArg->getPosition(),
- TempArg->isParameterPack(), TempArg->getIdentifier(),
- TempArg->wasDeclaredWithTypename(), TPL)));
+ assert(DefaultArguments.size() <= As->size());
+
+ SmallVector<NamedDecl *, 4> Params(As->size());
+ for (unsigned I = 0; I < DefaultArguments.size(); ++I)
+ Params[I] = getTemplateParameterWithDefault(S, As->getParam(I),
+ DefaultArguments[I]);
+ for (unsigned I = DefaultArguments.size(); I < As->size(); ++I)
+ Params[I] = As->getParam(I);
+ // FIXME: We could unique these, and also the parameters, but we don't
+ // expect programs to contain a large enough amount of these deductions
+ // for that to be worthwhile.
+ auto *TPL = TemplateParameterList::Create(
+ S.Context, SourceLocation(), SourceLocation(), Params,
+ SourceLocation(), As->getRequiresClause());
+
+ TemplateDecl *TD;
+ switch (TempArg->getKind()) {
+ case Decl::TemplateTemplateParm: {
+ auto *A = cast<TemplateTemplateParmDecl>(TempArg);
+ assert(!A->isExpandedParameterPack());
+ TD = TemplateTemplateParmDecl::Create(
+ S.Context, A->getDeclContext(), SourceLocation(), A->getDepth(),
+ A->getPosition(), A->isParameterPack(), A->getIdentifier(),
+ A->wasDeclaredWithTypename(), TPL);
+ break;
+ }
+ case Decl::ClassTemplate: {
+ auto *A = cast<ClassTemplateDecl>(TempArg);
+ auto *CT = ClassTemplateDecl::Create(S.Context, A->getDeclContext(),
+ SourceLocation(), A->getDeclName(),
+ TPL, A->getTemplatedDecl());
+ CT->setPreviousDecl(A);
+ TD = CT;
+ break;
+ }
+ default:
+ llvm_unreachable("Unexpected Template Kind");
}
+ TD->setImplicit(true);
+ NewDeduced = DeducedTemplateArgument(TemplateName(TD));
}
DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context,
diff --git a/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp b/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp
index a5b39fe5c51f7..bc39431253880 100644
--- a/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp
@@ -28,13 +28,14 @@ namespace StdExample {
{ /* ... */ }
template<template<class> class TT>
- void f(TT<int>); // expected-note {{candidate template ignored}}
+ void f(TT<int>);
template<template<class,class> class TT>
void g(TT<int, Alloc<int>>);
int h() {
- f(v); // expected-error {{no matching function for call to 'f'}}
+ f(v); // OK: TT = vector, Alloc<int> is used as the default argument for the
+ // second parameter.
g(v); // OK: TT = vector
}
diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp
index e3b5e575374d3..4cc946735a1e2 100644
--- a/clang/test/SemaTemplate/cwg2398.cpp
+++ b/clang/test/SemaTemplate/cwg2398.cpp
@@ -65,13 +65,10 @@ namespace class_template {
template <class T3> struct B;
template <template <class T4> class TT1, class T5> struct B<TT1<T5>>;
- // new-note at -1 {{partial specialization matches}}
template <class T6, class T7> struct B<A<T6, T7>> {};
- // new-note at -1 {{partial specialization matches}}
template struct B<A<int>>;
- // new-error at -1 {{ambiguous partial specialization}}
} // namespace class_template
namespace type_pack1 {
More information about the llvm-branch-commits
mailing list