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
