hamzasood updated this revision to Diff 111892.
hamzasood added a comment.

Sorry, I've just spotted a small mistake in how the auto parameter depth is 
recorded.
This update fixes it.


https://reviews.llvm.org/D36527

Files:
  include/clang/Basic/DiagnosticParseKinds.td
  include/clang/Sema/ScopeInfo.h
  include/clang/Sema/Sema.h
  lib/Parse/ParseExprCXX.cpp
  lib/Sema/Sema.cpp
  lib/Sema/SemaLambda.cpp
  lib/Sema/SemaType.cpp
  test/CXX/temp/temp.decls/temp.variadic/p4.cpp
  test/Parser/cxx2a-template-lambdas.cpp
  test/SemaCXX/cxx2a-template-lambdas.cpp
  www/cxx_status.html

Index: www/cxx_status.html
===================================================================
--- www/cxx_status.html
+++ www/cxx_status.html
@@ -822,7 +822,7 @@
     <tr>
       <td><i>template-parameter-list</i> for generic lambdas</td>
       <td><a href="http://wg21.link/p0428r2";>P0428R2</a></td>
-      <td class="none" align="center">No</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
     <tr>
       <td>Initializer list constructors in class template argument deduction</td>
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<typename, typename>
+constexpr bool is_same = false;
+
+template<typename T>
+constexpr bool is_same<T, T> = true;
+
+template<typename T>
+struct DummyTemplate { };
+
+void func() {
+  auto L0 = []<typename T>(T arg) {
+    static_assert(is_same<T, int>);
+  };
+  L0(0);
+
+  auto L1 = []<int I> {
+    static_assert(I == 5);
+  };
+  L1.operator()<5>();
+
+  auto L2 = []<template<typename> class T, class U>(T<U> &&arg) {
+    static_assert(is_same<T<U>, DummyTemplate<float>>);
+  };
+  L2(DummyTemplate<float>());
+}
+
+template<typename T> // expected-note {{declared here}}
+struct ShadowMe {
+  void member_func() {
+    auto L = []<typename T> { }; // expected-error {{'T' shadows template parameter}}
+  }
+};
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 = []<typename T1, typename T2> { };
+auto L2 = []<typename T1, typename T2>(T1 arg1, T2 arg2) -> T1 { };
+auto L3 = []<typename T>(auto arg) { T t; };
+auto L4 = []<int I>() { };
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 >= 201707L
       //    - in a template parameter pack that is a pack expansion
-      // FIXME: We do not support any way to reach this case yet.
+      swallow([]<T *...v, template<T *> typename ...W>(W<v> ...wv) { });
+#endif
 
       //    - in an initializer-list
       int arr[] = {T().x...};
@@ -279,11 +281,6 @@
   struct T { int x; using U = int; };
   void g() { f<T>(1, 2, 3); }
 
-  template<typename ...T, typename ...U> void pack_in_lambda(U ...u) { // expected-note {{here}}
-    // FIXME: Move this test into 'f' above once we support this syntax.
-    []<T *...v, template<T *> typename ...U>(U<v> ...uv) {}; // expected-error {{expected body of lambda}} expected-error {{does not refer to a value}}
-  }
-
   template<typename ...T> 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: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -2790,7 +2790,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
@@ -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: 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,32 @@
   LSI->finishedExplicitCaptures();
 }
 
+TemplateParameterList *
+Sema::ActOnLambdaTemplateParameterList(unsigned Depth,
+                                       SourceLocation LAngleLoc,
+                                       ArrayRef<Decl *> 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::addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope) {  
   // Introduce our parameters into the function scope
   for (unsigned p = 0, NumParams = CallOperator->getNumParams(); 
@@ -811,17 +839,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");
+    KnownDependent = TemplateParamScope->getParent()
+                                       ->getTemplateParamParent() != nullptr;
+  }
+  else {
+    KnownDependent = CurScope->getTemplateParamParent() != nullptr;
+  }
 
   // Determine the signature of the call operator.
   TypeSourceInfo *MethodTyInfo;
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -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/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] ']'
@@ -1105,6 +1107,31 @@
               << A->getName()->getName();
   };
 
+  // FIXME: Consider allowing this as an extension for GCC compatibiblity.
+  const bool HasExplicitTemplateParams = getLangOpts().CPlusPlus2a
+                                         && Tok.is(tok::less);
+  ParseScope TemplateParamScope(this, Scope::TemplateParamScope,
+                                /*EnteredScope=*/HasExplicitTemplateParams);
+  if (HasExplicitTemplateParams) {
+    SmallVector<Decl*, 4> 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);
+      ++CurTemplateDepthTracker;
+    }
+  }
+
   TypeResult TrailingReturnType;
   if (Tok.is(tok::l_paren)) {
     ParseScope PrototypeScope(this,
@@ -1119,15 +1146,25 @@
     // Parse parameter-declaration-clause.
     SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
     SourceLocation EllipsisLoc;
-    
+
     if (Tok.isNot(tok::r_paren)) {
-      Actions.RecordParsingTemplateParameterDepth(TemplateParameterDepth);
+      // Record the template parameter depth for auto parameters.
+      // Subtract 1 from the current depth if we've parsed an explicit template
+      // parameter list, because that will have increased the depth.
+      Actions.RecordParsingTemplateParameterDepth(HasExplicitTemplateParams
+                                                    ? TemplateParameterDepth - 1
+                                                    : TemplateParameterDepth);
+
       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()) 
+      // clause creates a template type parameter, so increment the depth
+      // (unless we've parsed an explicit template parameter list, in which
+      // case the depth will have already been increased).
+      if (!HasExplicitTemplateParams && Actions.getCurGenericLambda())
         ++CurTemplateDepthTracker;
     }
+
     T.consumeClose();
     SourceLocation RParenLoc = T.getCloseLocation();
     SourceLocation DeclEndLoc = RParenLoc;
@@ -1145,7 +1182,7 @@
     SourceLocation ConstexprLoc;
     tryConsumeMutableOrConstexprToken(*this, MutableLoc, ConstexprLoc,
                                       DeclEndLoc);
-    
+
     addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS);
 
     // Parse exception-specification[opt].
@@ -1296,6 +1333,7 @@
 
   StmtResult Stmt(ParseCompoundStatementBody());
   BodyScope.Exit();
+  TemplateParamScope.Exit();
 
   if (!Stmt.isInvalid() && !TrailingReturnType.isInvalid())
     return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get(), getCurScope());
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5466,6 +5466,14 @@
   /// 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<Decl *> TParams,
+                                   SourceLocation RAngleLoc);
+
   /// \brief Introduce the lambda parameters into scope.
   void addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope);
 
Index: include/clang/Sema/ScopeInfo.h
===================================================================
--- include/clang/Sema/ScopeInfo.h
+++ include/clang/Sema/ScopeInfo.h
@@ -769,16 +769,25 @@
   /// \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<TemplateTypeParmDecl*, 4> AutoTemplateParams;
-
+  SmallVector<Decl*, 4> 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;
   
@@ -820,7 +829,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 +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<Decl*> 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/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<CXX17>;
 
+ // 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]]'">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to