[Clang] Preserve partially substituted pack indexing type/expressions (#116782)

Substituting into pack indexing types/expressions can still result in
unexpanded types/expressions, such as `PackIndexingType` or
`PackIndexingExpr`. To handle these cases correctly, we should defer the
pack size checks to the next round of transformation, when the patterns
can be fully expanded.

To that end, the `FullySubstituted` flag is now necessary for computing
the dependencies of `PackIndexingExprs`. Conveniently, this flag can
also represent the prior `ExpandsToEmpty` status with an additional
emptiness check. Therefore, I converted all stored flags to use
`FullySubstituted`.

Fixes https://github.com/llvm/llvm-project/issues/116105
This commit is contained in:
Younan Zhang
2024-11-25 16:16:39 +08:00
committed by GitHub
parent 2585b6e8fa
commit df335b09ea
14 changed files with 85 additions and 42 deletions

View File

@@ -714,6 +714,7 @@ Bug Fixes to C++ Support
assumption if they also occur inside of a dependent lambda. (#GH114787)
- Clang now uses valid deduced type locations when diagnosing functions with trailing return type
missing placeholder return type. (#GH78694)
- Fixed a bug where bounds of partially expanded pack indexing expressions were checked too early. (#GH116105)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -4390,17 +4390,17 @@ class PackIndexingExpr final
unsigned TransformedExpressions : 31;
LLVM_PREFERRED_TYPE(bool)
unsigned ExpandedToEmptyPack : 1;
unsigned FullySubstituted : 1;
PackIndexingExpr(QualType Type, SourceLocation EllipsisLoc,
SourceLocation RSquareLoc, Expr *PackIdExpr, Expr *IndexExpr,
ArrayRef<Expr *> SubstitutedExprs = {},
bool ExpandedToEmptyPack = false)
bool FullySubstituted = false)
: Expr(PackIndexingExprClass, Type, VK_LValue, OK_Ordinary),
EllipsisLoc(EllipsisLoc), RSquareLoc(RSquareLoc),
SubExprs{PackIdExpr, IndexExpr},
TransformedExpressions(SubstitutedExprs.size()),
ExpandedToEmptyPack(ExpandedToEmptyPack) {
FullySubstituted(FullySubstituted) {
auto *Exprs = getTrailingObjects<Expr *>();
std::uninitialized_copy(SubstitutedExprs.begin(), SubstitutedExprs.end(),
@@ -4424,12 +4424,16 @@ public:
SourceLocation RSquareLoc, Expr *PackIdExpr,
Expr *IndexExpr, std::optional<int64_t> Index,
ArrayRef<Expr *> SubstitutedExprs = {},
bool ExpandedToEmptyPack = false);
bool FullySubstituted = false);
static PackIndexingExpr *CreateDeserialized(ASTContext &Context,
unsigned NumTransformedExprs);
bool isFullySubstituted() const { return FullySubstituted; }
/// Determine if the expression was expanded to empty.
bool expandsToEmptyPack() const { return ExpandedToEmptyPack; }
bool expandsToEmptyPack() const {
return isFullySubstituted() && TransformedExpressions == 0;
}
/// Determine the location of the 'sizeof' keyword.
SourceLocation getEllipsisLoc() const { return EllipsisLoc; }

View File

@@ -5922,12 +5922,12 @@ class PackIndexingType final
unsigned Size : 31;
LLVM_PREFERRED_TYPE(bool)
unsigned ExpandsToEmptyPack : 1;
unsigned FullySubstituted : 1;
protected:
friend class ASTContext; // ASTContext creates these.
PackIndexingType(const ASTContext &Context, QualType Canonical,
QualType Pattern, Expr *IndexExpr, bool ExpandsToEmptyPack,
QualType Pattern, Expr *IndexExpr, bool FullySubstituted,
ArrayRef<QualType> Expansions = {});
public:
@@ -5951,7 +5951,9 @@ public:
bool hasSelectedType() const { return getSelectedIndex() != std::nullopt; }
bool expandsToEmptyPack() const { return ExpandsToEmptyPack; }
bool isFullySubstituted() const { return FullySubstituted; }
bool expandsToEmptyPack() const { return isFullySubstituted() && Size == 0; }
ArrayRef<QualType> getExpansions() const {
return {getExpansionsPtr(), Size};
@@ -5965,10 +5967,10 @@ public:
if (hasSelectedType())
getSelectedType().Profile(ID);
else
Profile(ID, Context, getPattern(), getIndexExpr(), expandsToEmptyPack());
Profile(ID, Context, getPattern(), getIndexExpr(), isFullySubstituted());
}
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
QualType Pattern, Expr *E, bool ExpandsToEmptyPack);
QualType Pattern, Expr *E, bool FullySubstituted);
private:
const QualType *getExpansionsPtr() const {

View File

@@ -473,12 +473,12 @@ let Class = PackIndexingType in {
def : Property<"indexExpression", ExprRef> {
let Read = [{ node->getIndexExpr() }];
}
def : Property<"expandsToEmptyPack", Bool> {
let Read = [{ node->expandsToEmptyPack() }];
def : Property<"isFullySubstituted", Bool> {
let Read = [{ node->isFullySubstituted() }];
}
def : Creator<[{
return ctx.getPackIndexingType(pattern, indexExpression, expandsToEmptyPack);
return ctx.getPackIndexingType(pattern, indexExpression, isFullySubstituted);
}]>;
}

View File

@@ -14257,7 +14257,7 @@ public:
SourceLocation EllipsisLoc, Expr *IndexExpr,
SourceLocation RSquareLoc,
ArrayRef<Expr *> ExpandedExprs = {},
bool EmptyPack = false);
bool FullySubstituted = false);
/// Handle a C++1z fold-expression: ( expr op ... op expr ).
ExprResult ActOnCXXFoldExpr(Scope *S, SourceLocation LParenLoc, Expr *LHS,

View File

@@ -6223,13 +6223,11 @@ QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
ArrayRef<QualType> Expansions,
int Index) const {
QualType Canonical;
bool ExpandsToEmptyPack = FullySubstituted && Expansions.empty();
if (FullySubstituted && Index != -1) {
Canonical = getCanonicalType(Expansions[Index]);
} else {
llvm::FoldingSetNodeID ID;
PackIndexingType::Profile(ID, *this, Pattern, IndexExpr,
ExpandsToEmptyPack);
PackIndexingType::Profile(ID, *this, Pattern, IndexExpr, FullySubstituted);
void *InsertPos = nullptr;
PackIndexingType *Canon =
DependentPackIndexingTypes.FindNodeOrInsertPos(ID, InsertPos);
@@ -6238,7 +6236,7 @@ QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
PackIndexingType::totalSizeToAlloc<QualType>(Expansions.size()),
TypeAlignment);
Canon = new (Mem) PackIndexingType(*this, QualType(), Pattern, IndexExpr,
ExpandsToEmptyPack, Expansions);
FullySubstituted, Expansions);
DependentPackIndexingTypes.InsertNode(Canon, InsertPos);
}
Canonical = QualType(Canon, 0);
@@ -6248,7 +6246,7 @@ QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
Allocate(PackIndexingType::totalSizeToAlloc<QualType>(Expansions.size()),
TypeAlignment);
auto *T = new (Mem) PackIndexingType(*this, Canonical, Pattern, IndexExpr,
ExpandsToEmptyPack, Expansions);
FullySubstituted, Expansions);
Types.push_back(T);
return QualType(T, 0);
}

