https://github.com/cor3ntin created 
https://github.com/llvm/llvm-project/pull/227290

When matching a template template argument, we synthesized an argument for each 
constant template parameters in a way that was not consistent with 
Sema::BuildDeclarationNameExpr, causing an assertion failure in ClassifyExpr.

This fixes a regression introduced by #124137

Assisted-By: Opus 5.5

>From 396e627d76f27f14958844fbfcb299737b1dc8aa Mon Sep 17 00:00:00 2001
From: Corentin Jabot <[email protected]>
Date: Mon, 28 Sep 2026 19:59:40 +0200
Subject: [PATCH] [Clang] Fix TTP matching with constant TPs of reference type.

When matching a template template argument, we synthesized an argument
for each constant template parameters in a way that was not consistent with
Sema::BuildDeclarationNameExpr, causing an assertion failure in ClassifyExpr.

This fixes a regression introduced by #124137

Assisted-By: Opus 5.5
---
 clang/docs/ReleaseNotes.md                    |  7 +++
 clang/lib/AST/ASTContext.cpp                  | 10 ++--
 .../SemaTemplate/temp_arg_template_p0522.cpp  | 47 +++++++++++++++++++
 3 files changed, 60 insertions(+), 4 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 49d669db5749c..c4ec021c535d5 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -756,6 +756,13 @@ features cannot lower the translation-unit ABI level;
 - Fixed deduction of the template parameters appearing in the type of a
   constant template parameter of reference type. (#GH40328)
 
+- Template template parameters of the form `template <X &> class` no longer 
reject
+  template template arguments with the same parameters.
+
+- Fixed an assertion when matching a template template argument against a
+  template template parameter whose constant template parameter is declared
+  as `auto &&`.
+
 - Fixed an issue where an explicit specialization of a constexpr variable would
   result in a link error. (#GH219796)
 
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a361b07e201b3..9eb2f346cfc93 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6308,15 +6308,17 @@ TemplateArgument 
ASTContext::getInjectedTemplateArg(NamedDecl *Param) const {
     // of a real template argument.
     // FIXME: It would be more faithful to model this as something like an
     // lvalue-to-rvalue conversion applied to a const-qualified lvalue.
-    ExprValueKind VK;
-    if (T->isRecordType()) {
+    ExprValueKind VK = VK_PRValue;
+    if (NTTP->getType()->isReferenceType()) {
+      // As in Sema::BuildDeclarationNameExpr, a parameter of reference
+      // type, even an rvalue reference, is an lvalue.
+      VK = VK_LValue;
+    } else if (T->isRecordType()) {
       // C++ [temp.param]p8: An id-expression naming a non-type
       // template-parameter of class type T denotes a static storage duration
       // object of type const T.
       T.addConst();
       VK = VK_LValue;
-    } else {
-      VK = Expr::getValueKindForType(NTTP->getType());
     }
     Expr *E = new (*this)
         DeclRefExpr(*this, NTTP, /*RefersToEnclosingVariableOrCapture=*/false,
diff --git a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp 
b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
index ef1108557a37b..56ba4fdf931de 100644
--- a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
@@ -191,6 +191,11 @@ namespace nttp_ref {
   template <template <auto> class TT> using AutoRef_Auto = TakesAutoRef<TT>;
   template <template <auto &&> class TT> using AutoRef_AutoRRef = 
TakesAutoRef<TT>;
 
+  template <template <auto &&> class> struct TakesAutoRRef; // #TakesAutoRRef
+  template <template <auto &&> class TT> using AutoRRef_AutoRRef = 
TakesAutoRRef<TT>;
+  template <template <auto> class TT> using AutoRRef_Auto = TakesAutoRRef<TT>;
+  template <template <auto &> class TT> using AutoRRef_AutoRef = 
TakesAutoRRef<TT>;
+
   template <template <auto> class> struct TakesAuto; // #TakesAuto
   template <template <auto &> class TT> using Auto_AutoRef = TakesAuto<TT>;
   template <template <auto &&> class TT> using Auto_AutoRRef = TakesAuto<TT>;
@@ -214,9 +219,16 @@ namespace nttp_ref {
   template <auto> struct AutoClass;
   using AutoRef_AutoRefClass = TakesAutoRef<AutoRefClass>;
   using AutoRef_AutoRRefClass = TakesAutoRef<AutoRRefClass>;
+  using AutoRRef_AutoRefClass = TakesAutoRRef<AutoRefClass>;
   using Auto_AutoRefClass = TakesAuto<AutoRefClass>;
   using AutoRef_AutoClass = TakesAutoRef<AutoClass>;
 
+  // So does a member template in a dependent context.
+  template <class> struct DependentAuto {
+    template <auto> struct MemberAuto;
+    using AutoRRef_MemberAuto = TakesAutoRRef<MemberAuto>;
+  };
+
   template <class U, template <U &> class> struct TakesDependentRef;
   template <class U, template <U &> class TT> using DependentRef_DependentRef 
= TakesDependentRef<U, TT>;
 
@@ -231,6 +243,17 @@ namespace nttp_ref {
   template <class T, T &> struct TypeAndRefClass;
   using TypeAndRef_TypeAndRefClass = TakesTypeAndRef<TypeAndRefClass>;
 
+  // Naming a parameter of reference type gives an lvalue of the referenced
+  // type, even for a class type.
+  struct X {};
+  template <template <X &> class> struct TakesXRef; // #TakesXRef
+  template <template <X &> class TT> using XRef_XRef = TakesXRef<TT>;
+  template <template <decltype(auto)> class TT> using XRef_DecltypeAuto = 
TakesXRef<TT>;
+  template <X &> struct XRefClass;
+  using XRef_XRefClass = TakesXRef<XRefClass>;
+  template <template <X &...> class> struct TakesXRefPack;
+  template <template <X &...> class TT> using XRefPack_XRefPack = 
TakesXRefPack<TT>;
+
   // The argument for P's parameter must be a valid argument for A's parameter.
   template <template <int> class> struct TakesInt; // #TakesInt
   // expected-error@-1 {{value of type 'int' is not implicitly convertible to 
'int &'}}
@@ -270,6 +293,24 @@ namespace nttp_ref {
   // expected-error@#TakesIntPtr {{value of type 'int *' is not implicitly 
convertible to 'int *&'}}
   // expected-note@-2 {{different template parameters}}
 
+  template <template <int &> class TT> using XRef_IntRef = TakesXRef<TT>;
+  // expected-error@#TakesXRef {{value of type 'X' is not implicitly 
convertible to 'int &'}}
+  // expected-note@-2 {{different template parameters}}
+
+  // A template parameter object is const.
+  template <template <X> class> struct TakesX; // #TakesX
+  template <template <X &> class TT> using X_XRef = TakesX<TT>;
+  // expected-error@#TakesX {{value of type 'const X' is not implicitly 
convertible to 'X &'}}
+  // expected-note@-2 {{different template parameters}}
+  using X_XRefClass = TakesX<XRefClass>;
+  // expected-error@#TakesX {{value of type 'const X' is not implicitly 
convertible to 'X &'}}
+  // expected-note@-2 {{different template parameters}}
+
+  template <template <const X &> class> struct TakesConstXRef; // 
#TakesConstXRef
+  template <template <X &> class TT> using ConstXRef_XRef = TakesConstXRef<TT>;
+  // expected-error@#TakesConstXRef {{value of type 'const X' is not 
implicitly convertible to 'X &'}}
+  // expected-note@-2 {{different template parameters}}
+
   // Only a pointer can initialize a parameter of type `auto *`.
   template <template <auto *> class TT> using Int_AutoPtr = TakesInt<TT>;
   // expected-error@#TakesInt {{with type 'auto *' has incompatible 
