[clang] [Clang] Implement CWG2900 (PR #224291)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 17 06:17:17 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Corentin Jabot (cor3ntin)
<details>
<summary>Changes</summary>
We did not implement the CWG200 changes to [dcl.type.auto.deduct] The rest seems to be working - I added tests, both for this issue and all examplars in the wording.
(i.e, deducing `auto` from `a decltype(auto) template parameter is rejected)
I thought it was cleaner to reuse getNTTParameterFromExpr rather than duplicating it, so there is a bunch of churn there to rename it.
Note that GCC doesn't seem to fully support that resolution ( To say nothing of other implementations).
Assisted-By: Opus 5.
---
Full diff: https://github.com/llvm/llvm-project/pull/224291.diff
7 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+4)
- (modified) clang/include/clang/Sema/Sema.h (+6)
- (modified) clang/lib/Sema/SemaTemplate.cpp (+16)
- (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+30-23)
- (modified) clang/test/CXX/drs/cwg29xx.cpp (+32)
- (added) clang/test/SemaTemplate/temp_arg_nontype_arg_type.cpp (+63)
- (modified) clang/www/cxx_dr_status.html (+1-1)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 0d67179f91a49..bd4398e632340 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -194,6 +194,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 an 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 0864337a9374c..b91e82680edc0 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 50ff56ff7811e..2288cd7441f03 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5566,6 +5566,22 @@ 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.
+ auto isUndeducedAuto = [](QualType T, bool DecltypeAuto) {
+ const AutoType *AT = T->getContainedAutoType();
+ return AT && AT->isDecltypeAuto() == DecltypeAuto &&
+ AT->getDeducedType().isNull();
+ };
+ 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 653240092e64a..4ba2d54e61428 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());
}
/// Determine whether two declaration pointers refer to the same
@@ -1996,7 +2004,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;
@@ -2060,7 +2068,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");
@@ -2250,7 +2258,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;
@@ -2276,7 +2284,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;
@@ -2305,7 +2313,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;
@@ -2330,7 +2338,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;
@@ -2407,7 +2415,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
}
NonTypeOrVarTemplateParmDecl NTTP =
- getDeducedNTTParameterFromExpr(Info, ParamExpr);
+ getNTTParameterFromExpr(Info, ParamExpr);
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2454,7 +2462,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;
@@ -2479,7 +2487,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;
@@ -2499,7 +2507,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 at -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 at -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 at -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>
``````````
</details>
https://github.com/llvm/llvm-project/pull/224291
More information about the cfe-commits
mailing list