Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -1135,6 +1135,10 @@ /// lambda. TemplateParameterList *getGenericLambdaTemplateParameterList() const; + /// \brief Retrieve the number of lambda template parameter's that were + /// specified explicitly in code. + unsigned getLambdaExplicitTemplateParameterCount() const; + LambdaCaptureDefault getLambdaCaptureDefault() const { assert(isLambda()); return static_cast(getLambdaData().CaptureDefault); Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -157,6 +157,11 @@ return SourceRange(TemplateLoc, RAngleLoc); } + void print(raw_ostream &Out, const ASTContext &Context, + bool OmitTemplateKW = false) const; + void print(raw_ostream &Out, const ASTContext &Context, + const PrintingPolicy &Policy, bool OmitTemplateKW = false) const; + friend TrailingObjects; template Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -1684,6 +1684,10 @@ /// brackets ([...]). SourceRange getIntroducerRange() const { return IntroducerRange; } + /// \brief Retrieve the source range covering the explicit template parameter + /// list, including the surrounding angle brackets (<...>). + SourceRange getExplicitTemplateParameterListRange() const; + /// \brief Retrieve the class that corresponds to the lambda. /// /// This is the "closure type" (C++1y [expr.prim.lambda]), and stores the @@ -1699,6 +1703,10 @@ /// parameter list associated with it, or else return null. TemplateParameterList *getTemplateParameterList() const; + /// \brief How many of the template parameters were explicitly specified + /// (as opposed to being invented by use of an auto parameter). + unsigned getExplicitTemplateParameterCount() const; + /// \brief Whether this is a generic lambda. bool isGenericLambda() const { return getTemplateParameterList(); } Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2379,6 +2379,13 @@ } } + if (TemplateParameterList *TPL = S->getTemplateParameterList()) { + for (Decl *D : *TPL) { + if (!D->isImplicit() || getDerived().shouldVisitImplicitCode()) + TRY_TO(TraverseDecl(D)); + } + } + TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); FunctionProtoTypeLoc Proto = TL.getAsAdjusted(); Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -826,6 +826,16 @@ 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< + "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">; + // Availability attribute def err_expected_version : Error< "expected a version of the form 'major[.minor[.subminor]]'">; Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -231,7 +231,13 @@ Depth += D; AddedLevels += D; } + void setAddedDepth(unsigned D) { + Depth = Depth - AddedLevels + D; + AddedLevels = D; + } + unsigned getDepth() const { return Depth; } + unsigned getOriginalDepth() const { return Depth - AddedLevels; } }; /// Factory object for creating AttributeList objects. Index: include/clang/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -769,19 +769,28 @@ /// \brief If this is a generic lambda, use this as the depth of /// each 'auto' parameter, during initial AST construction. unsigned AutoTemplateParameterDepth; - - /// \brief Store the list of the auto parameters for a generic lambda. - /// If this is a generic lambda, store the list of the auto - /// parameters converted into TemplateTypeParmDecls into a vector - /// that can be used to construct the generic lambda's template + + /// \brief The number of parameters in the template parameter list that were + /// explicitly specified by the user, as opposed to being invented by use + /// of an auto parameter. + unsigned NumExplicitTemplateParams; + + /// \brief Source range covering the explicit template parameter list + /// (if it exists). + SourceRange ExplicitTemplateParamsRange; + + /// \brief Store the list of the template parameters for a generic lambda. + /// If this is a generic lambda, this holds the explicit template parameters + /// followed by the auto parameters converted into TemplateTypeParmDecls. + /// It can be used to construct the generic lambda's template /// parameter list, during initial AST construction. - SmallVector AutoTemplateParams; + SmallVector TemplateParams; /// If this is a generic lambda, and the template parameter - /// list has been created (from the AutoTemplateParams) then + /// list has been created (from the TemplateParams) then /// store a reference to it (cache it to avoid reconstructing it). TemplateParameterList *GLTemplateParameterList; - + /// \brief Contains all variable-referring-expressions (i.e. DeclRefExprs /// or MemberExprs) that refer to local variables in a generic lambda /// or a lambda in a potentially-evaluated-if-used context. @@ -795,7 +804,6 @@ /// will truly be odr-used (i.e. need to be captured) by that nested lambda, /// until its instantiation. But we still need to capture it in the /// enclosing lambda if all intervening lambdas can capture the variable. - llvm::SmallVector PotentiallyCapturingExprs; /// \brief Contains all variable-referring-expressions that refer @@ -820,7 +828,7 @@ CallOperator(nullptr), NumExplicitCaptures(0), Mutable(false), ExplicitParams(false), Cleanup{}, ContainsUnexpandedParameterPack(false), AutoTemplateParameterDepth(0), - GLTemplateParameterList(nullptr) { + NumExplicitTemplateParams(0), GLTemplateParameterList(nullptr) { Kind = SK_Lambda; } @@ -834,9 +842,9 @@ } /// Is this scope known to be for a generic lambda? (This will be false until - /// we parse the first 'auto'-typed parameter. + /// we parse a template parameter list or the first 'auto'-typed parameter). bool isGenericLambda() const { - return !AutoTemplateParams.empty() || GLTemplateParameterList; + return !TemplateParams.empty() || GLTemplateParameterList; } /// Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5489,6 +5489,12 @@ /// given lambda. void finishLambdaExplicitCaptures(sema::LambdaScopeInfo *LSI); + /// \brief This is called after parsing the explicit template parameter list + /// (if it exists) in C++2a. + void ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc, + ArrayRef TParams, + SourceLocation RAngleLoc); + /// \brief Introduce the lambda parameters into scope. void addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope); Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -10,6 +10,7 @@ // This file implements the C++ related Decl classes. // //===----------------------------------------------------------------------===// +#include #include "clang/AST/DeclCXX.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTLambda.h" @@ -1149,13 +1150,26 @@ TemplateParameterList * CXXRecordDecl::getGenericLambdaTemplateParameterList() const { - if (!isLambda()) return nullptr; + if (!isGenericLambda()) return nullptr; CXXMethodDecl *CallOp = getLambdaCallOperator(); if (FunctionTemplateDecl *Tmpl = CallOp->getDescribedFunctionTemplate()) return Tmpl->getTemplateParameters(); return nullptr; } +unsigned CXXRecordDecl::getLambdaExplicitTemplateParameterCount() const { + TemplateParameterList *List = getGenericLambdaTemplateParameterList(); + if (!List) + return 0; + + assert(std::is_partitioned(List->begin(), List->end(), + [](const NamedDecl *D) { return !D->isImplicit(); }) + && "Explicit template params should be ordered before implicit ones"); + + return std::count_if(List->begin(), List->end(), + [](const NamedDecl *D) { return !D->isImplicit(); }); +} + Decl *CXXRecordDecl::getLambdaContextDecl() const { assert(isLambda() && "Not a lambda closure type!"); ExternalASTSource *Source = getParentASTContext().getExternalSource(); Index: lib/AST/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -101,7 +102,8 @@ void VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D); void VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D); - void printTemplateParameters(const TemplateParameterList *Params); + void printTemplateParameters(const TemplateParameterList *Params, + bool OmitTemplateKW = false); void printTemplateArguments(const TemplateArgumentList &Args, const TemplateParameterList *Params = nullptr); void prettyPrintAttributes(Decl *D); @@ -122,6 +124,18 @@ Printer.Visit(const_cast(this)); } +void TemplateParameterList::print(raw_ostream &Out, const ASTContext &Context, + bool OmitTemplateKW) const { + print(Out, Context, Context.getPrintingPolicy(), OmitTemplateKW); +} + +void TemplateParameterList::print(raw_ostream &Out, const ASTContext &Context, + const PrintingPolicy &Policy, + bool OmitTemplateKW) const { + DeclPrinter Printer(Out, Policy, Context); + Printer.printTemplateParameters(this, OmitTemplateKW); +} + static QualType GetBaseType(QualType T) { // FIXME: This should be on the Type class! QualType BaseType = T; @@ -963,25 +977,35 @@ Visit(*D->decls_begin()); } -void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params) { +void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params, + bool OmitTemplateKW) { assert(Params); - Out << "template <"; + if (!OmitTemplateKW) + Out << "template "; + Out << '<'; + + bool NeedComma = false; + for (const Decl *Param : *Params) { + if (Param->isImplicit()) + continue; - for (unsigned i = 0, e = Params->size(); i != e; ++i) { - if (i != 0) + if (NeedComma) Out << ", "; + else + NeedComma = true; - const Decl *Param = Params->getParam(i); if (auto TTP = dyn_cast(Param)) { if (TTP->wasDeclaredWithTypename()) - Out << "typename "; + Out << "typename"; else - Out << "class "; + Out << "class"; if (TTP->isParameterPack()) - Out << "..."; + Out << " ..."; + else if (!TTP->getName().empty()) + Out << ' '; Out << *TTP; @@ -1006,7 +1030,9 @@ } } - Out << "> "; + Out << '>'; + if (!OmitTemplateKW) + Out << ' '; } void DeclPrinter::printTemplateArguments(const TemplateArgumentList &Args, Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -975,6 +975,13 @@ return capture_range(implicit_capture_begin(), implicit_capture_end()); } +SourceRange LambdaExpr::getExplicitTemplateParameterListRange() const { + TemplateParameterList *List = getTemplateParameterList(); + if (!List) + return SourceRange(); + return {List->getLAngleLoc(), List->getRAngleLoc()}; +} + CXXRecordDecl *LambdaExpr::getLambdaClass() const { return getType()->getAsCXXRecordDecl(); } @@ -987,7 +994,11 @@ TemplateParameterList *LambdaExpr::getTemplateParameterList() const { CXXRecordDecl *Record = getLambdaClass(); return Record->getGenericLambdaTemplateParameterList(); +} +unsigned LambdaExpr::getExplicitTemplateParameterCount() const { + const CXXRecordDecl *Record = getLambdaClass(); + return Record->getLambdaExplicitTemplateParameterCount(); } CompoundStmt *LambdaExpr::getBody() const { Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -503,6 +503,7 @@ const AbiTagList *AdditionalAbiTags); void mangleBlockForPrefix(const BlockDecl *Block); void mangleUnqualifiedBlock(const BlockDecl *Block); + void mangleTemplateParamDecl(const NamedDecl *Decl); void mangleLambda(const CXXRecordDecl *Lambda); void mangleNestedName(const NamedDecl *ND, const DeclContext *DC, const AbiTagList *AdditionalAbiTags, @@ -1378,7 +1379,8 @@ // ::= // // ::= Ul E [ ] _ - // ::= + # Parameter types or 'v' for 'void'. + // ::= * + + // # Parameter types or 'v' for 'void'. if (const CXXRecordDecl *Record = dyn_cast(TD)) { if (Record->isLambda() && Record->getLambdaManglingNumber()) { assert(!AdditionalAbiTags && @@ -1685,6 +1687,26 @@ Out << '_'; } +// +// ::= Ty # template type parameter +// ::= Tn # template non-type parameter +// ::= Tt * E # template template parameter +void CXXNameMangler::mangleTemplateParamDecl(const NamedDecl *Decl) { + if (isa(Decl)) { + Out << "Ty"; + } + else if (auto *Tn = dyn_cast(Decl)) { + Out << "Tn"; + mangleType(Tn->getType()); + } + else if (auto *Tt = dyn_cast(Decl)) { + Out << "Tt"; + for (auto *Param : *Tt->getTemplateParameters()) + mangleTemplateParamDecl(Param); + Out << "E"; + } +} + void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { // If the context of a closure type is an initializer for a class member // (static or nonstatic), it is encoded in a qualified name with a final @@ -1712,6 +1734,16 @@ } Out << "Ul"; + if (unsigned ExplcitTemplateParamCount + = Lambda->getLambdaExplicitTemplateParameterCount()) { + const auto *List = Lambda->getGenericLambdaTemplateParameterList(); + assert(List && "Lambda says it has explicit template parameters, " + "but it doesn't have a template parameter list"); + for (auto It = List->begin(), End = It + ExplcitTemplateParamCount; + It != End; + ++It) + mangleTemplateParamDecl(*It); + } const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()-> getAs(); mangleBareFunctionType(Proto, /*MangleReturnType=*/false, Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -2237,8 +2237,14 @@ } OS << ']'; + if (Node->getExplicitTemplateParameterCount() > 0) { + Node->getTemplateParameterList()->print( + OS, Node->getLambdaClass()->getASTContext(), + /*OmitTemplateKW*/true); + } + if (Node->hasExplicitParameters()) { - OS << " ("; + OS << '('; CXXMethodDecl *Method = Node->getCallOperator(); NeedComma = false; for (auto P : Method->parameters()) { @@ -2274,9 +2280,8 @@ } // Print the body. - CompoundStmt *Body = Node->getBody(); OS << ' '; - PrintStmt(Body); + PrintRawCompoundStmt(Node->getBody()); } void StmtPrinter::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *Node) { Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ lib/AST/TypePrinter.cpp @@ -1090,8 +1090,18 @@ raw_ostream &OS) { if (IdentifierInfo *Id = T->getIdentifier()) OS << Id->getName(); - else - OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); + else { + bool IsLambdaAutoParam = false; + if (auto D = T->getDecl()) { + if (auto M = dyn_cast_or_null(D->getDeclContext())) + IsLambdaAutoParam = D->isImplicit() && M->getParent()->isLambda(); + } + + if (IsLambdaAutoParam) + OS << "auto"; + else + OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); + } spaceBeforePlaceHolder(OS); } void TypePrinter::printTemplateTypeParmAfter(const TemplateTypeParmType *T, Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -635,6 +635,8 @@ /// /// lambda-expression: /// lambda-introducer lambda-declarator[opt] compound-statement +/// lambda-introducer '<' template-parameter-list '>' +/// lambda-declarator[opt] compound-statement /// /// lambda-introducer: /// '[' lambda-capture[opt] ']' @@ -1107,6 +1109,32 @@ << A->getName()->getName(); }; + // FIXME: Consider allowing this as an extension for GCC compatibiblity. + const bool HasExplicitTemplateParams = Tok.is(tok::less); + ParseScope TemplateParamScope(this, Scope::TemplateParamScope, + /*EnteredScope=*/HasExplicitTemplateParams); + if (HasExplicitTemplateParams) { + Diag(Tok, getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_lambda_template_parameter_list + : diag::ext_lambda_template_parameter_list); + + SmallVector TemplateParams; + SourceLocation LAngleLoc, RAngleLoc; + if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(), + TemplateParams, LAngleLoc, RAngleLoc)) { + return ExprError(); + } + + if (TemplateParams.empty()) { + Diag(RAngleLoc, + diag::err_lambda_template_parameter_list_empty); + } else { + Actions.ActOnLambdaExplicitTemplateParameterList( + LAngleLoc, TemplateParams, RAngleLoc); + ++CurTemplateDepthTracker; + } + } + TypeResult TrailingReturnType; if (Tok.is(tok::l_paren)) { ParseScope PrototypeScope(this, @@ -1121,15 +1149,25 @@ // Parse parameter-declaration-clause. SmallVector ParamInfo; SourceLocation EllipsisLoc; - + if (Tok.isNot(tok::r_paren)) { - Actions.RecordParsingTemplateParameterDepth(TemplateParameterDepth); + // Record the template parameter depth for auto parameters. + // We want the auto parameter depth to be the same as the explicit + // template params (if they exist). + Actions.RecordParsingTemplateParameterDepth( + CurTemplateDepthTracker.getOriginalDepth()); + ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); + // For a generic lambda, each 'auto' within the parameter declaration // clause creates a template type parameter, so increment the depth. - if (Actions.getCurGenericLambda()) - ++CurTemplateDepthTracker; + // Note that if there are any explicit template params, then the depth + // will have already been increased. So we make sure to not increment + // past 1 added depth level. + if (Actions.getCurGenericLambda()) + CurTemplateDepthTracker.setAddedDepth(1); } + T.consumeClose(); SourceLocation RParenLoc = T.getCloseLocation(); SourceLocation DeclEndLoc = RParenLoc; @@ -1147,7 +1185,7 @@ SourceLocation ConstexprLoc; tryConsumeMutableOrConstexprToken(*this, MutableLoc, ConstexprLoc, DeclEndLoc); - + addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS); // Parse exception-specification[opt]. @@ -1298,6 +1336,7 @@ StmtResult Stmt(ParseCompoundStatementBody()); BodyScope.Exit(); + TemplateParamScope.Exit(); if (!Stmt.isInvalid() && !TrailingReturnType.isInvalid()) return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get(), getCurScope()); Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -1427,7 +1427,7 @@ // an associated template parameter list. LambdaScopeInfo *Sema::getCurGenericLambda() { if (LambdaScopeInfo *LSI = getCurLambda()) { - return (LSI->AutoTemplateParams.size() || + return (LSI->TemplateParams.size() || LSI->GLTemplateParameterList) ? LSI : nullptr; } return nullptr; Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -21,6 +21,7 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaLambda.h" +#include "llvm/ADT/STLExtras.h" using namespace clang; using namespace sema; @@ -226,19 +227,14 @@ static inline TemplateParameterList * getGenericLambdaTemplateParameterList(LambdaScopeInfo *LSI, Sema &SemaRef) { - if (LSI->GLTemplateParameterList) - return LSI->GLTemplateParameterList; - - if (!LSI->AutoTemplateParams.empty()) { - SourceRange IntroRange = LSI->IntroducerRange; - SourceLocation LAngleLoc = IntroRange.getBegin(); - SourceLocation RAngleLoc = IntroRange.getEnd(); + if (!LSI->GLTemplateParameterList && !LSI->TemplateParams.empty()) { LSI->GLTemplateParameterList = TemplateParameterList::Create( SemaRef.Context, - /*Template kw loc*/ SourceLocation(), LAngleLoc, - llvm::makeArrayRef((NamedDecl *const *)LSI->AutoTemplateParams.data(), - LSI->AutoTemplateParams.size()), - RAngleLoc, nullptr); + /*Template kw loc*/ SourceLocation(), + /*L angle loc*/ LSI->ExplicitTemplateParamsRange.getBegin(), + LSI->TemplateParams, + /*R angle loc*/LSI->ExplicitTemplateParamsRange.getEnd(), + nullptr); } return LSI->GLTemplateParameterList; } @@ -493,6 +489,25 @@ LSI->finishedExplicitCaptures(); } +void Sema::ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc, + ArrayRef TParams, + SourceLocation RAngleLoc) { + LambdaScopeInfo *LSI = getCurLambda(); + assert(LSI && "Expected a lambda scope"); + + assert(LSI->NumExplicitTemplateParams == 0 && + "Already acted on explicit template parameters"); + assert(LSI->TemplateParams.size() == 0 && + "Explicit template parameters should come " + "before invented (auto) ones"); + assert(TParams.size() > 0 && + "No template parameters to act on"); + + LSI->TemplateParams.append(TParams.begin(), TParams.end()); + LSI->NumExplicitTemplateParams = TParams.size(); + LSI->ExplicitTemplateParamsRange = {LAngleLoc, RAngleLoc}; +} + void Sema::addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope) { // Introduce our parameters into the function scope for (unsigned p = 0, NumParams = CallOperator->getNumParams(); @@ -825,17 +840,23 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, Declarator &ParamInfo, Scope *CurScope) { - // Determine if we're within a context where we know that the lambda will - // be dependent, because there are template parameters in scope. - bool KnownDependent = false; LambdaScopeInfo *const LSI = getCurLambda(); assert(LSI && "LambdaScopeInfo should be on stack!"); - // The lambda-expression's closure type might be dependent even if its - // semantic context isn't, if it appears within a default argument of a - // function template. - if (CurScope->getTemplateParamParent()) - KnownDependent = true; + // Determine if we're within a context where we know that the lambda will + // be dependent, because there are template parameters in scope. + bool KnownDependent; + if (LSI->NumExplicitTemplateParams > 0) { + auto *TemplateParamScope = CurScope->getTemplateParamParent(); + assert(TemplateParamScope && + "Lambda with explicit template param list should establish a " + "template param scope"); + assert(TemplateParamScope->getParent()); + KnownDependent = TemplateParamScope->getParent() + ->getTemplateParamParent() != nullptr; + } else { + KnownDependent = CurScope->getTemplateParamParent() != nullptr; + } // Determine the signature of the call operator. TypeSourceInfo *MethodTyInfo; Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -2791,7 +2791,7 @@ sema::LambdaScopeInfo *LSI = SemaRef.getCurLambda(); assert(LSI && "No LambdaScopeInfo on the stack!"); const unsigned TemplateParameterDepth = LSI->AutoTemplateParameterDepth; - const unsigned AutoParameterPosition = LSI->AutoTemplateParams.size(); + const unsigned AutoParameterPosition = LSI->TemplateParams.size(); const bool IsParameterPack = D.hasEllipsis(); // Create the TemplateTypeParmDecl here to retrieve the corresponding @@ -2803,7 +2803,8 @@ /*KeyLoc*/SourceLocation(), /*NameLoc*/D.getLocStart(), TemplateParameterDepth, AutoParameterPosition, /*Identifier*/nullptr, false, IsParameterPack); - LSI->AutoTemplateParams.push_back(CorrespondingTemplateParam); + CorrespondingTemplateParam->setImplicit(); + LSI->TemplateParams.push_back(CorrespondingTemplateParam); // Replace the 'auto' in the function parameter with this invented // template type parameter. // FIXME: Retain some type sugar to indicate that this was written Index: test/CXX/temp/temp.decls/temp.variadic/p4.cpp =================================================================== --- test/CXX/temp/temp.decls/temp.variadic/p4.cpp +++ test/CXX/temp/temp.decls/temp.variadic/p4.cpp @@ -213,8 +213,10 @@ }; #endif +#if __cplusplus > 201703L // - in a template parameter pack that is a pack expansion - // FIXME: We do not support any way to reach this case yet. + swallow([] typename ...W>(W ...wv) { }); +#endif // - in an initializer-list int arr[] = {T().x...}; @@ -279,11 +281,6 @@ struct T { int x; using U = int; }; void g() { f(1, 2, 3); } - template void pack_in_lambda(U ...u) { // expected-note {{here}} - // FIXME: Move this test into 'f' above once we support this syntax. - [] typename ...U>(U ...uv) {}; // expected-error {{expected body of lambda}} expected-error {{does not refer to a value}} - } - template void pack_expand_attr() { // FIXME: Move this test into 'f' above once we support this. [[gnu::aligned(alignof(T))...]] int x; // expected-error {{cannot be used as an attribute pack}} expected-error {{unexpanded}} Index: test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp =================================================================== --- test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp +++ test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c++2a -triple %itanium_abi_triple -emit-llvm -o - %s -w | FileCheck %s + +template +struct DummyType { }; + +inline void inline_func() { + // CHECK: UlvE + []{}(); + + // CHECK: UlTyvE + []{}.operator()(); + + // CHECK: UlTyT_E + [](T){}(1); + + // CHECK: UlTyTyT_T0_E + [](T1, T2){}(1, 2); + + // CHECK: UlTyTyT0_T_E + [](T2, T1){}(2, 1); + + // CHECK: UlTniTyTnjT0_E + [](T){}.operator()<1, int, 2>(3); + + // CHECK: UlTyTtTyTniTyETniTyvE + [] class, + int, + class>{}.operator()(); +} + +void call_inline_func() { + inline_func(); +} Index: test/PCH/cxx11-lambdas.mm =================================================================== --- test/PCH/cxx11-lambdas.mm +++ test/PCH/cxx11-lambdas.mm @@ -54,7 +54,7 @@ } // CHECK-PRINT: inline int add_int_slowly_twice -// CHECK-PRINT: lambda = [&] (int z) +// CHECK-PRINT: lambda = [&](int z) // CHECK-PRINT: init_capture // CHECK-PRINT: [&, x(t)] Index: test/PCH/cxx1y-lambdas.mm =================================================================== --- test/PCH/cxx1y-lambdas.mm +++ test/PCH/cxx1y-lambdas.mm @@ -50,7 +50,7 @@ } // CHECK-PRINT: inline int add_int_slowly_twice -// CHECK-PRINT: lambda = [] (type-parameter-0-0 z +// CHECK-PRINT: lambda = [](auto z // CHECK-PRINT: init_capture // CHECK-PRINT: [&, x(t)] Index: test/PCH/cxx2a-template-lambdas.cpp =================================================================== --- test/PCH/cxx2a-template-lambdas.cpp +++ test/PCH/cxx2a-template-lambdas.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t +// RUN: %clang_cc1 -std=c++2a -include-pch %t -verify %s + +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +auto l1 = []() constexpr -> int { + return I; +}; + +auto l2 = []() constexpr -> decltype(I) { + return I; +}; + +auto l3 = [](auto i) constexpr -> T { + return T(i); +}; + +auto l4 = [] class T, class U>(T, auto i) constexpr -> U { + return U(i); +}; + +#else /*included pch*/ + +static_assert(l1.operator()<5>() == 5); +static_assert(l1.operator()<6>() == 6); + +static_assert(l2.operator()<7>() == 7); +static_assert(l2.operator()() == nullptr); + +static_assert(l3.operator()(8.4) == 8); +static_assert(l3.operator()(9.9) == 9); + +template +struct DummyTemplate { }; + +static_assert(l4(DummyTemplate(), 12) == 12.0); +static_assert(l4(DummyTemplate(), 19.8) == 19); + +#endif // HEADER Index: test/Parser/cxx2a-template-lambdas.cpp =================================================================== --- test/Parser/cxx2a-template-lambdas.cpp +++ test/Parser/cxx2a-template-lambdas.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify + +auto L0 = []<> { }; //expected-error {{cannot be empty}} + +auto L1 = [] { }; +auto L2 = [](T1 arg1, T2 arg2) -> T1 { }; +auto L3 = [](auto arg) { T t; }; +auto L4 = []() { }; Index: test/SemaCXX/cxx2a-template-lambdas.cpp =================================================================== --- test/SemaCXX/cxx2a-template-lambdas.cpp +++ test/SemaCXX/cxx2a-template-lambdas.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +template +constexpr bool is_same = false; + +template +constexpr bool is_same = true; + +template +struct DummyTemplate { }; + +void func() { + auto L0 = [](T arg) { + static_assert(is_same); + }; + L0(0); + + auto L1 = [] { + static_assert(I == 5); + }; + L1.operator()<5>(); + + auto L2 = [] class T, class U>(T &&arg) { + static_assert(is_same, DummyTemplate>); + }; + L2(DummyTemplate()); +} + +template // expected-note {{declared here}} +struct ShadowMe { + void member_func() { + auto L = [] { }; // expected-error {{'T' shadows template parameter}} + } +}; Index: unittests/AST/StmtPrinterTest.cpp =================================================================== --- unittests/AST/StmtPrinterTest.cpp +++ unittests/AST/StmtPrinterTest.cpp @@ -67,9 +67,8 @@ template ::testing::AssertionResult -PrintedStmtMatches(StringRef Code, const std::vector &Args, - const T &NodeMatch, StringRef ExpectedPrinted) { - +PrintedStmtMatchesInternal(StringRef Code, const std::vector &Args, + const T &NodeMatch, StringRef ExpectedPrinted) { PrintMatch Printer; MatchFinder Finder; Finder.addMatcher(NodeMatch, &Printer); @@ -97,65 +96,52 @@ return ::testing::AssertionSuccess(); } +enum class StdVer { CXX98, CXX11, CXX14, CXX17, CXX2a }; + +DeclarationMatcher FunctionBodyMatcher(StringRef ContainingFunction) { + return functionDecl(hasName(ContainingFunction), + has(compoundStmt(has(stmt().bind("id"))))); +} + +template ::testing::AssertionResult -PrintedStmtCXX98Matches(StringRef Code, const StatementMatcher &NodeMatch, - StringRef ExpectedPrinted) { - std::vector Args; - Args.push_back("-std=c++98"); - Args.push_back("-Wno-unused-value"); - return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted); -} - -::testing::AssertionResult PrintedStmtCXX98Matches( - StringRef Code, - StringRef ContainingFunction, - StringRef ExpectedPrinted) { - std::vector Args; - Args.push_back("-std=c++98"); - Args.push_back("-Wno-unused-value"); - return PrintedStmtMatches(Code, - Args, - functionDecl(hasName(ContainingFunction), - has(compoundStmt(has(stmt().bind("id"))))), - ExpectedPrinted); +PrintedStmtMatches(StdVer Standard, StringRef Code, + const T &NodeMatch, StringRef ExpectedPrinted) { + const char *StdOpt; + switch (Standard) { + case StdVer::CXX98: StdOpt = "-std=c++98"; break; + case StdVer::CXX11: StdOpt = "-std=c++11"; break; + case StdVer::CXX14: StdOpt = "-std=c++14"; break; + case StdVer::CXX17: StdOpt = "-std=c++17"; break; + case StdVer::CXX2a: StdOpt = "-std=c++2a"; break; + } + return PrintedStmtMatchesInternal(Code, {StdOpt, "-Wno-unused-value"}, + NodeMatch, ExpectedPrinted); } +template ::testing::AssertionResult -PrintedStmtCXX11Matches(StringRef Code, const StatementMatcher &NodeMatch, - StringRef ExpectedPrinted) { - std::vector Args; - Args.push_back("-std=c++11"); - Args.push_back("-Wno-unused-value"); - return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted); -} - -::testing::AssertionResult PrintedStmtMSMatches( - StringRef Code, - StringRef ContainingFunction, - StringRef ExpectedPrinted) { - std::vector Args; - Args.push_back("-target"); - Args.push_back("i686-pc-win32"); - Args.push_back("-std=c++98"); - Args.push_back("-fms-extensions"); - Args.push_back("-Wno-unused-value"); - return PrintedStmtMatches(Code, - Args, - functionDecl(hasName(ContainingFunction), - has(compoundStmt(has(stmt().bind("id"))))), - ExpectedPrinted); +PrintedStmtMSMatches(StringRef Code, + const T &NodeMatch, StringRef ExpectedPrinted) { + std::vector Args = { + "-std=c++98", + "-target", "i686-pc-win32", + "-fms-extensions", + "-Wno-unused-value", + }; + return PrintedStmtMatchesInternal(Code, Args, NodeMatch, ExpectedPrinted); } } // unnamed namespace TEST(StmtPrinter, TestIntegerLiteral) { - ASSERT_TRUE(PrintedStmtCXX98Matches( + ASSERT_TRUE(PrintedStmtMatches(StdVer::CXX98, "void A() {" " 1, -1, 1U, 1u," " 1L, 1l, -1L, 1UL, 1ul," " 1LL, -1LL, 1ULL;" "}", - "A", + FunctionBodyMatcher("A"), "1 , -1 , 1U , 1U , " "1L , 1L , -1L , 1UL , 1UL , " "1LL , -1LL , 1ULL")); @@ -170,7 +156,7 @@ " 1i32, -1i32, 1ui32, " " 1i64, -1i64, 1ui64;" "}", - "A", + FunctionBodyMatcher("A"), "1i8 , -1i8 , 1Ui8 , " "1i16 , -1i16 , 1Ui16 , " "1 , -1 , 1U , " @@ -179,15 +165,15 @@ } TEST(StmtPrinter, TestFloatingPointLiteral) { - ASSERT_TRUE(PrintedStmtCXX98Matches( + ASSERT_TRUE(PrintedStmtMatches(StdVer::CXX98, "void A() { 1.0f, -1.0f, 1.0, -1.0, 1.0l, -1.0l; }", - "A", + FunctionBodyMatcher("A"), "1.F , -1.F , 1. , -1. , 1.L , -1.L")); // Should be: with semicolon } TEST(StmtPrinter, TestCXXConversionDeclImplicit) { - ASSERT_TRUE(PrintedStmtCXX98Matches( + ASSERT_TRUE(PrintedStmtMatches(StdVer::CXX98, "struct A {" "operator void *();" "A operator&(A);" @@ -201,7 +187,7 @@ } TEST(StmtPrinter, TestCXXConversionDeclExplicit) { - ASSERT_TRUE(PrintedStmtCXX11Matches( + ASSERT_TRUE(PrintedStmtMatches(StdVer::CXX11, "struct A {" "operator void *();" "A operator&(A);" @@ -214,3 +200,40 @@ "(a & b)")); // WRONG; Should be: (a & b).operator void *() } + +TEST(StmtPrinter, TestCXXLamda) { + ASSERT_TRUE(PrintedStmtMatches(StdVer::CXX11, + "void A() {" + " auto l = [] { };" + "}", + lambdaExpr(anything()).bind("id"), + "[] {\n" + "}")); + + ASSERT_TRUE(PrintedStmtMatches(StdVer::CXX11, + "void A() {" + " int a = 0, b = 1;" + " auto l = [a,b](int c, float d) { };" + "}", + lambdaExpr(anything()).bind("id"), + "[a, b](int c, float d) {\n" + "}")); + + ASSERT_TRUE(PrintedStmtMatches(StdVer::CXX14, + "void A() {" + " auto l = [](auto a, int b, auto c, int, auto) { };" + "}", + lambdaExpr(anything()).bind("id"), + "[](auto a, int b, auto c, int, auto) {\n" + "}")); + + ASSERT_TRUE(PrintedStmtMatches(StdVer::CXX2a, + "void A() {" + " auto l = [] class T3>" + " (int a, auto, int, auto d) { };" + "}", + lambdaExpr(anything()).bind("id"), + "[] class T3>(int a, auto, int, auto d) {\n" + "}")); +} Index: unittests/Tooling/RecursiveASTVisitorTest.cpp =================================================================== --- unittests/Tooling/RecursiveASTVisitorTest.cpp +++ unittests/Tooling/RecursiveASTVisitorTest.cpp @@ -79,6 +79,41 @@ LambdaDefaultCaptureVisitor::Lang_CXX11)); } +// Matches (optional) explicit template parameters. +class LambdaTemplateParametersVisitor + : public ExpectedLocationVisitor { +public: + bool shouldVisitImplicitCode() const { return false; } + + bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } + bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } + bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } +}; + +TEST(RecursiveASTVisitor, VisitsLambdaExplicitTemplateParameters) { + LambdaTemplateParametersVisitor Visitor; + Visitor.ExpectMatch("T", 2, 15); + Visitor.ExpectMatch("I", 2, 24); + Visitor.ExpectMatch("TT", 2, 31); + EXPECT_TRUE(Visitor.runOver( + "void f() { \n" + " auto l = [] class TT>(auto p) { }; \n" + "}", + LambdaTemplateParametersVisitor::Lang_CXX2a)); +} + // Checks for lambda classes that are not marked as implicitly-generated. // (There should be none.) class ClassVisitor : public ExpectedLocationVisitor { Index: unittests/Tooling/TestVisitor.h =================================================================== --- unittests/Tooling/TestVisitor.h +++ unittests/Tooling/TestVisitor.h @@ -44,6 +44,8 @@ Lang_CXX98, Lang_CXX11, Lang_CXX14, + Lang_CXX17, + Lang_CXX2a, Lang_OBJC, Lang_OBJCXX11, Lang_CXX = Lang_CXX98 @@ -60,6 +62,8 @@ case Lang_CXX98: Args.push_back("-std=c++98"); break; case Lang_CXX11: Args.push_back("-std=c++11"); break; case Lang_CXX14: Args.push_back("-std=c++14"); break; + case Lang_CXX17: Args.push_back("-std=c++17"); break; + case Lang_CXX2a: Args.push_back("-std=c++2a"); break; case Lang_OBJC: Args.push_back("-ObjC"); Args.push_back("-fobjc-runtime=macosx-10.12.0"); Index: www/cxx_status.html =================================================================== --- www/cxx_status.html +++ www/cxx_status.html @@ -823,7 +823,7 @@ template-parameter-list for generic lambdas P0428R2 - No + SVN Initializer list constructors in class template argument deduction