View File

@@ -388,9 +388,8 @@ ExprDependence clang::computeDependence(PackIndexingExpr *E) {
ExprDependence::Instantiation;
ArrayRef<Expr *> Exprs = E->getExpressions();
if (Exprs.empty())
if (Exprs.empty() || !E->isFullySubstituted())
D |= PatternDep | ExprDependence::Instantiation;
else if (!E->getIndexExpr()->isInstantiationDependent()) {
std::optional<unsigned> Index = E->getSelectedIndex();
assert(Index && *Index < Exprs.size() && "pack index out of bound");

View File

@@ -1717,9 +1717,9 @@ NonTypeTemplateParmDecl *SubstNonTypeTemplateParmExpr::getParameter() const {
PackIndexingExpr *PackIndexingExpr::Create(
ASTContext &Context, SourceLocation EllipsisLoc, SourceLocation RSquareLoc,
Expr *PackIdExpr, Expr *IndexExpr, std::optional<int64_t> Index,
ArrayRef<Expr *> SubstitutedExprs, bool ExpandedToEmptyPack) {
ArrayRef<Expr *> SubstitutedExprs, bool FullySubstituted) {
QualType Type;
if (Index && !SubstitutedExprs.empty())
if (Index && FullySubstituted && !SubstitutedExprs.empty())
Type = SubstitutedExprs[*Index]->getType();
else
Type = Context.DependentTy;
@@ -1728,7 +1728,7 @@ PackIndexingExpr *PackIndexingExpr::Create(
Context.Allocate(totalSizeToAlloc<Expr *>(SubstitutedExprs.size()));
return new (Storage)
PackIndexingExpr(Type, EllipsisLoc, RSquareLoc, PackIdExpr, IndexExpr,
SubstitutedExprs, ExpandedToEmptyPack);
SubstitutedExprs, FullySubstituted);
}
NamedDecl *PackIndexingExpr::getPackDecl() const {

View File

@@ -4031,12 +4031,12 @@ void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID,
PackIndexingType::PackIndexingType(const ASTContext &Context,
QualType Canonical, QualType Pattern,
Expr *IndexExpr, bool ExpandsToEmptyPack,
Expr *IndexExpr, bool FullySubstituted,
ArrayRef<QualType> Expansions)
: Type(PackIndexing, Canonical,
computeDependence(Pattern, IndexExpr, Expansions)),
Context(Context), Pattern(Pattern), IndexExpr(IndexExpr),
Size(Expansions.size()), ExpandsToEmptyPack(ExpandsToEmptyPack) {
Size(Expansions.size()), FullySubstituted(FullySubstituted) {
std::uninitialized_copy(Expansions.begin(), Expansions.end(),
getTrailingObjects<QualType>());
@@ -4081,10 +4081,10 @@ PackIndexingType::computeDependence(QualType Pattern, Expr *IndexExpr,
void PackIndexingType::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &Context, QualType Pattern,
Expr *E, bool ExpandsToEmptyPack) {
Expr *E, bool FullySubstituted) {
Pattern.Profile(ID);
E->Profile(ID, Context, true);
ID.AddBoolean(ExpandsToEmptyPack);
ID.AddBoolean(FullySubstituted);
}
UnaryTransformType::UnaryTransformType(QualType BaseType,

View File

@@ -1157,10 +1157,12 @@ ExprResult Sema::ActOnPackIndexingExpr(Scope *S, Expr *PackExpression,
return Res;
}
ExprResult
Sema::BuildPackIndexingExpr(Expr *PackExpression, SourceLocation EllipsisLoc,
Expr *IndexExpr, SourceLocation RSquareLoc,
ArrayRef<Expr *> ExpandedExprs, bool EmptyPack) {
ExprResult Sema::BuildPackIndexingExpr(Expr *PackExpression,
SourceLocation EllipsisLoc,
Expr *IndexExpr,
SourceLocation RSquareLoc,
ArrayRef<Expr *> ExpandedExprs,
bool FullySubstituted) {
std::optional<int64_t> Index;
if (!IndexExpr->isInstantiationDependent()) {
@@ -1174,8 +1176,8 @@ Sema::BuildPackIndexingExpr(Expr *PackExpression, SourceLocation EllipsisLoc,
IndexExpr = Res.get();
}
if (Index && (!ExpandedExprs.empty() || EmptyPack)) {
if (*Index < 0 || EmptyPack || *Index >= int64_t(ExpandedExprs.size())) {
if (Index && FullySubstituted) {
if (*Index < 0 || *Index >= int64_t(ExpandedExprs.size())) {
Diag(PackExpression->getBeginLoc(), diag::err_pack_index_out_of_bound)
<< *Index << PackExpression << ExpandedExprs.size();
return ExprError();
@@ -1184,7 +1186,7 @@ Sema::BuildPackIndexingExpr(Expr *PackExpression, SourceLocation EllipsisLoc,
return PackIndexingExpr::Create(getASTContext(), EllipsisLoc, RSquareLoc,
PackExpression, IndexExpr, Index,
ExpandedExprs, EmptyPack);
ExpandedExprs, FullySubstituted);
}
TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern(

View File

@@ -3670,10 +3670,10 @@ public:
SourceLocation RSquareLoc,
Expr *PackIdExpression, Expr *IndexExpr,
ArrayRef<Expr *> ExpandedExprs,
bool EmptyPack = false) {
bool FullySubstituted = false) {
return getSema().BuildPackIndexingExpr(PackIdExpression, EllipsisLoc,
IndexExpr, RSquareLoc, ExpandedExprs,
EmptyPack);
FullySubstituted);
}
/// Build a new expression representing a call to a source location
@@ -6769,6 +6769,7 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
if (Out.isNull())
return QualType();
SubtitutedTypes.push_back(Out);
FullySubstituted &= !Out->containsUnexpandedParameterPack();
}
// If we're supposed to retain a pack expansion, do so by temporarily
// forgetting the partially-substituted parameter pack.
@@ -15581,6 +15582,7 @@ TreeTransform<Derived>::TransformPackIndexingExpr(PackIndexingExpr *E) {
}
SmallVector<Expr *, 5> ExpandedExprs;
bool FullySubstituted = true;
if (!E->expandsToEmptyPack() && E->getExpressions().empty()) {
Expr *Pattern = E->getPackIdExpression();
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
@@ -15605,7 +15607,7 @@ TreeTransform<Derived>::TransformPackIndexingExpr(PackIndexingExpr *E) {
return ExprError();
return getDerived().RebuildPackIndexingExpr(
E->getEllipsisLoc(), E->getRSquareLoc(), Pack.get(), IndexExpr.get(),
{});
{}, /*FullySubstituted=*/false);
}
for (unsigned I = 0; I != *NumExpansions; ++I) {
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
@@ -15617,6 +15619,7 @@ TreeTransform<Derived>::TransformPackIndexingExpr(PackIndexingExpr *E) {
OrigNumExpansions);
if (Out.isInvalid())
return true;
FullySubstituted = false;
}
ExpandedExprs.push_back(Out.get());
}
@@ -15633,6 +15636,7 @@ TreeTransform<Derived>::TransformPackIndexingExpr(PackIndexingExpr *E) {
OrigNumExpansions);
if (Out.isInvalid())
return true;
FullySubstituted = false;
ExpandedExprs.push_back(Out.get());
}
} else if (!E->expandsToEmptyPack()) {
@@ -15644,8 +15648,7 @@ TreeTransform<Derived>::TransformPackIndexingExpr(PackIndexingExpr *E) {
return getDerived().RebuildPackIndexingExpr(
E->getEllipsisLoc(), E->getRSquareLoc(), E->getPackIdExpression(),
IndexExpr.get(), ExpandedExprs,
/*EmptyPack=*/ExpandedExprs.size() == 0);
IndexExpr.get(), ExpandedExprs, FullySubstituted);
}
template<typename Derived>

View File

@@ -2191,7 +2191,7 @@ void ASTStmtReader::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) {
VisitExpr(E);
E->TransformedExpressions = Record.readInt();
E->ExpandedToEmptyPack = Record.readInt();
E->FullySubstituted = Record.readInt();
E->EllipsisLoc = readSourceLocation();
E->RSquareLoc = readSourceLocation();
E->SubExprs[0] = Record.readStmt();

View File

@@ -2191,7 +2191,7 @@ void ASTStmtWriter::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
void ASTStmtWriter::VisitPackIndexingExpr(PackIndexingExpr *E) {
VisitExpr(E);
Record.push_back(E->TransformedExpressions);
Record.push_back(E->ExpandedToEmptyPack);
Record.push_back(E->FullySubstituted);
Record.AddSourceLocation(E->getEllipsisLoc());
Record.AddSourceLocation(E->getRSquareLoc());
Record.AddStmt(E->getPackIdExpression());

View File

@@ -271,3 +271,37 @@ void f() {
}
} // namespace GH105903
namespace GH116105 {
template <unsigned long Np, class... Ts> using pack_type = Ts...[Np];
template <unsigned long Np, auto... Ts> using pack_expr = decltype(Ts...[Np]);
template <class...> struct types;
template <class, long... Is> struct indices;
template <class> struct repack;
template <long... Idx> struct repack<indices<long, Idx...>> {
template <class... Ts>
using pack_type_alias = types<pack_type<Idx, Ts...>...>;
template <class... Ts>
using pack_expr_alias = types<pack_expr<Idx, Ts{}...>...>;
};
template <class... Args> struct mdispatch_ {
using Idx = __make_integer_seq<indices, long, sizeof...(Args)>;
static_assert(__is_same(
typename repack<Idx>::template pack_type_alias<Args...>, types<Args...>));
static_assert(__is_same(
typename repack<Idx>::template pack_expr_alias<Args...>, types<Args...>));
};
mdispatch_<int, int> d;
} // namespace GH116105