[Clang][P1061] Add stuctured binding packs (#121417)
This is an implementation of P1061 Structure Bindings Introduce a Pack without the ability to use packs outside of templates. There is a couple of ways the AST could have been sliced so let me know what you think. The only part of this change that I am unsure of is the serialization/deserialization stuff. I followed the implementation of other Exprs, but I do not really know how it is tested. Thank you for your time considering this. --------- Co-authored-by: Yanzuo Liu <zwuis@outlook.com>
This commit is contained in:
parent
608012ace4
commit
abc8812df0
@ -698,6 +698,10 @@ public:
|
||||
return const_cast<ValueDecl *>(this)->getPotentiallyDecomposedVarDecl();
|
||||
}
|
||||
|
||||
/// Determine whether this value is actually a function parameter pack,
|
||||
/// init-capture pack, or structured binding pack
|
||||
bool isParameterPack() const;
|
||||
|
||||
// Implement isa/cast/dyncast/etc.
|
||||
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
|
||||
static bool classofKind(Kind K) { return K >= firstValue && K <= lastValue; }
|
||||
@ -1527,10 +1531,6 @@ public:
|
||||
NonParmVarDeclBits.IsInitCapture = IC;
|
||||
}
|
||||
|
||||
/// Determine whether this variable is actually a function parameter pack or
|
||||
/// init-capture pack.
|
||||
bool isParameterPack() const;
|
||||
|
||||
/// Whether this local extern variable declaration's previous declaration
|
||||
/// was declared in the same block scope. Only correct in C++.
|
||||
bool isPreviousDeclInSameBlockScope() const {
|
||||
|
||||
@ -4175,8 +4175,9 @@ class BindingDecl : public ValueDecl {
|
||||
/// binding).
|
||||
Expr *Binding = nullptr;
|
||||
|
||||
BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id)
|
||||
: ValueDecl(Decl::Binding, DC, IdLoc, Id, QualType()) {}
|
||||
BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id,
|
||||
QualType T)
|
||||
: ValueDecl(Decl::Binding, DC, IdLoc, Id, T) {}
|
||||
|
||||
void anchor() override;
|
||||
|
||||
@ -4184,7 +4185,8 @@ public:
|
||||
friend class ASTDeclReader;
|
||||
|
||||
static BindingDecl *Create(ASTContext &C, DeclContext *DC,
|
||||
SourceLocation IdLoc, IdentifierInfo *Id);
|
||||
SourceLocation IdLoc, IdentifierInfo *Id,
|
||||
QualType T);
|
||||
static BindingDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
|
||||
|
||||
/// Get the expression to which this declaration is bound. This may be null
|
||||
@ -4192,14 +4194,13 @@ public:
|
||||
/// decomposition declaration, and when the initializer is type-dependent.
|
||||
Expr *getBinding() const { return Binding; }
|
||||
|
||||
// Get the array of Exprs when the binding represents a pack.
|
||||
llvm::ArrayRef<Expr *> getBindingPackExprs() const;
|
||||
|
||||
/// Get the decomposition declaration that this binding represents a
|
||||
/// decomposition of.
|
||||
ValueDecl *getDecomposedDecl() const { return Decomp; }
|
||||
|
||||
/// Get the variable (if any) that holds the value of evaluating the binding.
|
||||
/// Only present for user-defined bindings for tuple-like types.
|
||||
VarDecl *getHoldingVar() const;
|
||||
|
||||
/// Set the binding for this BindingDecl, along with its declared type (which
|
||||
/// should be a possibly-cv-qualified form of the type of the binding, or a
|
||||
/// reference to such a type).
|
||||
@ -4211,6 +4212,10 @@ public:
|
||||
/// Set the decomposed variable for this BindingDecl.
|
||||
void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; }
|
||||
|
||||
/// Get the variable (if any) that holds the value of evaluating the binding.
|
||||
/// Only present for user-defined bindings for tuple-like types.
|
||||
VarDecl *getHoldingVar() const;
|
||||
|
||||
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
|
||||
static bool classofKind(Kind K) { return K == Decl::Binding; }
|
||||
};
|
||||
@ -4238,8 +4243,16 @@ class DecompositionDecl final
|
||||
NumBindings(Bindings.size()) {
|
||||
std::uninitialized_copy(Bindings.begin(), Bindings.end(),
|
||||
getTrailingObjects<BindingDecl *>());
|
||||
for (auto *B : Bindings)
|
||||
for (auto *B : Bindings) {
|
||||
B->setDecomposedDecl(this);
|
||||
if (B->isParameterPack() && B->getBinding()) {
|
||||
for (Expr *E : B->getBindingPackExprs()) {
|
||||
auto *DRE = cast<DeclRefExpr>(E);
|
||||
auto *NestedB = cast<BindingDecl>(DRE->getDecl());
|
||||
NestedB->setDecomposedDecl(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void anchor() override;
|
||||
@ -4257,8 +4270,33 @@ public:
|
||||
static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
|
||||
unsigned NumBindings);
|
||||
|
||||
ArrayRef<BindingDecl *> bindings() const {
|
||||
return llvm::ArrayRef(getTrailingObjects<BindingDecl *>(), NumBindings);
|
||||
// Provide the range of bindings which may have a nested pack.
|
||||
llvm::ArrayRef<BindingDecl *> bindings() const {
|
||||
return {getTrailingObjects<BindingDecl *>(), NumBindings};
|
||||
}
|
||||
|
||||
// Provide a flattened range to visit each binding.
|
||||
auto flat_bindings() const {
|
||||
llvm::ArrayRef<BindingDecl *> Bindings = bindings();
|
||||
llvm::ArrayRef<Expr *> PackExprs;
|
||||
|
||||
// Split the bindings into subranges split by the pack.
|
||||
auto S1 = Bindings.take_until(
|
||||
[](BindingDecl *BD) { return BD->isParameterPack(); });
|
||||
|
||||
Bindings = Bindings.drop_front(S1.size());
|
||||
if (!Bindings.empty()) {
|
||||
PackExprs = Bindings.front()->getBindingPackExprs();
|
||||
Bindings = Bindings.drop_front();
|
||||
}
|
||||
|
||||
auto S2 = llvm::map_range(PackExprs, [](Expr *E) {
|
||||
auto *DRE = cast<DeclRefExpr>(E);
|
||||
return cast<BindingDecl>(DRE->getDecl());
|
||||
});
|
||||
|
||||
return llvm::concat<BindingDecl *>(std::move(S1), std::move(S2),
|
||||
std::move(Bindings));
|
||||
}
|
||||
|
||||
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
|
||||
|
||||
@ -5319,6 +5319,59 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Represents an unexpanded pack where the list of expressions are
|
||||
// known. These are used when structured bindings introduce a pack.
|
||||
class ResolvedUnexpandedPackExpr final
|
||||
: public Expr,
|
||||
private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Expr *> {
|
||||
friend class ASTStmtReader;
|
||||
friend class ASTStmtWriter;
|
||||
friend TrailingObjects;
|
||||
|
||||
SourceLocation BeginLoc;
|
||||
unsigned NumExprs;
|
||||
|
||||
ResolvedUnexpandedPackExpr(SourceLocation BL, QualType QT, unsigned NumExprs);
|
||||
|
||||
public:
|
||||
static ResolvedUnexpandedPackExpr *CreateDeserialized(ASTContext &C,
|
||||
unsigned NumExprs);
|
||||
static ResolvedUnexpandedPackExpr *
|
||||
Create(ASTContext &C, SourceLocation BeginLoc, QualType T, unsigned NumExprs);
|
||||
static ResolvedUnexpandedPackExpr *Create(ASTContext &C,
|
||||
SourceLocation BeginLoc, QualType T,
|
||||
llvm::ArrayRef<Expr *> Exprs);
|
||||
|
||||
unsigned getNumExprs() const { return NumExprs; }
|
||||
|
||||
llvm::MutableArrayRef<Expr *> getExprs() {
|
||||
return {getTrailingObjects<Expr *>(), NumExprs};
|
||||
}
|
||||
|
||||
llvm::ArrayRef<Expr *> getExprs() const {
|
||||
return {getTrailingObjects<Expr *>(), NumExprs};
|
||||
}
|
||||
|
||||
Expr *getExpansion(unsigned Idx) { return getExprs()[Idx]; }
|
||||
Expr *getExpansion(unsigned Idx) const { return getExprs()[Idx]; }
|
||||
|
||||
// Iterators
|
||||
child_range children() {
|
||||
return child_range((Stmt **)getTrailingObjects<Expr *>(),
|
||||
(Stmt **)getTrailingObjects<Expr *>() + getNumExprs());
|
||||
}
|
||||
|
||||
SourceLocation getBeginLoc() const LLVM_READONLY { return BeginLoc; }
|
||||
SourceLocation getEndLoc() const LLVM_READONLY { return BeginLoc; }
|
||||
|
||||
// Returns the resolved pack of a decl or nullptr
|
||||
static ResolvedUnexpandedPackExpr *getFromDecl(Decl *);
|
||||
|
||||
static bool classof(const Stmt *T) {
|
||||
return T->getStmtClass() == ResolvedUnexpandedPackExprClass;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_EXPRCXX_H
|
||||
|
||||
@ -2950,6 +2950,7 @@ DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
|
||||
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
|
||||
DEF_TRAVERSE_STMT(AtomicExpr, {})
|
||||
DEF_TRAVERSE_STMT(CXXParenListInitExpr, {})
|
||||
DEF_TRAVERSE_STMT(ResolvedUnexpandedPackExpr, {})
|
||||
|
||||
DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {
|
||||
if (S->getLifetimeExtendedTemporaryDecl()) {
|
||||
|
||||
@ -1099,6 +1099,16 @@ def err_lambda_capture_misplaced_ellipsis : Error<
|
||||
"the name of the capture">;
|
||||
def err_lambda_capture_multiple_ellipses : Error<
|
||||
"multiple ellipses in pack capture">;
|
||||
def err_binding_multiple_ellipses : Error<
|
||||
"multiple packs in structured binding declaration">;
|
||||
def note_previous_ellipsis : Note<
|
||||
"previous binding pack specified here">;
|
||||
def ext_cxx_binding_pack : ExtWarn<
|
||||
"structured binding packs are a C++2c extension ">,
|
||||
InGroup<CXX26>;
|
||||
def warn_cxx23_compat_binding_pack : Warning<
|
||||
"structured binding packs are incompatible with C++ standards before C++2c">,
|
||||
InGroup<CXXPre26Compat>, DefaultIgnore;
|
||||
def err_capture_default_first : Error<
|
||||
"capture default must be first">;
|
||||
def ext_decl_attrs_on_lambda : ExtWarn<
|
||||
|
||||
@ -5945,6 +5945,9 @@ def warn_cxx23_pack_indexing : Warning<
|
||||
"pack indexing is incompatible with C++ standards before C++2c">,
|
||||
DefaultIgnore, InGroup<CXXPre26Compat>;
|
||||
|
||||
def err_pack_outside_template : Error<
|
||||
"pack declaration outside of template">;
|
||||
|
||||
def err_fold_expression_packs_both_sides : Error<
|
||||
"binary fold expression has unexpanded parameter packs in both operands">;
|
||||
def err_fold_expression_empty : Error<
|
||||
|
||||
@ -163,6 +163,7 @@ def MaterializeTemporaryExpr : StmtNode<Expr>;
|
||||
def LambdaExpr : StmtNode<Expr>;
|
||||
def CXXFoldExpr : StmtNode<Expr>;
|
||||
def CXXParenListInitExpr: StmtNode<Expr>;
|
||||
def ResolvedUnexpandedPackExpr : StmtNode<Expr>;
|
||||
|
||||
// C++ Coroutines expressions
|
||||
def CoroutineSuspendExpr : StmtNode<Expr, 1>;
|
||||
|
||||
@ -1795,6 +1795,7 @@ public:
|
||||
IdentifierInfo *Name;
|
||||
SourceLocation NameLoc;
|
||||
std::optional<ParsedAttributes> Attrs;
|
||||
SourceLocation EllipsisLoc;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@ -232,7 +232,8 @@ void threadSafetyCleanup(BeforeSet *Cache);
|
||||
|
||||
// FIXME: No way to easily map from TemplateTypeParmTypes to
|
||||
// TemplateTypeParmDecls, so we have this horrible PointerUnion.
|
||||
typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *>,
|
||||
typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *,
|
||||
ResolvedUnexpandedPackExpr *>,
|
||||
SourceLocation>
|
||||
UnexpandedParameterPack;
|
||||
|
||||
@ -6021,6 +6022,7 @@ public:
|
||||
RecordDecl *ClassDecl,
|
||||
const IdentifierInfo *Name);
|
||||
|
||||
unsigned GetDecompositionElementCount(QualType DecompType);
|
||||
void CheckCompleteDecompositionDeclaration(DecompositionDecl *DD);
|
||||
|
||||
/// Stack containing information needed when in C++2a an 'auto' is encountered
|
||||
|
||||
@ -1908,6 +1908,7 @@ enum StmtCode {
|
||||
EXPR_PACK_EXPANSION, // PackExpansionExpr
|
||||
EXPR_PACK_INDEXING, // PackIndexingExpr
|
||||
EXPR_SIZEOF_PACK, // SizeOfPackExpr
|
||||
EXPR_RESOLVED_UNEXPANDED_PACK, // ResolvedUnexpandedPackExpr
|
||||
EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr
|
||||
EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr
|
||||
EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr
|
||||
|
||||
@ -12841,11 +12841,12 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
|
||||
|
||||
// Likewise, variables with tuple-like bindings are required if their
|
||||
// bindings have side-effects.
|
||||
if (const auto *DD = dyn_cast<DecompositionDecl>(VD))
|
||||
for (const auto *BD : DD->bindings())
|
||||
if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
|
||||
for (const auto *BD : DD->flat_bindings())
|
||||
if (const auto *BindingVD = BD->getHoldingVar())
|
||||
if (DeclMustBeEmitted(BindingVD))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2553,7 +2553,7 @@ ExpectedDecl ASTNodeImporter::VisitBindingDecl(BindingDecl *D) {
|
||||
|
||||
BindingDecl *ToD;
|
||||
if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, Loc,
|
||||
Name.getAsIdentifierInfo()))
|
||||
Name.getAsIdentifierInfo(), D->getType()))
|
||||
return ToD;
|
||||
|
||||
Error Err = Error::success();
|
||||
|
||||
@ -2659,10 +2659,6 @@ bool VarDecl::checkForConstantInitialization(
|
||||
return Eval->HasConstantInitialization;
|
||||
}
|
||||
|
||||
bool VarDecl::isParameterPack() const {
|
||||
return isa<PackExpansionType>(getType());
|
||||
}
|
||||
|
||||
template<typename DeclT>
|
||||
static DeclT *getDefinitionOrSelf(DeclT *D) {
|
||||
assert(D);
|
||||
@ -5421,6 +5417,13 @@ bool ValueDecl::isInitCapture() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValueDecl::isParameterPack() const {
|
||||
if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(this))
|
||||
return NTTP->isParameterPack();
|
||||
|
||||
return isa_and_nonnull<PackExpansionType>(getType().getTypePtrOrNull());
|
||||
}
|
||||
|
||||
void ImplicitParamDecl::anchor() {}
|
||||
|
||||
ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC,
|
||||
|
||||
@ -245,7 +245,7 @@ bool Decl::isTemplateParameterPack() const {
|
||||
}
|
||||
|
||||
bool Decl::isParameterPack() const {
|
||||
if (const auto *Var = dyn_cast<VarDecl>(this))
|
||||
if (const auto *Var = dyn_cast<ValueDecl>(this))
|
||||
return Var->isParameterPack();
|
||||
|
||||
return isTemplateParameterPack();
|
||||
|
||||
@ -3462,19 +3462,21 @@ VarDecl *ValueDecl::getPotentiallyDecomposedVarDecl() {
|
||||
if (auto *Var = llvm::dyn_cast<VarDecl>(this))
|
||||
return Var;
|
||||
if (auto *BD = llvm::dyn_cast<BindingDecl>(this))
|
||||
return llvm::dyn_cast<VarDecl>(BD->getDecomposedDecl());
|
||||
return llvm::dyn_cast_if_present<VarDecl>(BD->getDecomposedDecl());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BindingDecl::anchor() {}
|
||||
|
||||
BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC,
|
||||
SourceLocation IdLoc, IdentifierInfo *Id) {
|
||||
return new (C, DC) BindingDecl(DC, IdLoc, Id);
|
||||
SourceLocation IdLoc, IdentifierInfo *Id,
|
||||
QualType T) {
|
||||
return new (C, DC) BindingDecl(DC, IdLoc, Id, T);
|
||||
}
|
||||
|
||||
BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
|
||||
return new (C, ID) BindingDecl(nullptr, SourceLocation(), nullptr);
|
||||
return new (C, ID)
|
||||
BindingDecl(nullptr, SourceLocation(), nullptr, QualType());
|
||||
}
|
||||
|
||||
VarDecl *BindingDecl::getHoldingVar() const {
|
||||
@ -3490,6 +3492,12 @@ VarDecl *BindingDecl::getHoldingVar() const {
|
||||
return VD;
|
||||
}
|
||||
|
||||
llvm::ArrayRef<Expr *> BindingDecl::getBindingPackExprs() const {
|
||||
assert(Binding && "expecting a pack expr");
|
||||
auto *RP = cast<ResolvedUnexpandedPackExpr>(Binding);
|
||||
return RP->getExprs();
|
||||
}
|
||||
|
||||
void DecompositionDecl::anchor() {}
|
||||
|
||||
DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
|
||||
|
||||
@ -3659,6 +3659,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
|
||||
case PackIndexingExprClass:
|
||||
case HLSLOutArgExprClass:
|
||||
case OpenACCAsteriskSizeExprClass:
|
||||
case ResolvedUnexpandedPackExprClass:
|
||||
// These never have a side-effect.
|
||||
return false;
|
||||
|
||||
|
||||
@ -1965,3 +1965,52 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
|
||||
SubExprs[SubExpr::RHS] = RHS;
|
||||
setDependence(computeDependence(this));
|
||||
}
|
||||
|
||||
ResolvedUnexpandedPackExpr::ResolvedUnexpandedPackExpr(SourceLocation BL,
|
||||
QualType QT,
|
||||
unsigned NumExprs)
|
||||
: Expr(ResolvedUnexpandedPackExprClass, QT, VK_PRValue, OK_Ordinary),
|
||||
BeginLoc(BL), NumExprs(NumExprs) {
|
||||
// C++ [temp.dep.expr]p3
|
||||
// An id-expression is type-dependent if it is
|
||||
// - associated by name lookup with a pack
|
||||
setDependence(ExprDependence::TypeValueInstantiation |
|
||||
ExprDependence::UnexpandedPack);
|
||||
}
|
||||
|
||||
ResolvedUnexpandedPackExpr *
|
||||
ResolvedUnexpandedPackExpr::CreateDeserialized(ASTContext &Ctx,
|
||||
unsigned NumExprs) {
|
||||
void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumExprs),
|
||||
alignof(ResolvedUnexpandedPackExpr));
|
||||
return new (Mem)
|
||||
ResolvedUnexpandedPackExpr(SourceLocation(), QualType(), NumExprs);
|
||||
}
|
||||
|
||||
ResolvedUnexpandedPackExpr *
|
||||
ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
|
||||
QualType T, unsigned NumExprs) {
|
||||
void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumExprs),
|
||||
alignof(ResolvedUnexpandedPackExpr));
|
||||
ResolvedUnexpandedPackExpr *New =
|
||||
new (Mem) ResolvedUnexpandedPackExpr(BL, T, NumExprs);
|
||||
|
||||
auto Exprs = New->getExprs();
|
||||
std::uninitialized_fill(Exprs.begin(), Exprs.end(), nullptr);
|
||||
|
||||
return New;
|
||||
}
|
||||
|
||||
ResolvedUnexpandedPackExpr *
|
||||
ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
|
||||
QualType T, ArrayRef<Expr *> Exprs) {
|
||||
auto *New = Create(Ctx, BL, T, Exprs.size());
|
||||
std::uninitialized_copy(Exprs.begin(), Exprs.end(), New->getExprs().begin());
|
||||
return New;
|
||||
}
|
||||
|
||||
ResolvedUnexpandedPackExpr *ResolvedUnexpandedPackExpr::getFromDecl(Decl *D) {
|
||||
if (auto *BD = dyn_cast<BindingDecl>(D))
|
||||
return dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -451,6 +451,13 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
|
||||
case Expr::PackExpansionExprClass:
|
||||
return ClassifyInternal(Ctx, cast<PackExpansionExpr>(E)->getPattern());
|
||||
|
||||
case Expr::ResolvedUnexpandedPackExprClass: {
|
||||
if (cast<ResolvedUnexpandedPackExpr>(E)->getNumExprs() > 0)
|
||||
return ClassifyInternal(
|
||||
Ctx, cast<ResolvedUnexpandedPackExpr>(E)->getExpansion(0));
|
||||
return Cl::CL_LValue;
|
||||
}
|
||||
|
||||
case Expr::MaterializeTemporaryExprClass:
|
||||
return cast<MaterializeTemporaryExpr>(E)->isBoundToLvalueReference()
|
||||
? Cl::CL_LValue
|
||||
|
||||
@ -5225,7 +5225,7 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
|
||||
OK &= EvaluateVarDecl(Info, VD);
|
||||
|
||||
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
|
||||
for (auto *BD : DD->bindings())
|
||||
for (auto *BD : DD->flat_bindings())
|
||||
if (auto *VD = BD->getHoldingVar())
|
||||
OK &= EvaluateDecl(Info, VD);
|
||||
|
||||
@ -17253,6 +17253,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
|
||||
case Expr::SYCLUniqueStableNameExprClass:
|
||||
case Expr::CXXParenListInitExprClass:
|
||||
case Expr::HLSLOutArgExprClass:
|
||||
case Expr::ResolvedUnexpandedPackExprClass:
|
||||
return ICEDiag(IK_NotICE, E->getBeginLoc());
|
||||
|
||||
case Expr::InitListExprClass: {
|
||||
|
||||
@ -4933,7 +4933,7 @@ recurse:
|
||||
case Expr::SourceLocExprClass:
|
||||
case Expr::EmbedExprClass:
|
||||
case Expr::BuiltinBitCastExprClass:
|
||||
{
|
||||
case Expr::ResolvedUnexpandedPackExprClass: {
|
||||
NotPrimaryExpr();
|
||||
if (!NullOut) {
|
||||
// As bad as this diagnostic is, it's better than crashing.
|
||||
|
||||
@ -45,9 +45,11 @@
|
||||
#include "clang/Basic/TypeTraits.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@ -2587,6 +2589,15 @@ void StmtPrinter::VisitPackIndexingExpr(PackIndexingExpr *E) {
|
||||
OS << "]";
|
||||
}
|
||||
|
||||
void StmtPrinter::VisitResolvedUnexpandedPackExpr(
|
||||
ResolvedUnexpandedPackExpr *E) {
|
||||
OS << "<<resolved pack(";
|
||||
llvm::interleave(
|
||||
E->getExprs().begin(), E->getExprs().end(),
|
||||
[this](auto *X) { PrintExpr(X); }, [this] { OS << ", "; });
|
||||
OS << ")>>";
|
||||
}
|
||||
|
||||
void StmtPrinter::VisitSubstNonTypeTemplateParmPackExpr(
|
||||
SubstNonTypeTemplateParmPackExpr *Node) {
|
||||
OS << *Node->getParameterPack();
|
||||
|
||||
@ -2280,6 +2280,10 @@ void StmtProfiler::VisitSizeOfPackExpr(const SizeOfPackExpr *S) {
|
||||
ID.AddInteger(0);
|
||||
}
|
||||
}
|
||||
void StmtProfiler::VisitResolvedUnexpandedPackExpr(
|
||||
const ResolvedUnexpandedPackExpr *S) {
|
||||
VisitExpr(S);
|
||||
}
|
||||
|
||||
void StmtProfiler::VisitPackIndexingExpr(const PackIndexingExpr *E) {
|
||||
VisitExpr(E);
|
||||
|
||||
@ -5083,10 +5083,9 @@ CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage,
|
||||
assert(CGM.getCodeGenOpts().hasReducedDebugInfo());
|
||||
|
||||
if (auto *DD = dyn_cast<DecompositionDecl>(VD)) {
|
||||
for (auto *B : DD->bindings()) {
|
||||
for (BindingDecl *B : DD->flat_bindings())
|
||||
EmitDeclare(B, Storage, std::nullopt, Builder,
|
||||
VD->getType()->isReferenceType());
|
||||
}
|
||||
// Don't emit an llvm.dbg.declare for the composite storage as it doesn't
|
||||
// correspond to a user variable.
|
||||
return nullptr;
|
||||
|
||||
@ -164,9 +164,10 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
|
||||
"Should not see file-scope variables inside a function!");
|
||||
EmitVarDecl(VD);
|
||||
if (auto *DD = dyn_cast<DecompositionDecl>(&VD))
|
||||
for (auto *B : DD->bindings())
|
||||
for (auto *B : DD->flat_bindings())
|
||||
if (auto *HD = B->getHoldingVar())
|
||||
EmitVarDecl(*HD);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -7014,9 +7014,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
|
||||
case Decl::VarTemplateSpecialization:
|
||||
EmitGlobal(cast<VarDecl>(D));
|
||||
if (auto *DD = dyn_cast<DecompositionDecl>(D))
|
||||
for (auto *B : DD->bindings())
|
||||
for (auto *B : DD->flat_bindings())
|
||||
if (auto *HD = B->getHoldingVar())
|
||||
EmitGlobal(HD);
|
||||
|
||||
break;
|
||||
|
||||
// Indirect fields from global anonymous structs and unions can be
|
||||
|
||||
@ -7315,15 +7315,16 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
|
||||
|
||||
// If this doesn't look like a structured binding, maybe it's a misplaced
|
||||
// array declarator.
|
||||
if (!(Tok.is(tok::identifier) &&
|
||||
if (!(Tok.isOneOf(tok::identifier, tok::ellipsis) &&
|
||||
NextToken().isOneOf(tok::comma, tok::r_square, tok::kw_alignas,
|
||||
tok::l_square)) &&
|
||||
tok::identifier, tok::l_square, tok::ellipsis)) &&
|
||||
!(Tok.is(tok::r_square) &&
|
||||
NextToken().isOneOf(tok::equal, tok::l_brace))) {
|
||||
PA.Revert();
|
||||
return ParseMisplacedBracketDeclarator(D);
|
||||
}
|
||||
|
||||
SourceLocation PrevEllipsisLoc;
|
||||
SmallVector<DecompositionDeclarator::Binding, 32> Bindings;
|
||||
while (Tok.isNot(tok::r_square)) {
|
||||
if (!Bindings.empty()) {
|
||||
@ -7338,11 +7339,11 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
|
||||
Diag(Tok, diag::err_expected_comma_or_rsquare);
|
||||
}
|
||||
|
||||
SkipUntil(tok::r_square, tok::comma, tok::identifier,
|
||||
SkipUntil({tok::r_square, tok::comma, tok::identifier, tok::ellipsis},
|
||||
StopAtSemi | StopBeforeMatch);
|
||||
if (Tok.is(tok::comma))
|
||||
ConsumeToken();
|
||||
else if (Tok.isNot(tok::identifier))
|
||||
else if (Tok.is(tok::r_square))
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -7350,6 +7351,21 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
|
||||
if (isCXX11AttributeSpecifier())
|
||||
DiagnoseAndSkipCXX11Attributes();
|
||||
|
||||
SourceLocation EllipsisLoc;
|
||||
|
||||
if (Tok.is(tok::ellipsis)) {
|
||||
Diag(Tok, getLangOpts().CPlusPlus26 ? diag::warn_cxx23_compat_binding_pack
|
||||
: diag::ext_cxx_binding_pack);
|
||||
if (PrevEllipsisLoc.isValid()) {
|
||||
Diag(Tok, diag::err_binding_multiple_ellipses);
|
||||
Diag(PrevEllipsisLoc, diag::note_previous_ellipsis);
|
||||
break;
|
||||
}
|
||||
EllipsisLoc = Tok.getLocation();
|
||||
PrevEllipsisLoc = EllipsisLoc;
|
||||
ConsumeToken();
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::identifier)) {
|
||||
Diag(Tok, diag::err_expected) << tok::identifier;
|
||||
break;
|
||||
@ -7359,6 +7375,13 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
|
||||
SourceLocation Loc = Tok.getLocation();
|
||||
ConsumeToken();
|
||||
|
||||
if (Tok.is(tok::ellipsis) && !PrevEllipsisLoc.isValid()) {
|
||||
DiagnoseMisplacedEllipsis(Tok.getLocation(), Loc, EllipsisLoc.isValid(),
|
||||
true);
|
||||
EllipsisLoc = Tok.getLocation();
|
||||
ConsumeToken();
|
||||
}
|
||||
|
||||
ParsedAttributes Attrs(AttrFactory);
|
||||
if (isCXX11AttributeSpecifier()) {
|
||||
Diag(Tok, getLangOpts().CPlusPlus26
|
||||
@ -7367,7 +7390,7 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
|
||||
MaybeParseCXX11Attributes(Attrs);
|
||||
}
|
||||
|
||||
Bindings.push_back({II, Loc, std::move(Attrs)});
|
||||
Bindings.push_back({II, Loc, std::move(Attrs), EllipsisLoc});
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::r_square))
|
||||
|
||||
@ -888,7 +888,15 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
|
||||
Previous.clear();
|
||||
}
|
||||
|
||||
auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, VarName);
|
||||
QualType QT;
|
||||
if (B.EllipsisLoc.isValid()) {
|
||||
if (!cast<Decl>(DC)->isTemplated())
|
||||
Diag(B.EllipsisLoc, diag::err_pack_outside_template);
|
||||
QT = Context.getPackExpansionType(Context.DependentTy, std::nullopt,
|
||||
/*ExpectsPackInType=*/false);
|
||||
}
|
||||
|
||||
auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name, QT);
|
||||
|
||||
ProcessDeclAttributeList(S, BD, *B.Attrs);
|
||||
|
||||
@ -951,20 +959,68 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
|
||||
return New;
|
||||
}
|
||||
|
||||
static bool checkSimpleDecomposition(
|
||||
Sema &S, ArrayRef<BindingDecl *> Bindings, ValueDecl *Src,
|
||||
QualType DecompType, const llvm::APSInt &NumElems, QualType ElemType,
|
||||
llvm::function_ref<ExprResult(SourceLocation, Expr *, unsigned)> GetInit) {
|
||||
if ((int64_t)Bindings.size() != NumElems) {
|
||||
S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings)
|
||||
<< DecompType << (unsigned)Bindings.size()
|
||||
<< (unsigned)NumElems.getLimitedValue(UINT_MAX)
|
||||
<< toString(NumElems, 10) << (NumElems < Bindings.size());
|
||||
return true;
|
||||
// Check the arity of the structured bindings.
|
||||
// Create the resolved pack expr if needed.
|
||||
static bool CheckBindingsCount(Sema &S, DecompositionDecl *DD,
|
||||
QualType DecompType,
|
||||
ArrayRef<BindingDecl *> Bindings,
|
||||
unsigned MemberCount) {
|
||||
auto BindingWithPackItr =
|
||||
std::find_if(Bindings.begin(), Bindings.end(),
|
||||
[](BindingDecl *D) -> bool { return D->isParameterPack(); });
|
||||
bool HasPack = BindingWithPackItr != Bindings.end();
|
||||
bool IsValid;
|
||||
if (!HasPack) {
|
||||
IsValid = Bindings.size() == MemberCount;
|
||||
} else {
|
||||
// There may not be more members than non-pack bindings.
|
||||
IsValid = MemberCount >= Bindings.size() - 1;
|
||||
}
|
||||
|
||||
if (IsValid && HasPack) {
|
||||
// Create the pack expr and assign it to the binding.
|
||||
unsigned PackSize = MemberCount - Bindings.size() + 1;
|
||||
QualType PackType = S.Context.getPackExpansionType(
|
||||
S.Context.DependentTy, std::nullopt, /*ExpectsPackInType=*/false);
|
||||
BindingDecl *BD = (*BindingWithPackItr);
|
||||
auto *RP = ResolvedUnexpandedPackExpr::Create(S.Context, DD->getBeginLoc(),
|
||||
DecompType, PackSize);
|
||||
BD->setDecomposedDecl(DD);
|
||||
BD->setBinding(PackType, RP);
|
||||
|
||||
BindingDecl *BPack = *BindingWithPackItr;
|
||||
// Create the nested BindingDecls.
|
||||
for (Expr *&E : RP->getExprs()) {
|
||||
auto *NestedBD = BindingDecl::Create(S.Context, BPack->getDeclContext(),
|
||||
BPack->getLocation(),
|
||||
BPack->getIdentifier(), QualType());
|
||||
NestedBD->setDecomposedDecl(DD);
|
||||
E = S.BuildDeclRefExpr(NestedBD, S.Context.DependentTy, VK_LValue,
|
||||
BPack->getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
if (IsValid)
|
||||
return false;
|
||||
|
||||
S.Diag(DD->getLocation(), diag::err_decomp_decl_wrong_number_bindings)
|
||||
<< DecompType << (unsigned)Bindings.size() << MemberCount << MemberCount
|
||||
<< (MemberCount < Bindings.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool checkSimpleDecomposition(
|
||||
Sema &S, ArrayRef<BindingDecl *> Bindings, ValueDecl *Src,
|
||||
QualType DecompType, const llvm::APSInt &NumElemsAPS, QualType ElemType,
|
||||
llvm::function_ref<ExprResult(SourceLocation, Expr *, unsigned)> GetInit) {
|
||||
unsigned NumElems = (unsigned)NumElemsAPS.getLimitedValue(UINT_MAX);
|
||||
auto *DD = cast<DecompositionDecl>(Src);
|
||||
|
||||
if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems))
|
||||
return true;
|
||||
|
||||
unsigned I = 0;
|
||||
for (auto *B : Bindings) {
|
||||
for (auto *B : DD->flat_bindings()) {
|
||||
SourceLocation Loc = B->getLocation();
|
||||
ExprResult E = S.BuildDeclRefExpr(Src, DecompType, VK_LValue, Loc);
|
||||
if (E.isInvalid())
|
||||
@ -1210,13 +1266,10 @@ static bool checkTupleLikeDecomposition(Sema &S,
|
||||
ArrayRef<BindingDecl *> Bindings,
|
||||
VarDecl *Src, QualType DecompType,
|
||||
const llvm::APSInt &TupleSize) {
|
||||
if ((int64_t)Bindings.size() != TupleSize) {
|
||||
S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings)
|
||||
<< DecompType << (unsigned)Bindings.size()
|
||||
<< (unsigned)TupleSize.getLimitedValue(UINT_MAX)
|
||||
<< toString(TupleSize, 10) << (TupleSize < Bindings.size());
|
||||
auto *DD = cast<DecompositionDecl>(Src);
|
||||
unsigned NumElems = (unsigned)TupleSize.getLimitedValue(UINT_MAX);
|
||||
if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Bindings.empty())
|
||||
return false;
|
||||
@ -1250,7 +1303,7 @@ static bool checkTupleLikeDecomposition(Sema &S,
|
||||
}
|
||||
|
||||
unsigned I = 0;
|
||||
for (auto *B : Bindings) {
|
||||
for (auto *B : DD->flat_bindings()) {
|
||||
InitializingBinding InitContext(S, B);
|
||||
SourceLocation Loc = B->getLocation();
|
||||
|
||||
@ -1433,20 +1486,18 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
|
||||
QualType BaseType = S.Context.getQualifiedType(S.Context.getRecordType(RD),
|
||||
DecompType.getQualifiers());
|
||||
|
||||
auto DiagnoseBadNumberOfBindings = [&]() -> bool {
|
||||
unsigned NumFields = llvm::count_if(
|
||||
RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); });
|
||||
assert(Bindings.size() != NumFields);
|
||||
S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings)
|
||||
<< DecompType << (unsigned)Bindings.size() << NumFields << NumFields
|
||||
<< (NumFields < Bindings.size());
|
||||
auto *DD = cast<DecompositionDecl>(Src);
|
||||
unsigned NumFields = llvm::count_if(
|
||||
RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); });
|
||||
if (CheckBindingsCount(S, DD, DecompType, Bindings, NumFields))
|
||||
return true;
|
||||
};
|
||||
|
||||
// all of E's non-static data members shall be [...] well-formed
|
||||
// when named as e.name in the context of the structured binding,
|
||||
// E shall not have an anonymous union member, ...
|
||||
unsigned I = 0;
|
||||
auto FlatBindings = DD->flat_bindings();
|
||||
assert(llvm::range_size(FlatBindings) == NumFields);
|
||||
auto FlatBindingsItr = FlatBindings.begin();
|
||||
for (auto *FD : RD->fields()) {
|
||||
if (FD->isUnnamedBitField())
|
||||
continue;
|
||||
@ -1471,9 +1522,8 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
|
||||
}
|
||||
|
||||
// We have a real field to bind.
|
||||
if (I >= Bindings.size())
|
||||
return DiagnoseBadNumberOfBindings();
|
||||
auto *B = Bindings[I++];
|
||||
assert(FlatBindingsItr != FlatBindings.end());
|
||||
BindingDecl *B = *(FlatBindingsItr++);
|
||||
SourceLocation Loc = B->getLocation();
|
||||
|
||||
// The field must be accessible in the context of the structured binding.
|
||||
@ -1511,9 +1561,6 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
|
||||
B->setBinding(S.BuildQualifiedType(FD->getType(), Loc, Q), E.get());
|
||||
}
|
||||
|
||||
if (I != Bindings.size())
|
||||
return DiagnoseBadNumberOfBindings();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1523,8 +1570,12 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
|
||||
// If the type of the decomposition is dependent, then so is the type of
|
||||
// each binding.
|
||||
if (DecompType->isDependentType()) {
|
||||
for (auto *B : DD->bindings())
|
||||
B->setType(Context.DependentTy);
|
||||
// Note that all of the types are still Null or PackExpansionType.
|
||||
for (auto *B : DD->bindings()) {
|
||||
// Do not overwrite any pack type.
|
||||
if (B->getType().isNull())
|
||||
B->setType(Context.DependentTy);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1068,7 +1068,7 @@ static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) {
|
||||
|
||||
// If this is a decomposition declaration, bindings might throw.
|
||||
if (auto *DD = dyn_cast<DecompositionDecl>(VD))
|
||||
for (auto *B : DD->bindings())
|
||||
for (auto *B : DD->flat_bindings())
|
||||
if (auto *HD = B->getHoldingVar())
|
||||
CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD));
|
||||
|
||||
@ -1286,6 +1286,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
|
||||
case Expr::ConvertVectorExprClass:
|
||||
case Expr::VAArgExprClass:
|
||||
case Expr::CXXParenListInitExprClass:
|
||||
case Expr::ResolvedUnexpandedPackExprClass:
|
||||
return canSubStmtsThrow(*this, S);
|
||||
|
||||
case Expr::CompoundLiteralExprClass:
|
||||
|
||||
@ -2716,8 +2716,10 @@ StmtResult Sema::BuildCXXForRangeStmt(
|
||||
// them in properly when we instantiate the loop.
|
||||
if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) {
|
||||
if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar))
|
||||
for (auto *Binding : DD->bindings())
|
||||
Binding->setType(Context.DependentTy);
|
||||
for (auto *Binding : DD->bindings()) {
|
||||
if (!Binding->isParameterPack())
|
||||
Binding->setType(Context.DependentTy);
|
||||
}
|
||||
LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
|
||||
}
|
||||
} else if (!BeginDeclStmt.get()) {
|
||||
|
||||
@ -1583,6 +1583,10 @@ namespace {
|
||||
/// pack.
|
||||
ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E);
|
||||
|
||||
// Transform a ResolvedUnexpandedPackExpr
|
||||
ExprResult
|
||||
TransformResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E);
|
||||
|
||||
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
|
||||
FunctionProtoTypeLoc TL) {
|
||||
// Call the base version; it will forward to our overridden version below.
|
||||
@ -1848,7 +1852,8 @@ bool TemplateInstantiator::AlreadyTransformed(QualType T) {
|
||||
if (T.isNull())
|
||||
return true;
|
||||
|
||||
if (T->isInstantiationDependentType() || T->isVariablyModifiedType())
|
||||
if (T->isInstantiationDependentType() || T->isVariablyModifiedType() ||
|
||||
T->containsUnexpandedParameterPack())
|
||||
return false;
|
||||
|
||||
getSema().MarkDeclarationsReferencedInType(Loc, T);
|
||||
@ -2473,6 +2478,15 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) {
|
||||
if (PD->isParameterPack())
|
||||
return TransformFunctionParmPackRefExpr(E, PD);
|
||||
|
||||
if (BindingDecl *BD = dyn_cast<BindingDecl>(D); BD && BD->isParameterPack()) {
|
||||
BD = cast_or_null<BindingDecl>(TransformDecl(BD->getLocation(), BD));
|
||||
if (!BD)
|
||||
return ExprError();
|
||||
if (auto *RP =
|
||||
dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding()))
|
||||
return TransformResolvedUnexpandedPackExpr(RP);
|
||||
}
|
||||
|
||||
return inherited::TransformDeclRefExpr(E);
|
||||
}
|
||||
|
||||
@ -2637,6 +2651,19 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
|
||||
return Result;
|
||||
}
|
||||
|
||||
ExprResult TemplateInstantiator::TransformResolvedUnexpandedPackExpr(
|
||||
ResolvedUnexpandedPackExpr *E) {
|
||||
if (getSema().ArgumentPackSubstitutionIndex != -1) {
|
||||
assert(static_cast<unsigned>(getSema().ArgumentPackSubstitutionIndex) <
|
||||
E->getNumExprs() &&
|
||||
"ArgumentPackSubstitutionIndex is out of range");
|
||||
return TransformExpr(
|
||||
E->getExpansion(getSema().ArgumentPackSubstitutionIndex));
|
||||
}
|
||||
|
||||
return inherited::TransformResolvedUnexpandedPackExpr(E);
|
||||
}
|
||||
|
||||
QualType TemplateInstantiator::TransformSubstTemplateTypeParmPackType(
|
||||
TypeLocBuilder &TLB, SubstTemplateTypeParmPackTypeLoc TL,
|
||||
bool SuppressObjCLifetime) {
|
||||
|
||||
@ -1168,26 +1168,57 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
|
||||
|
||||
Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) {
|
||||
auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(),
|
||||
D->getIdentifier());
|
||||
D->getIdentifier(), D->getType());
|
||||
NewBD->setReferenced(D->isReferenced());
|
||||
SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewBD);
|
||||
|
||||
return NewBD;
|
||||
}
|
||||
|
||||
Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) {
|
||||
// Transform the bindings first.
|
||||
// The transformed DD will have all of the concrete BindingDecls.
|
||||
SmallVector<BindingDecl*, 16> NewBindings;
|
||||
for (auto *OldBD : D->bindings())
|
||||
ResolvedUnexpandedPackExpr *OldResolvedPack = nullptr;
|
||||
for (auto *OldBD : D->bindings()) {
|
||||
Expr *BindingExpr = OldBD->getBinding();
|
||||
if (auto *RP =
|
||||
dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BindingExpr)) {
|
||||
assert(!OldResolvedPack && "no more than one pack is allowed");
|
||||
OldResolvedPack = RP;
|
||||
}
|
||||
NewBindings.push_back(cast<BindingDecl>(VisitBindingDecl(OldBD)));
|
||||
}
|
||||
ArrayRef<BindingDecl*> NewBindingArray = NewBindings;
|
||||
|
||||
auto *NewDD = cast_or_null<DecompositionDecl>(
|
||||
auto *NewDD = cast_if_present<DecompositionDecl>(
|
||||
VisitVarDecl(D, /*InstantiatingVarTemplate=*/false, &NewBindingArray));
|
||||
|
||||
if (!NewDD || NewDD->isInvalidDecl())
|
||||
for (auto *NewBD : NewBindings)
|
||||
NewBD->setInvalidDecl();
|
||||
|
||||
if (OldResolvedPack) {
|
||||
// Mark the holding vars (if any) in the pack as instantiated since
|
||||
// they are created implicitly.
|
||||
auto Bindings = NewDD->bindings();
|
||||
auto BPack = llvm::find_if(
|
||||
Bindings, [](BindingDecl *D) -> bool { return D->isParameterPack(); });
|
||||
auto *NewResolvedPack =
|
||||
cast<ResolvedUnexpandedPackExpr>((*BPack)->getBinding());
|
||||
auto OldExprs = OldResolvedPack->getExprs();
|
||||
auto NewExprs = NewResolvedPack->getExprs();
|
||||
assert(OldExprs.size() == NewExprs.size());
|
||||
for (unsigned I = 0; I < OldResolvedPack->getNumExprs(); I++) {
|
||||
DeclRefExpr *OldDRE = cast<DeclRefExpr>(OldExprs[I]);
|
||||
BindingDecl *OldNestedBD = cast<BindingDecl>(OldDRE->getDecl());
|
||||
DeclRefExpr *NewDRE = cast<DeclRefExpr>(NewExprs[I]);
|
||||
BindingDecl *NewNestedBD = cast<BindingDecl>(NewDRE->getDecl());
|
||||
SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldNestedBD,
|
||||
NewNestedBD);
|
||||
}
|
||||
}
|
||||
|
||||
return NewDD;
|
||||
}
|
||||
|
||||
@ -6240,8 +6271,16 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
|
||||
// declarations to their instantiations.
|
||||
if (CurrentInstantiationScope) {
|
||||
if (auto Found = CurrentInstantiationScope->findInstantiationOf(D)) {
|
||||
if (Decl *FD = Found->dyn_cast<Decl *>())
|
||||
if (Decl *FD = Found->dyn_cast<Decl *>()) {
|
||||
if (auto *BD = dyn_cast<BindingDecl>(FD);
|
||||
BD && BD->isParameterPack() &&
|
||||
ArgumentPackSubstitutionIndex != -1) {
|
||||
auto *DRE = cast<DeclRefExpr>(
|
||||
BD->getBindingPackExprs()[ArgumentPackSubstitutionIndex]);
|
||||
return cast<NamedDecl>(DRE->getDecl());
|
||||
}
|
||||
return cast<NamedDecl>(FD);
|
||||
}
|
||||
|
||||
int PackIdx = ArgumentPackSubstitutionIndex;
|
||||
assert(PackIdx != -1 &&
|
||||
|
||||
@ -50,17 +50,29 @@ class CollectUnexpandedParameterPacksVisitor
|
||||
auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
|
||||
if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
|
||||
return;
|
||||
} else if (getDepthAndIndex(ND).first >= DepthLimit)
|
||||
} else if (auto *BD = dyn_cast<BindingDecl>(ND)) {
|
||||
Expr *E = BD->getBinding();
|
||||
if (auto *RP = cast_if_present<ResolvedUnexpandedPackExpr>(E)) {
|
||||
addUnexpanded(RP);
|
||||
return;
|
||||
}
|
||||
} else if (getDepthAndIndex(ND).first >= DepthLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
Unexpanded.push_back({ND, Loc});
|
||||
}
|
||||
|
||||
void addUnexpanded(const TemplateTypeParmType *T,
|
||||
SourceLocation Loc = SourceLocation()) {
|
||||
if (T->getDepth() < DepthLimit)
|
||||
Unexpanded.push_back({T, Loc});
|
||||
}
|
||||
|
||||
void addUnexpanded(ResolvedUnexpandedPackExpr *E) {
|
||||
Unexpanded.push_back({E, E->getBeginLoc()});
|
||||
}
|
||||
|
||||
public:
|
||||
explicit CollectUnexpandedParameterPacksVisitor(
|
||||
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded)
|
||||
@ -103,6 +115,12 @@ class CollectUnexpandedParameterPacksVisitor
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
VisitResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E) override {
|
||||
addUnexpanded(E);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Record occurrences of template template parameter packs.
|
||||
bool TraverseTemplateName(TemplateName Template) override {
|
||||
if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
|
||||
@ -422,8 +440,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
|
||||
if (const TemplateTypeParmType *TTP
|
||||
= Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>())
|
||||
Name = TTP->getIdentifier();
|
||||
else
|
||||
Name = cast<NamedDecl *>(Unexpanded[I].first)->getIdentifier();
|
||||
else if (NamedDecl *ND = Unexpanded[I].first.dyn_cast<NamedDecl *>())
|
||||
Name = ND->getIdentifier();
|
||||
|
||||
if (Name && NamesKnown.insert(Name).second)
|
||||
Names.push_back(Name);
|
||||
@ -757,23 +775,39 @@ bool Sema::CheckParameterPacksForExpansion(
|
||||
bool HaveFirstPack = false;
|
||||
std::optional<unsigned> NumPartialExpansions;
|
||||
SourceLocation PartiallySubstitutedPackLoc;
|
||||
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
|
||||
|
||||
for (UnexpandedParameterPack ParmPack : Unexpanded) {
|
||||
// Compute the depth and index for this parameter pack.
|
||||
unsigned Depth = 0, Index = 0;
|
||||
IdentifierInfo *Name;
|
||||
bool IsVarDeclPack = false;
|
||||
ResolvedUnexpandedPackExpr *ResolvedPack = nullptr;
|
||||
|
||||
if (const TemplateTypeParmType *TTP =
|
||||
ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) {
|
||||
Depth = TTP->getDepth();
|
||||
Index = TTP->getIndex();
|
||||
Name = TTP->getIdentifier();
|
||||
} else if (auto *RP =
|
||||
ParmPack.first.dyn_cast<ResolvedUnexpandedPackExpr *>()) {
|
||||
ResolvedPack = RP;
|
||||
} else {
|
||||
NamedDecl *ND = cast<NamedDecl *>(ParmPack.first);
|
||||
if (isa<VarDecl>(ND))
|
||||
IsVarDeclPack = true;
|
||||
else
|
||||
else if (isa<BindingDecl>(ND)) {
|
||||
// Find the instantiated BindingDecl and check it for a resolved pack.
|
||||
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
|
||||
CurrentInstantiationScope->findInstantiationOf(ND);
|
||||
Decl *B = cast<Decl *>(*Instantiation);
|
||||
Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
|
||||
ResolvedPack = cast_if_present<ResolvedUnexpandedPackExpr>(BindingExpr);
|
||||
if (!ResolvedPack) {
|
||||
ShouldExpand = false;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
std::tie(Depth, Index) = getDepthAndIndex(ND);
|
||||
|
||||
Name = ND->getIdentifier();
|
||||
@ -783,8 +817,6 @@ bool Sema::CheckParameterPacksForExpansion(
|
||||
unsigned NewPackSize, PendingPackExpansionSize = 0;
|
||||
if (IsVarDeclPack) {
|
||||
// Figure out whether we're instantiating to an argument pack or not.
|
||||
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
|
||||
|
||||
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
|
||||
CurrentInstantiationScope->findInstantiationOf(
|
||||
cast<NamedDecl *>(ParmPack.first));
|
||||
@ -797,6 +829,8 @@ bool Sema::CheckParameterPacksForExpansion(
|
||||
ShouldExpand = false;
|
||||
continue;
|
||||
}
|
||||
} else if (ResolvedPack) {
|
||||
NewPackSize = ResolvedPack->getNumExprs();
|
||||
} else {
|
||||
// If we don't have a template argument at this depth/index, then we
|
||||
// cannot expand the pack expansion. Make a note of this, but we still
|
||||
@ -833,7 +867,7 @@ bool Sema::CheckParameterPacksForExpansion(
|
||||
// Template argument deduction can extend the sequence of template
|
||||
// arguments corresponding to a template parameter pack, even when the
|
||||
// sequence contains explicitly specified template arguments.
|
||||
if (!IsVarDeclPack && CurrentInstantiationScope) {
|
||||
if (!IsVarDeclPack && !ResolvedPack && CurrentInstantiationScope) {
|
||||
if (NamedDecl *PartialPack =
|
||||
CurrentInstantiationScope->getPartiallySubstitutedPack()) {
|
||||
unsigned PartialDepth, PartialIndex;
|
||||
@ -939,6 +973,12 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansionFromUnexpanded(
|
||||
Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) {
|
||||
Depth = TTP->getDepth();
|
||||
Index = TTP->getIndex();
|
||||
} else if (auto *PE = Unexpanded[I]
|
||||
.first.dyn_cast<ResolvedUnexpandedPackExpr *>()) {
|
||||
unsigned Size = PE->getNumExprs();
|
||||
assert((!Result || *Result == Size) && "inconsistent pack sizes");
|
||||
Result = Size;
|
||||
continue;
|
||||
} else {
|
||||
NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first);
|
||||
if (isa<VarDecl>(ND)) {
|
||||
@ -1167,8 +1207,12 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S,
|
||||
|
||||
MarkAnyDeclReferenced(OpLoc, ParameterPack, true);
|
||||
|
||||
std::optional<unsigned> Length;
|
||||
if (auto *RP = ResolvedUnexpandedPackExpr::getFromDecl(ParameterPack))
|
||||
Length = RP->getNumExprs();
|
||||
|
||||
return SizeOfPackExpr::Create(Context, OpLoc, ParameterPack, NameLoc,
|
||||
RParenLoc);
|
||||
RParenLoc, Length);
|
||||
}
|
||||
|
||||
static bool isParameterPack(Expr *PackExpression) {
|
||||
|
||||
@ -3680,6 +3680,13 @@ public:
|
||||
FullySubstituted);
|
||||
}
|
||||
|
||||
ExprResult RebuildResolvedUnexpandedPackExpr(SourceLocation BeginLoc,
|
||||
QualType T,
|
||||
ArrayRef<Expr *> Exprs) {
|
||||
return ResolvedUnexpandedPackExpr::Create(SemaRef.Context, BeginLoc, T,
|
||||
Exprs);
|
||||
}
|
||||
|
||||
/// Build a new expression representing a call to a source location
|
||||
/// builtin.
|
||||
///
|
||||
@ -15427,12 +15434,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
||||
// The transform has determined that we should perform an expansion;
|
||||
// transform and capture each of the arguments.
|
||||
// expansion of the pattern. Do so.
|
||||
auto *Pack = cast<VarDecl>(C->getCapturedVar());
|
||||
auto *Pack = cast<ValueDecl>(C->getCapturedVar());
|
||||
for (unsigned I = 0; I != *NumExpansions; ++I) {
|
||||
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
|
||||
VarDecl *CapturedVar
|
||||
= cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(),
|
||||
Pack));
|
||||
ValueDecl *CapturedVar = cast_if_present<ValueDecl>(
|
||||
getDerived().TransformDecl(C->getLocation(), Pack));
|
||||
if (!CapturedVar) {
|
||||
Invalid = true;
|
||||
continue;
|
||||
@ -16127,6 +16133,24 @@ TreeTransform<Derived>::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) {
|
||||
return E;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
ExprResult TreeTransform<Derived>::TransformResolvedUnexpandedPackExpr(
|
||||
ResolvedUnexpandedPackExpr *E) {
|
||||
bool ArgumentChanged = false;
|
||||
SmallVector<Expr *, 12> NewExprs;
|
||||
if (TransformExprs(E->getExprs().begin(), E->getNumExprs(),
|
||||
/*IsCall=*/false, NewExprs, &ArgumentChanged))
|
||||
return ExprError();
|
||||
|
||||
if (!AlwaysRebuild() && !ArgumentChanged)
|
||||
return E;
|
||||
|
||||
// NOTE: The type is just a superficial PackExpansionType
|
||||
// that needs no substitution.
|
||||
return RebuildResolvedUnexpandedPackExpr(E->getBeginLoc(), E->getType(),
|
||||
NewExprs);
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
ExprResult
|
||||
TreeTransform<Derived>::TransformMaterializeTemporaryExpr(
|
||||
|
||||
@ -2208,6 +2208,16 @@ void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) {
|
||||
Exprs[I] = Record.readExpr();
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitResolvedUnexpandedPackExpr(
|
||||
ResolvedUnexpandedPackExpr *E) {
|
||||
VisitExpr(E);
|
||||
E->NumExprs = Record.readInt();
|
||||
E->BeginLoc = readSourceLocation();
|
||||
auto **Exprs = E->getTrailingObjects<Expr *>();
|
||||
for (unsigned I = 0; I < E->NumExprs; ++I)
|
||||
Exprs[I] = Record.readExpr();
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr(
|
||||
SubstNonTypeTemplateParmExpr *E) {
|
||||
VisitExpr(E);
|
||||
@ -4291,6 +4301,12 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
|
||||
/*TransformedExprs=*/Record[ASTStmtReader::NumExprFields]);
|
||||
break;
|
||||
|
||||
case EXPR_RESOLVED_UNEXPANDED_PACK:
|
||||
S = ResolvedUnexpandedPackExpr::CreateDeserialized(
|
||||
Context,
|
||||
/*NumExprs=*/Record[ASTStmtReader::NumExprFields]);
|
||||
break;
|
||||
|
||||
case EXPR_SUBST_NON_TYPE_TEMPLATE_PARM:
|
||||
S = new (Context) SubstNonTypeTemplateParmExpr(Empty);
|
||||
break;
|
||||
|
||||
@ -874,6 +874,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
|
||||
RECORD(EXPR_PACK_EXPANSION);
|
||||
RECORD(EXPR_SIZEOF_PACK);
|
||||
RECORD(EXPR_PACK_INDEXING);
|
||||
RECORD(EXPR_RESOLVED_UNEXPANDED_PACK);
|
||||
RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM);
|
||||
RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK);
|
||||
RECORD(EXPR_FUNCTION_PARM_PACK);
|
||||
|
||||
@ -2210,6 +2210,16 @@ void ASTStmtWriter::VisitPackIndexingExpr(PackIndexingExpr *E) {
|
||||
Code = serialization::EXPR_PACK_INDEXING;
|
||||
}
|
||||
|
||||
void ASTStmtWriter::VisitResolvedUnexpandedPackExpr(
|
||||
ResolvedUnexpandedPackExpr *E) {
|
||||
VisitExpr(E);
|
||||
Record.push_back(E->getNumExprs());
|
||||
Record.AddSourceLocation(E->getBeginLoc());
|
||||
for (Expr *Sub : E->getExprs())
|
||||
Record.AddStmt(Sub);
|
||||
Code = serialization::EXPR_RESOLVED_UNEXPANDED_PACK;
|
||||
}
|
||||
|
||||
void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr(
|
||||
SubstNonTypeTemplateParmExpr *E) {
|
||||
VisitExpr(E);
|
||||
|
||||
@ -1743,6 +1743,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||
case Stmt::DependentCoawaitExprClass:
|
||||
case Stmt::CoreturnStmtClass:
|
||||
case Stmt::CoyieldExprClass:
|
||||
case Stmt::ResolvedUnexpandedPackExprClass:
|
||||
case Stmt::SEHTryStmtClass:
|
||||
case Stmt::SEHExceptStmtClass:
|
||||
case Stmt::SEHLeaveStmtClass:
|
||||
|
||||
74
clang/test/AST/ast-dump-binding-pack.cpp
Normal file
74
clang/test/AST/ast-dump-binding-pack.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
// RUN: %clang_cc1 -ast-dump -std=c++26 %s | FileCheck %s
|
||||
|
||||
// Test this with PCH.
|
||||
// RUN: %clang_cc1 %s -std=c++26 -emit-pch -o %t %s
|
||||
// RUN: %clang_cc1 %s -std=c++26 -include-pch %t -ast-dump-all | FileCheck %s
|
||||
|
||||
#ifndef PCH_HELPER
|
||||
#define PCH_HELPER
|
||||
|
||||
template <unsigned N>
|
||||
void foo() {
|
||||
int arr[4] = {1, 2, 3, 4};
|
||||
auto [binding_1, ...binding_rest, binding_4] = arr;
|
||||
int arr_2[] = {binding_rest...};
|
||||
};
|
||||
|
||||
// CHECK-LABEL: FunctionTemplateDecl {{.*}} foo
|
||||
// CHECK-LABEL: BindingDecl {{.*}} binding_1
|
||||
// CHECK-NEXT: ArraySubscriptExpr {{.*}}
|
||||
// CHECK-NEXT: ImplicitCastExpr
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}}
|
||||
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
|
||||
// CHECK-NOT: BindingDecl
|
||||
// CHECK-LABEL: BindingDecl {{.*}} binding_rest
|
||||
// CHECK-NEXT: ResolvedUnexpandedPackExpr
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest'
|
||||
// CHECK-NOT: BindingDecl
|
||||
// CHECK-LABEL: BindingDecl {{.*}} binding_4
|
||||
// CHECK-NEXT: ArraySubscriptExpr
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}}
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Decomposition {{.*}}
|
||||
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
|
||||
// CHECK-NOT: BindingDecl
|
||||
// CHECK-LABEL: VarDecl {{.*}} arr_2
|
||||
// CHECK-NEXT: InitListExpr
|
||||
// CHECK-NEXT: PackExpansionExpr {{.*}} '<dependent type>' lvalue
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest'
|
||||
|
||||
struct tag_t { };
|
||||
template <unsigned N>
|
||||
void bar() {
|
||||
auto [...empty_binding_pack] = tag_t{};
|
||||
static_assert(sizeof...(empty_binding_pack) == 0);
|
||||
};
|
||||
|
||||
// CHECK-LABEL: FunctionTemplateDecl {{.*}} bar
|
||||
// CHECK-NOT: BindingDecl
|
||||
// CHECK-LABEL: BindingDecl {{.*}} empty_binding_pack
|
||||
// CHECK-NEXT: ResolvedUnexpandedPackExpr
|
||||
// CHECK-NOT: DeclRefExpr {{.*}} 'empty_binding_pack'
|
||||
// CHECK-NOT: BindingDecl
|
||||
// CHECK: DeclStmt
|
||||
|
||||
struct int_pair { int x; int y; };
|
||||
template <typename T>
|
||||
void baz() {
|
||||
auto [binding_1, binding_2, ...empty_binding_pack] = T{};
|
||||
static_assert(sizeof...(empty_binding_pack) == 0);
|
||||
};
|
||||
|
||||
void(*f)() = baz<int_pair>;
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} baz {{.*}} implicit_instantiation
|
||||
// CHECK-NEXT: TemplateArgument type 'int_pair'
|
||||
// CHECK: BindingDecl {{.*}} binding_1
|
||||
// CHECK: BindingDecl {{.*}} binding_2
|
||||
// CHECK-NOT: BindingDecl
|
||||
// CHECK-LABEL: BindingDecl {{.*}} empty_binding_pack
|
||||
// CHECK-NEXT: ResolvedUnexpandedPackExpr
|
||||
// CHECK-NOT: DeclRefExpr {{.*}} 'empty_binding_pack'
|
||||
// CHECK-NOT: BindingDecl
|
||||
// CHECK: DeclStmt
|
||||
#endif
|
||||
56
clang/test/CodeGenCXX/cxx2c-decomposition.cpp
Normal file
56
clang/test/CodeGenCXX/cxx2c-decomposition.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
namespace std {
|
||||
using size_t = decltype(sizeof(0));
|
||||
template<typename> struct tuple_size;
|
||||
template<size_t, typename> struct tuple_element;
|
||||
}
|
||||
|
||||
struct Y { int n; };
|
||||
struct X { X(); X(Y); X(const X&); ~X(); };
|
||||
|
||||
struct A { int a : 13; bool b; };
|
||||
|
||||
struct B {};
|
||||
template<> struct std::tuple_size<B> { enum { value = 2 }; };
|
||||
template<> struct std::tuple_element<0,B> { using type = X; };
|
||||
template<> struct std::tuple_element<1,B> { using type = const int&; };
|
||||
template<int N> auto get(B) {
|
||||
if constexpr (N == 0)
|
||||
return Y();
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
using C = int[2];
|
||||
|
||||
template<typename T> T &make();
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z8big_testIiEiv()
|
||||
template <typename T>
|
||||
int big_test() {
|
||||
A& a = make<A>();
|
||||
A:
|
||||
auto &[...an] = a;
|
||||
an...[0] = 5;
|
||||
// CHECK: %[[a1:.*]].load = load i16, ptr %[[BITFIELD:.*]],
|
||||
// CHECK: %[[a1]].clear = and i16 %[[a1]].load, -8192
|
||||
// CHECK: %[[a1]].set = or i16 %[[a1]].clear, 5
|
||||
// CHECK: store i16 %[[a1]].set, ptr %[[BITFIELD]],
|
||||
B:
|
||||
auto [b1, ...bn] = make<B>();
|
||||
// CHECK: @_Z4makeI1BERT_v()
|
||||
// CHECK: call i32 @_Z3getILi0EEDa1B()
|
||||
// CHECK: call void @_ZN1XC1E1Y(ptr {{[^,]*}} %[[b1:.*]], i32
|
||||
//
|
||||
// CHECK: call noundef double @_Z3getILi1EEDa1B()
|
||||
// CHECK: %[[cvt:.*]] = fptosi double %{{.*}} to i32
|
||||
// CHECK: store i32 %[[cvt]], ptr %[[b2:.*]],
|
||||
// CHECK: store ptr %[[b2]], ptr %[[b2ref:.*]],
|
||||
int bn2 = bn...[0];
|
||||
// CHECK load ptr, ptr %[[b2ref]]
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int g = big_test<int>();
|
||||
25
clang/test/Parser/cxx2c-binding-pack.cpp
Normal file
25
clang/test/Parser/cxx2c-binding-pack.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only %s
|
||||
|
||||
template <unsigned N>
|
||||
void decompose_array() {
|
||||
int arr[4] = {1, 2, 3, 5};
|
||||
auto [x, ... // #1
|
||||
rest, ...more_rest] = arr; // expected-error{{multiple packs in structured binding declaration}}
|
||||
// expected-note@#1{{previous binding pack specified here}}
|
||||
|
||||
auto [y...] = arr; // expected-error{{'...' must immediately precede declared identifier}}
|
||||
|
||||
auto [...] = arr; // #2
|
||||
// expected-error@#2{{expected identifier}}
|
||||
// expected-error@#2{{{no names were provided}}}
|
||||
// expected-warning@#2{{{does not allow a decomposition group to be empty}}}
|
||||
auto [a, ..., b] = arr; // #3
|
||||
// expected-error@#3{{expected identifier}}
|
||||
// expected-error@#3{{{only 1 name was provided}}}
|
||||
auto [a1, ...] = arr; // #4
|
||||
// expected-error@#4{{expected identifier}}
|
||||
// expected-error@#4{{{only 1 name was provided}}}
|
||||
auto [..., b] = arr; // #5
|
||||
// expected-error@#5{{expected identifier}}
|
||||
// expected-error@#5{{{no names were provided}}}
|
||||
}
|
||||
11
clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
Normal file
11
clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
// RUN: %clang_cc1 -std=c++26 -fsyntax-only %s -verify=nontemplate
|
||||
// RUN: %clang_cc1 -std=c++2c -verify=cxx26,nontemplate -fsyntax-only -Wpre-c++26-compat %s
|
||||
// RUN: %clang_cc1 -std=c++23 -verify=cxx23,nontemplate -fsyntax-only -Wc++26-extensions %s
|
||||
|
||||
void decompose_array() {
|
||||
int arr[4] = {1, 2, 3, 6};
|
||||
// cxx26-warning@+3 {{structured binding packs are incompatible with C++ standards before C++2c}}
|
||||
// cxx23-warning@+2 {{structured binding packs are a C++2c extension}}
|
||||
// nontemplate-error@+1 {{pack declaration outside of template}}
|
||||
auto [x, ...rest, y] = arr;
|
||||
}
|
||||
190
clang/test/SemaCXX/cxx2c-binding-pack.cpp
Normal file
190
clang/test/SemaCXX/cxx2c-binding-pack.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -std=c++26 %s -verify
|
||||
|
||||
template <typename T>
|
||||
struct type_ { };
|
||||
|
||||
template <typename ...T>
|
||||
auto sum(T... t) { return (t + ...); }
|
||||
|
||||
struct my_struct {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
};
|
||||
|
||||
struct fake_tuple {
|
||||
int arr[4] = {1, 2, 3, 6};
|
||||
|
||||
template <unsigned i>
|
||||
int get() {
|
||||
return arr[i];
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <typename T>
|
||||
struct tuple_size;
|
||||
template <unsigned i, typename T>
|
||||
struct tuple_element;
|
||||
|
||||
template <>
|
||||
struct tuple_size<fake_tuple> {
|
||||
static constexpr unsigned value = 4;
|
||||
};
|
||||
|
||||
template <unsigned i>
|
||||
struct tuple_element<i, fake_tuple> {
|
||||
using type = int;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void decompose_tuple() {
|
||||
auto tup = T{{1, 2, 3, 6}};
|
||||
auto&& [x, ...rest, y] = tup;
|
||||
|
||||
((void)type_<int>(type_<decltype(rest)>{}), ...);
|
||||
|
||||
T arrtup[2] = {T{{1, 2, 3, 6}},
|
||||
T{{7, 9, 10, 11}}};
|
||||
int sum = 0;
|
||||
for (auto [...xs] : arrtup) {
|
||||
sum += (xs + ...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void decompose_struct() {
|
||||
T obj{1, 2, 3, 6};
|
||||
auto [x, ...rest, y] = obj;
|
||||
|
||||
auto [...empty] = type_<int>{};
|
||||
static_assert(sizeof...(empty) == 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void decompose_array() {
|
||||
int arr[4] = {1, 2, 3, 6};
|
||||
auto [x, ...rest, y] = arr;
|
||||
|
||||
static_assert(sizeof...(rest) == 2);
|
||||
int size = sizeof...(rest);
|
||||
T arr2[sizeof...(rest)] = {rest...};
|
||||
auto [...pack] = arr2;
|
||||
|
||||
// Array of size 1.
|
||||
int arr1[1] = {1};
|
||||
auto [a, ...b] = arr1;
|
||||
static_assert(sizeof...(b) == 0);
|
||||
auto [...c] = arr1;
|
||||
static_assert(sizeof...(c) == 1);
|
||||
auto [a1, ...b1, c1] = arr1; // expected-error{{decomposes into 1 element, but 3 names were provided}}
|
||||
}
|
||||
|
||||
// Test case by Younan Zhang.
|
||||
template <unsigned... P>
|
||||
struct S {
|
||||
template <unsigned... Q>
|
||||
struct N {
|
||||
void foo() {
|
||||
int arr[] = {P..., Q...};
|
||||
auto [x, y, ...rest] = arr;
|
||||
[&]() {
|
||||
static_assert(sizeof...(rest) + 2 == sizeof...(P) + sizeof...(Q));
|
||||
}();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct bit_fields {
|
||||
int a : 4 {1};
|
||||
int b : 4 {2};
|
||||
int c : 4 {3};
|
||||
int d : 4 {4};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void decompose_bit_field() {
|
||||
auto [...x] = T{};
|
||||
static_assert(sizeof...(x) == 4);
|
||||
int a = x...[0];
|
||||
int b = x...[1];
|
||||
int c = x...[2];
|
||||
int d = x...[3];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void lambda_capture() {
|
||||
auto [...x] = T{};
|
||||
[=] { (void)sum(x...); }();
|
||||
[&] { (void)sum(x...); }();
|
||||
[x...] { (void)sum(x...); }();
|
||||
[&x...] { (void)sum(x...); }();
|
||||
}
|
||||
|
||||
int main() {
|
||||
decompose_array<int>();
|
||||
decompose_tuple<fake_tuple>();
|
||||
decompose_struct<my_struct>();
|
||||
S<1, 2, 3, 4>::N<5, 6>().foo();
|
||||
decompose_bit_field<bit_fields>();
|
||||
lambda_capture<int[5]>();
|
||||
lambda_capture<fake_tuple>();
|
||||
lambda_capture<my_struct>();
|
||||
}
|
||||
|
||||
// P1061R10 Stuff
|
||||
namespace {
|
||||
struct C { int x, y, z; };
|
||||
|
||||
template <class T>
|
||||
void now_i_know_my() {
|
||||
auto [a, b, c] = C(); // OK, SB0 is a, SB1 is b, and SB2 is c
|
||||
auto [d, ...e] = C(); // OK, SB0 is d, the pack e (v1) contains two structured bindings: SB1 and SB2
|
||||
static_assert(sizeof...(e) == 2);
|
||||
auto [...f, g] = C(); // OK, the pack f (v0) contains two structured bindings: SB0 and SB1, and SB2 is g
|
||||
static_assert(sizeof...(e) == 2);
|
||||
auto [h, i, j, ...k] = C(); // OK, the pack k is empty
|
||||
static_assert(sizeof...(e) == 0);
|
||||
auto [l, m, n, o, ...p] = C(); // expected-error{{{decomposes into 3 elements, but 5 names were provided}}}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto g() -> int(&)[4];
|
||||
|
||||
template <unsigned long N>
|
||||
void h(int (&arr)[N]) {
|
||||
auto [a, ...b, c] = arr; // a names the first element of the array,
|
||||
// b is a pack referring to the second and
|
||||
// third elements, and c names the fourth element
|
||||
static_assert(sizeof...(b) == 2);
|
||||
auto& [...e] = arr; // e is a pack referring to the four elements of the array
|
||||
static_assert(sizeof...(e) == 4);
|
||||
}
|
||||
|
||||
void call_h() {
|
||||
h(g());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
struct D { };
|
||||
|
||||
int g(...) { return 1; }
|
||||
|
||||
template <typename T>
|
||||
constexpr int f() {
|
||||
D arr[1];
|
||||
auto [...e] = arr;
|
||||
return g(e...);
|
||||
}
|
||||
|
||||
constexpr int g(D) { return 2; }
|
||||
|
||||
void other_main() {
|
||||
static_assert(f<int>() == 2);
|
||||
}
|
||||
} // namespace
|
||||
@ -338,6 +338,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
|
||||
case Stmt::EmbedExprClass:
|
||||
case Stmt::HLSLOutArgExprClass:
|
||||
case Stmt::OpenACCAsteriskSizeExprClass:
|
||||
case Stmt::ResolvedUnexpandedPackExprClass:
|
||||
K = CXCursor_UnexposedExpr;
|
||||
break;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user