[clang] d824e1b - [clang] Fix an accepts-invalid related to concepts and parameter packs (#227494)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 4 19:28:28 PDT 2026
Author: Nico Weber
Date: 2026-10-05T02:28:21Z
New Revision: d824e1ba595a4d86e763428c888885fa47a0766f
URL: https://github.com/llvm/llvm-project/commit/d824e1ba595a4d86e763428c888885fa47a0766f
DIFF: https://github.com/llvm/llvm-project/commit/d824e1ba595a4d86e763428c888885fa47a0766f.diff
LOG: [clang] Fix an accepts-invalid related to concepts and parameter packs (#227494)
HashParameterMapping computes the keys of
UnsubstitutedConstraintSatisfactionCache. In a fold expression, it
hashes the current element for every use of the expanded pack. But in
P...[sizeof(P)], P refers to the whole pack, not the current element. So
two checks with the same element at the same position got the same key
even if the indexed pack differs, and the second one used the first
one's result. This accepted e.g.
template <class T> concept two_bytes = sizeof(T) == 2;
template <typename... P>
void f() requires(two_bytes<P...[sizeof(P)]> && ...) {}
void g() {
f<char, short, short>();
f<char, int, short, short, short>(); // P...[sizeof(char)] is int
}
(without the first call, the second one is rejected). This is a
regression exposed by #218577 (which fixed #218548), even though the
underlying bug is older.
As fix, hash the whole pack for the pattern of a PackIndexingType.
Added:
Modified:
clang/lib/Sema/SemaConcept.cpp
clang/test/SemaCXX/cxx2c-fold-exprs.cpp
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 9ef76b758d5fe..05c44c0c1ce74 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -404,6 +404,15 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
return inherited::TraverseType(TT->desugar());
}
+ bool TraversePackIndexingType(PackIndexingType *T, bool TraverseQualifier) {
+ {
+ Sema::ArgPackSubstIndexRAII _(SemaRef, std::nullopt);
+ if (!TraverseType(T->getPattern()))
+ return false;
+ }
+ return TraverseStmt(T->getIndexExpr());
+ }
+
bool TraverseDecl(Decl *D) {
if (auto *VD = dyn_cast<ValueDecl>(D)) {
if (auto *Var = dyn_cast<VarDecl>(VD))
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index b4f763f7fdb7d..f0819ed04881b 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -677,7 +677,7 @@ void g() { f<long long, float>(); }
namespace GH218548 {
template <class T>
-concept same_as_impl = sizeof(T) == 2;
+concept same_as_impl = sizeof(T) == 2; // #GH218548_same_as_impl
template <typename... P>
void f() requires(same_as_impl<P...[sizeof(P)]> && ...) // #GH218548_f
{}
@@ -687,8 +687,42 @@ void g() {
f<char, int, short>();
// expected-error at -1 {{no matching function}}
// expected-note@#GH218548_f {{constraints not satisfied}}
- // expected-note@#GH218548_f {{does not satisfy 'same_as_impl'}}
- // expected-note@#GH218548_f {{invalid index}}
+ // expected-note@#GH218548_f {{because 'int' does not satisfy 'same_as_impl'}}
+ // expected-note@#GH218548_same_as_impl {{because 'sizeof(int) == 2' (4 == 2) evaluated to false}}
+}
+
+}
+
+namespace GH218548_cache {
+
+// The cached satisfaction of an element must depend on the whole pack that
+// P...[sizeof(P)] indexes: the first element's P...[sizeof(P)] is short in the
+// first call but int in the second.
+template <class T>
+concept two_bytes = sizeof(T) == 2; // #GH218548_cache_two_bytes
+template <typename... P>
+void f() requires(two_bytes<P...[sizeof(P)]> && ...) // #GH218548_cache_f
+{}
+template <class T>
+concept wrapped = two_bytes<T>; // #GH218548_cache_wrapped
+template <typename... P>
+void g() requires(wrapped<P...[sizeof(P)]> && ...) // #GH218548_cache_g
+{}
+void h() {
+ f<char, short, short>();
+ f<char, int, short, short, short>();
+ // expected-error at -1 {{no matching function}}
+ // expected-note@#GH218548_cache_f {{constraints not satisfied}}
+ // expected-note@#GH218548_cache_f {{because 'int' does not satisfy 'two_bytes'}}
+ // expected-note@#GH218548_cache_two_bytes {{because 'sizeof(int) == 2' (4 == 2) evaluated to false}}
+
+ g<char, short, short>();
+ g<char, int, short, short, short>();
+ // expected-error at -1 {{no matching function}}
+ // expected-note@#GH218548_cache_g {{constraints not satisfied}}
+ // expected-note@#GH218548_cache_g {{because 'int' does not satisfy 'wrapped'}}
+ // expected-note@#GH218548_cache_wrapped {{because 'int' does not satisfy 'two_bytes'}}
+ // expected-note@#GH218548_cache_two_bytes {{because 'sizeof(int) == 2' (4 == 2) evaluated to false}}
}
}
More information about the cfe-commits
mailing list