From 3e512ba17d90d5ab5382bae61a3e5762d2e3f42a Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Mon, 26 Aug 2024 14:30:26 +0800 Subject: [PATCH] [Clang][Concepts] Fix the constraint equivalence checking involving parameter packs (#102131) We established an instantiation scope in order for constraint equivalence checking to properly map the uninstantiated parameters. That mechanism mapped even packs to themselves. Consequently, parameter packs e.g. appearing in a function call, were not expanded. So they would end up becoming `SubstTemplateTypeParmPackType`s that circularly depend on the canonical declaration of the function template, which is not yet determined, hence the spurious error. No release note as I plan to backport it to 19. Fixes https://github.com/llvm/llvm-project/issues/101735 --------- Co-authored-by: cor3ntin (cherry picked from commit e6974daa7bc100c8b88057d50f3ec3eca7282243) --- clang/lib/Sema/SemaConcept.cpp | 26 +++++++++++++++++-- .../SemaTemplate/concepts-out-of-line-def.cpp | 23 ++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index c34d32002b5ad7..244f6ef2f53faa 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -969,8 +969,30 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction( // equivalence. LocalInstantiationScope ScopeForParameters(S); if (auto *FD = DeclInfo.getDecl()->getAsFunction()) - for (auto *PVD : FD->parameters()) - ScopeForParameters.InstantiatedLocal(PVD, PVD); + for (auto *PVD : FD->parameters()) { + if (!PVD->isParameterPack()) { + ScopeForParameters.InstantiatedLocal(PVD, PVD); + continue; + } + // This is hacky: we're mapping the parameter pack to a size-of-1 argument + // to avoid building SubstTemplateTypeParmPackTypes for + // PackExpansionTypes. The SubstTemplateTypeParmPackType node would + // otherwise reference the AssociatedDecl of the template arguments, which + // is, in this case, the template declaration. + // + // However, as we are in the process of comparing potential + // re-declarations, the canonical declaration is the declaration itself at + // this point. So if we didn't expand these packs, we would end up with an + // incorrect profile difference because we will be profiling the + // canonical types! + // + // FIXME: Improve the "no-transform" machinery in FindInstantiatedDecl so + // that we can eliminate the Scope in the cases where the declarations are + // not necessarily instantiated. It would also benefit the noexcept + // specifier comparison. + ScopeForParameters.MakeInstantiatedLocalArgPack(PVD); + ScopeForParameters.InstantiatedLocalPackArg(PVD, PVD); + } std::optional ThisScope; diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp index 0142efcdc3ee86..333187b0d74ad6 100644 --- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp +++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp @@ -599,3 +599,26 @@ template unsigned long DerivedCollection::index() {} } // namespace GH72557 + +namespace GH101735 { + +template +concept True = true; + +template +class A { + template + void method(Ts&... ts) + requires requires (T t) { + { t.method(static_cast(ts)...) } -> True; + }; +}; + +template +template +void A::method(Ts&... ts) + requires requires (T t) { + { t.method(static_cast(ts)...) } -> True; + } {} + +}