[clang] [clang] Remove the deprecated flag `-frelaxed-template-template-args`. (PR #111894)

Matheus Izvekov via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 10 11:55:54 PDT 2024


https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/111894

This flag has been deprecated since Clang 19, having been the default since then.

It has remained because its negation was still useful to work around backwards compatibility breaking changes from P0522.

However, in Clang 20 we have landed various changes which implemented P3310R2 and beyond, which solve almost all of the expected issues, the known remaining few being a bit obscure.

So this change removes the flag completely and all of its implementation and support code.

Hopefully any remaining users can just stop using the flag. If there are still important issues remaining, this removal will also shake the tree and help us know.

>From 87eca134643b003f3546aaaaf3c3be51bd58eb0b Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Thu, 10 Oct 2024 15:40:48 -0300
Subject: [PATCH] [clang] Remove the deprecated flag
 `-frelaxed-template-template-args`.

This flag has been deprecated since Clang 19, having been the default
since then.

It has remained because its negation was still useful to work around
backwards compatibility breaking changes from P0522.

However, in Clang 20 we have landed various changes which implemented
P3310R2 and beyond, which solve almost all of the expected issues,
the known remaining few being a bit obscure.

So this change removes the flag completely and all of its implementation
and support code.

Hopefully any remaining users can just stop using the flag.
If there are still important issues remaining, this removal will
also shake the tree and help us know.
---
 clang/docs/ReleaseNotes.rst                   |   3 +
 .../clang/Basic/DiagnosticDriverKinds.td      |   3 -
 clang/include/clang/Basic/DiagnosticGroups.td |   2 -
 clang/include/clang/Basic/LangOptions.def     |   1 -
 clang/include/clang/Driver/Options.td         |   5 -
 clang/include/clang/Sema/Sema.h               |  11 --
 clang/lib/Driver/ToolChains/Clang.cpp         |  14 --
 clang/lib/Frontend/InitPreprocessor.cpp       |   4 +-
 clang/lib/Sema/SemaTemplate.cpp               | 108 ++++--------
 .../frelaxed-template-template-args.cpp       |   9 -
 clang/test/Lexer/cxx-features.cpp             |   5 +-
 clang/test/SemaTemplate/cwg2398.cpp           | 157 +++++++-----------
 clang/www/cxx_status.html                     |   4 +-
 13 files changed, 98 insertions(+), 228 deletions(-)
 delete mode 100644 clang/test/Driver/frelaxed-template-template-args.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e48835d4738007..5346c1ed0009c5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -209,6 +209,9 @@ Resolutions to C++ Defect Reports
 - Clang now has improved resolution to CWG2398, allowing class templates to have
   default arguments deduced when partial ordering, and better backwards compatibility
   in overload resolution.
+  As a benefit of this improved support, the flag `-frelaxed-template-template-args`
+  and its negation have been removed altogether, having been deprecated since the previous
+  release.
 
 - Clang now allows comparing unequal object pointers that have been cast to ``void *``
   in constant expressions. These comparisons always worked in non-constant expressions.
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 68722ad9633120..51a39ff6e623bd 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -446,9 +446,6 @@ def warn_drv_clang_unsupported : Warning<
   "the clang compiler does not support '%0'">;
 def warn_drv_deprecated_arg : Warning<
   "argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup<Deprecated>;
-def warn_drv_deprecated_arg_no_relaxed_template_template_args : Warning<
-  "argument '-fno-relaxed-template-template-args' is deprecated">,
-  InGroup<DeprecatedNoRelaxedTemplateTemplateArgs>;
 def warn_drv_deprecated_arg_ofast : Warning<
   "argument '-Ofast' is deprecated; use '-O3 -ffast-math' for the same behavior,"
   " or '-O3' to enable only conforming optimizations">,
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 8273701e7b0963..385fcdda279c20 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -102,7 +102,6 @@ def EnumConversion : DiagGroup<"enum-conversion",
                                [EnumEnumConversion,
                                 EnumFloatConversion,
                                 EnumCompareConditional]>;
-def DeprecatedNoRelaxedTemplateTemplateArgs : DiagGroup<"deprecated-no-relaxed-template-template-args">;
 def DeprecatedOFast : DiagGroup<"deprecated-ofast">;
 def ObjCSignedCharBoolImplicitIntConversion :
   DiagGroup<"objc-signed-char-bool-implicit-int-conversion">;
@@ -228,7 +227,6 @@ def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion,
                                           DeprecatedLiteralOperator,
                                           DeprecatedPragma,
                                           DeprecatedRegister,
-                                          DeprecatedNoRelaxedTemplateTemplateArgs,
                                           DeprecatedOFast,
                                           DeprecatedThisCapture,
                                           DeprecatedType,
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 68db400c22e6c1..133c1c3df3432f 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -160,7 +160,6 @@ 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, 1, "C++17 relaxed matching of template template arguments")
 LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")
 LANGOPT(RetainSubstTemplateTypeParmTypeAstNodes, 1, 0, "retain SubstTemplateTypeParmType nodes in the AST's representation of alias template specializations")
 
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index d306c751505e98..211bcfe4e0c899 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3499,11 +3499,6 @@ defm application_extension : BoolFOption<"application-extension",
   PosFlag<SetTrue, [], [ClangOption, CC1Option],
           "Restrict code to those available for App Extensions">,
   NegFlag<SetFalse>>;
-defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-args",
-  LangOpts<"RelaxedTemplateTemplateArgs">, DefaultTrue,
-  PosFlag<SetTrue, [], [], "Enable">,
-  NegFlag<SetFalse, [], [CC1Option], "Disable">,
-  BothFlags<[], [ClangOption], " C++17 relaxed template template argument matching">>;
 defm retain_subst_template_type_parm_type_ast_nodes : BoolFOption<"retain-subst-template-type-parm-type-ast-nodes",
   LangOpts<"RetainSubstTemplateTypeParmTypeAstNodes">, DefaultFalse,
   PosFlag<SetTrue, [], [CC1Option], "Enable">,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f8118ca64ad3f2..2e3cbc101c2eff 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11765,17 +11765,6 @@ class Sema final : public SemaBase {
     /// \endcode
     TPL_TemplateTemplateParmMatch,
 
-    /// We are matching the template parameter lists of a template
-    /// template argument against the template parameter lists of a template
-    /// template parameter.
-    ///
-    /// \code
-    /// template<template<int Value> class Metafun> struct X;
-    /// template<int Value> struct integer_c;
-    /// X<integer_c> xic;
-    /// \endcode
-    TPL_TemplateTemplateArgumentMatch,
-
     /// We are determining whether the template-parameters are equivalent
     /// according to C++ [temp.over.link]/6. This comparison does not consider
     /// constraints.
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 49b07322a21a52..44d357a8ccaa8d 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7398,20 +7398,6 @@ 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 deprecated.
-  if (Arg *A =
-          Args.getLastArg(options::OPT_frelaxed_template_template_args,
-                          options::OPT_fno_relaxed_template_template_args)) {
-    if (A->getOption().matches(
-            options::OPT_fno_relaxed_template_template_args)) {
-      D.Diag(diag::warn_drv_deprecated_arg_no_relaxed_template_template_args);
-      CmdArgs.push_back("-fno-relaxed-template-template-args");
-    } else {
-      D.Diag(diag::warn_drv_deprecated_arg)
-          << A->getAsString(Args) << /*hasReplacement=*/false;
-    }
-  }
-
   // -fsized-deallocation is on by default in C++14 onwards and otherwise off
   // by default.
   Args.addLastArg(CmdArgs, options::OPT_fsized_deallocation,
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 9a0fdb175ff29e..2c97afd865c190 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -727,8 +727,8 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
   }
   if (LangOpts.AlignedAllocation && !LangOpts.AlignedAllocationUnavailable)
     Builder.defineMacro("__cpp_aligned_new", "201606L");
-  if (LangOpts.RelaxedTemplateTemplateArgs)
-    Builder.defineMacro("__cpp_template_template_args", "201611L");
+
+  Builder.defineMacro("__cpp_template_template_args", "201611L");
 
   // C++20 features.
   if (LangOpts.CPlusPlus20) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 62d0d0914fa306..d0b0cd300c6bc0 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -7330,11 +7330,6 @@ bool Sema::CheckTemplateTemplateArgument(
       << Template;
   }
 
-  if (!getLangOpts().RelaxedTemplateTemplateArgs)
-    return !TemplateParameterListsAreEqual(
-        Template->getTemplateParameters(), Params, /*Complain=*/true,
-        TPL_TemplateTemplateArgumentMatch, Arg.getLocation());
-
   // 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.
@@ -7706,9 +7701,7 @@ static bool MatchTemplateParameterKind(
   // However, if we are matching a template template argument to a
   // template template parameter, the template template parameter can have
   // a parameter pack where the template template argument does not.
-  if (Old->isTemplateParameterPack() != New->isTemplateParameterPack() &&
-      !(Kind == Sema::TPL_TemplateTemplateArgumentMatch &&
-        Old->isTemplateParameterPack())) {
+  if (Old->isTemplateParameterPack() != New->isTemplateParameterPack()) {
     if (Complain) {
       unsigned NextDiag = diag::err_template_parameter_pack_non_pack;
       if (TemplateArgLoc.isValid()) {
@@ -7734,37 +7727,28 @@ static bool MatchTemplateParameterKind(
                                     = dyn_cast<NonTypeTemplateParmDecl>(Old)) {
     NonTypeTemplateParmDecl *NewNTTP = cast<NonTypeTemplateParmDecl>(New);
 
-    // If we are matching a template template argument to a template
-    // template parameter and one of the non-type template parameter types
-    // is dependent, then we must wait until template instantiation time
-    // to actually compare the arguments.
-    if (Kind != Sema::TPL_TemplateTemplateArgumentMatch ||
-        (!OldNTTP->getType()->isDependentType() &&
-         !NewNTTP->getType()->isDependentType())) {
-      // C++20 [temp.over.link]p6:
-      //   Two [non-type] template-parameters are equivalent [if] they have
-      //   equivalent types ignoring the use of type-constraints for
-      //   placeholder types
-      QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
-      QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
-      if (!S.Context.hasSameType(OldType, NewType)) {
-        if (Complain) {
-          unsigned NextDiag = diag::err_template_nontype_parm_different_type;
-          if (TemplateArgLoc.isValid()) {
-            S.Diag(TemplateArgLoc,
-                   diag::err_template_arg_template_params_mismatch);
-            NextDiag = diag::note_template_nontype_parm_different_type;
-          }
-          S.Diag(NewNTTP->getLocation(), NextDiag)
-            << NewNTTP->getType()
-            << (Kind != Sema::TPL_TemplateMatch);
-          S.Diag(OldNTTP->getLocation(),
-                 diag::note_template_nontype_parm_prev_declaration)
-            << OldNTTP->getType();
+    // C++20 [temp.over.link]p6:
+    //   Two [non-type] template-parameters are equivalent [if] they have
+    //   equivalent types ignoring the use of type-constraints for
+    //   placeholder types
+    QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
+    QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
+    if (!S.Context.hasSameType(OldType, NewType)) {
+      if (Complain) {
+        unsigned NextDiag = diag::err_template_nontype_parm_different_type;
+        if (TemplateArgLoc.isValid()) {
+          S.Diag(TemplateArgLoc,
+                 diag::err_template_arg_template_params_mismatch);
+          NextDiag = diag::note_template_nontype_parm_different_type;
         }
-
-        return false;
+        S.Diag(NewNTTP->getLocation(), NextDiag)
+            << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch);
+        S.Diag(OldNTTP->getLocation(),
+               diag::note_template_nontype_parm_prev_declaration)
+            << OldNTTP->getType();
       }
+
+      return false;
     }
   }
   // For template template parameters, check the template parameter types.
@@ -7784,7 +7768,6 @@ static bool MatchTemplateParameterKind(
   }
 
   if (Kind != Sema::TPL_TemplateParamsEquivalent &&
-      Kind != Sema::TPL_TemplateTemplateArgumentMatch &&
       !isa<TemplateTemplateParmDecl>(Old)) {
     const Expr *NewC = nullptr, *OldC = nullptr;
 
@@ -7855,7 +7838,7 @@ bool Sema::TemplateParameterListsAreEqual(
     const TemplateCompareNewDeclInfo &NewInstFrom, TemplateParameterList *New,
     const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
     TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
-  if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
+  if (Old->size() != New->size()) {
     if (Complain)
       DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
                                                  TemplateArgLoc);
@@ -7872,40 +7855,18 @@ bool Sema::TemplateParameterListsAreEqual(
   TemplateParameterList::iterator NewParm = New->begin();
   TemplateParameterList::iterator NewParmEnd = New->end();
   for (TemplateParameterList::iterator OldParm = Old->begin(),
-                                    OldParmEnd = Old->end();
-       OldParm != OldParmEnd; ++OldParm) {
-    if (Kind != TPL_TemplateTemplateArgumentMatch ||
-        !(*OldParm)->isTemplateParameterPack()) {
-      if (NewParm == NewParmEnd) {
-        if (Complain)
-          DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
-                                                     TemplateArgLoc);
-
-        return false;
-      }
-
-      if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
-                                      OldInstFrom, Complain, Kind,
-                                      TemplateArgLoc))
-        return false;
-
-      ++NewParm;
-      continue;
-    }
-
-    // C++0x [temp.arg.template]p3:
-    //   [...] When P's template- parameter-list contains a template parameter
-    //   pack (14.5.3), the template parameter pack will match zero or more
-    //   template parameters or template parameter packs in the
-    //   template-parameter-list of A with the same type and form as the
-    //   template parameter pack in P (ignoring whether those template
-    //   parameters are template parameter packs).
-    for (; NewParm != NewParmEnd; ++NewParm) {
-      if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
-                                      OldInstFrom, Complain, Kind,
-                                      TemplateArgLoc))
-        return false;
+                                       OldParmEnd = Old->end();
+       OldParm != OldParmEnd; ++OldParm, ++NewParm) {
+    if (NewParm == NewParmEnd) {
+      if (Complain)
+        DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
+                                                   TemplateArgLoc);
+      return false;
     }
+    if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
+                                    OldInstFrom, Complain, Kind,
+                                    TemplateArgLoc))
+      return false;
   }
 
   // Make sure we exhausted all of the arguments.
@@ -7917,8 +7878,7 @@ bool Sema::TemplateParameterListsAreEqual(
     return false;
   }
 
-  if (Kind != TPL_TemplateTemplateArgumentMatch &&
-      Kind != TPL_TemplateParamsEquivalent) {
+  if (Kind != TPL_TemplateParamsEquivalent) {
     const Expr *NewRC = New->getRequiresClause();
     const Expr *OldRC = Old->getRequiresClause();
 
diff --git a/clang/test/Driver/frelaxed-template-template-args.cpp b/clang/test/Driver/frelaxed-template-template-args.cpp
deleted file mode 100644
index 7a7fd6f0bbc8f9..00000000000000
--- a/clang/test/Driver/frelaxed-template-template-args.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-// RUN: %clang -fsyntax-only -### %s 2>&1 | FileCheck --check-prefix=CHECK-DEF %s
-// 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
-// RUN: %clang -fsyntax-only -fno-relaxed-template-template-args -Wno-deprecated-no-relaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-DIS --allow-empty %s
-
-// CHECK-DEF-NOT: "-cc1"{{.*}} "-fno-relaxed-template-template-args"
-// CHECK-ON:  warning: argument '-frelaxed-template-template-args' is deprecated [-Wdeprecated]
-// CHECK-OFF: warning: argument '-fno-relaxed-template-template-args' is deprecated [-Wdeprecated-no-relaxed-template-template-args]
-// CHECK-DIS-NOT: warning: argument '-fno-relaxed-template-template-args' is deprecated
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 5b88e00b715080..6db984a6a659d8 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -7,7 +7,6 @@
 // RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -verify %s
 
 //
-// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fno-relaxed-template-template-args -DNO_RELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s
 // RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -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
 // RUN: %clang_cc1 -std=c++14 -fchar8_t -DNO_EXCEPTIONS -DCHAR8_T -verify %s
@@ -239,9 +238,7 @@
 #error "wrong value for __cpp_nontype_template_args"
 #endif
 
-#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)
+#if check(template_template_args, 201611, 201611, 201611, 201611, 201611, 201611, 201611)
 #error "wrong value for __cpp_template_template_args"
 #endif
 
diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp
index 3825239de4a285..fb4df2edb51a01 100644
--- a/clang/test/SemaTemplate/cwg2398.cpp
+++ b/clang/test/SemaTemplate/cwg2398.cpp
@@ -1,14 +1,12 @@
-// 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
+// RUN: %clang_cc1 %s -fsyntax-only -std=c++23 -verify
 
 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 = B, T = int]}}
-  // old-note at -2 2{{template template argument has different template parameters}}
+  // expected-note at -1 {{deduced type 'B<[...], (default) int>' of 1st parameter does not match adjusted type 'B<[...], float>' of argument [with P = B, T = int]}}
 
   void g() {
-    f(B<int>()); // old-error {{no matching function for call}}
+    f(B<int>());
     f(B<int,float>()); // expected-error {{no matching function for call}}
   }
 } // namespace issue1
@@ -116,41 +114,32 @@ 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}}
+  // expected-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}}
+  // expected-note at -1 {{partial specialization matches}}
 
   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}}
