[Clang] Remove the wrong assumption when rebuilding SizeOfPackExprs for constraint normalization (#115120)

In 463a4f150, we assumed that all the template argument packs are of
size 1 when normalizing a constraint expression because I mistakenly
thought those packs were obtained from their injected template
parameters. This was wrong because we might be checking constraints when
instantiating a friend declaration within a class template
specialization, where the parent class template is specialized with
non-dependent template arguments.

In that sense, we shouldn't assume any pack size nor expand anything in
such a scenario. Moreover, there are no intermediate (substituted but
unexpanded) AST nodes for template template parameters, so we have to
special-case their transformations by looking into the instantiation
scope instead of extracting anything from template arguments.

Fixes #115098
This commit is contained in:
Younan Zhang 2024-11-08 14:04:57 +08:00 committed by GitHub
parent 2f40e3e713
commit 37b4df434d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 45 additions and 25 deletions

View File

@ -4326,6 +4326,8 @@ public:
/// Retrieve the parameter pack.
NamedDecl *getPack() const { return Pack; }
void setPack(NamedDecl *NewPack) { Pack = NewPack; }
/// Retrieve the length of the parameter pack.
///
/// This routine may only be invoked when the expression is not

View File

@ -1749,31 +1749,21 @@ namespace {
return inherited::TransformLambdaBody(E, Body);
}
ExprResult RebuildSizeOfPackExpr(SourceLocation OperatorLoc,
NamedDecl *Pack, SourceLocation PackLoc,
SourceLocation RParenLoc,
std::optional<unsigned> Length,
ArrayRef<TemplateArgument> PartialArgs) {
if (SemaRef.CodeSynthesisContexts.back().Kind !=
Sema::CodeSynthesisContext::ConstraintNormalization)
return inherited::RebuildSizeOfPackExpr(OperatorLoc, Pack, PackLoc,
RParenLoc, Length, PartialArgs);
#ifndef NDEBUG
for (auto *Iter = TemplateArgs.begin(); Iter != TemplateArgs.end();
++Iter)
for (const TemplateArgument &TA : Iter->Args)
assert(TA.getKind() != TemplateArgument::Pack || TA.pack_size() == 1);
#endif
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(
SemaRef, /*NewSubstitutionIndex=*/0);
Decl *NewPack = TransformDecl(PackLoc, Pack);
if (!NewPack)
return ExprError();
return inherited::RebuildSizeOfPackExpr(OperatorLoc,
cast<NamedDecl>(NewPack), PackLoc,
RParenLoc, Length, PartialArgs);
ExprResult TransformSizeOfPackExpr(SizeOfPackExpr *E) {
ExprResult Transformed = inherited::TransformSizeOfPackExpr(E);
if (!Transformed.isUsable())
return Transformed;
auto *TransformedExpr = cast<SizeOfPackExpr>(Transformed.get());
if (SemaRef.CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::ConstraintNormalization &&
TransformedExpr->getPack() == E->getPack()) {
Decl *NewPack =
TransformDecl(E->getPackLoc(), TransformedExpr->getPack());
if (!NewPack)
return ExprError();
TransformedExpr->setPack(cast<NamedDecl>(NewPack));
}
return TransformedExpr;
}
ExprResult TransformRequiresExpr(RequiresExpr *E) {
@ -1899,6 +1889,15 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
if (TTP->isParameterPack()) {
// We might not have an index for pack expansion when normalizing
// constraint expressions. In that case, resort to instantiation scopes
// for the transformed declarations.
if (SemaRef.ArgumentPackSubstitutionIndex == -1 &&
SemaRef.CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::ConstraintNormalization) {
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D),
TemplateArgs);
}
assert(Arg.getKind() == TemplateArgument::Pack &&
"Missing argument pack");
Arg = getPackSubstitutedTemplateArgument(getSema(), Arg);

View File

@ -703,6 +703,25 @@ C v;
} // namespace GH93099
namespace GH115098 {
template <typename... Ts> struct c {
template <typename T>
requires(sizeof...(Ts) > 0)
friend bool operator==(c, c);
};
template <typename... Ts> struct d {
template <typename T>
requires(sizeof...(Ts) > 0)
friend bool operator==(d, d);
};
template struct c<int>;
template struct d<int, int>;
} // namespace GH115098
namespace GH114685 {
template <typename T> struct ptr {