[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 <corentinjabot@gmail.com>
This commit is contained in:
Younan Zhang
2024-08-26 14:30:26 +08:00
committed by GitHub
parent 7e6b1504c7
commit e6974daa7b
2 changed files with 47 additions and 2 deletions

View File

@@ -977,8 +977,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<Sema::CXXThisScopeRAII> ThisScope;

View File

@@ -599,3 +599,26 @@ template <class DerT>
unsigned long DerivedCollection<DerTs...>::index() {}
} // namespace GH72557
namespace GH101735 {
template <class, class>
concept True = true;
template <typename T>
class A {
template <typename... Ts>
void method(Ts&... ts)
requires requires (T t) {
{ t.method(static_cast<Ts &&>(ts)...) } -> True<void>;
};
};
template <typename T>
template <typename... Ts>
void A<T>::method(Ts&... ts)
requires requires (T t) {
{ t.method(static_cast<Ts &&>(ts)...) } -> True<void>;
} {}
}