hamzasood created this revision.

This patch provides an implementation for P0428R2 <http://wg21.link/p0428r2>.


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/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
@@ -777,13 +777,12 @@
 
 <h2 id="cxx20">C++2a implementation status</h2>
 
-<p>Clang does not yet support any of the proposed features of
-<!--<p>Clang has <b>experimental</b> support for some proposed features of-->
+<p>Clang has <b>experimental</b> support for some proposed features of
 the C++ standard following C++17, provisionally named C++2a.
 Note that support for these features may change or be removed without notice,
 as the draft C++2a standard evolves.
 
-<!--<p>You can use Clang in C++2a mode with the <code>-std=c++2a</code> option.</p>-->
+<p>You can use Clang in C++2a mode with the <code>-std=c++2a</code> option.</p>
 
 <details open>
 <summary>List of features and minimum Clang version with support</summary>
@@ -823,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 {{expected template parameter list}}
+
+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: 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: 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<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::addLambdaExplicitTemplateParameters(ArrayRef<Decl*> TParams,
+                                               Scope *CurScope) {
+  for (Decl *T : TParams) {
+    NamedDecl *NT = dyn_cast<NamedDecl>(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(); 
@@ -1123,6 +1163,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/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/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] ']'
@@ -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,26 @@
               << A->getName()->getName();
   };
 
+  ParseScope TemplateParamScope(this, Scope::TemplateParamScope);
+  if (getLangOpts().CPlusPlus2a && Tok.is(tok::less)) {
+    SmallVector<Decl*, 4> TemplateParams;
+    SourceLocation LAngleLoc, RAngleLoc;
+    if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
+                                TemplateParams, LAngleLoc, RAngleLoc)) {
+      return ExprError();
+    }
+
+    if (TemplateParams.empty()) {
+      Diag(RAngleLoc,
+           diag::err_expected_lambda_template_parameter_list);
+    }
+    else {
+      Actions.ActOnLambdaTemplateParameterList(
+          CurTemplateDepthTracker.getDepth(),
+          LAngleLoc, TemplateParams, RAngleLoc);
+    }
+  }
+
   TypeResult TrailingReturnType;
   if (Tok.is(tok::l_paren)) {
     ParseScope PrototypeScope(this,
@@ -1120,14 +1144,9 @@
     SmallVector<DeclaratorChunk::ParamInfo, 16> 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;
@@ -1278,6 +1297,12 @@
                                                TrailingReturnType),
                   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.
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5463,6 +5463,18 @@
   /// given lambda.
   void finishLambdaExplicitCaptures(sema::LambdaScopeInfo *LSI);
 
+  /// ActOnLambdaTemplateParameterList - 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 explicit lambda template parameters into scope.
+  void addLambdaExplicitTemplateParameters(ArrayRef<Decl*> TParams,
+                                           Scope *CurScope);
+
   /// \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
@@ -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;
+  /// each template parameter, during initial AST construction.
+  unsigned TemplateParameterDepth;
 
-  /// \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
+  /// 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<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;
   
@@ -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;
   }
 
@@ -932,6 +941,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_cxx1z : ExtWarn<
   "'constexpr' on lambda expressions is a C++1z extension">, InGroup<CXX1z>;
 
+// C++2a template lambdas
+def err_expected_lambda_template_parameter_list : Error<
+  "expected template parameter list">;
+
 // 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