+  // expected-error at -1 {{ambiguous partial specializations}}
 } // 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'}}
+  // expected-note at -1 {{explicit instantiation candidate}}
 
   // 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}}
+  // expected-note at -1 {{explicit instantiation candidate}}
 
   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}}
+  // expected-error at -1 {{partial ordering for explicit instantiation of 'f' is ambiguous}}
 } // namespace ttp_defaults
 
 namespace ttp_only {
@@ -193,16 +182,16 @@ namespace consistency {
     template<template<class, class> class TT1,
              class T1, class T2, class T3, class T4>
     struct A<TT1<T1, T2>, TT1<T3, T4>, typename nondeduced<TT1<T1, T4>>::type> {};
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template<template<class> class UU1,
              template<class> class UU2,
              class U1, class U2>
     struct A<UU1<U1>, UU2<U2>, typename nondeduced<UU1<U1>>::type>;
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template struct A<B<int>, B<int>, B<int>>;
-    // new-error at -1 {{ambiguous partial specializations}}
+    // expected-error at -1 {{ambiguous partial specializations}}
   } // namespace t2
   namespace t3 {
     template<class T1, class T2, class T3> struct A;
@@ -210,15 +199,15 @@ namespace consistency {
     template<template<class, class> class TT1,
              class T1, class T2, class T3, class T4>
     struct A<TT1<T1, T2>, TT1<T3, T4>, typename nondeduced<TT1<T1, T2>>::type> {};
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template<template<class> class UU1,
              class U1, class U2>
     struct A<UU1<U1>, UU1<U2>, typename nondeduced<UU1<U1>>::type>;
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template struct A<B<int>, B<int>, B<int>>;
-    // new-error at -1 {{ambiguous partial specializations}}
+    // expected-error at -1 {{ambiguous partial specializations}}
   } // namespace t3
   namespace t4 {
     template<class T1, class T2, class T3> struct A;
@@ -226,15 +215,15 @@ namespace consistency {
     template<template<class, class> class TT1,
              class T1, class T2, class T3, class T4>
     struct A<TT1<T1, T2>, TT1<T3, T4>, typename nondeduced<TT1<T1, T4>>::type> {};
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template<template<class> class UU1,
              class U1, class U2>
     struct A<UU1<U1>, UU1<U2>, typename nondeduced<UU1<U1>>::type>;
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template struct A<B<int>, B<int>, B<int>>;
-    // new-error at -1 {{ambiguous partial specializations}}
+    // expected-error at -1 {{ambiguous partial specializations}}
   } // namespace t4
   namespace t5 {
     template<class T1, class T2> struct A;
@@ -242,15 +231,15 @@ namespace consistency {
     template<template<class, class> class TT1,
              class T1, class T2, class T3, class T4>
     struct A<TT1<T1, T2>, TT1<T3, T4>> {};
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template<template<class> class UU1,
              class U1, class U2>
     struct A<UU1<U1>, UU1<U2>>;
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template struct A<B<int>, B<int>>;
-    // new-error at -1 {{ambiguous partial specializations}}
+    // expected-error at -1 {{ambiguous partial specializations}}
   } // namespace t5
   namespace t6 {
     template<class T1, class T2> struct A;
@@ -258,15 +247,15 @@ namespace consistency {
     template<template<class, class> class TT1,
              class T1, class T2, class T3>
     struct A<TT1<T1, T2>, TT1<T1, T3>> {};
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template<template<class> class UU1,
              class U1, class U2>
     struct A<UU1<U1>, UU1<U2>>;
-    // new-note at -1 {{partial specialization matches}}
+    // expected-note at -1 {{partial specialization matches}}
 
     template struct A<B<int>, B<int>>;
-    // new-error at -1 {{ambiguous partial specializations}}
+    // expected-error at -1 {{ambiguous partial specializations}}
   } // namespace t6
 } // namespace consistency
 
@@ -275,8 +264,7 @@ namespace classes {
     template<class T, class U> struct A {};
 
     template<template<class> class TT> auto f(TT<int> a) { return a; }
-    // old-note at -1 2{{template template argument has different template parameters}}
-    // new-note at -2 2{{substitution failure: too few template arguments}}
+    // expected-note at -1 2{{substitution failure: too few template arguments}}
 
     A<int, float> v1;
     A<int, double> v2;
@@ -292,8 +280,7 @@ namespace classes {
       static constexpr auto val = E1;
     };
     template <template <class T3> class TT> void f(TT<int> v) {
-      // old-note at -1 {{template template argument has different template parameters}}
-      // new-note at -2 {{substitution failure: too few template arguments}}
+      // expected-note at -1 {{substitution failure: too few template arguments}}
       static_assert(v.val == 3);
     };
     void test() {
@@ -307,8 +294,7 @@ namespace classes {
     };
 
     template <template <class T3> class TT> void f(TT<int> v) {
-      // old-note at -1 {{template template argument has different template parameters}}
-      // new-note at -2 {{deduced type 'A<[...], (no argument), (no argument), (no argument)>' of 1st parameter does not match adjusted type 'A<[...], void, void, void>' of argument [with TT = A]}}
+      // expected-note at -1 {{deduced type 'A<[...], (no argument), (no argument), (no argument)>' of 1st parameter does not match adjusted type 'A<[...], void, void, void>' of argument [with TT = A]}}
       static_assert(v.val == 3);
     };
     void test() {
@@ -327,8 +313,7 @@ namespace classes {
     }
 
     template <template <class T2, int V3> class TT2> auto g(TT2<double, 1>) {
-      // new-note at -1 {{too few template arguments for class template 'A'}}
-      // old-note at -2 {{template template argument has different template parameters}}
+      // expected-note at -1 {{too few template arguments for class template 'A'}}
       return f(TT2<int, 2>());
     }
 
@@ -346,13 +331,11 @@ namespace classes {
     };
 
     template <template <class> class TT> TT<float> f(TT<int>);
-    // new-note at -1  {{deduced type 'A<[...], (default) int *>' of 1st parameter does not match adjusted type 'A<[...], double *>' of argument [with TT = A]}}
-    // old-note at -2 2{{template template argument has different template parameters}}
+    // expected-note at -1  {{deduced type 'A<[...], (default) int *>' of 1st parameter does not match adjusted type 'A<[...], double *>' of argument [with TT = A]}}
 
-    using X = int*; // new-note {{previous definition is here}}
+    using X = int*; // expected-note {{previous definition is here}}
     using X = decltype(f(A<int>()))::type;
-    // new-error at -1 {{different types ('decltype(f(A<int>()))::type' (aka 'float *') vs 'int *')}}
-    // old-error at -2 {{no matching function for call}}
+    // expected-error at -1 {{different types ('decltype(f(A<int>()))::type' (aka 'float *') vs 'int *')}}
 
     using Y = double*;
     using Y = decltype(f(A<int, double*>()))::type;
@@ -364,54 +347,32 @@ namespace packs {
   namespace t1 {
     // FIXME: This should be rejected
     template<template<int, int...> class> struct A {};
-    // old-note at -1 {{previous non-type template parameter with type 'int' is here}}
-
     template<char> struct B;
-    // old-note at -1 {{template non-type parameter has a different type 'char' in template argument}}
-
     template struct A<B>;
-    // old-error at -1 {{has different template parameters}}
   } // namespace t1
   namespace t2 {
     template<template<char, int...> class> struct A {};
-    // old-note at -1 {{previous non-type template parameter with type 'char' is here}}
-
     template<int> struct B;
-    // old-note at -1 {{template non-type parameter has a different type 'int' in template argument}}
-
     template struct A<B>;
-    // old-error at -1 {{has different template parameters}}
   } // namespace t2
   namespace t3 {
     // FIXME: This should be rejected
     template<template<int...> class> struct A {};
-    // old-note at -1 {{previous non-type template parameter with type 'int' is here}}
-
     template<char> struct B;
-    // old-note at -1 {{template non-type parameter has a different type 'char' in template argument}}
-
     template struct A<B>;
-    // old-error at -1 {{has different template parameters}}
   } // namespace t3
   namespace t4 {
     template<template<char...> class> struct A {};
-    // old-note at -1 {{previous non-type template parameter with type 'char' is here}}
-
     template<int> struct B;
-    // old-note at -1 {{template non-type parameter has a different type 'int' in template argument}}
-
     template struct A<B>;
-    // old-error at -1 {{has different template parameters}}
   } // namespace t4
 } // namespace packs
 
 namespace fun_tmpl_call {
   namespace match_func {
     template <template <class> class TT> void f(TT<int>) {};
-    // old-note at -1 {{has different template parameters}}
     template <class...> struct A {};
     void test() { f(A<int>()); }
-    // old-error at -1 {{no matching function for call to 'f'}}
   } // namespace match_func
   namespace order_func_nonpack {
     template <template <class> class TT> void f(TT<int>) {}
@@ -423,18 +384,15 @@ namespace fun_tmpl_call {
   namespace order_func_pack {
     template <template <class> class TT> void f(TT<int>) = delete;
     template <template <class...> class TT> void f(TT<int>) {}
-
     template <class...> struct A {};
     void test() { f(A<int>()); }
   } // namespace order_func_pack
   namespace match_method {
     struct A {
       template <template <class> class TT> void f(TT<int>) {};
-      // old-note at -1 {{has different template parameters}}
     };
     template <class...> struct B {};
     void test() { A().f(B<int>()); }
-    // old-error at -1 {{no matching member function for call to 'f'}}
   } // namespace t2
   namespace order_method_nonpack {
     struct A {
@@ -455,12 +413,9 @@ namespace fun_tmpl_call {
   namespace match_conv {
     struct A {
       template <template <class> class TT> operator TT<int>() { return {}; }
-      // old-note at -1 {{different template parameters}}
     };
     template <class...> struct B {};
-    // old-note at -1 2{{not viable}}
     void test() { B<int> b = A(); }
-    // old-error at -1 {{no viable conversion from 'A' to 'B<int>'}}
   } // namespace match_conv
   namespace order_conv_nonpack {
     struct A {
@@ -524,21 +479,21 @@ namespace regression1 {
 
 namespace constraints {
   template <class T> concept C1 = true;
-  // new-note at -1 {{similar constraint expression here}}
-  // new-note at -2 2{{similar constraint expressions not considered equivalent}}
+  // expected-note at -1 {{similar constraint expression here}}
+  // expected-note at -2 2{{similar constraint expressions not considered equivalent}}
 
   template <class T> concept C2 = C1<T> && true;
-  // new-note at -1 2{{similar constraint expression here}}
+  // expected-note at -1 2{{similar constraint expression here}}
 
   template <class T> concept D1 = true;
-  // new-note at -1 {{similar constraint expressions not considered equivalent}}
+  // expected-note at -1 {{similar constraint expressions not considered equivalent}}
 
   namespace t1 {
-    template<template<C1, class... T1s> class TT1> // new-note {{TT1' declared here}}
+    template<template<C1, class... T1s> class TT1> // expected-note {{TT1' declared here}}
     struct A {};
-    template<D1, class T2> struct B {}; // new-note {{'B' declared here}}
+    template<D1, class T2> struct B {}; // expected-note {{'B' declared here}}
     template struct A<B>;
-    // new-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
+    // expected-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
   } // namespace t1
   namespace t2 {
     template<template<C2, class... T1s> class TT1> struct A {};
@@ -546,34 +501,34 @@ namespace constraints {
     template struct A<B>;
   } // namespace t2
   namespace t3 {
-    template<template<C1, class... T1s> class TT1> // new-note {{'TT1' declared here}}
+    template<template<C1, class... T1s> class TT1> // expected-note {{'TT1' declared here}}
     struct A {};
-    template<C2, class T2> struct B {}; // new-note {{'B' declared here}}
+    template<C2, class T2> struct B {}; // expected-note {{'B' declared here}}
     template struct A<B>;
-    // new-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
+    // expected-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
   } // namespace t2
   namespace t4 {
     // FIXME: This should be accepted.
-    template<template<C1... T1s> class TT1> // new-note {{'TT1' declared here}}
+    template<template<C1... T1s> class TT1> // expected-note {{'TT1' declared here}}
     struct A {};
-    template<C1 T2> struct B {}; // new-note {{'B' declared here}}
+    template<C1 T2> struct B {}; // expected-note {{'B' declared here}}
     template struct A<B>;
-    // new-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
+    // expected-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
   } // namespace t4
   namespace t5 {
     // FIXME: This should be accepted
-    template<template<C2... T1s> class TT1> // new-note {{'TT1' declared here}}
+    template<template<C2... T1s> class TT1> // expected-note {{'TT1' declared here}}
     struct A {};
-    template<C1 T2> struct B {}; // new-note {{'B' declared here}}
+    template<C1 T2> struct B {}; // expected-note {{'B' declared here}}
     template struct A<B>;
-    // new-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
+    // expected-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
   } // namespace t5
   namespace t6 {
-    template<template<C1... T1s> class TT1> // new-note {{'TT1' declared here}}
+    template<template<C1... T1s> class TT1> // expected-note {{'TT1' declared here}}
     struct A {};
-    template<C2 T2> struct B {}; // new-note {{'B' declared here}}
+    template<C2 T2> struct B {}; // expected-note {{'B' declared here}}
     template struct A<B>;
-    // new-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
+    // expected-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
   } // namespace t6
   namespace t7 {
     template<template<class... T1s> class TT1>
@@ -588,19 +543,19 @@ namespace constraints {
     template struct A<B>;
   } // namespace t8
   namespace t9 {
-    template<template<C1... T1s> class TT1> // new-note {{'TT1' declared here}}
+    template<template<C1... T1s> class TT1> // expected-note {{'TT1' declared here}}
     struct A {};
-    template<D1 T2> struct B {}; // new-note {{'B' declared here}}
+    template<D1 T2> struct B {}; // expected-note {{'B' declared here}}
     template struct A<B>;
-    // new-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
+    // expected-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
   } // namespace t9
   namespace t10 {
-    template<template<class...> requires C1<int> class TT1> // new-note {{'TT1' declared here}}
+    template<template<class...> requires C1<int> class TT1> // expected-note {{'TT1' declared here}}
     struct A {};
 
-    template<class> requires C2<int> struct B {}; // new-note {{'B' declared here}}
+    template<class> requires C2<int> struct B {}; // expected-note {{'B' declared here}}
     template struct A<B>;
-    // new-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
+    // expected-error at -1 {{'B' is more constrained than template template parameter 'TT1'}}
   } // namespace t10
   namespace t11 {
     template<template<class...> requires C2<int> class TT1> struct A {};
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index d59cbbbbec1b5b..ea758aaee4e9c4 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -1178,8 +1178,8 @@ <h2 id="cxx17">C++17 implementation status</h2>
 <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.
+In Clang 19, the flag was deprecated.
+In Clang 20, the flag was removed altogether.
 </span>
 </p>
 </details>



More information about the cfe-commits mailing list