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
