[clang] [clang] Enable C++17 relaxed template template argument matching by default (PR #89807)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 27 22:18:10 PDT 2024
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/89807
>From 4ee58efa0f154b531dcc674b6f4fe084182aa803 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Tue, 9 Apr 2024 01:14:28 -0300
Subject: [PATCH] [clang] Enable C++17 relaxed template template argument
matching by default
In order to implement this as a DR and avoid breaking reasonable code
that worked before P0522, this patch implements a provisional resolution
for CWG2398: When deducing template template parameters against each other,
and the argument side names a template specialization, instead of just
deducing A, we instead deduce a synthesized template template parameter based
on A, but with it's parameters using the template specialization's arguments
as defaults.
The driver flag is deprecated with a warning.
With this patch, we finally mark C++17 support in clang as complete.
Fixes https://github.com/llvm/llvm-project/issues/36505
---
clang/docs/ReleaseNotes.rst | 19 +++
.../clang/Basic/DiagnosticDriverKinds.td | 2 +-
clang/include/clang/Basic/LangOptions.def | 2 +-
clang/include/clang/Driver/Options.td | 8 +-
clang/lib/Driver/SanitizerArgs.cpp | 9 +-
clang/lib/Driver/ToolChains/Clang.cpp | 16 ++-
clang/lib/Sema/SemaTemplate.cpp | 3 -
clang/lib/Sema/SemaTemplateDeduction.cpp | 107 +++++++++++++++-
.../temp/temp.arg/temp.arg.template/p3-2a.cpp | 2 +-
clang/test/CodeGenCXX/mangle-concept.cpp | 4 +-
.../frelaxed-template-template-args.cpp | 5 +
clang/test/Lexer/cxx-features.cpp | 6 +-
clang/test/SemaTemplate/cwg2398.cpp | 115 ++++++++++++++++++
clang/test/SemaTemplate/default-arguments.cpp | 7 +-
.../instantiate-template-template-parm.cpp | 17 ++-
clang/test/SemaTemplate/nested-template.cpp | 8 +-
clang/test/SemaTemplate/temp_arg_template.cpp | 6 +-
.../SemaTemplate/temp_arg_template_cxx1z.cpp | 2 +-
clang/www/cxx_status.html | 18 ++-
19 files changed, 293 insertions(+), 63 deletions(-)
create mode 100644 clang/test/Driver/frelaxed-template-template-args.cpp
create mode 100644 clang/test/SemaTemplate/cwg2398.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a1390d6536b28c..bd7f96246fd407 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -48,6 +48,11 @@ C++ Specific Potentially Breaking Changes
- Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template<class T> void T();``.
This error can be disabled via `-Wno-strict-primary-template-shadow` for compatibility with previous versions of clang.
+- The behavior controlled by the `-frelaxed-template-template-args` flag is now
+ on by default, and the flag is deprecated. Until the flag is finally removed,
+ it's negative spelling can be used to obtain compatibility with previous
+ versions of clang.
+
ABI Changes in This Version
---------------------------
- Fixed Microsoft name mangling of implicitly defined variables used for thread
@@ -88,6 +93,17 @@ sections with improvements to Clang's support for those languages.
C++ Language Changes
--------------------
+- C++17 support is now completed, with the enablement of the
+ relaxed temlate template argument matching rules introduced in P0522,
+ which was retroactively applied as a defect report.
+ While the implementation already existed since Clang 4, it was turned off by
+ default, and was controlled with the `-frelaxed-template-template-args` flag.
+ In this release, we implement provisional wording for a core defect on
+ P0522 (CWG2398), which avoids the most serious compatibility issues caused
+ by it, allowing us to enable it by default in this release.
+ The flag is now deprecated, and will be removed in the next release, but can
+ still be used to turn it off and regain compatibility with previous versions
+ (#GH36505).
- Implemented ``_BitInt`` literal suffixes ``__wb`` or ``__WB`` as a Clang extension with ``unsigned`` modifiers also allowed. (#GH85223).
C++17 Feature Support
@@ -164,6 +180,9 @@ Resolutions to C++ Defect Reports
- Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
(`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers <https://cplusplus.github.io/CWG/issues/2858.html>`_).
+- P0522 implementation is enabled by default in all language versions, and
+ provisional wording for CWG2398 is implemented.
+
C Language Changes
------------------
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index ed3fd9b1c4a55b..9781fcaa4ff5e9 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -435,7 +435,7 @@ def warn_drv_diagnostics_misexpect_requires_pgo : Warning<
def warn_drv_clang_unsupported : Warning<
"the clang compiler does not support '%0'">;
def warn_drv_deprecated_arg : Warning<
- "argument '%0' is deprecated, use '%1' instead">, InGroup<Deprecated>;
+ "argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup<Deprecated>;
def warn_drv_deprecated_custom : Warning<
"argument '%0' is deprecated, %1">, InGroup<Deprecated>;
def warn_drv_assuming_mfloat_abi_is : Warning<
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 8ef6700ecdc78e..2a79e451f25f14 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -158,7 +158,7 @@ LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly")
LANGOPT(Coroutines , 1, 0, "C++20 coroutines")
LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2")
LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods")
-LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
+LANGOPT(RelaxedTemplateTemplateArgs, 1, 1, "C++17 relaxed matching of template template arguments")
LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")
LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics")
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 086aedefc11878..5e7160fbf61de7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3365,10 +3365,10 @@ defm application_extension : BoolFOption<"application-extension",
"Restrict code to those available for App Extensions">,
NegFlag<SetFalse>>;
defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-args",
- LangOpts<"RelaxedTemplateTemplateArgs">, DefaultFalse,
- PosFlag<SetTrue, [], [ClangOption, CC1Option],
- "Enable C++17 relaxed template template argument matching">,
- NegFlag<SetFalse>>;
+ LangOpts<"RelaxedTemplateTemplateArgs">, DefaultTrue,
+ PosFlag<SetTrue, [], [], "Enable">,
+ NegFlag<SetFalse, [], [CC1Option], "Disable">,
+ BothFlags<[], [ClangOption], " C++17 relaxed template template argument matching">>;
defm sized_deallocation : BoolFOption<"sized-deallocation",
LangOpts<"SizedDeallocation">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 6a4f2548c0bffa..273f215ca94a88 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -797,7 +797,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
Arg->claim();
if (LegacySanitizeCoverage != 0 && DiagnoseErrors) {
D.Diag(diag::warn_drv_deprecated_arg)
- << Arg->getAsString(Args) << "-fsanitize-coverage=trace-pc-guard";
+ << Arg->getAsString(Args) << /*hasReplacement=*/true
+ << "-fsanitize-coverage=trace-pc-guard";
}
continue;
}
@@ -833,11 +834,11 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
// enabled.
if (CoverageFeatures & CoverageTraceBB)
D.Diag(clang::diag::warn_drv_deprecated_arg)
- << "-fsanitize-coverage=trace-bb"
+ << "-fsanitize-coverage=trace-bb" << /*hasReplacement=*/true
<< "-fsanitize-coverage=trace-pc-guard";
if (CoverageFeatures & Coverage8bitCounters)
D.Diag(clang::diag::warn_drv_deprecated_arg)
- << "-fsanitize-coverage=8bit-counters"
+ << "-fsanitize-coverage=8bit-counters" << /*hasReplacement=*/true
<< "-fsanitize-coverage=trace-pc-guard";
}
@@ -849,7 +850,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
if ((CoverageFeatures & InsertionPointTypes) &&
!(CoverageFeatures & InstrumentationTypes) && DiagnoseErrors) {
D.Diag(clang::diag::warn_drv_deprecated_arg)
- << "-fsanitize-coverage=[func|bb|edge]"
+ << "-fsanitize-coverage=[func|bb|edge]" << /*hasReplacement=*/true
<< "-fsanitize-coverage=[func|bb|edge],[trace-pc-guard|trace-pc],["
"control-flow]";
}
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 651a2b5aac368b..42e77b62c277ff 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6529,7 +6529,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (const Arg *A =
Args.getLastArg(options::OPT_fvisibility_global_new_delete_hidden)) {
D.Diag(diag::warn_drv_deprecated_arg)
- << A->getAsString(Args)
+ << A->getAsString(Args) << /*hasReplacement=*/true
<< "-fvisibility-global-new-delete=force-hidden";
}
@@ -7256,11 +7256,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables,
options::OPT_fno_assume_unique_vtables);
- // -frelaxed-template-template-args is off by default, as it is a severe
- // breaking change until a corresponding change to template partial ordering
- // is provided.
- Args.addOptInFlag(CmdArgs, options::OPT_frelaxed_template_template_args,
- options::OPT_fno_relaxed_template_template_args);
+ // -frelaxed-template-template-args is deprecated.
+ if (Arg *A =
+ Args.getLastArg(options::OPT_frelaxed_template_template_args,
+ options::OPT_fno_relaxed_template_template_args)) {
+ D.Diag(diag::warn_drv_deprecated_arg)
+ << A->getAsString(Args) << /*hasReplacement=*/false;
+ if (A->getOption().matches(options::OPT_fno_relaxed_template_template_args))
+ CmdArgs.push_back("-fno-relaxed-template-template-args");
+ }
// -fsized-deallocation is off by default, as it is an ABI-breaking change for
// most platforms.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index bbcb7c33a98579..447e3e2f3a35b5 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -8343,9 +8343,6 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
// C++1z [temp.arg.template]p3: (DR 150)
// A template-argument matches a template template-parameter P when P
// is at least as specialized as the template-argument A.
- // FIXME: We should enable RelaxedTemplateTemplateArgs by default as it is a
- // defect report resolution from C++17 and shouldn't be introduced by
- // concepts.
if (getLangOpts().RelaxedTemplateTemplateArgs) {
// Quick check for the common case:
// If P contains a parameter pack, then A [...] matches P if each of A's
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c3815bca038554..c20a7da64bda60 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -507,10 +507,70 @@ static TemplateDeductionResult DeduceNonTypeTemplateArgument(
S, TemplateParams, NTTP, DeducedTemplateArgument(New), T, Info, Deduced);
}
+/// Create a shallow copy of a given template parameter declaration, with
+/// empty source locations and using the given TemplateArgument as it's
+/// default argument.
+///
+/// \returns The new template parameter declaration.
+static NamedDecl *getTemplateParameterWithDefault(Sema &S, NamedDecl *A,
+ TemplateArgument Default) {
+ switch (A->getKind()) {
+ case Decl::TemplateTypeParm: {
+ auto *T = cast<TemplateTypeParmDecl>(A);
+ // FIXME: A TemplateTypeParmDecl's DefaultArgument can't hold a full
+ // TemplateArgument, so there is currently no way to specify a pack as a
+ // default argument for these.
+ if (T->isParameterPack())
+ return A;
+ auto *R = TemplateTypeParmDecl::Create(
+ S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(),
+ T->getDepth(), T->getIndex(), T->getIdentifier(),
+ T->wasDeclaredWithTypename(), /*ParameterPack=*/false,
+ T->hasTypeConstraint());
+ R->setDefaultArgument(
+ S.Context.getTrivialTypeSourceInfo(Default.getAsType()));
+ if (R->hasTypeConstraint()) {
+ auto *C = R->getTypeConstraint();
+ R->setTypeConstraint(C->getConceptReference(),
+ C->getImmediatelyDeclaredConstraint());
+ }
+ return R;
+ }
+ case Decl::NonTypeTemplateParm: {
+ auto *T = cast<NonTypeTemplateParmDecl>(A);
+ // FIXME: Ditto, as above for TemplateTypeParm case.
+ if (T->isParameterPack())
+ return A;
+ auto *R = NonTypeTemplateParmDecl::Create(
+ S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(),
+ T->getDepth(), T->getIndex(), T->getIdentifier(), T->getType(),
+ /*ParameterPack=*/false, T->getTypeSourceInfo());
+ R->setDefaultArgument(Default.getAsExpr());
+ if (auto *PTC = T->getPlaceholderTypeConstraint())
+ R->setPlaceholderTypeConstraint(PTC);
+ return R;
+ }
+ case Decl::TemplateTemplateParm: {
+ auto *T = cast<TemplateTemplateParmDecl>(A);
+ auto *R = TemplateTemplateParmDecl::Create(
+ S.Context, A->getDeclContext(), SourceLocation(), T->getDepth(),
+ T->getIndex(), T->isParameterPack(), T->getIdentifier(),
+ T->wasDeclaredWithTypename(), T->getTemplateParameters());
+ R->setDefaultArgument(
+ S.Context,
+ S.getTrivialTemplateArgumentLoc(Default, QualType(), SourceLocation()));
+ return R;
+ }
+ default:
+ llvm_unreachable("Unexpected Decl Kind");
+ }
+}
+
static TemplateDeductionResult
DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
TemplateName Param, TemplateName Arg,
TemplateDeductionInfo &Info,
+ ArrayRef<TemplateArgument> DefaultArguments,
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
TemplateDecl *ParamDecl = Param.getAsTemplateDecl();
if (!ParamDecl) {
@@ -519,13 +579,45 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
return TemplateDeductionResult::Success;
}
- if (TemplateTemplateParmDecl *TempParam
- = dyn_cast<TemplateTemplateParmDecl>(ParamDecl)) {
+ if (auto *TempParam = dyn_cast<TemplateTemplateParmDecl>(ParamDecl)) {
// If we're not deducing at this depth, there's nothing to deduce.
if (TempParam->getDepth() != Info.getDeducedDepth())
return TemplateDeductionResult::Success;
- DeducedTemplateArgument NewDeduced(S.Context.getCanonicalTemplateName(Arg));
+ 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())) {
+ assert(Arg.getKind() == TemplateName::Template);
+ assert(!TempArg->isExpandedParameterPack());
+
+ 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)));
+ }
+ }
+
DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context,
Deduced[TempParam->getIndex()],
NewDeduced);
@@ -604,7 +696,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
// Perform template argument deduction for the template name.
if (auto Result =
- DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info, Deduced);
+ DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info,
+ SA->template_arguments(), Deduced);
Result != TemplateDeductionResult::Success)
return Result;
// Perform template argument deduction on each template
@@ -630,7 +723,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
// Perform template argument deduction for the template name.
if (auto Result = DeduceTemplateArguments(
S, TemplateParams, TP->getTemplateName(),
- TemplateName(SA->getSpecializedTemplate()), Info, Deduced);
+ TemplateName(SA->getSpecializedTemplate()), Info,
+ SA->getTemplateArgs().asArray(), Deduced);
Result != TemplateDeductionResult::Success)
return Result;
@@ -2323,7 +2417,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
case TemplateArgument::Template:
if (A.getKind() == TemplateArgument::Template)
return DeduceTemplateArguments(S, TemplateParams, P.getAsTemplate(),
- A.getAsTemplate(), Info, Deduced);
+ A.getAsTemplate(), Info,
+ /*DefaultArguments=*/{}, Deduced);
Info.FirstArg = P;
Info.SecondArg = A;
return TemplateDeductionResult::NonDeducedMismatch;
diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
index f586069638614b..342ffba53dbfaf 100644
--- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
+++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++2a -frelaxed-template-template-args -verify %s
+// RUN: %clang_cc1 -std=c++2a -verify %s
template<typename T> concept C = T::f(); // #C
template<typename T> concept D = C<T> && T::g();
diff --git a/clang/test/CodeGenCXX/mangle-concept.cpp b/clang/test/CodeGenCXX/mangle-concept.cpp
index bbd2cf6555e3ec..e9c46d87635abb 100644
--- a/clang/test/CodeGenCXX/mangle-concept.cpp
+++ b/clang/test/CodeGenCXX/mangle-concept.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -verify -frelaxed-template-template-args -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=latest | FileCheck %s
-// RUN: %clang_cc1 -verify -frelaxed-template-template-args -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=16 | FileCheck %s --check-prefix=CLANG16
+// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=latest | FileCheck %s
+// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=16 | FileCheck %s --check-prefix=CLANG16
// expected-no-diagnostics
namespace test1 {
diff --git a/clang/test/Driver/frelaxed-template-template-args.cpp b/clang/test/Driver/frelaxed-template-template-args.cpp
new file mode 100644
index 00000000000000..dd6265ba8375eb
--- /dev/null
+++ b/clang/test/Driver/frelaxed-template-template-args.cpp
@@ -0,0 +1,5 @@
+// RUN: %clang -fsyntax-only -frelaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-ON %s
+// RUN: %clang -fsyntax-only -fno-relaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+
+// CHECK-ON: warning: argument '-frelaxed-template-template-args' is deprecated [-Wdeprecated]
+// CHECK-OFF: warning: argument '-fno-relaxed-template-template-args' is deprecated [-Wdeprecated]
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index baaa9d4434e9b7..4f1fe70d1191c1 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -7,7 +7,7 @@
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -fsized-deallocation -verify %s
//
-// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -frelaxed-template-template-args -DRELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s
+// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -fno-relaxed-template-template-args -DNO_RELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -DCONCEPTS_TS=1 -verify %s
// RUN: %clang_cc1 -std=c++14 -fno-rtti -fno-threadsafe-statics -verify %s -DNO_EXCEPTIONS -DNO_RTTI -DNO_THREADSAFE_STATICS -fsized-deallocation
// RUN: %clang_cc1 -std=c++14 -fchar8_t -DNO_EXCEPTIONS -DCHAR8_T -verify -fsized-deallocation %s
@@ -231,8 +231,8 @@
#error "wrong value for __cpp_nontype_template_args"
#endif
-#if defined(RELAXED_TEMPLATE_TEMPLATE_ARGS) \
- ? check(template_template_args, 0, 0, 0, 201611, 201611, 201611, 201611) \
+#if !defined(NO_RELAXED_TEMPLATE_TEMPLATE_ARGS) \
+ ? check(template_template_args, 201611, 201611, 201611, 201611, 201611, 201611, 201611) \
: check(template_template_args, 0, 0, 0, 0, 0, 0, 0)
#error "wrong value for __cpp_template_template_args"
#endif
diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp
new file mode 100644
index 00000000000000..bda7143d2cc5e1
--- /dev/null
+++ b/clang/test/SemaTemplate/cwg2398.cpp
@@ -0,0 +1,115 @@
+// RUN: %clang_cc1 %s -fsyntax-only -std=c++23 -verify=expected,new
+// RUN: %clang_cc1 %s -fsyntax-only -std=c++23 -fno-relaxed-template-template-args -verify=expected,old
+
+namespace issue1 {
+ template<class T, class U = T> class B {};
+ template<template<class> class P, class T> void f(P<T>);
+ // new-note at -1 {{deduced type 'B<[...], (default) int>' of 1st parameter does not match adjusted type 'B<[...], float>' of argument [with P = issue1::B, T = int]}}
+ // old-note at -2 2{{template template argument has different template parameters}}
+
+ void g() {
+ f(B<int>()); // old-error {{no matching function for call}}
+ f(B<int,float>()); // expected-error {{no matching function for call}}
+ }
+} // namespace issue1
+
+namespace issue2 {
+ template<typename> struct match;
+
+ template<template<typename> class t,typename T> struct match<t<T>>;
+
+ template<template<typename,typename> class t,typename T0,typename T1>
+ struct match<t<T0,T1>> {};
+
+ template<typename,typename = void> struct other {};
+ template struct match<other<void,void>>;
+} // namespace issue2
+
+namespace type {
+ 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 >> ;
+ template<template<class T6, class T7> class TT2, class T8, class T9> struct B<TT2<T8, T9>> {};
+ template struct B<A<int>>;
+} // namespace type
+
+namespace value {
+ template<class T1, int V1 = 1> struct A {};
+
+ template<class T2> struct B;
+ template<template<class T3 > class TT1, class T4 > struct B<TT1<T4 >> ;
+ template<template<class T5, int V2> class TT2, class T6, int V3> struct B<TT2<T6, V3>> {};
+ template struct B<A<int>>;
+} // namespace value
+
+namespace templ {
+ template <class T1> struct A {};
+
+ template<class T2, template <class T3> class T4 = A> struct B {};
+
+ template<class T5> struct C;
+
+ template<template<class T6> class TT1, class T7> struct C<TT1<T7>>;
+
+ template<template<class T8, template <class T9> class> class TT2,
+ class T10, template <class T11> class TT3>
+ struct C<TT2<T10, TT3>> {};
+
+ template struct C<B<int>>;
+} // namespace templ
+
+namespace type_pack {
+ 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 >>;
+ // new-note at -1 {{template is declared here}}
+ template<template<class T6, class ...T7s> class TT2, class T8, class ...T9s> struct B<TT2<T8, T9s...>>;
+ // old-note at -1 {{template is declared here}}
+
+ template struct B<A<int>>;
+ // expected-error at -1 {{explicit instantiation of undefined template}}
+} // namespace type_pack
+
+namespace gcc_issue {
+ template<class T1, class T2> struct A;
+
+ template<template<class T1> class TT1, class T2> struct A<TT1<T2>, typename TT1<T2>::type>;
+ // new-note at -1 {{partial specialization matches}}
+
+ template<template<class T3, class T4> class TT2, class T5, class T6>
+ struct A<TT2<T5, T6>, typename TT2<T5, T5>::type>;
+ // new-note at -1 {{partial specialization matches}}
+ // old-note at -2 {{template is declared here}}
+
+ template <class T7, class T8 = T7> struct B { using type = int; };
+
+ template struct A<B<int>, int>;
+ // new-error at -1 {{ambiguous partial specializations}}
+ // old-error at -2 {{explicit instantiation of undefined template}}
+} // namespace gcc_issue
+
+namespace ttp_defaults {
+ template <template <class T1> class TT1> struct A {};
+ // old-note at -1 2{{previous template template parameter}}
+
+ template <template <class T2> class TT2> void f(A<TT2>);
+ // new-note at -1 {{explicit instantiation candidate}}
+ // old-note at -2 {{invalid explicitly-specified argument for template parameter 'TT2'}}
+
+ // FIXME: The default arguments on the TTP are not available during partial ordering.
+ template <template <class T3, class T4 = float> class TT3> void f(A<TT3>) {};
+ // new-note at -1 {{explicit instantiation candidate}}
+ // old-error at -2 {{template template argument has different template parameters}}
+ // old-note at -3 {{too many template parameters}}
+
+ template <class T5, class T6 = int> struct B;
+ // old-note at -1 {{too many template parameters}}
+
+ template void f<B>(A<B>);
+ // new-error at -1 {{partial ordering for explicit instantiation of 'f' is ambiguous}}
+ // old-error at -2 {{template template argument has different template parameters}}
+ // old-error at -3 {{explicit instantiation of 'f' does not refer to a function template}}
+} // namespace ttp_defaults
diff --git a/clang/test/SemaTemplate/default-arguments.cpp b/clang/test/SemaTemplate/default-arguments.cpp
index a850d273ccba51..d5d9687cc90f49 100644
--- a/clang/test/SemaTemplate/default-arguments.cpp
+++ b/clang/test/SemaTemplate/default-arguments.cpp
@@ -112,15 +112,14 @@ template<typename T, template<typename> class X = T::template apply>
int array4[is_same<X4<add_pointer>,
X4<add_pointer, add_pointer::apply> >::value? 1 : -1];
-template<int> struct X5 {}; // expected-note{{has a different type 'int'}}
+template<int> struct X5 {};
template<long> struct X5b {};
template<typename T,
- template<T> class B = X5> // expected-error{{template template argument has different}} \
- // expected-note{{previous non-type template parameter}}
+ template<T> class B = X5>
struct X6 {};
X6<int> x6a;
-X6<long> x6b; // expected-note{{while checking a default template argument}}
+X6<long> x6b;
X6<long, X5b> x6c;
diff --git a/clang/test/SemaTemplate/instantiate-template-template-parm.cpp b/clang/test/SemaTemplate/instantiate-template-template-parm.cpp
index a70c7e8b081a41..39aeeb1c1a6a32 100644
--- a/clang/test/SemaTemplate/instantiate-template-template-parm.cpp
+++ b/clang/test/SemaTemplate/instantiate-template-template-parm.cpp
@@ -20,30 +20,29 @@ apply<add_reference, int>::type ir = i;
apply<add_reference, float>::type fr = i; // expected-error{{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'int'}}
// Template template parameters
-template<int> struct B; // expected-note{{has a different type 'int'}}
+template<int> struct B;
-template<typename T,
- template<T Value> class X> // expected-error{{cannot have type 'float'}} \
- // expected-note{{with type 'long'}}
+template<typename T,
+ template<T Value> class X> // expected-error{{cannot have type 'float'}}
struct X0 { };
X0<int, B> x0b1;
X0<float, B> x0b2; // expected-note{{while substituting}}
-X0<long, B> x0b3; // expected-error{{template template argument has different template parameters}}
+X0<long, B> x0b3;
-template<template<int V> class TT> // expected-note{{parameter with type 'int'}}
+template<template<int V> class TT>
struct X1 { };
template<typename T, template<T V> class TT>
struct X2 {
- X1<TT> x1; // expected-error{{has different template parameters}}
+ X1<TT> x1;
};
template<int V> struct X3i { };
-template<long V> struct X3l { }; // expected-note{{different type 'long'}}
+template<long V> struct X3l { };
X2<int, X3i> x2okay;
-X2<long, X3l> x2bad; // expected-note{{instantiation}}
+X2<long, X3l> x2okay2;
template <typename T, template <T, T> class TT, class R = TT<1, 2> >
struct Comp {
diff --git a/clang/test/SemaTemplate/nested-template.cpp b/clang/test/SemaTemplate/nested-template.cpp
index efbde2076b9fa1..5bd388d4dff3d7 100644
--- a/clang/test/SemaTemplate/nested-template.cpp
+++ b/clang/test/SemaTemplate/nested-template.cpp
@@ -112,18 +112,16 @@ template struct X1<int>::B<bool>;
// Template template parameters
template<typename T>
struct X2 {
- template<template<class U, T Value> class> // expected-error{{cannot have type 'float'}} \
- // expected-note{{previous non-type template}}
+ template<template<class U, T Value> class> // expected-error{{cannot have type 'float'}}
struct Inner { };
};
-template<typename T,
- int Value> // expected-note{{template non-type parameter}}
+template<typename T, int Value>
struct X2_arg;
X2<int>::Inner<X2_arg> x2i1;
X2<float> x2a; // expected-note{{instantiation}}
-X2<long>::Inner<X2_arg> x2i3; // expected-error{{template template argument has different}}
+X2<long>::Inner<X2_arg> x2i3;
namespace PR10896 {
template<typename TN>
diff --git a/clang/test/SemaTemplate/temp_arg_template.cpp b/clang/test/SemaTemplate/temp_arg_template.cpp
index 3c2697329212e9..a7236669276aa3 100644
--- a/clang/test/SemaTemplate/temp_arg_template.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template.cpp
@@ -5,11 +5,11 @@ template<template<typename T> class X> struct A; // expected-note 2{{previous te
template<template<typename T, int I> class X> struct B; // expected-note{{previous template template parameter is here}}
-template<template<int I> class X> struct C; // expected-note 2{{previous non-type template parameter with type 'int' is here}}
+template<template<int I> class X> struct C; // expected-note {{previous non-type template parameter with type 'int' is here}}
template<class> struct X; // expected-note{{too few template parameters in template template argument}}
template<int N> struct Y; // expected-note{{template parameter has a different kind in template argument}}
-template<long N> struct Ylong; // expected-note{{template non-type parameter has a different type 'long' in template argument}}
+template<long N> struct Ylong;
template<const int &N> struct Yref; // expected-note{{template non-type parameter has a different type 'const int &' in template argument}}
namespace N {
@@ -26,7 +26,7 @@ A<Y> *a4; // expected-error{{template template argument has different template p
A<TooMany> *a5; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
B<X> *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
C<Y> *a7;
-C<Ylong> *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
+C<Ylong> *a8;
C<Yref> *a9; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
template<typename T> void f(int);
diff --git a/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp
index 03ef78f8cf14e1..372a00efc601e4 100644
--- a/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z -frelaxed-template-template-args %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
// expected-note at temp_arg_template_cxx1z.cpp:* 1+{{}}
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index c233171e63c811..d7401b5cda269f 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -916,7 +916,7 @@ <h2 id="cxx17">C++17 implementation status</h2>
You can use Clang in C++17 mode with the <code>-std=c++17</code> option
(use <code>-std=c++1z</code> in Clang 4 and earlier).</p>
-<details open>
+<details>
<summary>List of features and minimum Clang version with support</summary>
<table width="689" border="1" cellspacing="0">
@@ -1133,8 +1133,8 @@ <h2 id="cxx17">C++17 implementation status</h2>
<!-- Issaquah 2016 papers -->
<tr>
<td>Matching template template parameters to compatible arguments</td>
- <td><a href="https://wg21.link/p0522r0">P0522R0</a></td>
- <td class="partial" align="center">Partial <a href="#p0522">(10)</a></td>
+ <td><a href="https://wg21.link/p0522r0">P0522R0</a> (<a href="#dr">DR</a>)</td>
+ <td class="full" align="center">Clang 19 <a href="#p0522">(10)</a></td>
</tr>
<tr>
<td>Removing deprecated dynamic exception specifications</td>
@@ -1162,13 +1162,11 @@ <h2 id="cxx17">C++17 implementation status</h2>
reverse construction order in that ABI.
This is not fully supported during constant expression evaluation until Clang 12.
</span><br>
-<span id="p0522">(10): Despite being the resolution to a Defect Report, this
-feature is disabled by default in all language versions, and can be enabled
-explicitly with the flag <tt>-frelaxed-template-template-args</tt> in Clang 4
-onwards.
-The change to the standard lacks a corresponding change for template partial
-ordering, resulting in ambiguity errors for reasonable and previously-valid
-code. This issue is expected to be rectified soon.
+<span id="p0522">(10): While this feature was initially implemented in Clang 4,
+it was not enabled by default prior to clang 19, but could be enabled with
+<tt>-frelaxed-template-template-args</tt>.
+Starting from Clang 19, the flag is deprecated and will be removed in a future
+version.
</span>
</p>
</details>
More information about the cfe-commits
mailing list