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 043a0ddae2a6c..b5dfb5930c3f4 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 b66152f2d971d..795d199ee5224 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 5077d5ff8ad89..a39bb02084aa7 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 0000000000000..2020f564f68fc --- /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 730bbdec0c794a1aefc15a27cc0c3ebd4563dae1 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <[email protected]> Date: Tue, 15 Sep 2026 20:53:10 +0200 Subject: [PATCH 2/2] cwg 2900 (wip) --- clang/docs/ReleaseNotes.md | 4 ++ clang/include/clang/Sema/Sema.h | 6 ++ clang/lib/Sema/SemaTemplate.cpp | 19 ++++++ clang/lib/Sema/SemaTemplateDeduction.cpp | 63 ++++++++++--------- clang/test/CXX/drs/cwg29xx.cpp | 32 ++++++++++ .../temp_arg_nontype_arg_type.cpp | 63 +++++++++++++++++++ clang/www/cxx_dr_status.html | 2 +- 7 files changed, 160 insertions(+), 29 deletions(-) create mode 100644 clang/test/SemaTemplate/temp_arg_nontype_arg_type.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index b5dfb5930c3f4..7777a2a103cac 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -183,6 +183,10 @@ features cannot lower the translation-unit ABI level; them to an enumeration type with a fixed `bool` underlying type. This resolves [CWG1094](https://wg21.link/cwg1094). +- Implemented [CWG2900](https://wg21.link/cwg2900), removing a spurious + ambiguity when partial ordering constant template parameters declared + with placeholder types. + ### C Language Changes #### C2y Feature Support diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d32eb3600ce54..8c0f9bbb2f17e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12955,6 +12955,12 @@ class Sema final : public SemaBase { const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, bool PartialOrdering, bool *StrictPackMatch); + /// Determine the declared type of the constant template parameter that \p A + /// names, if any. + static QualType + getTypeOfConstantTemplateParameter(const TemplateArgument &A, + UnsignedOrNone Depth = std::nullopt); + /// Mark which template parameters are used in a given expression. /// /// \param E the expression from which template parameters will be deduced. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index b8b0c71894daa..507ea8693e112 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5544,6 +5544,14 @@ convertTypeTemplateArgumentToTemplate(ASTContext &Context, TypeLoc TLoc) { TagLoc.getQualifierLoc(), TagLoc.getNameLoc()); } +/// Determine whether \p T is an undeduced 'decltype(auto)', or an undeduced +/// placeholder of the form 'type-constraint_opt auto'. +static bool isUndeducedAuto(QualType T, bool DecltypeAuto) { + const AutoType *AT = T->getContainedAutoType(); + return AT && AT->isDecltypeAuto() == DecltypeAuto && + AT->getDeducedType().isNull(); +} + bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, NamedDecl *Template, SourceLocation TemplateLoc, @@ -5566,6 +5574,17 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, if (NTTP->isParameterPack() && NTTP->isExpandedParameterPack()) NTTPType = NTTP->getExpansionType(ArgumentPackIndex); + // C++26 [dcl.type.auto.deduct]p3: + // If the placeholder-type-specifier is of the form type-constraint_opt + // auto, [...] If E is a value synthesized for a constant template + // parameter of type decltype(auto) ([temp.func.order]), the declaration + // is ill-formed. + if (CTAI.PartialOrdering && + isUndeducedAuto(NTTPType, /*DecltypeAuto=*/false) && + isUndeducedAuto(getTypeOfConstantTemplateParameter(Arg), + /*DecltypeAuto=*/true)) + return true; + if (NTTPType->isInstantiationDependentType()) { // Do substitution on the type of the non-type template parameter. InstantiatingTemplate Inst(*this, TemplateLoc, Template, NTTP, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 795d199ee5224..2247fba693a23 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -234,31 +234,39 @@ class NonTypeOrVarTemplateParmDecl { const NamedDecl *Template; }; -/// If the given expression is of a form that permits the deduction -/// of a non-type template parameter, return the declaration of that -/// non-type template parameter. +/// If the given expression is of a form that names a non-type template +/// parameter, return the declaration of that parameter. A null \p Depth +/// accepts a parameter at any depth. static NonTypeOrVarTemplateParmDecl -getDeducedNTTParameterFromExpr(const Expr *E, unsigned Depth) { +getNTTParameterFromExpr(const Expr *E, UnsignedOrNone Depth) { // If we are within an alias template, the expression may have undergone // any number of parameter substitutions already. E = unwrapExpressionForDeduction(E); if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl())) - if (NTTP->getDepth() == Depth) + if (!Depth || NTTP->getDepth() == *Depth) return NTTP; // A pack-index-template-name is not deducible. if (const auto *DTI = dyn_cast<DependentTemplateIdExpr>(E)) if (!DTI->getTemplateName().getAsPackIndexingTemplate() && - DTI->getParameter()->getDepth() == Depth) + (!Depth || DTI->getParameter()->getDepth() == *Depth)) return DTI->getParameter(); return nullptr; } +QualType Sema::getTypeOfConstantTemplateParameter(const TemplateArgument &A, + UnsignedOrNone Depth) { + if (NonTypeOrVarTemplateParmDecl NTTP = + getNTTParameterFromExpr(A.getAsExpr(), Depth)) + return NTTP.getType(); + return QualType(); +} + static const NonTypeOrVarTemplateParmDecl -getDeducedNTTParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) { - return getDeducedNTTParameterFromExpr(E, Info.getDeducedDepth()); +getNTTParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) { + return getNTTParameterFromExpr(E, Info.getDeducedDepth()); } /// C++26 [temp.deduct.type]p13: @@ -268,11 +276,11 @@ getDeducedNTTParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) { /// 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(); + if (QualType T = + Sema::getTypeOfConstantTemplateParameter(A, Info.getDeducedDepth()); + !T.isNull()) + return T; + return unwrapExpressionForDeduction(A.getAsExpr())->getType(); } /// Determine whether two declaration pointers refer to the same @@ -2010,7 +2018,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // Determine the array bound is something we can deduce. NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, DAP->getSizeExpr()); + getNTTParameterFromExpr(Info, DAP->getSizeExpr()); if (!NTTP) return TemplateDeductionResult::Success; @@ -2074,7 +2082,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // type. libstdc++ relies on this. Expr *NoexceptExpr = FPP->getNoexceptExpr(); if (NonTypeOrVarTemplateParmDecl NTTP = - NoexceptExpr ? getDeducedNTTParameterFromExpr(Info, NoexceptExpr) + NoexceptExpr ? getNTTParameterFromExpr(Info, NoexceptExpr) : nullptr) { assert(NTTP.getDepth() == Info.getDeducedDepth() && "saw non-type template parameter with wrong depth"); @@ -2264,7 +2272,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // Perform deduction on the vector size, if we can. NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr()); + getNTTParameterFromExpr(Info, VP->getSizeExpr()); if (!NTTP) return TemplateDeductionResult::Success; @@ -2290,7 +2298,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // Perform deduction on the vector size, if we can. NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr()); + getNTTParameterFromExpr(Info, VP->getSizeExpr()); if (!NTTP) return TemplateDeductionResult::Success; @@ -2319,7 +2327,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // Perform deduction on the vector size, if we can. NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr()); + getNTTParameterFromExpr(Info, VP->getSizeExpr()); if (!NTTP) return TemplateDeductionResult::Success; @@ -2344,7 +2352,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // Perform deduction on the vector size, if we can. NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr()); + getNTTParameterFromExpr(Info, VP->getSizeExpr()); if (!NTTP) return TemplateDeductionResult::Success; @@ -2421,7 +2429,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( } NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, ParamExpr); + getNTTParameterFromExpr(Info, ParamExpr); if (!NTTP) return TemplateDeductionResult::Success; @@ -2468,7 +2476,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // Perform deduction on the address space, if we can. NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, ASP->getAddrSpaceExpr()); + getNTTParameterFromExpr(Info, ASP->getAddrSpaceExpr()); if (!NTTP) return TemplateDeductionResult::Success; @@ -2493,7 +2501,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // Perform deduction on the address space, if we can. NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, ASP->getAddrSpaceExpr()); + getNTTParameterFromExpr(Info, ASP->getAddrSpaceExpr()); if (!NTTP) return TemplateDeductionResult::Success; @@ -2513,7 +2521,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( return TemplateDeductionResult::NonDeducedMismatch; NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, IP->getNumBitsExpr()); + getNTTParameterFromExpr(Info, IP->getNumBitsExpr()); if (!NTTP) return TemplateDeductionResult::Success; @@ -2648,7 +2656,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, case TemplateArgument::Expression: if (NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(Info, P.getAsExpr())) { + getNTTParameterFromExpr(Info, P.getAsExpr())) { switch (A.getKind()) { case TemplateArgument::Expression: { return DeduceNonTypeTemplateArgument( @@ -4569,8 +4577,8 @@ static TemplateDeductionResult DeduceFromInitializerList( // from the length of the initializer list. if (auto *DependentArrTy = dyn_cast_or_null<DependentSizedArrayType>(ArrTy)) { // Determine the array bound is something we can deduce. - if (NonTypeOrVarTemplateParmDecl NTTP = getDeducedNTTParameterFromExpr( - Info, DependentArrTy->getSizeExpr())) { + if (NonTypeOrVarTemplateParmDecl NTTP = + getNTTParameterFromExpr(Info, DependentArrTy->getSizeExpr())) { // We can perform template argument deduction for the given non-type // template parameter. // C++ [temp.deduct.type]p13: @@ -6920,8 +6928,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx, return; } - const NonTypeOrVarTemplateParmDecl NTTP = - getDeducedNTTParameterFromExpr(E, Depth); + const NonTypeOrVarTemplateParmDecl NTTP = getNTTParameterFromExpr(E, Depth); if (!NTTP) return; if (NTTP.getDepth() == Depth) diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp index 165c2943b6b4a..88f57afbf7e99 100644 --- a/clang/test/CXX/drs/cwg29xx.cpp +++ b/clang/test/CXX/drs/cwg29xx.cpp @@ -8,6 +8,38 @@ // cxx98-no-diagnostics +namespace cwg2900 { // cwg2900: 24 +#if __cplusplus >= 201703L +// [temp.deduct.type] Example 13. +template <int &> struct E; +template <auto x> void f(E<x> *); // #cwg2900-f-E +int v; +void g(E<v> *bp) { + f(bp); + // since-cxx11-error@-1 {{no matching function for call to 'f'}} + // since-cxx11-note@#cwg2900-f-E {{candidate template ignored: substitution failure: non-type template argument is not a constant expression}} +} + +template <const int &> struct F; +template <decltype(auto) x> void f(F<x> *); +int i; +void g(F<i> *ap) { + f(ap); // OK, deduces x as a constant template parameter of type const int & +} + +template <decltype(auto) q> struct G; +template <auto x> long *f(G<x> *); // #1 +template <decltype(auto) x> short *f(G<x> *); // #2 +const int j = 0; +short *g(G<(j)> *ap) { // OK, q has type const int & + return f(ap); // OK, only #2 matches +} +long *g(G<j> *ap) { // OK, q has type int + return f(ap); // OK, #1 is more specialized +} +#endif +} // namespace cwg2900 + namespace cwg2913 { // cwg2913: 20 #if __cplusplus >= 202002L diff --git a/clang/test/SemaTemplate/temp_arg_nontype_arg_type.cpp b/clang/test/SemaTemplate/temp_arg_nontype_arg_type.cpp new file mode 100644 index 0000000000000..01290d91a2e84 --- /dev/null +++ b/clang/test/SemaTemplate/temp_arg_nontype_arg_type.cpp @@ -0,0 +1,63 @@ +// 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 + +namespace ex1 { + template<int i> class A { }; + template<short s> void f(A<s>); + // expected-note@-1 {{candidate template ignored: substitution failure: deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'short')}} + void k1() { + A<1> a; + f(a); // expected-error {{no matching function for call to 'f'}} + f<1>(a); + } +} + +namespace ex2 { + template<const short cs> class B { }; + template<short s> void g(B<s>); + void k2() { + B<1> b; + g(b); + } +} + +#if __cplusplus >= 201703L +namespace ex3 { + template<auto> struct C; + template<long long x> void f(C<x> *); + void g(C<0LL> *ap) { f(ap); } +} + +namespace ex4 { + template<int> struct D; + template<auto x> void f(D<x> *); + void g(D<0LL> *ap) { f(ap); } +} + +namespace ex5 { + template<int &> struct E; + template<auto x> void f(E<x> *); + // expected-note@-1 {{candidate template ignored: substitution failure: non-type template argument is not a constant expression}} + int v; + void g(E<v> *bp) { f(bp); } // expected-error {{no matching function for call to 'f'}} +} + +namespace ex6 { + template<const int &> struct F; + template<decltype(auto) x> void f(F<x> *); + int i; + void g(F<i> *ap) { f(ap); } +} + +namespace ex7 { + template <decltype(auto) q> struct G; + template <auto x> long *f(G<x> *); + template <decltype(auto) x> short *f(G<x> *); + const int j = 0; + short *g1(G<(j)> *ap) { return f(ap); } + long *g2(G<j> *ap) { return f(ap); } +} +#endif diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index e7679da30d5c2..4970d64f754f4 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -20117,7 +20117,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td>[<a href="https://wg21.link/temp.deduct.type">temp.deduct.type</a>]</td> <td>C++26</td> <td>Deduction of non-type template arguments with placeholder types</td> - <td class="unknown" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 24</td> </tr> <tr id="2901"> <td><a href="https://cplusplus.github.io/CWG/issues/2901.html">2901</a></td> _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
