[clang] [Clang] Fix template template param matching with constant template params of reference type. (PR #227290)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 29 05:29:05 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Corentin Jabot (cor3ntin)

<details>
<summary>Changes</summary>

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

---
Full diff: https://github.com/llvm/llvm-project/pull/227290.diff


3 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+7) 
- (modified) clang/lib/AST/ASTContext.cpp (+6-4) 
- (modified) clang/test/SemaTemplate/temp_arg_template_p0522.cpp (+47) 


``````````diff
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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -2 {{different template parameters}}
   template <class> struct DependentAutoPtr {
     template <auto *> struct MemberAutoPtr;
     using Auto_MemberAutoPtr = TakesAuto<MemberAutoPtr>;

``````````

</details>


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


More information about the cfe-commits mailing list