initializer of type 'int'}}
@@ -277,6 +318,9 @@ namespace nttp_ref {
   template <template <auto *> class TT> using AutoRef_AutoPtr = 
TakesAutoRef<TT>;
   // expected-error@#TakesAutoRef {{with type 'auto *' has incompatible 
initializer of type 'auto'}}
   // expected-note@-2 {{different template parameters}}
+  template <template <auto *> class TT> using AutoRRef_AutoPtr = 
TakesAutoRRef<TT>;
+  // expected-error@#TakesAutoRRef {{with type 'auto *' has incompatible 
initializer of type 'auto'}}
+  // expected-note@-2 {{different template parameters}}
   template <template <auto *> class TT> using Auto_AutoPtr = TakesAuto<TT>;
   // expected-error@#TakesAuto {{with type 'auto *' has incompatible 
initializer of type 'auto'}}
   // expected-note@-2 {{different template parameters}}
@@ -286,6 +330,9 @@ namespace nttp_ref {
   template <template <auto *> class TT> using DecltypeAuto_AutoPtr = 
TakesDecltypeAuto<TT>;
   // expected-error@#TakesDecltypeAuto {{with type 'auto *' has incompatible 
initializer of type 'decltype(auto)'}}
   // expected-note@-2 {{different template parameters}}
+  template <template <auto *> class TT> using XRef_AutoPtr = TakesXRef<TT>;
+  // expected-error@#TakesXRef {{with type 'auto *' has incompatible 
initializer of type 'X'}}
+  // expected-note@-2 {{different template parameters}}
   template <class> struct DependentAutoPtr {
     template <auto *> struct MemberAutoPtr;
     using Auto_MemberAutoPtr = TakesAuto<MemberAutoPtr>;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to