https://github.com/nico updated https://github.com/llvm/llvm-project/pull/227494
>From 1cffd99626c0b52f7723abf1d3e1cff2969ea9da Mon Sep 17 00:00:00 2001 From: Nico Weber <[email protected]> Date: Tue, 29 Sep 2026 12:01:43 -0700 Subject: [PATCH] [clang] Fix an accepts-invalid related to concepts and parameter packs 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. --- clang/lib/Sema/SemaConcept.cpp | 9 ++++++ clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 40 +++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 9ef76b758d5fe16..05c44c0c1ce7422 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 b4f763f7fdb7d5f..f0819ed04881b34 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@-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@-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@-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}} } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
