[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