r310946 - PR33082: Improve tracking of unexpanded parameter packs within variadic generic lambdas.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 15 12:11:21 PDT 2017


Author: rsmith
Date: Tue Aug 15 12:11:21 2017
New Revision: 310946

URL: http://llvm.org/viewvc/llvm-project?rev=310946&view=rev
Log:
PR33082: Improve tracking of unexpanded parameter packs within variadic generic lambdas.

Modified:
    cfe/trunk/include/clang/Sema/ScopeInfo.h
    cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp
    cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp

Modified: cfe/trunk/include/clang/Sema/ScopeInfo.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ScopeInfo.h?rev=310946&r1=310945&r2=310946&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/ScopeInfo.h (original)
+++ cfe/trunk/include/clang/Sema/ScopeInfo.h Tue Aug 15 12:11:21 2017
@@ -833,6 +833,12 @@ public:
     return FSI->Kind == SK_Lambda;
   }
 
+  /// Is this scope known to be for a generic lambda? (This will be false until
+  /// we parse the first 'auto'-typed parameter.
+  bool isGenericLambda() const {
+    return !AutoTemplateParams.empty() || GLTemplateParameterList;
+  }
+
   ///
   /// \brief Add a variable that might potentially be captured by the 
   /// lambda and therefore the enclosing lambdas. 

Modified: cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp?rev=310946&r1=310945&r2=310946&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp Tue Aug 15 12:11:21 2017
@@ -26,6 +26,19 @@ using namespace clang;
 // Visitor that collects unexpanded parameter packs
 //----------------------------------------------------------------------------
 
