From 1756044e71d756f7102f962d0298627ede27871c Mon Sep 17 00:00:00 2001 From: Matheus Izvekov 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 | 139 ++++++++++++++++++ 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, 317 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 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 `_). +- 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; + "argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup; def warn_drv_deprecated_custom : Warning< "argument '%0' is deprecated, %1">, InGroup; 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>; defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-args", - LangOpts<"RelaxedTemplateTemplateArgs">, DefaultFalse, - PosFlag, - NegFlag>; + LangOpts<"RelaxedTemplateTemplateArgs">, DefaultTrue, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption], " C++17 relaxed template template argument matching">>; defm sized_deallocation : BoolFOption<"sized-deallocation", LangOpts<"SizedDeallocation">, DefaultFalse, PosFlagclaim(); 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(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(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(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 DefaultArguments, SmallVectorImpl &Deduced) { TemplateDecl *ParamDecl = Param.getAsTemplateDecl(); if (!ParamDecl) { @@ -519,13 +579,45 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, return TemplateDeductionResult::Success; } - if (TemplateTemplateParmDecl *TempParam - = dyn_cast(ParamDecl)) { + if (auto *TempParam = dyn_cast(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( + Arg.getAsTemplateDecl())) { + assert(Arg.getKind() == TemplateName::Template); + assert(!TempArg->isExpandedParameterPack()); + + TemplateParameterList *As = TempArg->getTemplateParameters(); + if (DefaultArguments.size() != 0) { + assert(DefaultArguments.size() <= As->size()); + SmallVector 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 concept C = T::f(); // #C template concept D = C && 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..a20155486b123d --- /dev/null +++ b/clang/test/SemaTemplate/cwg2398.cpp @@ -0,0 +1,139 @@ +// 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 B {}; + template class P, class T> void f(P); + // new-note@-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@-2 2{{template template argument has different template parameters}} + + void g() { + f(B()); // old-error {{no matching function for call}} + f(B()); // expected-error {{no matching function for call}} + } +} // namespace issue1 + +namespace issue2 { + template struct match; + + template class t,typename T> struct match>; + + template class t,typename T0,typename T1> + struct match> {}; + + template struct other {}; + template struct match>; +} // namespace issue2 + +namespace type { + template struct A; + + template struct B; + template class TT1, class T5 > struct B> ; + template class TT2, class T8, class T9> struct B> {}; + template struct B>; +} // namespace type + +namespace value { + template struct A; + + template struct B; + template class TT1, class T4 > struct B> ; + template class TT2, class T6, int V3> struct B> {}; + template struct B>; +} // namespace value + +namespace templ { + template struct A; + + template class T4 = A> struct B {}; + + template struct C; + + template class TT1, class T7> struct C>; + + template class> class TT2, + class T10, template class TT3> + struct C> {}; + + template struct C>; +} // namespace templ + +namespace type_pack1 { + template struct A; + template class TT1, class T4> struct A> ; + // new-note@-1 {{partial specialization matches}} + template class TT2, class T6> struct A> {}; + // new-note@-1 {{partial specialization matches}} + + template struct B; + template struct A>; + // new-error@-1 {{ambiguous partial specialization}} +} // namespace type_pack1 + +namespace type_pack2 { + template struct A; + template class TT1, class ...T4> struct A> ; + // new-note@-1 {{partial specialization matches}} + template class TT2, class ...T6> struct A> {}; + // new-note@-1 {{partial specialization matches}} + + template struct B; + template struct A>; + // new-error@-1 {{ambiguous partial specialization}} +} // namespace type_pack2 + +namespace type_pack3 { + template struct A; + + template struct B; + + template class TT1, class T5 > struct B>; + // new-note@-1 {{template is declared here}} + template class TT2, class T8, class ...T9s> struct B>; + // old-note@-1 {{template is declared here}} + + template struct B>; + // expected-error@-1 {{explicit instantiation of undefined template}} +} // namespace type_pack3 + +namespace gcc_issue { + template struct A; + + template class TT1, class T2> struct A, typename TT1::type>; + // new-note@-1 {{partial specialization matches}} + + template class TT2, class T5, class T6> + struct A, typename TT2::type>; + // new-note@-1 {{partial specialization matches}} + // old-note@-2 {{template is declared here}} + + template struct B { using type = int; }; + + template struct A, int>; + // new-error@-1 {{ambiguous partial specializations}} + // old-error@-2 {{explicit instantiation of undefined template}} +} // namespace gcc_issue + +namespace ttp_defaults { + template