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

In #223645, I removed a fixme I thought was no longer necessary. But it was, 
our test coverage is just patchy.

This just reintroduces it to fix the regression,
I have not investigated whether there was a better solution.

I got the AI to write as much tests as i could think of, however this revealed 
regressions introduced in an earlier version of clang, this weill be fixed 
separately.

Reported in
https://github.com/llvm/llvm-project/pull/223645#issuecomment-5868853308

Assisted-By: Opus 5.5

>From 36eeafc460c395e13cf7d8598e1bbb62e59dd91b 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 a regression introduced by #223645.

In #223645, I removed a fixme I thought was no longer
necessary. But it was, our test coverage is just patchy.

This just reintroduces it to fix the regression,
I have not investigated whether there was a better solution.

I got the AI to write as much tests as i could think of,
however this revealed regressions introduced in an earlier version of clang,
this weill be fixed separately.

Reported in
https://github.com/llvm/llvm-project/pull/223645#issuecomment-5868853308

Assisted-By: Opus 5.5
---
 clang/lib/Sema/SemaTemplateDeduction.cpp      |  10 ++
 .../SemaTemplate/temp_arg_nontype_ref.cpp     |  24 ++++
 .../SemaTemplate/temp_arg_template_p0522.cpp  | 118 ++++++++++++++++++
 3 files changed, 152 insertions(+)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 653240092e64a..2257964a4310b 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -497,6 +497,16 @@ DeduceNonTypeTemplateArgument(Sema &S, 
TemplateParameterList *TemplateParams,
   if (auto *Expansion = dyn_cast<PackExpansionType>(ParamType))
     ParamType = Expansion->getPattern();
 
+  // FIXME: It's not clear how deduction of a parameter of reference type from
+  // an argument should be performed. For now, we just make the argument have
+  // the same kind of reference type as the parameter.
+  if (ParamType->isReferenceType()) {
+    ValueType = ValueType.getNonReferenceType();
+    ValueType = ParamType->isRValueReferenceType()
+                    ? S.Context.getRValueReferenceType(ValueType)
+                    : S.Context.getLValueReferenceType(ValueType);
+  }
+
   return DeduceTemplateArgumentsByTypeMatch(
       S, TemplateParams, ParamType, ValueType, Info, Deduced,
       TDF_SkipNonDependent | TDF_IgnoreQualifiers,
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_ref.cpp 
b/clang/test/SemaTemplate/temp_arg_nontype_ref.cpp
index 2020f564f68fc..c433088519add 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_ref.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_ref.cpp
@@ -45,3 +45,27 @@ namespace GH40328 {
   template <bool &v> int *f(A<bool &, v>);
   int *p = f(A<bool &, b>());
 } // namespace GH40328
+
+#if __cplusplus >= 201703L
+// Partial specializations of templates whose constant template parameter is
+// declared as `auto &&`.
+namespace auto_ref_primary {
+  int i;
+  long l;
+
+  template <auto &&> struct A { static const int k = 0; };
+  template <int &v> struct A<v> { static const int k = 1; };
+  static_assert(A<i>::k == 1, "");
+  static_assert(A<l>::k == 0, "");
+
+  template <auto &&, class> struct B { static const int k = 0; };
+  template <auto &v> struct B<v, int> { static const int k = 1; };
+  static_assert(B<i, int>::k == 1, "");
+  static_assert(B<i, long>::k == 0, "");
+
+  template <auto &&> const int V = 0;
+  template <int &v> const int V<v> = 1;
+  static_assert(V<i> == 1, "");
+  static_assert(V<l> == 0, "");
+} // namespace auto_ref_primary
+#endif
diff --git a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp 
b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
index bde811c3bf685..ef1108557a37b 100644
--- a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
@@ -175,3 +175,121 @@ namespace GH181166 {
   template <class ...Ts> struct B {};
   using T = decltype(f<B>());
 } // namespace GH181166
+
+// Matching of template template arguments whose constant parameters have
+// reference type. The argument value is an expression, so its type drops the
+// top-level reference (`auto &` is seen as `auto`).
+//
+// Names spell out the types of constant template parameters: `TakesFoo` has a
+// template template parameter P whose parameter has type `Foo`, and `Foo_Bar`
+// matches P against a template template argument A whose parameter has type
+// `Bar`. A is a template template parameter, a class template `BarClass`, or a
+// member template `MemberBar`.
+namespace nttp_ref {
+  template <template <auto &> class> struct TakesAutoRef; // #TakesAutoRef
+  template <template <auto &> class TT> using AutoRef_AutoRef = 
TakesAutoRef<TT>;
+  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 TakesAuto; // #TakesAuto
+  template <template <auto &> class TT> using Auto_AutoRef = TakesAuto<TT>;
+  template <template <auto &&> class TT> using Auto_AutoRRef = TakesAuto<TT>;
+  template <template <const auto &> class TT> using Auto_ConstAutoRef = 
TakesAuto<TT>;
+
+  template <template <const auto &> class> struct TakesConstAutoRef; // 
#TakesConstAutoRef
+  template <template <const auto &> class TT> using ConstAutoRef_ConstAutoRef 
= TakesConstAutoRef<TT>;
+  template <template <auto &> class TT> using ConstAutoRef_AutoRef = 
TakesConstAutoRef<TT>;
+  template <template <auto> class TT> using ConstAutoRef_Auto = 
TakesConstAutoRef<TT>;
+
+  template <template <auto &...> class> struct TakesAutoRefPack;
+  template <template <auto &...> class TT> using AutoRefPack_AutoRefPack = 
TakesAutoRefPack<TT>;
+
+  template <template <decltype(auto)> class> struct TakesDecltypeAuto; // 
#TakesDecltypeAuto
+  template <template <decltype(auto)> class TT> using 
DecltypeAuto_DecltypeAuto = TakesDecltypeAuto<TT>;
+
+  // A class template argument has its parameters at the same depth as those
+  // of the template template parameter.
+  template <auto &> struct AutoRefClass;
+  template <auto &&> struct AutoRRefClass;
+  template <auto> struct AutoClass;
+  using AutoRef_AutoRefClass = TakesAutoRef<AutoRefClass>;
+  using AutoRef_AutoRRefClass = TakesAutoRef<AutoRRefClass>;
+  using Auto_AutoRefClass = TakesAuto<AutoRefClass>;
+  using AutoRef_AutoClass = TakesAutoRef<AutoClass>;
+
+  template <class U, template <U &> class> struct TakesDependentRef;
+  template <class U, template <U &> class TT> using DependentRef_DependentRef 
= TakesDependentRef<U, TT>;
+
+  template <class U> struct Outer {
+    template <template <U &> class> struct TakesOuterRef;
+    template <template <U &> class TT> using OuterRef_OuterRef = 
TakesOuterRef<TT>;
+  };
+
+  template <template <class T, T &> class> struct TakesTypeAndRef;
+  template <template <class T, T &> class TT> using TypeAndRef_TypeAndRef = 
TakesTypeAndRef<TT>;
+  template <template <class T, T> class TT> using TypeAndRef_TypeAndValue = 
TakesTypeAndRef<TT>;
+  template <class T, T &> struct TypeAndRefClass;
+  using TypeAndRef_TypeAndRefClass = TakesTypeAndRef<TypeAndRefClass>;
+
+  // 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 &'}}
+  template <template <int &> class TT> using Int_IntRef = TakesInt<TT>;
+  // expected-note@-1 {{different template parameters}}
+  template <template <const int &> class TT> using Int_ConstIntRef = 
TakesInt<TT>;
+  // expected-error@#TakesInt {{conversion from 'int' to 'const int &' in 
converted constant expression would bind reference to a temporary}}
+  // expected-note@-2 {{different template parameters}}
+  template <template <auto &> class TT> using Int_AutoRef = TakesInt<TT>;
+  // expected-error@#TakesInt {{value of type 'int' is not implicitly 
convertible to 'int &'}}
+  // expected-note@-2 {{different template parameters}}
+  using Int_AutoRefClass = TakesInt<AutoRefClass>;
+  // expected-error@#TakesInt {{value of type 'int' is not implicitly 
convertible to 'int &'}}
+  // expected-note@-2 {{different template parameters}}
+  template <template <auto &&> class TT> using Int_AutoRRef = TakesInt<TT>;
+  // expected-error@#TakesInt {{non-type template parameter has rvalue 
reference type 'int &&'}}
+  // expected-note@-2 {{different template parameters}}
+  template <template <const auto &> class TT> using Int_ConstAutoRef = 
TakesInt<TT>;
+  // expected-error@#TakesInt {{conversion from 'int' to 'const int &' in 
converted constant expression would bind reference to a temporary}}
+  // expected-note@-2 {{different template parameters}}
+
+  template <template <int...> class> struct TakesIntPack; // #TakesIntPack
+  template <template <auto &...> class TT> using IntPack_AutoRefPack = 
TakesIntPack<TT>;
+  // expected-error@#TakesIntPack {{value of type 'int' is not implicitly 
convertible to 'int &'}}
+  // expected-note@-2 {{different template parameters}}
+
+  template <template <const int &> class> struct TakesConstIntRef; // 
#TakesConstIntRef
+  template <template <int &> class TT> using ConstIntRef_IntRef = 
TakesConstIntRef<TT>;
+  // expected-error@#TakesConstIntRef {{value of type 'const int' is not 
implicitly convertible to 'int &'}}
+  // expected-note@-2 {{different template parameters}}
+
+  template <template <int *> class> struct TakesIntPtr; // #TakesIntPtr
+  template <template <int> class TT> using IntPtr_Int = TakesIntPtr<TT>;
+  // expected-error@#TakesIntPtr {{value of type 'int *' is not implicitly 
convertible to 'int'}}
+  // expected-note@-2 {{different template parameters}}
+  template <template <auto &> class TT> using IntPtr_AutoRef = TakesIntPtr<TT>;
+  // expected-error@#TakesIntPtr {{value of type 'int *' is not implicitly 
convertible to 'int *&'}}
+  // 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'}}
+  // expected-note@-2 {{different template parameters}}
+  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 Auto_AutoPtr = TakesAuto<TT>;
+  // expected-error@#TakesAuto {{with type 'auto *' has incompatible 
initializer of type 'auto'}}
+  // expected-note@-2 {{different template parameters}}
+  template <template <auto *> class TT> using ConstAutoRef_AutoPtr = 
TakesConstAutoRef<TT>;
+  // expected-error@#TakesConstAutoRef {{with type 'auto *' has incompatible 
initializer of type 'const auto'}}
+  // expected-note@-2 {{different template parameters}}
+  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 <class> struct DependentAutoPtr {
+    template <auto *> struct MemberAutoPtr;
+    using Auto_MemberAutoPtr = TakesAuto<MemberAutoPtr>;
+    // expected-error@#TakesAuto {{with type 'auto *' has incompatible 
initializer of type 'auto'}}
+    // expected-note@-2 {{different template parameters}}
+  };
+} // namespace nttp_ref

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

Reply via email to