+/// \brief Retrieve the depth and index of a parameter pack.
+static std::pair<unsigned, unsigned> 
+getDepthAndIndex(NamedDecl *ND) {
+  if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(ND))
+    return std::make_pair(TTP->getDepth(), TTP->getIndex());
+  
+  if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(ND))
+    return std::make_pair(NTTP->getDepth(), NTTP->getIndex());
+  
+  TemplateTemplateParmDecl *TTP = cast<TemplateTemplateParmDecl>(ND);
+  return std::make_pair(TTP->getDepth(), TTP->getIndex());
+}
+
 namespace {
   /// \brief A class that collects unexpanded parameter packs.
   class CollectUnexpandedParameterPacksVisitor :
@@ -36,15 +49,36 @@ namespace {
 
     SmallVectorImpl<UnexpandedParameterPack> &Unexpanded;
 
-    bool InLambda;
+    bool InLambda = false;
+    unsigned DepthLimit = (unsigned)-1;
 
+    void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
+      if (auto *PVD = dyn_cast<ParmVarDecl>(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<FunctionDecl>(PVD->getDeclContext());
+        auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
+        if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
+          return;
+      } else if (getDepthAndIndex(ND).first >= DepthLimit)
+        return;
+
+      Unexpanded.push_back({ND, Loc});
+    }
+    void addUnexpanded(const TemplateTypeParmType *T,
+                       SourceLocation Loc = SourceLocation()) {
+      if (T->getDepth() < DepthLimit)
+        Unexpanded.push_back({T, Loc});
+    }
+    
   public:
     explicit CollectUnexpandedParameterPacksVisitor(
-                  SmallVectorImpl<UnexpandedParameterPack> &Unexpanded)
-      : Unexpanded(Unexpanded), InLambda(false) { }
+        SmallVectorImpl<UnexpandedParameterPack> &Unexpanded)
+        : Unexpanded(Unexpanded) {}
 
     bool shouldWalkTypesOfTypeLocs() const { return false; }
-    
+
     //------------------------------------------------------------------------
     // Recording occurrences of (unexpanded) parameter packs.
     //------------------------------------------------------------------------
@@ -52,7 +86,7 @@ namespace {
     /// \brief Record occurrences of template type parameter packs.
     bool VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) {
       if (TL.getTypePtr()->isParameterPack())
-        Unexpanded.push_back(std::make_pair(TL.getTypePtr(), TL.getNameLoc()));
+        addUnexpanded(TL.getTypePtr(), TL.getNameLoc());
       return true;
     }
 
@@ -63,7 +97,7 @@ namespace {
     /// Ideally, this routine would never be used.
     bool VisitTemplateTypeParmType(TemplateTypeParmType *T) {
       if (T->isParameterPack())
-        Unexpanded.push_back(std::make_pair(T, SourceLocation()));
+        addUnexpanded(T);
 
       return true;
     }
@@ -72,18 +106,18 @@ namespace {
     /// parameter packs in an expression.
     bool VisitDeclRefExpr(DeclRefExpr *E) {
       if (E->getDecl()->isParameterPack())
-        Unexpanded.push_back(std::make_pair(E->getDecl(), E->getLocation()));
+        addUnexpanded(E->getDecl(), E->getLocation());
       
       return true;
     }
     
     /// \brief Record occurrences of template template parameter packs.
     bool TraverseTemplateName(TemplateName Template) {
-      if (TemplateTemplateParmDecl *TTP 
-            = dyn_cast_or_null<TemplateTemplateParmDecl>(
-                                                  Template.getAsTemplateDecl()))
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              Template.getAsTemplateDecl())) {
         if (TTP->isParameterPack())
-          Unexpanded.push_back(std::make_pair(TTP, SourceLocation()));
+          addUnexpanded(TTP);
+      }
       
       return inherited::TraverseTemplateName(Template);
     }
@@ -141,7 +175,13 @@ namespace {
     /// \brief Suppress traversal of non-parameter declarations, since
     /// they cannot contain unexpanded parameter packs.
     bool TraverseDecl(Decl *D) { 
-      if ((D && isa<ParmVarDecl>(D)) || InLambda)
+      auto *PVD = dyn_cast_or_null<ParmVarDecl>(D);
+      // A function parameter pack is a pack expansion, so cannot contain
+      // an unexpanded parameter pack.
+      if (PVD && PVD->isParameterPack())
+        return true;
+
+      if (PVD || InLambda)
         return inherited::TraverseDecl(D);
 
       return true;
@@ -175,25 +215,26 @@ namespace {
         return true;
 
       bool WasInLambda = InLambda;
-      InLambda = true;
+      unsigned OldDepthLimit = DepthLimit;
 
-      // If any capture names a function parameter pack, that pack is expanded
-      // when the lambda is expanded.
-      for (LambdaExpr::capture_iterator I = Lambda->capture_begin(),
-                                        E = Lambda->capture_end();
-           I != E; ++I) {
-        if (I->capturesVariable()) {
-          VarDecl *VD = I->getCapturedVar();
-          if (VD->isParameterPack())
-            Unexpanded.push_back(std::make_pair(VD, I->getLocation()));
-        }
-      }
+      InLambda = true;
+      if (auto *TPL = Lambda->getTemplateParameterList())
+        DepthLimit = TPL->getDepth();
 
       inherited::TraverseLambdaExpr(Lambda);
 
       InLambda = WasInLambda;
+      DepthLimit = OldDepthLimit;
       return true;
     }
+
+    /// Suppress traversal within pack expansions in lambda captures.
+    bool TraverseLambdaCapture(LambdaExpr *Lambda, const LambdaCapture *C,
+                               Expr *Init) {
+      if (C->isPackExpansion())
+        return true;
+      return inherited::TraverseLambdaCapture(Lambda, C, Init);
+    }
   };
 }
 
@@ -220,13 +261,33 @@ Sema::DiagnoseUnexpandedParameterPacks(S
   if (Unexpanded.empty())
     return false;
 
-  // If we are within a lambda expression, that lambda contains an unexpanded
+  // If we are within a lambda expression and referencing a pack that is not
+  // a parameter of the lambda itself, that lambda contains an unexpanded
   // parameter pack, and we are done.
   // FIXME: Store 'Unexpanded' on the lambda so we don't need to recompute it
   // later.
+  SmallVector<UnexpandedParameterPack, 4> GenericLambdaParamReferences;
   for (unsigned N = FunctionScopes.size(); N; --N) {
     if (sema::LambdaScopeInfo *LSI =
           dyn_cast<sema::LambdaScopeInfo>(FunctionScopes[N-1])) {
+      if (LSI->isGenericLambda()) {
+        for (auto &Param : Unexpanded) {
+          auto *PD = dyn_cast_or_null<ParmVarDecl>(
+              Param.first.dyn_cast<NamedDecl *>());
+          if (PD && PD->getDeclContext() == LSI->CallOperator)
+            GenericLambdaParamReferences.push_back(Param);
+        }
+      }
+
+      // If we have references to a parameter of a generic lambda, only
+      // diagnose those ones. We don't know whether any other unexpanded
+      // parameters referenced herein are actually unexpanded; they might
+      // be expanded at an outer level.
+      if (!GenericLambdaParamReferences.empty()) {
+        Unexpanded = GenericLambdaParamReferences;
+        break;
+      }
+
       LSI->ContainsUnexpandedParameterPack = true;
       return false;
     }
@@ -520,19 +581,6 @@ ExprResult Sema::CheckPackExpansion(Expr
     PackExpansionExpr(Context.DependentTy, Pattern, EllipsisLoc, NumExpansions);
 }
 
-/// \brief Retrieve the depth and index of a parameter pack.
-static std::pair<unsigned, unsigned> 
-getDepthAndIndex(NamedDecl *ND) {
-  if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(ND))
-    return std::make_pair(TTP->getDepth(), TTP->getIndex());
-  
-  if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(ND))
-    return std::make_pair(NTTP->getDepth(), NTTP->getIndex());
-  
-  TemplateTemplateParmDecl *TTP = cast<TemplateTemplateParmDecl>(ND);
-  return std::make_pair(TTP->getDepth(), TTP->getIndex());
-}
-
 bool Sema::CheckParameterPacksForExpansion(
     SourceLocation EllipsisLoc, SourceRange PatternRange,
     ArrayRef<UnexpandedParameterPack> Unexpanded,

Modified: cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp?rev=310946&r1=310945&r2=310946&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp Tue Aug 15 12:11:21 2017
@@ -98,3 +98,27 @@ namespace variadic_expansion {
 }
 #endif
 
+namespace PR33082 {
+  template<int ...I> void a() {
+    int arr[] = { [](auto ...K) { (void)I; } ... }; // expected-error {{no viable conversion}} expected-note {{candidate}}
+  }
+
+  template<typename ...T> struct Pack {};
+  template<typename ...T, typename ...U> void b(Pack<U...>, T ...t) {
+    int arr[] = {[t...]() { // expected-error 2{{cannot initialize an array element of type 'int' with}}
+      U u;
+      return u;
+    }()...};
+  }
+
+  void c() {
+    int arr[] = {[](auto... v) {
+      v; // expected-error {{unexpanded parameter pack 'v'}}
+    }...}; // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
+  }
+
+  void run() {
+    a<1>(); // expected-note {{instantiation of}}
+    b(Pack<int*, float*>(), 1, 2, 3); // expected-note {{instantiation of}}
+  }
+}




More information about the cfe-commits mailing list