[clang] c6a561d - [Clang] Fix a regression introduced by #223645. (#227085)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 29 04:53:52 PDT 2026


Author: Corentin Jabot
Date: 2026-09-29T13:53:46+02:00
New Revision: c6a561d18f5d0e5af25200d18a2653b9f18c8e87

URL: https://github.com/llvm/llvm-project/commit/c6a561d18f5d0e5af25200d18a2653b9f18c8e87
DIFF: https://github.com/llvm/llvm-project/commit/c6a561d18f5d0e5af25200d18a2653b9f18c8e87.diff

LOG: [Clang] Fix a regression introduced by #223645. (#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

Added: 
    

Modified: 
    clang/lib/Sema/SemaTemplateDeduction.cpp
    clang/test/SemaTemplate/temp_arg_nontype_ref.cpp
    clang/test/SemaTemplate/temp_arg_template_p0522.cpp

Removed: 
    


################################################################################
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 at -1 {{value of type 'int' is not implicitly convertible to 'int &'}}
+  template <template <int &> class TT> using Int_IntRef = TakesInt<TT>;
+  // expected-note at -1 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent template parameters}}
+  using Int_AutoRefClass = TakesInt<AutoRefClass>;
+  // expected-error@#TakesInt {{value of type 'int' is not implicitly convertible to 'int &'}}
+  // expected-note at -2 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent 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 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent 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 at -2 {{
diff erent template parameters}}
+  };
+} // namespace nttp_ref


        


More information about the cfe-commits mailing list