From b2997f579a8b6552a49eab97e33c437b9251eb0a Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 21 May 2019 20:10:50 +0000 Subject: [PATCH] [c++20] P0780R2: Support pack-expansion of init-captures. This permits an init-capture to introduce a new pack: template auto x = [...a = T()] { /* a is a pack */ }; To support this, the mechanism for allowing ParmVarDecls to be packs has been extended to support arbitrary local VarDecls. llvm-svn: 361300 --- clang/include/clang/AST/ASTContext.h | 2 +- clang/include/clang/AST/Decl.h | 8 +- clang/include/clang/AST/ExprCXX.h | 24 +-- clang/include/clang/AST/Type.h | 10 +- .../clang/Basic/DiagnosticParseKinds.td | 13 +- .../clang/Basic/DiagnosticSemaKinds.td | 5 + clang/include/clang/Sema/Sema.h | 15 +- clang/include/clang/Sema/Template.h | 4 +- clang/lib/AST/ASTContext.cpp | 17 ++- clang/lib/AST/Decl.cpp | 8 +- clang/lib/AST/DeclBase.cpp | 4 +- clang/lib/AST/ExprCXX.cpp | 14 +- clang/lib/AST/ItaniumMangle.cpp | 61 ++++---- clang/lib/AST/JSONNodeDumper.cpp | 1 + clang/lib/AST/TextNodeDumper.cpp | 2 + clang/lib/AST/Type.cpp | 4 + clang/lib/Parse/ParseExprCXX.cpp | 136 +++++++++++------ clang/lib/Sema/SemaLambda.cpp | 42 +++-- clang/lib/Sema/SemaTemplateDeduction.cpp | 33 +++- clang/lib/Sema/SemaTemplateInstantiate.cpp | 56 ++++--- clang/lib/Sema/SemaTemplateVariadic.cpp | 38 ++--- clang/lib/Sema/TreeTransform.h | 143 ++++++++++++------ clang/lib/Serialization/ASTReader.cpp | 9 +- clang/lib/Serialization/ASTReaderStmt.cpp | 4 +- clang/lib/Serialization/ASTWriter.cpp | 3 +- .../expr.prim.lambda.capture/p17.cpp | 42 +++++ .../expr/expr.prim/expr.prim.lambda/p23.cpp | 26 +++- .../temp.decls/temp.variadic/init-capture.cpp | 39 +++++ clang/test/FixIt/fixit-c++2a.cpp | 15 ++ clang/test/SemaTemplate/sizeof-pack.cpp | 8 +- clang/www/cxx_status.html | 2 +- 31 files changed, 543 insertions(+), 245 deletions(-) create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.capture/p17.cpp create mode 100644 clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp create mode 100644 clang/test/FixIt/fixit-c++2a.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index e27d9155be1a..5cb4a82ced19 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1522,7 +1522,7 @@ public: /// C++11 deduced auto type. QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, - bool IsDependent) const; + bool IsDependent, bool IsPack = false) const; /// C++11 deduction pattern for 'auto' type. QualType getAutoDeductType() const; diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index cfb9ff894879..6c5f5944f387 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1395,6 +1395,10 @@ 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 { @@ -1688,10 +1692,6 @@ public: QualType getOriginalType() const; - /// Determine whether this parameter is actually a function - /// parameter pack. - bool isParameterPack() const; - /// Sets the function declaration that owns this /// ParmVarDecl. Since ParmVarDecls are often created before the /// FunctionDecls that own them, this routine is required to update diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 401cd33c0374..4a6aa9014c9e 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4233,8 +4233,8 @@ public: } }; -/// Represents a reference to a function parameter pack that has been -/// substituted but not yet expanded. +/// Represents a reference to a function parameter pack or init-capture pack +/// that has been substituted but not yet expanded. /// /// When a pack expansion contains multiple parameter packs at different levels, /// this node is used to represent a function parameter pack at an outer level @@ -4249,13 +4249,13 @@ public: /// \endcode class FunctionParmPackExpr final : public Expr, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend class ASTReader; friend class ASTStmtReader; friend TrailingObjects; /// The function parameter pack which was referenced. - ParmVarDecl *ParamPack; + VarDecl *ParamPack; /// The location of the function parameter pack reference. SourceLocation NameLoc; @@ -4263,35 +4263,35 @@ class FunctionParmPackExpr final /// The number of expansions of this pack. unsigned NumParameters; - FunctionParmPackExpr(QualType T, ParmVarDecl *ParamPack, + FunctionParmPackExpr(QualType T, VarDecl *ParamPack, SourceLocation NameLoc, unsigned NumParams, - ParmVarDecl *const *Params); + VarDecl *const *Params); public: static FunctionParmPackExpr *Create(const ASTContext &Context, QualType T, - ParmVarDecl *ParamPack, + VarDecl *ParamPack, SourceLocation NameLoc, - ArrayRef Params); + ArrayRef Params); static FunctionParmPackExpr *CreateEmpty(const ASTContext &Context, unsigned NumParams); /// Get the parameter pack which this expression refers to. - ParmVarDecl *getParameterPack() const { return ParamPack; } + VarDecl *getParameterPack() const { return ParamPack; } /// Get the location of the parameter pack. SourceLocation getParameterPackLocation() const { return NameLoc; } /// Iterators over the parameters which the parameter pack expanded /// into. - using iterator = ParmVarDecl * const *; - iterator begin() const { return getTrailingObjects(); } + using iterator = VarDecl * const *; + iterator begin() const { return getTrailingObjects(); } iterator end() const { return begin() + NumParameters; } /// Get the number of parameters in this parameter pack. unsigned getNumExpansions() const { return NumParameters; } /// Get an expansion of the parameter pack by index. - ParmVarDecl *getExpansion(unsigned I) const { return begin()[I]; } + VarDecl *getExpansion(unsigned I) const { return begin()[I]; } SourceLocation getBeginLoc() const LLVM_READONLY { return NameLoc; } SourceLocation getEndLoc() const LLVM_READONLY { return NameLoc; } diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 7c457ccee196..bc5484d3c11b 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -4797,9 +4797,9 @@ class AutoType : public DeducedType, public llvm::FoldingSetNode { friend class ASTContext; // ASTContext creates these AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, - bool IsDeducedAsDependent) + bool IsDeducedAsDependent, bool IsDeducedAsPack) : DeducedType(Auto, DeducedAsType, IsDeducedAsDependent, - IsDeducedAsDependent, /*ContainsPack=*/false) { + IsDeducedAsDependent, IsDeducedAsPack) { AutoTypeBits.Keyword = (unsigned)Keyword; } @@ -4813,14 +4813,16 @@ public: } void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getDeducedType(), getKeyword(), isDependentType()); + Profile(ID, getDeducedType(), getKeyword(), isDependentType(), + containsUnexpandedParameterPack()); } static void Profile(llvm::FoldingSetNodeID &ID, QualType Deduced, - AutoTypeKeyword Keyword, bool IsDependent) { + AutoTypeKeyword Keyword, bool IsDependent, bool IsPack) { ID.AddPointer(Deduced.getAsOpaquePtr()); ID.AddInteger((unsigned)Keyword); ID.AddBoolean(IsDependent); + ID.AddBoolean(IsPack); } static bool classof(const Type *T) { diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index a52cdbd4cf43..fb281a5be86a 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -878,6 +878,11 @@ def err_lambda_missing_parens : Error< "attribute specifier|'constexpr'}0">; def err_lambda_decl_specifier_repeated : Error< "%select{'mutable'|'constexpr'}0 cannot appear multiple times in a lambda declarator">; +def err_lambda_capture_misplaced_ellipsis : Error< + "ellipsis in pack %select{|init-}0capture must appear %select{after|before}0 " + "the name of the capture">; +def err_lambda_capture_multiple_ellipses : Error< + "multiple ellipses in pack capture">; // C++17 lambda expressions def err_expected_star_this_capture : Error< "expected 'this' following '*' in lambda capture list">; @@ -889,15 +894,15 @@ def warn_cxx14_compat_constexpr_on_lambda : Warning< def ext_constexpr_on_lambda_cxx17 : ExtWarn< "'constexpr' on lambda expressions is a C++17 extension">, InGroup; - // C++2a template lambdas - def ext_lambda_template_parameter_list: ExtWarn< +// C++2a template lambdas +def ext_lambda_template_parameter_list: ExtWarn< "explicit template parameter list for lambdas is a C++2a extension">, InGroup; def warn_cxx17_compat_lambda_template_parameter_list: Warning< "explicit template parameter list for lambdas is incompatible with " "C++ standards before C++2a">, InGroup, DefaultIgnore; - def err_lambda_template_parameter_list_empty : Error< - "lambda template parameter list cannot be empty">; +def err_lambda_template_parameter_list_empty : Error< + "lambda template parameter list cannot be empty">; // Availability attribute def err_expected_version : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 50832bd6bc3d..48b0ec4b378e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6663,6 +6663,11 @@ let CategoryName = "Lambda Issue" in { "cannot deduce type for lambda capture %0 from initializer of type %2">; def err_init_capture_deduction_failure_from_init_list : Error< "cannot deduce type for lambda capture %0 from initializer list">; + def warn_cxx17_compat_init_capture_pack : Warning< + "initialized lambda capture packs are incompatible with C++ standards " + "before C++2a">, InGroup, DefaultIgnore; + def ext_init_capture_pack : ExtWarn< + "initialized lambda pack captures are a C++2a extension">, InGroup; // C++14 generic lambdas. def warn_cxx11_compat_generic_lambda : Warning< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 8e13467a4a07..ddf393d46e21 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5711,14 +5711,16 @@ public: /// any implicit conversions such as an lvalue-to-rvalue conversion if /// not being used to initialize a reference. ParsedType actOnLambdaInitCaptureInitialization( - SourceLocation Loc, bool ByRef, IdentifierInfo *Id, - LambdaCaptureInitKind InitKind, Expr *&Init) { + SourceLocation Loc, bool ByRef, SourceLocation EllipsisLoc, + IdentifierInfo *Id, LambdaCaptureInitKind InitKind, Expr *&Init) { return ParsedType::make(buildLambdaInitCaptureInitialization( - Loc, ByRef, Id, InitKind != LambdaCaptureInitKind::CopyInit, Init)); + Loc, ByRef, EllipsisLoc, None, Id, + InitKind != LambdaCaptureInitKind::CopyInit, Init)); } - QualType buildLambdaInitCaptureInitialization(SourceLocation Loc, bool ByRef, - IdentifierInfo *Id, - bool DirectInit, Expr *&Init); + QualType buildLambdaInitCaptureInitialization( + SourceLocation Loc, bool ByRef, SourceLocation EllipsisLoc, + Optional NumExpansions, IdentifierInfo *Id, bool DirectInit, + Expr *&Init); /// Create a dummy variable within the declcontext of the lambda's /// call operator, for name lookup purposes for a lambda init capture. @@ -5727,6 +5729,7 @@ public: /// variables appropriately. VarDecl *createLambdaInitCaptureVarDecl(SourceLocation Loc, QualType InitCaptureType, + SourceLocation EllipsisLoc, IdentifierInfo *Id, unsigned InitStyle, Expr *Init); diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index 98f7dd584bbb..68c8c83c3631 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -227,7 +227,7 @@ class VarDecl; class LocalInstantiationScope { public: /// A set of declarations. - using DeclArgumentPack = SmallVector; + using DeclArgumentPack = SmallVector; private: /// Reference to the semantic analysis that is performing @@ -378,7 +378,7 @@ class VarDecl; findInstantiationOf(const Decl *D); void InstantiatedLocal(const Decl *D, Decl *Inst); - void InstantiatedLocalPackArg(const Decl *D, ParmVarDecl *Inst); + void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst); void MakeInstantiatedLocalArgPack(const Decl *D); /// Note that the given parameter pack has been partially substituted diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index b8240d1fb4df..4480b0f79e6b 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4372,7 +4372,13 @@ QualType ASTContext::getPackExpansionType(QualType Pattern, llvm::FoldingSetNodeID ID; PackExpansionType::Profile(ID, Pattern, NumExpansions); - assert(Pattern->containsUnexpandedParameterPack() && + // A deduced type can deduce to a pack, eg + // auto ...x = some_pack; + // That declaration isn't (yet) valid, but is created as part of building an + // init-capture pack: + // [...x = some_pack] {} + assert((Pattern->containsUnexpandedParameterPack() || + Pattern->getContainedDeducedType()) && "Pack expansions must expand one or more parameter packs"); void *InsertPos = nullptr; PackExpansionType *T @@ -4872,19 +4878,20 @@ QualType ASTContext::getUnaryTransformType(QualType BaseType, /// deduced to the given type, or to the canonical undeduced 'auto' type, or the /// canonical deduced-but-dependent 'auto' type. QualType ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, - bool IsDependent) const { + bool IsDependent, bool IsPack) const { + assert((!IsPack || IsDependent) && "only use IsPack for a dependent pack"); if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && !IsDependent) return getAutoDeductType(); // Look in the folding set for an existing type. void *InsertPos = nullptr; llvm::FoldingSetNodeID ID; - AutoType::Profile(ID, DeducedType, Keyword, IsDependent); + AutoType::Profile(ID, DeducedType, Keyword, IsDependent, IsPack); if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(AT, 0); auto *AT = new (*this, TypeAlignment) - AutoType(DeducedType, Keyword, IsDependent); + AutoType(DeducedType, Keyword, IsDependent, IsPack); Types.push_back(AT); if (InsertPos) AutoTypes.InsertNode(AT, InsertPos); @@ -4946,7 +4953,7 @@ QualType ASTContext::getAutoDeductType() const { if (AutoDeductTy.isNull()) AutoDeductTy = QualType( new (*this, TypeAlignment) AutoType(QualType(), AutoTypeKeyword::Auto, - /*dependent*/false), + /*dependent*/false, /*pack*/false), 0); return AutoDeductTy; } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 4e42dd7d3bd4..bc0a5a4c67e3 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2397,6 +2397,10 @@ bool VarDecl::checkInitIsICE() const { return Eval->IsICE; } +bool VarDecl::isParameterPack() const { + return isa(getType()); +} + template static DeclT *getDefinitionOrSelf(DeclT *D) { assert(D); @@ -2683,10 +2687,6 @@ bool ParmVarDecl::hasDefaultArg() const { !Init.isNull(); } -bool ParmVarDecl::isParameterPack() const { - return isa(getType()); -} - void ParmVarDecl::setParameterIndexLarge(unsigned parameterIndex) { getASTContext().setParameterIndex(this, parameterIndex); ParmVarDeclBits.ParameterIndex = ParameterIndexSentinel; diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index f40896da48d0..511925d1b140 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -208,8 +208,8 @@ bool Decl::isTemplateParameterPack() const { } bool Decl::isParameterPack() const { - if (const auto *Parm = dyn_cast(this)) - return Parm->isParameterPack(); + if (const auto *Var = dyn_cast(this)) + return Var->isParameterPack(); return isTemplateParameterPack(); } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 870190af7b78..b30f785ba8f5 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1541,30 +1541,30 @@ TemplateArgument SubstNonTypeTemplateParmPackExpr::getArgumentPack() const { return TemplateArgument(llvm::makeArrayRef(Arguments, NumArguments)); } -FunctionParmPackExpr::FunctionParmPackExpr(QualType T, ParmVarDecl *ParamPack, +FunctionParmPackExpr::FunctionParmPackExpr(QualType T, VarDecl *ParamPack, SourceLocation NameLoc, unsigned NumParams, - ParmVarDecl *const *Params) + VarDecl *const *Params) : Expr(FunctionParmPackExprClass, T, VK_LValue, OK_Ordinary, true, true, true, true), ParamPack(ParamPack), NameLoc(NameLoc), NumParameters(NumParams) { if (Params) std::uninitialized_copy(Params, Params + NumParams, - getTrailingObjects()); + getTrailingObjects()); } FunctionParmPackExpr * FunctionParmPackExpr::Create(const ASTContext &Context, QualType T, - ParmVarDecl *ParamPack, SourceLocation NameLoc, - ArrayRef Params) { - return new (Context.Allocate(totalSizeToAlloc(Params.size()))) + VarDecl *ParamPack, SourceLocation NameLoc, + ArrayRef Params) { + return new (Context.Allocate(totalSizeToAlloc(Params.size()))) FunctionParmPackExpr(T, ParamPack, NameLoc, Params.size(), Params.data()); } FunctionParmPackExpr * FunctionParmPackExpr::CreateEmpty(const ASTContext &Context, unsigned NumParams) { - return new (Context.Allocate(totalSizeToAlloc(NumParams))) + return new (Context.Allocate(totalSizeToAlloc(NumParams))) FunctionParmPackExpr(QualType(), nullptr, SourceLocation(), 0, nullptr); } diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 0ab4422b6a93..6207c6228dfd 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -538,6 +538,7 @@ private: unsigned knownArity); void mangleCastExpression(const Expr *E, StringRef CastEncoding); void mangleInitListElements(const InitListExpr *InitList); + void mangleDeclRefExpr(const NamedDecl *D); void mangleExpression(const Expr *E, unsigned Arity = UnknownArity); void mangleCXXCtorType(CXXCtorType T, const CXXRecordDecl *InheritedFrom); void mangleCXXDtorType(CXXDtorType T); @@ -3499,6 +3500,32 @@ void CXXNameMangler::mangleInitListElements(const InitListExpr *InitList) { mangleExpression(InitList->getInit(i)); } +void CXXNameMangler::mangleDeclRefExpr(const NamedDecl *D) { + switch (D->getKind()) { + default: + // ::= L E # external name + Out << 'L'; + mangle(D); + Out << 'E'; + break; + + case Decl::ParmVar: + mangleFunctionParam(cast(D)); + break; + + case Decl::EnumConstant: { + const EnumConstantDecl *ED = cast(D); + mangleIntegerLiteral(ED->getType(), ED->getInitVal()); + break; + } + + case Decl::NonTypeTemplateParm: + const NonTypeTemplateParmDecl *PD = cast(D); + mangleTemplateParameter(PD->getIndex()); + break; + } +} + void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { // ::= // ::= @@ -4089,37 +4116,9 @@ recurse: mangleExpression(cast(E)->getSubExpr(), Arity); break; - case Expr::DeclRefExprClass: { - const NamedDecl *D = cast(E)->getDecl(); - - switch (D->getKind()) { - default: - // ::= L E # external name - Out << 'L'; - mangle(D); - Out << 'E'; - break; - - case Decl::ParmVar: - mangleFunctionParam(cast(D)); - break; - - case Decl::EnumConstant: { - const EnumConstantDecl *ED = cast(D); - mangleIntegerLiteral(ED->getType(), ED->getInitVal()); - break; - } - - case Decl::NonTypeTemplateParm: { - const NonTypeTemplateParmDecl *PD = cast(D); - mangleTemplateParameter(PD->getIndex()); - break; - } - - } - + case Expr::DeclRefExprClass: + mangleDeclRefExpr(cast(E)->getDecl()); break; - } case Expr::SubstNonTypeTemplateParmPackExprClass: // FIXME: not clear how to mangle this! @@ -4133,7 +4132,7 @@ recurse: // FIXME: not clear how to mangle this! const FunctionParmPackExpr *FPPE = cast(E); Out << "v110_SUBSTPACK"; - mangleFunctionParam(FPPE->getParameterPack()); + mangleDeclRefExpr(FPPE->getParameterPack()); break; } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 8fdafd4f184a..4b207b606321 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -539,6 +539,7 @@ void JSONNodeDumper::VisitVarDecl(const VarDecl *VD) { case VarDecl::ListInit: JOS.attribute("init", "list"); break; } } + attributeOnlyIfTrue("isParameterPack", VD->isParameterPack()); } void JSONNodeDumper::VisitFieldDecl(const FieldDecl *FD) { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index f287e961b620..c15713b50db4 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1366,6 +1366,8 @@ void TextNodeDumper::VisitVarDecl(const VarDecl *D) { break; } } + if (D->isParameterPack()) + OS << " pack"; } void TextNodeDumper::VisitBindingDecl(const BindingDecl *D) { diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 101c5184a5ac..e45b1611d1fa 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1743,6 +1743,10 @@ namespace { Type *VisitAdjustedType(const AdjustedType *T) { return Visit(T->getOriginalType()); } + + Type *VisitPackExpansionType(const PackExpansionType *T) { + return Visit(T->getPattern()); + } }; } // namespace diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index cb0fb72c1938..6173a6cd63ed 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -20,7 +20,7 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "llvm/Support/ErrorHandling.h" - +#include using namespace clang; @@ -714,10 +714,10 @@ ExprResult Parser::TryParseLambdaExpression() { if (Next.is(tok::r_square) || // [] Next.is(tok::equal) || // [= (Next.is(tok::amp) && // [&] or [&, - (After.is(tok::r_square) || - After.is(tok::comma))) || + After.isOneOf(tok::r_square, tok::comma)) || (Next.is(tok::identifier) && // [identifier] - After.is(tok::r_square))) { + After.is(tok::r_square)) || + Next.is(tok::ellipsis)) { // [... return ParseLambdaExpression(); } @@ -798,6 +798,15 @@ bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, return true; }; + // Perform some irreversible action if this is a non-tentative parse; + // otherwise note that our actions were incomplete. + auto NonTentativeAction = [&](llvm::function_ref Action) { + if (Tentative) + *Tentative = LambdaIntroducerTentativeParse::Incomplete; + else + Action(); + }; + // Parse capture-default. if (Tok.is(tok::amp) && (NextToken().is(tok::comma) || NextToken().is(tok::r_square))) { @@ -857,7 +866,7 @@ bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, LambdaCaptureInitKind InitKind = LambdaCaptureInitKind::NoInit; SourceLocation Loc; IdentifierInfo *Id = nullptr; - SourceLocation EllipsisLoc; + SourceLocation EllipsisLocs[4]; ExprResult Init; SourceLocation LocStart = Tok.getLocation(); @@ -875,6 +884,8 @@ bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, Kind = LCK_This; Loc = ConsumeToken(); } else { + TryConsumeToken(tok::ellipsis, EllipsisLocs[0]); + if (Tok.is(tok::amp)) { Kind = LCK_ByRef; ConsumeToken(); @@ -887,6 +898,8 @@ bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, } } + TryConsumeToken(tok::ellipsis, EllipsisLocs[1]); + if (Tok.is(tok::identifier)) { Id = Tok.getIdentifierInfo(); Loc = ConsumeToken(); @@ -901,6 +914,8 @@ bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, }); } + TryConsumeToken(tok::ellipsis, EllipsisLocs[2]); + if (Tok.is(tok::l_paren)) { BalancedDelimiterTracker Parens(*this, tok::l_paren); Parens.consumeOpen(); @@ -982,9 +997,9 @@ bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, ConsumeAnnotationToken(); } } - } else { - TryConsumeToken(tok::ellipsis, EllipsisLoc); } + + TryConsumeToken(tok::ellipsis, EllipsisLocs[3]); } // Check if this is a message send before we act on a possible init-capture. @@ -995,60 +1010,81 @@ bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, return false; } - // If this is an init capture, process the initialization expression - // right away. For lambda init-captures such as the following: - // const int x = 10; - // auto L = [i = x+1](int a) { - // return [j = x+2, - // &k = x](char b) { }; - // }; - // keep in mind that each lambda init-capture has to have: - // - its initialization expression executed in the context - // of the enclosing/parent decl-context. - // - but the variable itself has to be 'injected' into the - // decl-context of its lambda's call-operator (which has - // not yet been created). - // Each init-expression is a full-expression that has to get - // Sema-analyzed (for capturing etc.) before its lambda's - // call-operator's decl-context, scope & scopeinfo are pushed on their - // respective stacks. Thus if any variable is odr-used in the init-capture - // it will correctly get captured in the enclosing lambda, if one exists. - // The init-variables above are created later once the lambdascope and - // call-operators decl-context is pushed onto its respective stack. + // Ensure that any ellipsis was in the right place. + SourceLocation EllipsisLoc; + if (std::any_of(std::begin(EllipsisLocs), std::end(EllipsisLocs), + [](SourceLocation Loc) { return Loc.isValid(); })) { + // The '...' should appear before the identifier in an init-capture, and + // after the identifier otherwise. + bool InitCapture = InitKind != LambdaCaptureInitKind::NoInit; + SourceLocation *ExpectedEllipsisLoc = + !InitCapture ? &EllipsisLocs[2] : + Kind == LCK_ByRef ? &EllipsisLocs[1] : + &EllipsisLocs[0]; + EllipsisLoc = *ExpectedEllipsisLoc; - // Since the lambda init-capture's initializer expression occurs in the - // context of the enclosing function or lambda, therefore we can not wait - // till a lambda scope has been pushed on before deciding whether the - // variable needs to be captured. We also need to process all - // lvalue-to-rvalue conversions and discarded-value conversions, - // so that we can avoid capturing certain constant variables. - // For e.g., - // void test() { - // const int x = 10; - // auto L = [&z = x](char a) { <-- don't capture by the current lambda - // return [y = x](int i) { <-- don't capture by enclosing lambda - // return y; - // } - // }; - // } - // If x was not const, the second use would require 'L' to capture, and - // that would be an error. + unsigned DiagID = 0; + if (EllipsisLoc.isInvalid()) { + DiagID = diag::err_lambda_capture_misplaced_ellipsis; + for (SourceLocation Loc : EllipsisLocs) { + if (Loc.isValid()) + EllipsisLoc = Loc; + } + } else { + unsigned NumEllipses = std::accumulate( + std::begin(EllipsisLocs), std::end(EllipsisLocs), 0, + [](int N, SourceLocation Loc) { return N + Loc.isValid(); }); + if (NumEllipses > 1) + DiagID = diag::err_lambda_capture_multiple_ellipses; + } + if (DiagID) { + NonTentativeAction([&] { + // Point the diagnostic at the first misplaced ellipsis. + SourceLocation DiagLoc; + for (SourceLocation &Loc : EllipsisLocs) { + if (&Loc != ExpectedEllipsisLoc && Loc.isValid()) { + DiagLoc = Loc; + break; + } + } + assert(DiagLoc.isValid() && "no location for diagnostic"); + // Issue the diagnostic and produce fixits showing where the ellipsis + // should have been written. + auto &&D = Diag(DiagLoc, DiagID); + if (DiagID == diag::err_lambda_capture_misplaced_ellipsis) { + SourceLocation ExpectedLoc = + InitCapture ? Loc + : Lexer::getLocForEndOfToken( + Loc, 0, PP.getSourceManager(), getLangOpts()); + D << InitCapture << FixItHint::CreateInsertion(ExpectedLoc, "..."); + } + for (SourceLocation &Loc : EllipsisLocs) { + if (&Loc != ExpectedEllipsisLoc && Loc.isValid()) + D << FixItHint::CreateRemoval(Loc); + } + }); + } + } + + // Process the init-capture initializers now rather than delaying until we + // form the lambda-expression so that they can be handled in the context + // enclosing the lambda-expression, rather than in the context of the + // lambda-expression itself. ParsedType InitCaptureType; - if (Tentative && Init.isUsable()) - *Tentative = LambdaIntroducerTentativeParse::Incomplete; - else if (Init.isUsable()) { + if (Init.isUsable()) Init = Actions.CorrectDelayedTyposInExpr(Init.get()); - if (Init.isUsable()) { + if (Init.isUsable()) { + NonTentativeAction([&] { // Get the pointer and store it in an lvalue, so we can use it as an // out argument. Expr *InitExpr = Init.get(); // This performs any lvalue-to-rvalue conversions if necessary, which // can affect what gets captured in the containing decl-context. InitCaptureType = Actions.actOnLambdaInitCaptureInitialization( - Loc, Kind == LCK_ByRef, Id, InitKind, InitExpr); + Loc, Kind == LCK_ByRef, EllipsisLoc, Id, InitKind, InitExpr); Init = InitExpr; - } + }); } SourceLocation LocEnd = PrevTokLocation; diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 1966e9e0803e..b2055dd650e2 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -753,11 +753,10 @@ void Sema::deduceClosureReturnType(CapturingScopeInfo &CSI) { } } -QualType Sema::buildLambdaInitCaptureInitialization(SourceLocation Loc, - bool ByRef, - IdentifierInfo *Id, - bool IsDirectInit, - Expr *&Init) { +QualType Sema::buildLambdaInitCaptureInitialization( + SourceLocation Loc, bool ByRef, SourceLocation EllipsisLoc, + Optional NumExpansions, IdentifierInfo *Id, bool IsDirectInit, + Expr *&Init) { // Create an 'auto' or 'auto&' TypeSourceInfo that we can use to // deduce against. QualType DeductType = Context.getAutoDeductType(); @@ -768,6 +767,18 @@ QualType Sema::buildLambdaInitCaptureInitialization(SourceLocation Loc, assert(!DeductType.isNull() && "can't build reference to auto"); TLB.push(DeductType).setSigilLoc(Loc); } + if (EllipsisLoc.isValid()) { + if (Init->containsUnexpandedParameterPack()) { + Diag(EllipsisLoc, getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_init_capture_pack + : diag::ext_init_capture_pack); + DeductType = Context.getPackExpansionType(DeductType, NumExpansions); + TLB.push(DeductType).setEllipsisLoc(EllipsisLoc); + } else { + // Just ignore the ellipsis for now and form a non-pack variable. We'll + // diagnose this later when we try to capture it. + } + } TypeSourceInfo *TSI = TLB.getTypeSourceInfo(Context, DeductType); // Deduce the type of the init capture. @@ -808,10 +819,15 @@ QualType Sema::buildLambdaInitCaptureInitialization(SourceLocation Loc, VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc, QualType InitCaptureType, + SourceLocation EllipsisLoc, IdentifierInfo *Id, unsigned InitStyle, Expr *Init) { - TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType, - Loc); + // FIXME: Retain the TypeSourceInfo from buildLambdaInitCaptureInitialization + // rather than reconstructing it here. + TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType, Loc); + if (auto PETL = TSI->getTypeLoc().getAs()) + PETL.setEllipsisLoc(EllipsisLoc); + // Create a dummy variable representing the init-capture. This is not actually // used as a variable, and only exists as a way to name and refer to the // init-capture. @@ -1036,8 +1052,6 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, ? diag::warn_cxx11_compat_init_capture : diag::ext_init_capture); - if (C->Init.get()->containsUnexpandedParameterPack()) - ContainsUnexpandedParameterPack = true; // If the initializer expression is usable, but the InitCaptureType // is not, then an error has occurred - so ignore the capture for now. // for e.g., [n{0}] { }; <-- if no is included. @@ -1046,6 +1060,10 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, if (C->InitCaptureType.get().isNull()) continue; + if (C->Init.get()->containsUnexpandedParameterPack() && + !C->InitCaptureType.get()->getAs()) + ContainsUnexpandedParameterPack = true; + unsigned InitStyle; switch (C->InitKind) { case LambdaCaptureInitKind::NoInit: @@ -1061,7 +1079,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, break; } Var = createLambdaInitCaptureVarDecl(C->Loc, C->InitCaptureType.get(), - C->Id, InitStyle, C->Init.get()); + C->EllipsisLoc, C->Id, InitStyle, + C->Init.get()); // C++1y [expr.prim.lambda]p11: // An init-capture behaves as if it declares and explicitly // captures a variable [...] whose declarative region is the @@ -1153,7 +1172,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, EllipsisLoc = C->EllipsisLoc; } else { Diag(C->EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) - << SourceRange(C->Loc); + << (C->Init.isUsable() ? C->Init.get()->getSourceRange() + : SourceRange(C->Loc)); // Just ignore the ellipsis. } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 4dccf2f459c9..263bc3104efe 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4280,19 +4280,26 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( } namespace { + struct DependentAuto { bool IsPack; }; /// Substitute the 'auto' specifier or deduced template specialization type /// specifier within a type for a given replacement type. class SubstituteDeducedTypeTransform : public TreeTransform { QualType Replacement; + bool ReplacementIsPack; bool UseTypeSugar; public: + SubstituteDeducedTypeTransform(Sema &SemaRef, DependentAuto DA) + : TreeTransform(SemaRef), Replacement(), + ReplacementIsPack(DA.IsPack), UseTypeSugar(true) {} + SubstituteDeducedTypeTransform(Sema &SemaRef, QualType Replacement, - bool UseTypeSugar = true) + bool UseTypeSugar = true) : TreeTransform(SemaRef), - Replacement(Replacement), UseTypeSugar(UseTypeSugar) {} + Replacement(Replacement), ReplacementIsPack(false), + UseTypeSugar(UseTypeSugar) {} QualType TransformDesugared(TypeLocBuilder &TLB, DeducedTypeLoc TL) { assert(isa(Replacement) && @@ -4317,7 +4324,8 @@ namespace { return TransformDesugared(TLB, TL); QualType Result = SemaRef.Context.getAutoType( - Replacement, TL.getTypePtr()->getKeyword(), Replacement.isNull()); + Replacement, TL.getTypePtr()->getKeyword(), Replacement.isNull(), + ReplacementIsPack); auto NewTL = TLB.push(Result); NewTL.setNameLoc(TL.getNameLoc()); return Result; @@ -4408,9 +4416,12 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, Init = NonPlaceholder.get(); } + DependentAuto DependentResult = { + /*.IsPack = */ (bool)Type.getAs()}; + if (!DependentDeductionDepth && (Type.getType()->isDependentType() || Init->isTypeDependent())) { - Result = SubstituteDeducedTypeTransform(*this, QualType()).Apply(Type); + Result = SubstituteDeducedTypeTransform(*this, DependentResult).Apply(Type); assert(!Result.isNull() && "substituting DependentTy can't fail"); return DAR_Succeeded; } @@ -4478,7 +4489,8 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, auto DeductionFailed = [&](TemplateDeductionResult TDK, ArrayRef Ranges) -> DeduceAutoResult { if (Init->isTypeDependent()) { - Result = SubstituteDeducedTypeTransform(*this, QualType()).Apply(Type); + Result = + SubstituteDeducedTypeTransform(*this, DependentResult).Apply(Type); assert(!Result.isNull() && "substituting DependentTy can't fail"); return DAR_Succeeded; } @@ -4559,7 +4571,10 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, QualType Sema::SubstAutoType(QualType TypeWithAuto, QualType TypeToReplaceAuto) { if (TypeToReplaceAuto->isDependentType()) - TypeToReplaceAuto = QualType(); + return SubstituteDeducedTypeTransform( + *this, DependentAuto{ + TypeToReplaceAuto->containsUnexpandedParameterPack()}) + .TransformType(TypeWithAuto); return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto) .TransformType(TypeWithAuto); } @@ -4567,7 +4582,11 @@ QualType Sema::SubstAutoType(QualType TypeWithAuto, TypeSourceInfo *Sema::SubstAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, QualType TypeToReplaceAuto) { if (TypeToReplaceAuto->isDependentType()) - TypeToReplaceAuto = QualType(); + return SubstituteDeducedTypeTransform( + *this, + DependentAuto{ + TypeToReplaceAuto->containsUnexpandedParameterPack()}) + .TransformType(TypeWithAuto); return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto) .TransformType(TypeWithAuto); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 55d1d94bd825..ba54d5010bab 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -819,7 +819,19 @@ namespace { SemaRef.InstantiateAttrs(TemplateArgs, Old, New); } - void transformedLocalDecl(Decl *Old, Decl *New) { + void transformedLocalDecl(Decl *Old, ArrayRef NewDecls) { + if (Old->isParameterPack()) { + SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Old); + for (auto *New : NewDecls) + SemaRef.CurrentInstantiationScope->InstantiatedLocalPackArg( + Old, cast(New)); + return; + } + + assert(NewDecls.size() == 1 && + "should only have multiple expansions for a pack"); + Decl *New = NewDecls.front(); + // If we've instantiated the call operator of a lambda or the call // operator template of a generic lambda, update the "instantiation of" // information. @@ -888,12 +900,11 @@ namespace { ExprResult TransformSubstNonTypeTemplateParmPackExpr( SubstNonTypeTemplateParmPackExpr *E); - /// Rebuild a DeclRefExpr for a ParmVarDecl reference. - ExprResult RebuildParmVarDeclRefExpr(ParmVarDecl *PD, SourceLocation Loc); + /// Rebuild a DeclRefExpr for a VarDecl reference. + ExprResult RebuildVarDeclRefExpr(VarDecl *PD, SourceLocation Loc); - /// Transform a reference to a function parameter pack. - ExprResult TransformFunctionParmPackRefExpr(DeclRefExpr *E, - ParmVarDecl *PD); + /// Transform a reference to a function or init-capture parameter pack. + ExprResult TransformFunctionParmPackRefExpr(DeclRefExpr *E, VarDecl *PD); /// Transform a FunctionParmPackExpr which was built when we couldn't /// expand a function parameter pack reference which refers to an expanded @@ -1324,9 +1335,8 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmPackExpr( Arg); } -ExprResult -TemplateInstantiator::RebuildParmVarDeclRefExpr(ParmVarDecl *PD, - SourceLocation Loc) { +ExprResult TemplateInstantiator::RebuildVarDeclRefExpr(VarDecl *PD, + SourceLocation Loc) { DeclarationNameInfo NameInfo(PD->getDeclName(), Loc); return getSema().BuildDeclarationNameExpr(CXXScopeSpec(), NameInfo, PD); } @@ -1335,11 +1345,11 @@ ExprResult TemplateInstantiator::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) { if (getSema().ArgumentPackSubstitutionIndex != -1) { // We can expand this parameter pack now. - ParmVarDecl *D = E->getExpansion(getSema().ArgumentPackSubstitutionIndex); - ValueDecl *VD = cast_or_null(TransformDecl(E->getExprLoc(), D)); + VarDecl *D = E->getExpansion(getSema().ArgumentPackSubstitutionIndex); + VarDecl *VD = cast_or_null(TransformDecl(E->getExprLoc(), D)); if (!VD) return ExprError(); - return RebuildParmVarDeclRefExpr(cast(VD), E->getExprLoc()); + return RebuildVarDeclRefExpr(VD, E->getExprLoc()); } QualType T = TransformType(E->getType()); @@ -1348,25 +1358,24 @@ TemplateInstantiator::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) { // Transform each of the parameter expansions into the corresponding // parameters in the instantiation of the function decl. - SmallVector Parms; - Parms.reserve(E->getNumExpansions()); + SmallVector Vars; + Vars.reserve(E->getNumExpansions()); for (FunctionParmPackExpr::iterator I = E->begin(), End = E->end(); I != End; ++I) { - ParmVarDecl *D = - cast_or_null(TransformDecl(E->getExprLoc(), *I)); + VarDecl *D = cast_or_null(TransformDecl(E->getExprLoc(), *I)); if (!D) return ExprError(); - Parms.push_back(D); + Vars.push_back(D); } return FunctionParmPackExpr::Create(getSema().Context, T, E->getParameterPack(), - E->getParameterPackLocation(), Parms); + E->getParameterPackLocation(), Vars); } ExprResult TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E, - ParmVarDecl *PD) { + VarDecl *PD) { typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; llvm::PointerUnion *Found = getSema().CurrentInstantiationScope->findInstantiationOf(PD); @@ -1390,8 +1399,7 @@ TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E, } // We have either an unexpanded pack or a specific expansion. - return RebuildParmVarDeclRefExpr(cast(TransformedDecl), - E->getExprLoc()); + return RebuildVarDeclRefExpr(cast(TransformedDecl), E->getExprLoc()); } ExprResult @@ -1409,7 +1417,7 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) { } // Handle references to function parameter packs. - if (ParmVarDecl *PD = dyn_cast(D)) + if (VarDecl *PD = dyn_cast(D)) if (PD->isParameterPack()) return TransformFunctionParmPackRefExpr(E, PD); @@ -2984,14 +2992,14 @@ void LocalInstantiationScope::InstantiatedLocal(const Decl *D, Decl *Inst) { #endif Stored = Inst; } else if (DeclArgumentPack *Pack = Stored.dyn_cast()) { - Pack->push_back(cast(Inst)); + Pack->push_back(cast(Inst)); } else { assert(Stored.get() == Inst && "Already instantiated this local"); } } void LocalInstantiationScope::InstantiatedLocalPackArg(const Decl *D, - ParmVarDecl *Inst) { + VarDecl *Inst) { D = getCanonicalParmVarDecl(D); DeclArgumentPack *Pack = LocalDecls[D].get(); Pack->push_back(Inst); diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 8773f45c30ab..9b23624a9a81 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -39,11 +39,11 @@ namespace { unsigned DepthLimit = (unsigned)-1; void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) { - if (auto *PVD = dyn_cast(ND)) { + if (auto *VD = dyn_cast(ND)) { // For now, the only problematic case is a generic lambda's templated // call operator, so we don't need to look for all the other ways we // could have reached a dependent parameter pack. - auto *FD = dyn_cast(PVD->getDeclContext()); + auto *FD = dyn_cast(VD->getDeclContext()); auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr; if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit) return; @@ -313,11 +313,11 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, if (auto *LSI = dyn_cast(Func)) { if (N == FunctionScopes.size()) { - for (auto &Param : Unexpanded) { - auto *PD = dyn_cast_or_null( - Param.first.dyn_cast()); - if (PD && PD->getDeclContext() == LSI->CallOperator) - LambdaParamPackReferences.push_back(Param); + for (auto &Pack : Unexpanded) { + auto *VD = dyn_cast_or_null( + Pack.first.dyn_cast()); + if (VD && VD->getDeclContext() == LSI->CallOperator) + LambdaParamPackReferences.push_back(Pack); } } @@ -586,11 +586,15 @@ Sema::CheckPackExpansion(TypeSourceInfo *Pattern, SourceLocation EllipsisLoc, QualType Sema::CheckPackExpansion(QualType Pattern, SourceRange PatternRange, SourceLocation EllipsisLoc, Optional NumExpansions) { - // C++0x [temp.variadic]p5: + // C++11 [temp.variadic]p5: // The pattern of a pack expansion shall name one or more // parameter packs that are not expanded by a nested pack // expansion. - if (!Pattern->containsUnexpandedParameterPack()) { + // + // A pattern containing a deduced type can't occur "naturally" but arises in + // the desugaring of an init-capture pack. + if (!Pattern->containsUnexpandedParameterPack() && + !Pattern->getContainedDeducedType()) { Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) << PatternRange; return QualType(); @@ -641,7 +645,7 @@ bool Sema::CheckParameterPacksForExpansion( // Compute the depth and index for this parameter pack. unsigned Depth = 0, Index = 0; IdentifierInfo *Name; - bool IsFunctionParameterPack = false; + bool IsVarDeclPack = false; if (const TemplateTypeParmType *TTP = i->first.dyn_cast()) { @@ -650,8 +654,8 @@ bool Sema::CheckParameterPacksForExpansion( Name = TTP->getIdentifier(); } else { NamedDecl *ND = i->first.get(); - if (isa(ND)) - IsFunctionParameterPack = true; + if (isa(ND)) + IsVarDeclPack = true; else std::tie(Depth, Index) = getDepthAndIndex(ND); @@ -660,7 +664,7 @@ bool Sema::CheckParameterPacksForExpansion( // Determine the size of this argument pack. unsigned NewPackSize; - if (IsFunctionParameterPack) { + if (IsVarDeclPack) { // Figure out whether we're instantiating to an argument pack or not. typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; @@ -694,7 +698,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 (!IsFunctionParameterPack && CurrentInstantiationScope) { + if (!IsVarDeclPack && CurrentInstantiationScope) { if (NamedDecl *PartialPack = CurrentInstantiationScope->getPartiallySubstitutedPack()){ unsigned PartialDepth, PartialIndex; @@ -778,8 +782,8 @@ Optional Sema::getNumArgumentsInExpansion(QualType T, Index = TTP->getIndex(); } else { NamedDecl *ND = Unexpanded[I].first.get(); - if (isa(ND)) { - // Function parameter pack. + if (isa(ND)) { + // Function parameter pack or init-capture pack. typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; llvm::PointerUnion *Instantiation @@ -1084,7 +1088,7 @@ Optional Sema::getFullyPackExpandedSize(TemplateArgument Arg) { dyn_cast(Arg.getAsExpr())) Pack = Subst->getArgumentPack(); else if (auto *Subst = dyn_cast(Arg.getAsExpr())) { - for (ParmVarDecl *PD : *Subst) + for (VarDecl *PD : *Subst) if (PD->isParameterPack()) return None; return Subst->getNumExpansions(); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index f8b9b34ae7f6..c653fb1d6e2c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -450,8 +450,10 @@ public: /// TransformDefinition. However, in some cases (e.g., lambda expressions), /// the transformer itself has to transform the declarations. This routine /// can be overridden by a subclass that keeps track of such mappings. - void transformedLocalDecl(Decl *Old, Decl *New) { - TransformedLocalDecls[Old] = New; + void transformedLocalDecl(Decl *Old, ArrayRef New) { + assert(New.size() == 1 && + "must override transformedLocalDecl if performing pack expansion"); + TransformedLocalDecls[Old] = New.front(); } /// Transform the definition of the given declaration. @@ -7122,7 +7124,7 @@ TreeTransform::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) { auto *Promise = SemaRef.buildCoroutinePromise(FD->getLocation()); if (!Promise) return StmtError(); - getDerived().transformedLocalDecl(S->getPromiseDecl(), Promise); + getDerived().transformedLocalDecl(S->getPromiseDecl(), {Promise}); ScopeInfo->CoroutinePromise = Promise; // Transform the implicit coroutine statements we built during the initial @@ -11176,33 +11178,80 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { // Transform any init-capture expressions before entering the scope of the // lambda body, because they are not semantically within that scope. typedef std::pair InitCaptureInfoTy; - SmallVector InitCaptureExprsAndTypes; - InitCaptureExprsAndTypes.resize(E->explicit_capture_end() - - E->explicit_capture_begin()); + struct TransformedInitCapture { + // The location of the ... if the result is retaining a pack expansion. + SourceLocation EllipsisLoc; + // Zero or more expansions of the init-capture. + SmallVector Expansions; + }; + SmallVector InitCaptures; + InitCaptures.resize(E->explicit_capture_end() - E->explicit_capture_begin()); for (LambdaExpr::capture_iterator C = E->capture_begin(), CEnd = E->capture_end(); C != CEnd; ++C) { if (!E->isInitCapture(C)) continue; - EnterExpressionEvaluationContext EEEC( - getSema(), Sema::ExpressionEvaluationContext::PotentiallyEvaluated); - ExprResult NewExprInitResult = getDerived().TransformInitializer( - C->getCapturedVar()->getInit(), - C->getCapturedVar()->getInitStyle() == VarDecl::CallInit); - - if (NewExprInitResult.isInvalid()) - return ExprError(); - Expr *NewExprInit = NewExprInitResult.get(); + TransformedInitCapture &Result = InitCaptures[C - E->capture_begin()]; VarDecl *OldVD = C->getCapturedVar(); - QualType NewInitCaptureType = - getSema().buildLambdaInitCaptureInitialization( - C->getLocation(), OldVD->getType()->isReferenceType(), - OldVD->getIdentifier(), - C->getCapturedVar()->getInitStyle() != VarDecl::CInit, NewExprInit); - NewExprInitResult = NewExprInit; - InitCaptureExprsAndTypes[C - E->capture_begin()] = - std::make_pair(NewExprInitResult, NewInitCaptureType); + + auto SubstInitCapture = [&](SourceLocation EllipsisLoc, + Optional NumExpansions) { + EnterExpressionEvaluationContext EEEC( + getSema(), Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + ExprResult NewExprInitResult = getDerived().TransformInitializer( + OldVD->getInit(), OldVD->getInitStyle() == VarDecl::CallInit); + + if (NewExprInitResult.isInvalid()) { + Result.Expansions.push_back(InitCaptureInfoTy(ExprError(), QualType())); + return; + } + Expr *NewExprInit = NewExprInitResult.get(); + + QualType NewInitCaptureType = + getSema().buildLambdaInitCaptureInitialization( + C->getLocation(), OldVD->getType()->isReferenceType(), + EllipsisLoc, NumExpansions, OldVD->getIdentifier(), + C->getCapturedVar()->getInitStyle() != VarDecl::CInit, + NewExprInit); + Result.Expansions.push_back( + InitCaptureInfoTy(NewExprInit, NewInitCaptureType)); + }; + + // If this is an init-capture pack, consider expanding the pack now. + if (OldVD->isParameterPack()) { + PackExpansionTypeLoc ExpansionTL = OldVD->getTypeSourceInfo() + ->getTypeLoc() + .castAs(); + SmallVector Unexpanded; + SemaRef.collectUnexpandedParameterPacks(OldVD->getInit(), Unexpanded); + + // Determine whether the set of unexpanded parameter packs can and should + // be expanded. + bool Expand = true; + bool RetainExpansion = false; + Optional OrigNumExpansions = + ExpansionTL.getTypePtr()->getNumExpansions(); + Optional NumExpansions = OrigNumExpansions; + if (getDerived().TryExpandParameterPacks( + ExpansionTL.getEllipsisLoc(), + OldVD->getInit()->getSourceRange(), Unexpanded, Expand, + RetainExpansion, NumExpansions)) + return ExprError(); + if (Expand) { + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); + SubstInitCapture(SourceLocation(), None); + } + } + if (!Expand || RetainExpansion) { + ForgetPartiallySubstitutedPackRAII Forget(getDerived()); + SubstInitCapture(ExpansionTL.getEllipsisLoc(), NumExpansions); + Result.EllipsisLoc = ExpansionTL.getEllipsisLoc(); + } + } else { + SubstInitCapture(SourceLocation(), None); + } } // Transform the template parameters, and add them to the current @@ -11245,7 +11294,7 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { NewCallOpTSI, /*KnownDependent=*/false, E->getCaptureDefault()); - getDerived().transformedLocalDecl(E->getLambdaClass(), Class); + getDerived().transformedLocalDecl(E->getLambdaClass(), {Class}); // Build the call operator. CXXMethodDecl *NewCallOperator = getSema().startLambdaDefinition( @@ -11270,7 +11319,7 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { } getDerived().transformAttrs(E->getCallOperator(), NewCallOperator); - getDerived().transformedLocalDecl(E->getCallOperator(), NewCallOperator); + getDerived().transformedLocalDecl(E->getCallOperator(), {NewCallOperator}); // Introduce the context of the call operator. Sema::ContextRAII SavedContext(getSema(), NewCallOperator, @@ -11313,24 +11362,33 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { // Rebuild init-captures, including the implied field declaration. if (E->isInitCapture(C)) { - InitCaptureInfoTy InitExprTypePair = - InitCaptureExprsAndTypes[C - E->capture_begin()]; - ExprResult Init = InitExprTypePair.first; - QualType InitQualType = InitExprTypePair.second; - if (Init.isInvalid() || InitQualType.isNull()) { - Invalid = true; - continue; - } + TransformedInitCapture &NewC = InitCaptures[C - E->capture_begin()]; + VarDecl *OldVD = C->getCapturedVar(); - VarDecl *NewVD = getSema().createLambdaInitCaptureVarDecl( - OldVD->getLocation(), InitExprTypePair.second, OldVD->getIdentifier(), - OldVD->getInitStyle(), Init.get()); - if (!NewVD) - Invalid = true; - else { - getDerived().transformedLocalDecl(OldVD, NewVD); + llvm::SmallVector NewVDs; + + for (InitCaptureInfoTy &Info : NewC.Expansions) { + ExprResult Init = Info.first; + QualType InitQualType = Info.second; + if (Init.isInvalid() || InitQualType.isNull()) { + Invalid = true; + break; + } + VarDecl *NewVD = getSema().createLambdaInitCaptureVarDecl( + OldVD->getLocation(), InitQualType, NewC.EllipsisLoc, + OldVD->getIdentifier(), OldVD->getInitStyle(), Init.get()); + if (!NewVD) { + Invalid = true; + break; + } + NewVDs.push_back(NewVD); + getSema().buildInitCaptureField(LSI, NewVD); } - getSema().buildInitCaptureField(LSI, NewVD); + + if (Invalid) + break; + + getDerived().transformedLocalDecl(OldVD, NewVDs); continue; } @@ -12471,8 +12529,7 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { VarDecl *oldCapture = I.getVariable(); // Ignore parameter packs. - if (isa(oldCapture) && - cast(oldCapture)->isParameterPack()) + if (oldCapture->isParameterPack()) continue; VarDecl *newCapture = diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index a09507f6daa3..55e1f132c56a 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6141,8 +6141,13 @@ QualType ASTReader::readTypeRecord(unsigned Index) { case TYPE_AUTO: { QualType Deduced = readType(*Loc.F, Record, Idx); AutoTypeKeyword Keyword = (AutoTypeKeyword)Record[Idx++]; - bool IsDependent = Deduced.isNull() ? Record[Idx++] : false; - return Context.getAutoType(Deduced, Keyword, IsDependent); + bool IsDependent = false, IsPack = false; + if (Deduced.isNull()) { + IsDependent = Record[Idx] > 0; + IsPack = Record[Idx] > 1; + ++Idx; + } + return Context.getAutoType(Deduced, Keyword, IsDependent, IsPack); } case TYPE_DEDUCED_TEMPLATE_SPECIALIZATION: { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index c1e971e5ab00..e0647fc6b82b 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1803,9 +1803,9 @@ void ASTStmtReader::VisitFunctionParmPackExpr(FunctionParmPackExpr *E) { E->NumParameters = Record.readInt(); E->ParamPack = ReadDeclAs(); E->NameLoc = ReadSourceLocation(); - auto **Parms = E->getTrailingObjects(); + auto **Parms = E->getTrailingObjects(); for (unsigned i = 0, n = E->NumParameters; i != n; ++i) - Parms[i] = ReadDeclAs(); + Parms[i] = ReadDeclAs(); } void ASTStmtReader::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index a0eb2f3b3eb8..a6950e490fc2 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -369,7 +369,8 @@ void ASTTypeWriter::VisitAutoType(const AutoType *T) { Record.AddTypeRef(T->getDeducedType()); Record.push_back((unsigned)T->getKeyword()); if (T->getDeducedType().isNull()) - Record.push_back(T->isDependentType()); + Record.push_back(T->containsUnexpandedParameterPack() ? 2 : + T->isDependentType() ? 1 : 0); Code = TYPE_AUTO; } diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.capture/p17.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.capture/p17.cpp new file mode 100644 index 000000000000..e2bd513aeffe --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.capture/p17.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +namespace std_example { + namespace std { template T &&move(T &); } + + void g(...); + + template void f(Args... args) { + auto lm = [&, args...] { return g(args...); }; + lm(); + + auto lm2 = [... xs = std::move(args)] { return g(xs...); }; + lm2(); + } +} + +template constexpr int f(int k, T ...t) { + auto a = [...v = t] (bool b) mutable { + if (!b) { + ((v += 1), ...); + return (__SIZE_TYPE__)0; + } + return (v * ... * 1) + sizeof...(v); + }; + for (int i = 0; i != k; ++i) + a(false); + return a(true); +} + +static_assert(f(1, 2, 3, 4) == 3 * 4 * 5 + 3); +static_assert(f(5) == 1); + +auto q = [...x = 0] {}; // expected-error {{does not contain any unexpanded parameter packs}} + +template constexpr int nested(T ...t) { + return [...a = t] { + return [a...] { + return (a + ...); + }(); + }(); +} +static_assert(nested(1, 2, 3) == 6); diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp index 4ae34dec3e34..028fcee5fda4 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify -Wno-c++1y-extensions -// RUN: %clang_cc1 -fsyntax-only -std=c++1y %s -verify +// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify -Wno-c++1y-extensions -Wno-c++2a-extensions +// RUN: %clang_cc1 -fsyntax-only -std=c++1y %s -verify -Wno-c++2a-extensions +// RUN: %clang_cc1 -fsyntax-only -std=c++2a %s -verify void print(); @@ -60,8 +61,25 @@ template void variadic_lambda(int*, float*, double*); template void init_capture_pack_err(Args ...args) { - [as(args)...] {} (); // expected-error {{expected ','}} - [as...(args)]{} (); // expected-error {{expected ','}} + [...as(args)]{} (); + [as(args)...] {} (); // expected-error {{ellipsis in pack init-capture must appear before the name of the capture}} + [as...(args)]{} (); // expected-error {{ellipsis in pack init-capture must appear before the name of the capture}} + [...as{args}]{} (); + [as{args}...] {} (); // expected-error {{ellipsis in pack init-capture must appear before the name of the capture}} + [as...{args}]{} (); // expected-error {{ellipsis in pack init-capture must appear before the name of the capture}} + [...as = args]{} (); + [as = args...] {} (); // expected-error {{ellipsis in pack init-capture must appear before the name of the capture}} + [as... = args]{} (); // expected-error {{ellipsis in pack init-capture must appear before the name of the capture}} + + [&...as(args)]{} (); + [...&as(args)]{} (); // expected-error {{ellipsis in pack init-capture must appear before the name of the capture}} + + [args...] {} (); + [...args] {} (); // expected-error {{ellipsis in pack capture must appear after the name of the capture}} + + [&args...] {} (); + [...&args] {} (); // expected-error {{ellipsis in pack capture must appear after the name of the capture}} + [&...args] {} (); // expected-error {{ellipsis in pack capture must appear after the name of the capture}} } template diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp b/clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp new file mode 100644 index 000000000000..4d5b6b47459c --- /dev/null +++ b/clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +namespace p3 { + void bar(...); + template void foo(Args... args) { + (void)[... xs = args] { + bar(xs...); + }; + } + + void use() { + foo(); + foo(1); + } +} + +template void f(T ...t) { + (void)[&...x = t] { + x; // expected-error {{unexpanded parameter pack 'x'}} + }; + + // Not OK: can't expand 'x' outside its scope. + weird((void)[&...x = t] { + return &x; // expected-error {{unexpanded parameter pack 'x'}} + }... // expected-error {{does not contain any unexpanded}} + ); + + // OK, capture only one 'slice' of 'x'. + weird((void)[&x = t] { + return &x; + }... + ); + + // 'x' is not expanded by the outer '...', but 'T' is. + weird((void)[&... x = t] { + return T() + &x; // expected-error {{unexpanded parameter pack 'x'}} + }... // expected-error {{does not contain any unexpanded}} + ); +} diff --git a/clang/test/FixIt/fixit-c++2a.cpp b/clang/test/FixIt/fixit-c++2a.cpp new file mode 100644 index 000000000000..c97bb7ae610c --- /dev/null +++ b/clang/test/FixIt/fixit-c++2a.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -verify -std=c++2a %s +// RUN: cp %s %t +// RUN: not %clang_cc1 -x c++ -std=c++2a -fixit %t +// RUN: %clang_cc1 -Wall -pedantic -x c++ -std=c++2a %t + +/* This is a test of the various code modification hints that only + apply in C++2a. */ +template void init_capture_pack(T ...a) { + [x... = a]{}; // expected-error {{must appear before the name}} + [x = a...]{}; // expected-error {{must appear before the name}} + [...&x = a]{}; // expected-error {{must appear before the name}} + [...a]{}; // expected-error {{must appear after the name}} + [&...a]{}; // expected-error {{must appear after the name}} + [...&a]{}; // expected-error {{must appear after the name}} +} diff --git a/clang/test/SemaTemplate/sizeof-pack.cpp b/clang/test/SemaTemplate/sizeof-pack.cpp index 4b0c883a24ac..274726a23150 100644 --- a/clang/test/SemaTemplate/sizeof-pack.cpp +++ b/clang/test/SemaTemplate/sizeof-pack.cpp @@ -1,7 +1,13 @@ // RUN: %clang_cc1 -std=c++11 -verify %s -// expected-no-diagnostics template int f() { return sizeof...(Ns); } template int f<>(); + +template int g() { + return [...x = T()] { // expected-warning 2{{extension}} + return sizeof...(x); + }(); +} +template int g<>(); diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 5acdd563914d..968d453a60e1 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -949,7 +949,7 @@ as the draft C++2a standard evolves. Pack expansion in lambda init-capture P0780R2 - No + SVN