https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/223645
>From 7be5dfb1f7e0a6fa2a8f62bde77bae02c55961f1 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <[email protected]> Date: Tue, 15 Sep 2026 12:12:25 +0200 Subject: [PATCH 1/2] [Clang] Fix deduction from constant TP of reference type. We were not implementing https://eel.is/c++draft/temp.deduct.type#13 properly. Fixes #40328 Assisted-By: Opus 5 --- clang/docs/ReleaseNotes.md | 3 ++ clang/lib/Sema/SemaTemplateDeduction.cpp | 30 ++++++------ .../SemaTemplate/temp_arg_nontype_cxx1z.cpp | 13 +++++ .../SemaTemplate/temp_arg_nontype_ref.cpp | 47 +++++++++++++++++++ 4 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 clang/test/SemaTemplate/temp_arg_nontype_ref.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 043a0ddae2a6cd..b5dfb5930c3f4b 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -676,6 +676,9 @@ features cannot lower the translation-unit ABI level; class with an invalid non-static data member, such as one qualified with an address space. (#GH194605) +- Fixed deduction of the template parameters appearing in the type of a + constant template parameter of reference type. (#GH40328) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index b66152f2d971d5..795d199ee52243 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -261,6 +261,20 @@ getDeducedNTTParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) { return getDeducedNTTParameterFromExpr(E, Info.getDeducedDepth()); } +/// C++26 [temp.deduct.type]p13: +/// When the value of the argument corresponding to a constant template +/// parameter P that is declared with a dependent type is deduced from an +/// expression, the template parameters in the type of P are deduced from the +/// type of the value. +static QualType getTypeOfTemplateArgumentValue(TemplateDeductionInfo &Info, + const TemplateArgument &A) { + const Expr *E = unwrapExpressionForDeduction(A.getAsExpr()); + if (NonTypeOrVarTemplateParmDecl NTTP = + getDeducedNTTParameterFromExpr(E, Info.getDeducedDepth())) + return NTTP.getType(); + return E->getType(); +} + /// Determine whether two declaration pointers refer to the same /// declaration. static bool isSameDeclaration(Decl *X, Decl *Y) { @@ -497,17 +511,6 @@ 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 (of non-reference type) should be performed. - // For now, we just make the argument have same reference type as the - // parameter. - if (ParamType->isReferenceType() && !ValueType->isReferenceType()) { - if (ParamType->isRValueReferenceType()) - ValueType = S.Context.getRValueReferenceType(ValueType); - else - ValueType = S.Context.getLValueReferenceType(ValueType); - } - return DeduceTemplateArgumentsByTypeMatch( S, TemplateParams, ParamType, ValueType, Info, Deduced, TDF_SkipNonDependent | TDF_IgnoreQualifiers, @@ -2648,11 +2651,10 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, getDeducedNTTParameterFromExpr(Info, P.getAsExpr())) { switch (A.getKind()) { case TemplateArgument::Expression: { - // The type of the value is the type of the expression as written. return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(A), - A.getAsExpr()->IgnoreImplicitAsWritten()->getType(), Info, - PartialOrdering, Deduced, HasDeducedAnyParam); + getTypeOfTemplateArgumentValue(Info, A), Info, PartialOrdering, + Deduced, HasDeducedAnyParam); } case TemplateArgument::Integral: case TemplateArgument::StructuralValue: diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp index 5077d5ff8ad892..a39bb02084aa7c 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -648,3 +648,16 @@ namespace GH58682 { template <decltype(auto) v> struct B<A<v>> { static constexpr int k = 1; }; static_assert(B<A<(g)>>::k == 1, ""); } // namespace GH58682 + +// C++26 [temp.deduct.type]p13, Example 8. +namespace temp_deduct_type_p13 { + template<long n> struct A { }; + + template<typename T> struct C; + template<typename T, T n> struct C<A<n>> { + using Q = T; + }; + + using R = long; + using R = C<A<2>>::Q; +} // namespace temp_deduct_type_p13 diff --git a/clang/test/SemaTemplate/temp_arg_nontype_ref.cpp b/clang/test/SemaTemplate/temp_arg_nontype_ref.cpp new file mode 100644 index 00000000000000..2020f564f68fca --- /dev/null +++ b/clang/test/SemaTemplate/temp_arg_nontype_ref.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s + +// expected-no-diagnostics + +namespace GH40328 { + template <typename T, T v> struct X {}; + template <bool &v> struct X<bool &, v> {}; + + template <typename T, T v> struct A { static const int k = 0; }; + template <bool &v> struct A<bool &, v> { static const int k = 1; }; + template <const int &v> struct A<const int &, v> { static const int k = 2; }; + template <int (&v)[3]> struct A<int (&)[3], v> { static const int k = 3; }; + template <void (&v)()> struct A<void (&)(), v> { static const int k = 4; }; + template <int *v> struct A<int *, v> { static const int k = 5; }; + + bool b; + extern const int ci; + const int ci = 0; + int arr[3]; + void fn(); + int n; + + static_assert(A<bool, true>::k == 0, ""); + static_assert(A<bool &, b>::k == 1, ""); + static_assert(A<const int &, ci>::k == 2, ""); + static_assert(A<int (&)[3], arr>::k == 3, ""); + static_assert(A<void (&)(), fn>::k == 4, ""); + static_assert(A<int *, &n>::k == 5, ""); + + template <typename T, T... v> struct P { static const int k = 0; }; + template <bool &...v> struct P<bool &, v...> { static const int k = 1; }; + static_assert(P<bool &, b, b>::k == 1, ""); + +#if __cplusplus >= 201402L + template <typename T, T v> const int V = 0; + template <bool &v> const int V<bool &, v> = 1; + static_assert(V<bool &, b> == 1, ""); +#endif + + template <typename T, T v> int f(A<T, v>); + template <bool &v> int *f(A<bool &, v>); + int *p = f(A<bool &, b>()); +} // namespace GH40328 >From a89045ae985ff432e079ec203e6ebcdc8d75da9d Mon Sep 17 00:00:00 2001 From: Corentin Jabot <[email protected]> Date: Thu, 17 Sep 2026 12:08:54 +0200 Subject: [PATCH 2/2] cleanups --- clang/lib/Sema/SemaTemplateDeduction.cpp | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 795d199ee52243..653240092e64a7 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -261,20 +261,6 @@ getDeducedNTTParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) { return getDeducedNTTParameterFromExpr(E, Info.getDeducedDepth()); } -/// C++26 [temp.deduct.type]p13: -/// When the value of the argument corresponding to a constant template -/// parameter P that is declared with a dependent type is deduced from an -/// expression, the template parameters in the type of P are deduced from the -/// type of the value. -static QualType getTypeOfTemplateArgumentValue(TemplateDeductionInfo &Info, - const TemplateArgument &A) { - const Expr *E = unwrapExpressionForDeduction(A.getAsExpr()); - if (NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(E, Info.getDeducedDepth())) - return NTTP.getType(); - return E->getType(); -} - /// Determine whether two declaration pointers refer to the same /// declaration. static bool isSameDeclaration(Decl *X, Decl *Y) { @@ -2567,6 +2553,20 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( llvm_unreachable("Invalid Type Class!"); } +/// C++26 [temp.deduct.type]p13: +/// When the value of the argument corresponding to a constant template +/// parameter P that is declared with a dependent type is deduced from an +/// expression, the template parameters in the type of P are deduced from the +/// type of the value. +static QualType getTypeOfTemplateArgumentValue(TemplateDeductionInfo &Info, + const TemplateArgument &A) { + const Expr *E = A.getAsExpr(); + if (NonTypeOrVarTemplateParmDecl NTTP = + getDeducedNTTParameterFromExpr(E, Info.getDeducedDepth())) + return NTTP.getType(); + return unwrapExpressionForDeduction(E)->getType(); +} + static TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, const TemplateArgument &P, TemplateArgument A, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
