https://github.com/ricejasonf created https://github.com/llvm/llvm-project/pull/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. >From 3c81c5bd989f26331917f1401becc1cfa7f4a454 Mon Sep 17 00:00:00 2001 From: Jason Rice <ricejas...@gmail.com> Date: Thu, 22 Jul 2021 16:46:33 -0700 Subject: [PATCH] [Clang][P1061] stuctured binding packs --- clang/include/clang/AST/Decl.h | 8 +- clang/include/clang/AST/DeclCXX.h | 22 +- clang/include/clang/AST/ExprCXX.h | 48 ++++ clang/include/clang/AST/RecursiveASTVisitor.h | 1 + .../clang/Basic/DiagnosticParseKinds.td | 5 + .../clang/Basic/DiagnosticSemaKinds.td | 3 + clang/include/clang/Basic/StmtNodes.td | 1 + clang/include/clang/Sema/DeclSpec.h | 1 + clang/include/clang/Sema/Sema.h | 4 +- .../include/clang/Serialization/ASTBitCodes.h | 1 + clang/lib/AST/ASTContext.cpp | 14 +- clang/lib/AST/ASTImporter.cpp | 2 +- clang/lib/AST/Decl.cpp | 11 +- clang/lib/AST/DeclBase.cpp | 6 +- clang/lib/AST/DeclCXX.cpp | 60 ++++- clang/lib/AST/Expr.cpp | 5 + clang/lib/AST/ExprCXX.cpp | 48 ++++ clang/lib/AST/ExprClassification.cpp | 7 + clang/lib/AST/ExprConstant.cpp | 5 +- clang/lib/AST/ItaniumMangle.cpp | 2 +- clang/lib/AST/StmtPrinter.cpp | 11 + clang/lib/AST/StmtProfile.cpp | 4 + clang/lib/CodeGen/CGDebugInfo.cpp | 4 +- clang/lib/CodeGen/CGDecl.cpp | 5 +- clang/lib/CodeGen/CodeGenModule.cpp | 4 +- clang/lib/Parse/ParseDecl.cpp | 28 ++- clang/lib/Sema/SemaDecl.cpp | 10 + clang/lib/Sema/SemaDeclCXX.cpp | 209 ++++++++++++++---- clang/lib/Sema/SemaExceptionSpec.cpp | 7 +- clang/lib/Sema/SemaLambda.cpp | 1 + clang/lib/Sema/SemaStmt.cpp | 14 +- clang/lib/Sema/SemaTemplate.cpp | 7 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 40 +++- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 32 ++- clang/lib/Sema/SemaTemplateVariadic.cpp | 73 +++++- clang/lib/Sema/TreeTransform.h | 7 + clang/lib/Serialization/ASTReaderStmt.cpp | 11 + clang/lib/Serialization/ASTWriter.cpp | 1 + clang/lib/Serialization/ASTWriterStmt.cpp | 11 + clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/test/Parser/cxx2c-binding-pack.cpp | 7 + .../cxx2c-binding-pack-nontemplate.cpp | 12 + clang/test/SemaCXX/cxx2c-binding-pack.cpp | 82 +++++++ clang/test/SemaCXX/typo-correction-crash.cpp | 3 +- clang/tools/libclang/CXCursor.cpp | 1 + 45 files changed, 728 insertions(+), 111 deletions(-) create mode 100644 clang/test/Parser/cxx2c-binding-pack.cpp create mode 100644 clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp create mode 100644 clang/test/SemaCXX/cxx2c-binding-pack.cpp diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 67ee0bb412692a..bdf6c81732d0bc 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -698,6 +698,10 @@ class ValueDecl : public NamedDecl { 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 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { 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 { diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index c232556edeff70..12002db17fb3ad 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -4131,8 +4131,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; @@ -4140,7 +4141,8 @@ class BindingDecl : public ValueDecl { 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 @@ -4152,10 +4154,6 @@ class BindingDecl : public ValueDecl { /// 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). @@ -4167,6 +4165,9 @@ class BindingDecl : public ValueDecl { /// Set the decomposed variable for this BindingDecl. void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; } + VarDecl *getHoldingVar() const; + static VarDecl *getHoldingVar(Expr *E); + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Decl::Binding; } }; @@ -4219,6 +4220,13 @@ class DecompositionDecl final void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override; + /// Visit the variables (if any) that hold the values of evaluating the + /// binding. Only present for user-defined bindings for tuple-like types. + void VisitHoldingVars(llvm::function_ref<void(VarDecl *)> F) const; + + // Visit the concrete bindings. (workaround) + void VisitBindings(llvm::function_ref<void(BindingDecl *)> F) const; + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Decomposition; } }; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 4cec89c979f775..6162c712c90dc3 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5321,6 +5321,54 @@ class BuiltinBitCastExpr final } }; +class ResolvedUnexpandedPackExpr final + : public Expr, + private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Stmt *> { + 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; } + + Expr **getExprs() { + return reinterpret_cast<Expr **>(getTrailingObjects<Stmt *>()); + } + Expr *const *getExprs() const { + return reinterpret_cast<Expr *const *>(getTrailingObjects<Stmt *>()); + } + + Expr *getExpansion(unsigned Idx) { return getExprs()[Idx]; } + Expr *getExpansion(unsigned Idx) const { return getExprs()[Idx]; } + + // Iterators + child_range children() { + return child_range(getTrailingObjects<Stmt *>(), + getTrailingObjects<Stmt *>() + 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 diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index f5b32ed51698e0..9f8a8f2a8348f2 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2936,6 +2936,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()) { diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 86fcae209c40db..5ee6f9ff28c4c8 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1099,6 +1099,11 @@ 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 ellipses in structured binding declaration">; +def warn_cxx2c_binding_pack : Warning< + "structured binding pack is incompatible with C++ standards before C++2c">, + DefaultIgnore, InGroup<CXXPre26Compat>; def err_capture_default_first : Error< "capture default must be first">; def ext_decl_attrs_on_lambda : ExtWarn< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 330ae045616aba..22012fb2663534 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5906,6 +5906,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< diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 31280df93e4c6e..a5ac8eba371f2f 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -162,6 +162,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>; diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 06243f2624876f..5f5df3a45d41dc 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1795,6 +1795,7 @@ class DecompositionDeclarator { IdentifierInfo *Name; SourceLocation NameLoc; std::optional<ParsedAttributes> Attrs; + SourceLocation EllipsisLoc; }; private: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5ee7ea48cc983c..24e13278cb2a89 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -230,7 +230,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; @@ -6012,6 +6013,7 @@ class Sema final : public SemaBase { 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 diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index dfd82afad40070..bab63be73e58e8 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1890,6 +1890,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 diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 8b4ae58e8427a9..6216d896a88ac3 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12726,11 +12726,15 @@ 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 *BindingVD = BD->getHoldingVar()) - if (DeclMustBeEmitted(BindingVD)) - return true; + if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) { + bool BindingResult = false; + DD->VisitHoldingVars([&](VarDecl *BindingVD) { + if (DeclMustBeEmitted(BindingVD)) + BindingResult = true; + }); + if (BindingResult) + return true; + } return false; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 26d33b0d94795f..33b4fe0b8fecc9 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2552,7 +2552,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(); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 741e908cf9bc56..76c208bef6031c 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -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); @@ -5397,6 +5393,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, diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index fb701f76231bcd..6a5662f9d074e4 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -237,10 +237,12 @@ bool Decl::isTemplateParameterPack() const { } bool Decl::isParameterPack() const { - if (const auto *Var = dyn_cast<VarDecl>(this)) + if (isTemplateParameterPack()) + return true; + if (const auto *Var = dyn_cast<ValueDecl>(this)) return Var->isParameterPack(); - return isTemplateParameterPack(); + return false; } FunctionDecl *Decl::getAsFunction() { diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index af73c658d6a0c5..371bf5dcf02206 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -3395,26 +3395,37 @@ 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_or_null<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 { Expr *B = getBinding(); if (!B) return nullptr; - auto *DRE = dyn_cast<DeclRefExpr>(B->IgnoreImplicit()); + return getHoldingVar(B); +} + +VarDecl *BindingDecl::getHoldingVar(Expr *E) { + auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit()); + if (!DRE) + return nullptr; + if (auto *BD = dyn_cast<BindingDecl>(DRE->getDecl())) { + DRE = dyn_cast<DeclRefExpr>(BD->getBinding()); + } if (!DRE) return nullptr; @@ -3423,6 +3434,45 @@ VarDecl *BindingDecl::getHoldingVar() const { return VD; } +void DecompositionDecl::VisitHoldingVars( + llvm::function_ref<void(VarDecl *)> F) const { + for (BindingDecl *B : bindings()) { + Expr *BE = B->getBinding(); + // All BindingDecls will contain holding vars or none will + if (!BE) + return; + + llvm::ArrayRef<Expr *> Exprs; + if (auto *RP = dyn_cast<ResolvedUnexpandedPackExpr>(BE)) + Exprs = llvm::ArrayRef(RP->getExprs(), RP->getNumExprs()); + else + Exprs = BE; + + for (Expr *E : Exprs) { + VarDecl *VD = BindingDecl::getHoldingVar(E); + if (!VD) + return; + F(VD); + } + } +} + +void DecompositionDecl::VisitBindings( + llvm::function_ref<void(BindingDecl *)> F) const { + for (BindingDecl *B : bindings()) { + llvm::ArrayRef<Expr *> Exprs; + if (B->isParameterPack()) { + auto *RP = cast<ResolvedUnexpandedPackExpr>(B->getBinding()); + Exprs = llvm::ArrayRef(RP->getExprs(), RP->getNumExprs()); + for (Expr *E : Exprs) { + auto *DRE = cast<DeclRefExpr>(E); + F(cast<BindingDecl>(DRE->getDecl())); + } + } else + F(B); + } +} + void DecompositionDecl::anchor() {} DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 8c8ccdb61dc01c..39f02ebf85b2ce 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3656,6 +3656,11 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, // These never have a side-effect. return false; + // ResolvedUnexpandedPackExpr is currently only used for + // structed bindings which have no side effects + case ResolvedUnexpandedPackExprClass: + return false; + case ConstantExprClass: // FIXME: Move this into the "return false;" block above. return cast<ConstantExpr>(this)->getSubExpr()->HasSideEffects( diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index fc09d24fc30cb4..190af789d306ed 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1965,3 +1965,51 @@ 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) { + setDependence(ExprDependence::TypeValueInstantiation | + ExprDependence::UnexpandedPack); +} + +ResolvedUnexpandedPackExpr * +ResolvedUnexpandedPackExpr::CreateDeserialized(ASTContext &Ctx, + unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *>(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<Stmt *>(NumExprs), + alignof(ResolvedUnexpandedPackExpr)); + ResolvedUnexpandedPackExpr *New = + new (Mem) ResolvedUnexpandedPackExpr(BL, T, NumExprs); + + auto Exprs = llvm::MutableArrayRef(New->getExprs(), New->getNumExprs()); + std::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::copy(Exprs.begin(), Exprs.end(), New->getExprs()); + return New; +} + +ResolvedUnexpandedPackExpr *ResolvedUnexpandedPackExpr::getFromDecl(Decl *D) { + // TODO P1858: Extend to VarDecls for P1858 + if (auto *BD = dyn_cast<BindingDecl>(D)) { + return dyn_cast_or_null<ResolvedUnexpandedPackExpr>(BD->getBinding()); + } + return nullptr; +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 3f37d06cc8f3a0..29a869bd76ca37 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -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_PRValue; + } + case Expr::MaterializeTemporaryExprClass: return cast<MaterializeTemporaryExpr>(E)->isBoundToLvalueReference() ? Cl::CL_LValue diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index dd75dca647540a..b7ff7aa1d61ecc 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5155,9 +5155,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()) - if (auto *VD = BD->getHoldingVar()) - OK &= EvaluateDecl(Info, VD); + DD->VisitHoldingVars([&](VarDecl *HD) { OK &= EvaluateDecl(Info, HD); }); return OK; } @@ -17122,6 +17120,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: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 47aa9b40dab845..ffa9703536b7f5 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4926,7 +4926,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, 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. diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index c5d19f70fc6ea0..50bf880a4d37ea 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -44,9 +44,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" @@ -2556,6 +2558,15 @@ void StmtPrinter::VisitPackIndexingExpr(PackIndexingExpr *E) { OS << "]"; } +void StmtPrinter::VisitResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + OS << "<<resolved pack("; + llvm::interleave( + E->getExprs(), E->getExprs() + E->getNumExprs(), + [this](auto *X) { PrintExpr(X); }, [this] { OS << ", "; }); + OS << ")>>"; +} + void StmtPrinter::VisitSubstNonTypeTemplateParmPackExpr( SubstNonTypeTemplateParmPackExpr *Node) { OS << *Node->getParameterPack(); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 27313f9ae12752..1424f03ebf78bc 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2276,6 +2276,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); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index f29ddece5dbc95..db424dabc0575f 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -5067,10 +5067,10 @@ CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage, assert(CGM.getCodeGenOpts().hasReducedDebugInfo()); if (auto *DD = dyn_cast<DecompositionDecl>(VD)) { - for (auto *B : DD->bindings()) { + DD->VisitBindings([&](BindingDecl *B) { 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; diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 47b21bc9f63f04..06b4f39e1e3d81 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -104,7 +104,6 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Binding: case Decl::UnresolvedUsingIfExists: case Decl::HLSLBuffer: - llvm_unreachable("Declaration should not be in declstmts!"); case Decl::Record: // struct/union/class X; case Decl::CXXRecord: // struct/union/class X; [C++] if (CGDebugInfo *DI = getDebugInfo()) @@ -163,9 +162,7 @@ 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()) - if (auto *HD = B->getHoldingVar()) - EmitVarDecl(*HD); + DD->VisitHoldingVars([&](VarDecl *HD) { EmitVarDecl(*HD); }); return; } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index c49f7631488285..bfd459dabafe10 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6987,9 +6987,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::VarTemplateSpecialization: EmitGlobal(cast<VarDecl>(D)); if (auto *DD = dyn_cast<DecompositionDecl>(D)) - for (auto *B : DD->bindings()) - if (auto *HD = B->getHoldingVar()) - EmitGlobal(HD); + DD->VisitHoldingVars([&](VarDecl *HD) { EmitGlobal(HD); }); break; // Indirect fields from global anonymous structs and unions can be diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 937a94b02458c6..c298825fb88280 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -7308,15 +7308,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.is(tok::r_square) && NextToken().isOneOf(tok::equal, tok::l_brace))) { PA.Revert(); return ParseMisplacedBracketDeclarator(D); } + bool HasEllipsis = false; SmallVector<DecompositionDeclarator::Binding, 32> Bindings; while (Tok.isNot(tok::r_square)) { if (!Bindings.empty()) { @@ -7331,11 +7332,12 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { Diag(Tok, diag::err_expected_comma_or_rsquare); } - SkipUntil(tok::r_square, tok::comma, tok::identifier, - StopAtSemi | StopBeforeMatch); + // I don't know why this skipping was here + // SkipUntil(tok::r_square, tok::comma, tok::identifier, + // StopAtSemi | StopBeforeMatch); if (Tok.is(tok::comma)) ConsumeToken(); - else if (Tok.isNot(tok::identifier)) + else if (Tok.is(tok::r_square)) break; } } @@ -7343,6 +7345,20 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { if (isCXX11AttributeSpecifier()) DiagnoseAndSkipCXX11Attributes(); + SourceLocation EllipsisLoc = {}; + + if (Tok.is(tok::ellipsis)) { + if (!getLangOpts().CPlusPlus26) + Diag(Tok, diag::warn_cxx2c_binding_pack); + if (HasEllipsis) { + Diag(Tok, diag::err_binding_multiple_ellipses); + break; + } + HasEllipsis = true; + EllipsisLoc = Tok.getLocation(); + ConsumeToken(); + } + if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected) << tok::identifier; break; @@ -7360,7 +7376,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)) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4001c4d263f1d2..ddb677dc1e70f2 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9780,6 +9780,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, QualType R = TInfo->getType(); assert(R->isFunctionType()); + bool NeedsExpansion = false; + if (R.getCanonicalType()->castAs<FunctionType>()->getCmseNSCallAttr()) Diag(D.getIdentifierLoc(), diag::err_function_decl_cmse_ns_call); @@ -10964,6 +10966,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, D.getFunctionDefinitionKind() == FunctionDefinitionKind::Declaration) ExternalDeclarations.push_back(NewFD); + if (NeedsExpansion) { + CodeSynthesisContext SynthCtx{}; + pushCodeSynthesisContext(SynthCtx); + NewFD = dyn_cast_or_null<FunctionDecl>( + SubstDecl(NewFD, DC, MultiLevelTemplateArgumentList{})); + popCodeSynthesisContext(); + } + return NewFD; } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c5a72cf812ebc9..adabca19107f2b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -888,7 +888,15 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, Previous.clear(); } - auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, VarName); + QualType QT = Context.DependentTy; + if (B.EllipsisLoc.isValid()) { + if (!cast<Decl>(DC)->isTemplated()) + Diag(B.EllipsisLoc, diag::err_pack_outside_template); + QT = Context.getPackExpansionType(QT, std::nullopt, + /*ExpectsPackInType=*/false); + } + + auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name, QT); ProcessDeclAttributeList(S, BD, *B.Attrs); @@ -951,28 +959,130 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, return New; } +namespace { +// CheckBindingsCount +// - Checks the arity of the structured bindings +// - Creates the resolved pack expr if there is +// one +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) { + TemplateTypeParmDecl *DummyTemplateParam = TemplateTypeParmDecl::Create( + S.Context, S.Context.getTranslationUnitDecl(), + /*KeyLoc*/ SourceLocation(), /*NameLoc*/ SourceLocation(), + /*TemplateDepth*/ 0, /*AutoParameterPosition*/ 0, + /*Identifier*/ nullptr, false, /*IsParameterPack*/ true); + + // create the pack expr and assign it to the binding + unsigned PackSize = MemberCount - Bindings.size() + 1; + QualType PackType = S.Context.getPackExpansionType( + QualType(DummyTemplateParam->getTypeForDecl(), 0), PackSize); + (*BindingWithPackItr) + ->setBinding(PackType, + ResolvedUnexpandedPackExpr::Create( + S.Context, DD->getBeginLoc(), DecompType, PackSize)); + } + + 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; +} + +// BindingInitWalker +// - This implements a forward iterating flattened view +// of structured bindings that may have a nested pack. +// It allows the user to set the init expr for either the +// BindingDecl or its ResolvedUnexpandedPackExpr +struct BindingInitWalker { + using BindingItrTy = typename ArrayRef<BindingDecl *>::iterator; + using PackExprItrTy = typename MutableArrayRef<Expr *>::iterator; + Sema &SemaRef; + ArrayRef<BindingDecl *> Bindings; + ResolvedUnexpandedPackExpr *PackExpr = nullptr; + MutableArrayRef<Expr *> PackExprNodes; + BindingItrTy BindingItr; + PackExprItrTy PackExprItr; + + BindingInitWalker(Sema &S, ArrayRef<BindingDecl *> Bs) + : SemaRef(S), Bindings(Bs), BindingItr(Bindings.begin()) {} + + BindingDecl *get() { return *BindingItr; } + + void commitAndAdvance(QualType T, Expr *E) { + BindingDecl *B = *BindingItr; + bool IsPackExpr = + isa_and_nonnull<ResolvedUnexpandedPackExpr>(B->getBinding()); + if (IsPackExpr && !PackExpr) { + PackExpr = cast<ResolvedUnexpandedPackExpr>(B->getBinding()); + PackExprNodes = + llvm::MutableArrayRef(PackExpr->getExprs(), PackExpr->getNumExprs()); + PackExprItr = PackExprNodes.begin(); + } + + if (IsPackExpr) { + // Build a nested BindingDecl with a DeclRefExpr + auto *NestedBD = + BindingDecl::Create(SemaRef.Context, B->getDeclContext(), + B->getLocation(), B->getIdentifier(), T); + + NestedBD->setBinding(T, E); + NestedBD->setDecomposedDecl(nullptr); + auto *DE = SemaRef.BuildDeclRefExpr(NestedBD, T.getNonReferenceType(), + VK_LValue, B->getLocation()); + *PackExprItr = DE; + if (++PackExprItr != PackExprNodes.end()) + return; + // If we hit the end of the pack exprs then + // continue to advance BindingItr + } else { + (*BindingItr)->setBinding(T, E); + } + + ++BindingItr; + } +}; +} // namespace + static bool checkSimpleDecomposition( Sema &S, ArrayRef<BindingDecl *> Bindings, ValueDecl *Src, - QualType DecompType, const llvm::APSInt &NumElems, QualType ElemType, + QualType DecompType, const llvm::APSInt &NumElemsAPS, 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()); + unsigned NumElems = (unsigned)NumElemsAPS.getLimitedValue(UINT_MAX); + + if (CheckBindingsCount(S, cast<DecompositionDecl>(Src), DecompType, Bindings, + NumElems)) { + return true; } - unsigned I = 0; - for (auto *B : Bindings) { + auto Walker = BindingInitWalker(S, Bindings); + for (unsigned I = 0; I < NumElems; I++) { + BindingDecl *B = Walker.get(); SourceLocation Loc = B->getLocation(); ExprResult E = S.BuildDeclRefExpr(Src, DecompType, VK_LValue, Loc); if (E.isInvalid()) return true; - E = GetInit(Loc, E.get(), I++); + E = GetInit(Loc, E.get(), I); if (E.isInvalid()) return true; - B->setBinding(ElemType, E.get()); + Walker.commitAndAdvance(ElemType, E.get()); } return false; @@ -1210,11 +1320,9 @@ 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()); + unsigned NumElems = (unsigned)TupleSize.getLimitedValue(UINT_MAX); + if (CheckBindingsCount(S, cast<DecompositionDecl>(Src), DecompType, Bindings, + NumElems)) { return true; } @@ -1249,8 +1357,9 @@ static bool checkTupleLikeDecomposition(Sema &S, } } - unsigned I = 0; - for (auto *B : Bindings) { + auto Walker = BindingInitWalker(S, Bindings); + for (unsigned I = 0; I < NumElems; I++) { + BindingDecl *B = Walker.get(); InitializingBinding InitContext(S, B); SourceLocation Loc = B->getLocation(); @@ -1336,8 +1445,7 @@ static bool checkTupleLikeDecomposition(Sema &S, if (E.isInvalid()) return true; - B->setBinding(T, E.get()); - I++; + Walker.commitAndAdvance(T, E.get()); } return false; @@ -1433,20 +1541,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()); + unsigned NumFields = llvm::count_if( + RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); }); + if (CheckBindingsCount(S, cast<DecompositionDecl>(Src), DecompType, Bindings, + NumFields)) { return true; - }; + } + + auto Walker = BindingInitWalker(S, Bindings); // 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; for (auto *FD : RD->fields()) { if (FD->isUnnamedBitField()) continue; @@ -1471,9 +1577,7 @@ 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++]; + BindingDecl *B = Walker.get(); SourceLocation Loc = B->getLocation(); // The field must be accessible in the context of the structured binding. @@ -1508,23 +1612,52 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings, Qualifiers Q = DecompType.getQualifiers(); if (FD->isMutable()) Q.removeConst(); - B->setBinding(S.BuildQualifiedType(FD->getType(), Loc, Q), E.get()); + Walker.commitAndAdvance(S.BuildQualifiedType(FD->getType(), Loc, Q), + E.get()); } - if (I != Bindings.size()) - return DiagnoseBadNumberOfBindings(); - return false; } +unsigned Sema::GetDecompositionElementCount(QualType DecompType) { + assert(!DecompType->isDependentType() && "expecting non-dependent type"); + SourceLocation Loc = SourceLocation(); // FIXME + DecompType = DecompType.getNonReferenceType(); + if (auto *CAT = Context.getAsConstantArrayType(DecompType)) + return CAT->getSize().getLimitedValue(UINT_MAX); + if (auto *VT = DecompType->getAs<VectorType>()) + return VT->getNumElements(); + if (auto *CT = DecompType->getAs<ComplexType>()) + return 2; + llvm::APSInt TupleSize(32); + if (IsTupleLike TL = isTupleLike(*this, Loc, DecompType, TupleSize); + TL == IsTupleLike::TupleLike) + return (unsigned)TupleSize.getLimitedValue(UINT_MAX); + + if (CXXRecordDecl *RD = DecompType->getAsCXXRecordDecl(); + RD && !RD->isUnion()) { + CXXCastPath BasePath; + DeclAccessPair BasePair = + findDecomposableBaseClass(*this, Loc, RD, BasePath); + RD = cast_or_null<CXXRecordDecl>(BasePair.getDecl()); + if (RD) + return llvm::count_if( + RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); }); + } + + llvm_unreachable("unknown type for decomposition"); +} + void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) { QualType DecompType = DD->getType(); // 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); + for (auto *B : DD->bindings()) { + if (B->getType().isNull()) + B->setType(Context.DependentTy); + } return; } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index ac3666394d0e86..079ba597ed0cf1 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1068,9 +1068,9 @@ 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()) - if (auto *HD = B->getHoldingVar()) - CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD)); + DD->VisitHoldingVars([&](VarDecl *HD) { + CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD)); + }); return CT; } @@ -1278,6 +1278,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: diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index a67c0b2b367d1a..e008a26ff7f062 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -1526,6 +1526,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, // - a member of a templated entity, // - an enumerator for an enumeration that is a templated entity, or // - the closure type of a lambda-expression ([expr.prim.lambda.closure]) + // - an entity defined with an implicit template region // appearing in the declaration of a templated entity. [Note 6: A local // class, a local or block variable, or a friend function defined in a // templated entity is a templated entity. — end note] diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index d9149f7ee40bbf..2d7ba56657a0c2 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -755,7 +755,7 @@ bool Sema::checkMustTailAttr(const Stmt *St, const Attr &MTA) { return true; }; - const auto *CallerDecl = dyn_cast<FunctionDecl>(CurContext); + const auto *CallerDecl = getCurFunctionDecl(); // Find caller function signature. if (!CallerDecl) { @@ -1010,8 +1010,7 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool Immediate = ExprEvalContexts.back().Context == ExpressionEvaluationContext::ImmediateFunctionContext; if (CurContext->isFunctionOrMethod()) { - const auto *FD = - dyn_cast<FunctionDecl>(Decl::castFromDeclContext(CurContext)); + const auto *FD = getCurFunctionDecl(); if (FD && FD->isImmediateFunction()) Immediate = true; } @@ -2670,8 +2669,11 @@ 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()) { @@ -3916,7 +3918,7 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, // deduction. if (getLangOpts().CPlusPlus14) { if (AutoType *AT = FnRetType->getContainedAutoType()) { - FunctionDecl *FD = cast<FunctionDecl>(CurContext); + FunctionDecl *FD = getCurFunctionDecl(); // If we've already decided this function is invalid, e.g. because // we saw a `return` whose expression had an error, don't keep // trying to deduce its return type. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 5e7a3c8484c88f..ef19d0d52213b2 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -928,9 +928,10 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef, void Sema::translateTemplateArguments(const ASTTemplateArgsPtr &TemplateArgsIn, TemplateArgumentListInfo &TemplateArgs) { - for (unsigned I = 0, Last = TemplateArgsIn.size(); I != Last; ++I) - TemplateArgs.addArgument(translateTemplateArgument(*this, - TemplateArgsIn[I])); + for (unsigned I = 0, Last = TemplateArgsIn.size(); I != Last; ++I) { + TemplateArgs.addArgument( + translateTemplateArgument(*this, TemplateArgsIn[I])); + } } static void maybeDiagnoseTemplateParameterShadow(Sema &SemaRef, Scope *S, diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index fb0f38df62a744..ca11a1b7c910fa 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -805,7 +805,6 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( SemaRef, CodeSynthesisContext::BuildingDeductionGuides, PointOfInstantiation, InstantiationRange, Entity) {} - void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext; InNonInstantiationSFINAEContext = false; @@ -1569,6 +1568,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. @@ -1851,7 +1854,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); @@ -2484,6 +2488,14 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) { if (PD->isParameterPack()) return TransformFunctionParmPackRefExpr(E, PD); + if (BindingDecl *BD = dyn_cast<BindingDecl>(D); BD && BD->isParameterPack()) { + BD = cast<BindingDecl>(TransformDecl(BD->getLocation(), BD)); + if (auto *RP = + dyn_cast_or_null<ResolvedUnexpandedPackExpr>(BD->getBinding())) { + return TransformResolvedUnexpandedPackExpr(RP); + } + } + return inherited::TransformDeclRefExpr(E); } @@ -2648,6 +2660,30 @@ 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)); + } + + if (!AlwaysRebuild()) + return E; + + SmallVector<Expr *, 12> NewExprs; + if (TransformExprs(E->getExprs(), E->getNumExprs(), + /*IsCall=*/false, NewExprs)) + return ExprError(); + + // NOTE: The type is just a superficial PackExpansionType + // that needs no substitution. + return ResolvedUnexpandedPackExpr::Create(SemaRef.Context, E->getBeginLoc(), + E->getType(), NewExprs); +} + QualType TemplateInstantiator::TransformSubstTemplateTypeParmPackType( TypeLocBuilder &TLB, SubstTemplateTypeParmPackTypeLoc TL, bool SuppressObjCLifetime) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index e058afe81da589..68d090c3a558ad 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1166,17 +1166,24 @@ 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_or_null<ResolvedUnexpandedPackExpr>(BindingExpr)) + OldResolvedPack = RP; NewBindings.push_back(cast<BindingDecl>(VisitBindingDecl(OldBD))); + } ArrayRef<BindingDecl*> NewBindingArray = NewBindings; auto *NewDD = cast_or_null<DecompositionDecl>( @@ -1186,6 +1193,27 @@ Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) { 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 = std::find_if( + Bindings.begin(), Bindings.end(), + [](BindingDecl *D) -> bool { return D->isParameterPack(); }); + auto *NewResolvedPack = + cast<ResolvedUnexpandedPackExpr>((*BPack)->getBinding()); + Expr **OldExprs = OldResolvedPack->getExprs(); + Expr **NewExprs = NewResolvedPack->getExprs(); + 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; } diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index c8452db6bc9014..c4e6e090705104 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -19,6 +19,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/Support/SaveAndRestore.h" #include <optional> @@ -50,16 +51,28 @@ class CollectUnexpandedParameterPacksVisitor auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr; if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit) return; - } else if (getDepthAndIndex(ND).first >= DepthLimit) + } else if (ND->isTemplateParameterPack() && + getDepthAndIndex(ND).first >= DepthLimit) { return; + } else if (auto *BD = dyn_cast<BindingDecl>(ND)) { + Expr *E = BD->getBinding(); + if (auto *RP = dyn_cast_or_null<ResolvedUnexpandedPackExpr>(E)) { + addUnexpanded(RP); + 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( @@ -103,6 +116,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>( @@ -210,6 +229,10 @@ class CollectUnexpandedParameterPacksVisitor bool TraversePackIndexingTypeLoc(PackIndexingTypeLoc TL) override { return DynamicRecursiveASTVisitor::TraverseStmt(TL.getIndexExpr()); } + bool + TraverseResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E) override { + return true; + } ///@} @@ -422,8 +445,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 +780,42 @@ 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); + if (Decl *B = Instantiation->dyn_cast<Decl *>()) { + Expr *BindingExpr = cast<BindingDecl>(B)->getBinding(); + if (auto *RP = dyn_cast<ResolvedUnexpandedPackExpr>(BindingExpr)) { + ResolvedPack = RP; + } + } + if (!ResolvedPack) { + ShouldExpand = false; + continue; + } + } else std::tie(Depth, Index) = getDepthAndIndex(ND); Name = ND->getIdentifier(); @@ -797,6 +839,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 +877,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 +983,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 +1217,13 @@ 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) { @@ -1439,8 +1494,10 @@ ExprResult Sema::ActOnCXXFoldExpr(Scope *S, SourceLocation LParenLoc, Expr *LHS, } } - return BuildCXXFoldExpr(ULE, LParenLoc, LHS, Opc, EllipsisLoc, RHS, RParenLoc, - std::nullopt); + ExprResult Result = BuildCXXFoldExpr(ULE, LParenLoc, LHS, Opc, EllipsisLoc, + RHS, RParenLoc, std::nullopt); + + return Result; } ExprResult Sema::BuildCXXFoldExpr(UnresolvedLookupExpr *Callee, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 5d43d98ce49e46..7af74c6d469a55 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -15991,6 +15991,13 @@ TreeTransform<Derived>::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) { return E; } +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + // Default behavior is to do nothing with this transformation. + return E; +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformMaterializeTemporaryExpr( diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 9e8cf19a6f0f72..f1ed2d81676bf2 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2201,6 +2201,11 @@ void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) { Exprs[I] = Record.readExpr(); } +void ASTStmtReader::VisitResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + VisitExpr(E); +} + void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { VisitExpr(E); @@ -4270,6 +4275,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; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 4a6027943072c0..2b531659f1920f 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -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); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 1d42b43c3e2ca0..26967160d40b4e 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2202,6 +2202,17 @@ 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()); + auto SubExprs = llvm::ArrayRef(E->getExprs(), E->getNumExprs()); + for (Expr *Sub : SubExprs) + Record.AddStmt(Sub); + Code = serialization::EXPR_RESOLVED_UNEXPANDED_PACK; +} + void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { VisitExpr(E); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index db385e891e762f..312c7870948cd9 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -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: diff --git a/clang/test/Parser/cxx2c-binding-pack.cpp b/clang/test/Parser/cxx2c-binding-pack.cpp new file mode 100644 index 00000000000000..ab8bdd54935f91 --- /dev/null +++ b/clang/test/Parser/cxx2c-binding-pack.cpp @@ -0,0 +1,7 @@ +// 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, ...rest, ...more_rest] = arr; // expected-error{{multiple ellipses in structured binding declaration}} +} diff --git a/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp b/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp new file mode 100644 index 00000000000000..9c02083cf88f7e --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++2c %s -verify + +void decompose_array() { + int arr[4] = {1, 2, 3, 6}; + auto [x, ...rest, y] = arr; // expected-error{{pack declaration outside of template}} +} + +template <unsigned N> +void decompose_array_2() { + int arr[4] = {1, 2, 3, N}; + auto [x, ...rest, ...y] = arr; // expected-error{{multiple ellipses in structured binding declaration}} +} diff --git a/clang/test/SemaCXX/cxx2c-binding-pack.cpp b/clang/test/SemaCXX/cxx2c-binding-pack.cpp new file mode 100644 index 00000000000000..ee9466e3b12c70 --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-binding-pack.cpp @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++2b %s -verify +// expected-no-diagnostics + +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; +} + +template <typename T> +void decompose_array() { + // previously unable to use non-dependent array here + // Fixes https://bugs.llvm.org/show_bug.cgi?id=45964 + 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; +} + +int main() { + decompose_array<int>(); + decompose_tuple<fake_tuple>(); + decompose_struct<my_struct>(); +} diff --git a/clang/test/SemaCXX/typo-correction-crash.cpp b/clang/test/SemaCXX/typo-correction-crash.cpp index 2a77c9df505e8f..9ba2d92fa8bd79 100644 --- a/clang/test/SemaCXX/typo-correction-crash.cpp +++ b/clang/test/SemaCXX/typo-correction-crash.cpp @@ -32,7 +32,8 @@ FooRecord::NestedNamespace::type x; // expected-error {{no member named 'NestedN void cast_expr(int g) { +int(n)(g); } // expected-error {{undeclared identifier 'n'}} -void bind() { for (const auto& [test,_] : _test_) { }; } // expected-error {{undeclared identifier '_test_'}} +void bind() { for (const auto& [test,_] : _test_) { }; } // expected-error {{undeclared identifier '_test_'}} \ + // expected-note {{'test' declared here}} namespace NoCrash { class S { diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index f56e77b42f9d73..694d86a2ce9aec 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits