Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -822,6 +822,10 @@ def ext_constexpr_on_lambda_cxx17 : ExtWarn< "'constexpr' on lambda expressions is a C++17 extension">, InGroup; + // C++2a template lambdas + 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/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -767,18 +767,27 @@ bool ContainsUnexpandedParameterPack; /// \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 + /// each template parameter, during initial AST construction. + unsigned TemplateParameterDepth; + + /// \brief The number of parameters in the template parameter list that were + /// explicitely 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; @@ -819,8 +828,8 @@ : CapturingScopeInfo(Diag, ImpCap_None), Lambda(nullptr), CallOperator(nullptr), NumExplicitCaptures(0), Mutable(false), ExplicitParams(false), Cleanup{}, - ContainsUnexpandedParameterPack(false), AutoTemplateParameterDepth(0), - GLTemplateParameterList(nullptr) { + ContainsUnexpandedParameterPack(false), TemplateParameterDepth(0), + NumExplicitTemplateParams(0), GLTemplateParameterList(nullptr) { Kind = SK_Lambda; } @@ -834,9 +843,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; } /// @@ -938,6 +947,12 @@ PotentialThisCaptureLocation.isValid(); } + llvm::ArrayRef getExplicitTemplateParams() const { + // Explicit template parameters should always be first in the list. + return llvm::makeArrayRef(TemplateParams) + .slice(0, NumExplicitTemplateParams); + } + // When passed the index, returns the VarDecl and Expr associated // with the index. void getPotentialVariableCapture(unsigned Idx, VarDecl *&VD, Expr *&E) const; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5466,6 +5466,18 @@ /// given lambda. void finishLambdaExplicitCaptures(sema::LambdaScopeInfo *LSI); + /// \brief This is called after parsing the explicit template parameter list + /// (if it exists) in C++2a. + TemplateParameterList * + ActOnLambdaTemplateParameterList(unsigned Depth, + SourceLocation LAngleLoc, + ArrayRef TParams, + SourceLocation RAngleLoc); + + /// \brief Introduce the explicit lambda template parameters into scope. + void addLambdaExplicitTemplateParameters(ArrayRef TParams, + Scope *CurScope); + /// \brief Introduce the lambda parameters into scope. void addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope); 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 lambda-declarator[opt] +/// compound-statement /// /// lambda-introducer: /// '[' lambda-capture[opt] ']' @@ -1082,9 +1084,11 @@ // Parse lambda-declarator[opt]. DeclSpec DS(AttrFactory); Declarator D(DS, Declarator::LambdaExprContext); - TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); Actions.PushLambdaScope(); + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + Actions.RecordParsingTemplateParameterDepth(TemplateParameterDepth); + ParsedAttributes Attr(AttrFactory); SourceLocation DeclLoc = Tok.getLocation(); if (getLangOpts().CUDA) { @@ -1105,6 +1109,30 @@ << A->getName()->getName(); }; + // FIXME: Consider allowing this as an extension for GCC compatibiblity. + bool HasExplicitTemplateParams = getLangOpts().CPlusPlus2a + && Tok.is(tok::less); + ParseScope TemplateParamScope(this, Scope::TemplateParamScope, + /*EnteredScope=*/HasExplicitTemplateParams); + if (HasExplicitTemplateParams) { + 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.ActOnLambdaTemplateParameterList( + CurTemplateDepthTracker.getDepth(), + LAngleLoc, TemplateParams, RAngleLoc); + } + } + TypeResult TrailingReturnType; if (Tok.is(tok::l_paren)) { ParseScope PrototypeScope(this, @@ -1120,14 +1148,9 @@ SmallVector ParamInfo; SourceLocation EllipsisLoc; - if (Tok.isNot(tok::r_paren)) { - Actions.RecordParsingTemplateParameterDepth(TemplateParameterDepth); + if (Tok.isNot(tok::r_paren)) 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; - } + T.consumeClose(); SourceLocation RParenLoc = T.getCloseLocation(); SourceLocation DeclEndLoc = RParenLoc; @@ -1279,6 +1302,13 @@ Attr, DeclEndLoc); } + TemplateParamScope.Exit(); + + // getCurGenericLambda is used to see if we've added any template parameters. + // If so, the template depth needs to be increased. + if (Actions.getCurGenericLambda()) + ++CurTemplateDepthTracker; + // FIXME: Rename BlockScope -> ClosureScope if we decide to continue using // it. unsigned ScopeFlags = Scope::BlockScope | Scope::FnScope | Scope::DeclScope | Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -1292,7 +1292,7 @@ void Sema::RecordParsingTemplateParameterDepth(unsigned Depth) { if (LambdaScopeInfo *const LSI = getCurLambda()) { - LSI->AutoTemplateParameterDepth = Depth; + LSI->TemplateParameterDepth = Depth; return; } llvm_unreachable( @@ -1376,7 +1376,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 @@ -229,15 +229,17 @@ if (LSI->GLTemplateParameterList) return LSI->GLTemplateParameterList; - if (!LSI->AutoTemplateParams.empty()) { - SourceRange IntroRange = LSI->IntroducerRange; - SourceLocation LAngleLoc = IntroRange.getBegin(); - SourceLocation RAngleLoc = IntroRange.getEnd(); + if (!LSI->TemplateParams.empty()) { + SourceRange ListRange = LSI->ExplicitTemplateParamsRange.isValid() + ? LSI->ExplicitTemplateParamsRange + : LSI->IntroducerRange; + SourceLocation LAngleLoc = ListRange.getBegin(); + SourceLocation RAngleLoc = ListRange.getEnd(); LSI->GLTemplateParameterList = TemplateParameterList::Create( SemaRef.Context, /*Template kw loc*/ SourceLocation(), LAngleLoc, - llvm::makeArrayRef((NamedDecl *const *)LSI->AutoTemplateParams.data(), - LSI->AutoTemplateParams.size()), + llvm::makeArrayRef((NamedDecl *const *)LSI->TemplateParams.data(), + LSI->TemplateParams.size()), RAngleLoc, nullptr); } return LSI->GLTemplateParameterList; @@ -479,6 +481,44 @@ LSI->finishedExplicitCaptures(); } +TemplateParameterList * +Sema::ActOnLambdaTemplateParameterList(unsigned Depth, + 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 ones"); + + TemplateParameterList *ret = ActOnTemplateParameterList( + Depth, + /*ExportLoc=*/SourceLocation(), /*TemplateLoc=*/SourceLocation(), + LAngleLoc, TParams, RAngleLoc, + /*RequiresClause=*/nullptr); + + LSI->TemplateParams.append(TParams.begin(), TParams.end()); + LSI->NumExplicitTemplateParams = TParams.size(); + LSI->ExplicitTemplateParamsRange = {LAngleLoc, RAngleLoc}; + + return ret; +} + +void Sema::addLambdaExplicitTemplateParameters(ArrayRef TParams, + Scope *CurScope) { + for (Decl *T : TParams) { + NamedDecl *NT = dyn_cast(T); + assert(NT && "Template parameter should be convertable to NamedDecl"); + + // If this has an identifier, add it to the scope stack. + if (CurScope && NT->getIdentifier()) + PushOnScopeChains(NT, CurScope); + } +} + void Sema::addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope) { // Introduce our parameters into the function scope for (unsigned p = 0, NumParams = CallOperator->getNumParams(); @@ -1121,6 +1161,10 @@ LSI->ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack; + // Add explicit template parameters into scope. + addLambdaExplicitTemplateParameters(LSI->getExplicitTemplateParams(), + CurScope); + // Add lambda parameters into scope. addLambdaParameters(Method, CurScope); Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -2789,8 +2789,8 @@ // template parameter type. 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 TemplateParameterDepth = LSI->TemplateParameterDepth; + const unsigned AutoParameterPosition = LSI->TemplateParams.size(); const bool IsParameterPack = D.hasEllipsis(); // Create the TemplateTypeParmDecl here to retrieve the corresponding @@ -2802,7 +2802,7 @@ /*KeyLoc*/SourceLocation(), /*NameLoc*/D.getLocStart(), TemplateParameterDepth, AutoParameterPosition, /*Identifier*/nullptr, false, IsParameterPack); - LSI->AutoTemplateParams.push_back(CorrespondingTemplateParam); + 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/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: www/cxx_status.html =================================================================== --- www/cxx_status.html +++ www/cxx_status.html @@ -822,7 +822,7 @@ template-parameter-list for generic lambdas P0428R2 - No + SVN Initializer list constructors in class template argument deduction