[clang] [Clang] Fix evaluation of fold expanded constraints for NTTP (PR #210005)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 16 01:04:19 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Younan Zhang (zyn0217)
<details>
<summary>Changes</summary>
This is a rework of #<!-- -->200185 and also reflects what we did for TTP in C++26 fold expression constraints (adc64c6e1745)
Fixes https://github.com/llvm/llvm-project/issues/199569
---
Full diff: https://github.com/llvm/llvm-project/pull/210005.diff
3 Files Affected:
- (modified) clang/lib/Sema/SemaConcept.cpp (+12-3)
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+9-3)
- (modified) clang/test/SemaCXX/cxx2c-fold-exprs.cpp (+29)
``````````diff
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 8831a26224e7d..4e7ff0d930a97 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -293,12 +293,14 @@ class AdjustConstraints : public TreeTransform<AdjustConstraints> {
SemaRef.getASTContext(), NTTP->getDeclContext(),
NTTP->getInnerLocStart(), NTTP->getLocation(),
NTTP->getDepth() + TemplateDepth, NTTP->getPosition(),
- NTTP->getIdentifier(), TSI->getType(), NTTP->isParameterPack(), TSI);
+ NTTP->getIdentifier(), TSI->getType(),
+ RemoveNonPackExpansionPacks ? false : NTTP->isParameterPack(), TSI);
return DeclRefExpr::Create(
SemaRef.getASTContext(), E->getQualifierLoc(),
E->getTemplateKeywordLoc(), D, E->refersToEnclosingVariableOrCapture(),
- E->getNameInfo(), TSI->getType(), E->getValueKind(), D,
+ E->getNameInfo(), TSI->getType(), E->getValueKind(),
+ RemoveNonPackExpansionPacks ? NTTP : D,
/*TemplateArgs=*/nullptr, E->isNonOdrUse());
}
};
@@ -372,7 +374,14 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
return true;
TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
- if (NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex) {
+ // In concept parameter mapping for fold expressions, packs that aren't
+ // expanded in place are treated as having non-pack dependency, so that
+ // a PackExpansionType won't prevent expanding the packs outside the
+ // TreeTransform. However we still need to check the pack at this point.
+ if ((NTTP->isParameterPack() ||
+ (E->getFoundDecl() && E->getFoundDecl() != E->getDecl() &&
+ E->getFoundDecl()->isParameterPack())) &&
+ SemaRef.ArgPackSubstIndex) {
assert(Arg.getKind() == TemplateArgument::Pack &&
"Missing argument pack");
Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg);
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index bad0cdd1b7067..513827aad29a1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2247,9 +2247,15 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
auto [AssociatedDecl, Final] =
TemplateArgs.getAssociatedDecl(NTTP->getDepth());
UnsignedOrNone PackIndex = std::nullopt;
- if (NTTP->isParameterPack()) {
- assert(Arg.getKind() == TemplateArgument::Pack &&
- "Missing argument pack");
+ if (NTTP->isParameterPack() ||
+ // In concept parameter mapping for fold expressions, packs that aren't
+ // expanded in place are treated as having non-pack dependency, so that
+ // a PackExpansionType won't prevent expanding the packs outside the
+ // TreeTransform. However, we still need to unpack the arguments during
+ // any template argument substitution, so we also check its FoundDecl.
+ (E->getFoundDecl() && E->getFoundDecl() != E->getDecl() &&
+ E->getFoundDecl()->isParameterPack())) {
+ assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack");
if (!getSema().ArgPackSubstIndex) {
// We have an argument pack, but we can't select a particular argument
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 0312022912dca..b0e3eabd8ca24 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -509,6 +509,35 @@ static_assert(!__callable<__mdispatch<int>>);
}
+namespace GH199569 {
+
+template<typename F, typename... A>
+concept callable = requires(F f, A... a) {
+ f(a...);
+};
+
+struct S {
+ void operator()(auto);
+};
+
+template<int> struct B {};
+
+template<int... N> requires(... and callable<S, B<N>>)
+struct X {};
+
+template<int... N> requires(... and !callable<S, B<N>>)
+// expected-note at -1 {{because '!callable<S, B<0>>' evaluated to false}}
+struct Y {};
+
+static_assert(callable<S, B<0>>);
+static_assert(callable<S, B<1>>);
+
+X<0, 1> x;
+Y<0, 1, 2> y;
+// expected-error at -1 {{constraints not satisfied for class template 'Y'}}
+
+}
+
namespace GH190169 {
namespace _1 {
``````````
</details>
https://github.com/llvm/llvm-project/pull/210005
More information about the cfe-commits
mailing list