r369985 - PR42587: diagnose unexpanded uses of a pack parameter of a generic

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 26 15:51:29 PDT 2019


Author: rsmith
Date: Mon Aug 26 15:51:28 2019
New Revision: 369985

URL: http://llvm.org/viewvc/llvm-project?rev=369985&view=rev
Log:
PR42587: diagnose unexpanded uses of a pack parameter of a generic
lambda from within the lambda-declarator.

Instead of trying to reconstruct whether a parameter pack was declared
inside a lambda (which we can't do correctly in general because we might
not have attached parameters to their declaration contexts yet), track
the set of parameter packs introduced in each live lambda scope, and
require only those parameters to be immediately expanded when they
appear inside that lambda.

In passing, fix incorrect disambiguation of a lambda-expression starting
with an init-capture pack in a braced-init-list. We previously
incorrectly parsed that as a designated initializer.

Modified:
    cfe/trunk/include/clang/Sema/ScopeInfo.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseInit.cpp
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaLambda.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    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=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/ScopeInfo.h (original)
+++ cfe/trunk/include/clang/Sema/ScopeInfo.h Mon Aug 26 15:51:28 2019
@@ -820,6 +820,9 @@ public:
   /// Whether the lambda contains an unexpanded parameter pack.
   bool ContainsUnexpandedParameterPack = false;
 
+  /// Packs introduced by this lambda, if any.
+  SmallVector<NamedDecl*, 4> LocalPacks;
+
   /// If this is a generic lambda, use this as the depth of
   /// each 'auto' parameter, during initial AST construction.
   unsigned AutoTemplateParameterDepth = 0;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Aug 26 15:51:28 2019
@@ -1474,6 +1474,11 @@ public:
   /// Retrieve the current block, if any.
   sema::BlockScopeInfo *getCurBlock();
 
+  /// Get the innermost lambda enclosing the current location, if any. This
+  /// looks through intervening non-lambda scopes such as local functions and
+  /// blocks.
+  sema::LambdaScopeInfo *getEnclosingLambda() const;
+
   /// Retrieve the current lambda scope info, if any.
   /// \param IgnoreNonLambdaCapturingScope true if should find the top-most
   /// lambda scope info ignoring all inner capturing scopes that are not

Modified: cfe/trunk/lib/Parse/ParseInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseInit.cpp?rev=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseInit.cpp (original)
+++ cfe/trunk/lib/Parse/ParseInit.cpp Mon Aug 26 15:51:28 2019
@@ -39,6 +39,7 @@ bool Parser::MayBeDesignationStart() {
     // cases here, and fall back to tentative parsing if those fail.
     switch (PP.LookAhead(0).getKind()) {
     case tok::equal:
+    case tok::ellipsis:
     case tok::r_square:
       // Definitely starts a lambda expression.
       return false;

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Mon Aug 26 15:51:28 2019
@@ -1804,6 +1804,22 @@ FunctionScopeInfo *Sema::getEnclosingFun
   return nullptr;
 }
 
+LambdaScopeInfo *Sema::getEnclosingLambda() const {
+  for (auto *Scope : llvm::reverse(FunctionScopes)) {
+    if (auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope)) {
+      if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext)) {
+        // We have switched contexts due to template instantiation.
+        // FIXME: We should swap out the FunctionScopes during code synthesis
+        // so that we don't need to check for this.
+        assert(!CodeSynthesisContexts.empty());
+        return nullptr;
+      }
+      return LSI;
+    }
+  }
+  return nullptr;
+}
+
 LambdaScopeInfo *Sema::getCurLambda(bool IgnoreNonLambdaCapturingScope) {
   if (FunctionScopes.empty())
     return nullptr;
@@ -1826,6 +1842,7 @@ LambdaScopeInfo *Sema::getCurLambda(bool
 
   return CurLSI;
 }
+
 // We have a generic lambda if we parsed auto parameters, or we have
 // an associated template parameter list.
 LambdaScopeInfo *Sema::getCurGenericLambda() {

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon Aug 26 15:51:28 2019
@@ -12706,6 +12706,13 @@ ParmVarDecl *Sema::CheckParameter(DeclCo
                                          Context.getAdjustedParameterType(T),
                                          TSInfo, SC, nullptr);
 
+  // Make a note if we created a new pack in the scope of a lambda, so that
+  // we know that references to that pack must also be expanded within the
+  // lambda scope.
+  if (New->isParameterPack())
+    if (auto *LSI = getEnclosingLambda())
+      LSI->LocalPacks.push_back(New);
+
   // Parameters can not be abstract class types.
   // For record types, this is done by the AbstractClassUsageDiagnoser once
   // the class has been completely parsed.

Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLambda.cpp Mon Aug 26 15:51:28 2019
@@ -824,8 +824,11 @@ VarDecl *Sema::createLambdaInitCaptureVa
   // FIXME: Retain the TypeSourceInfo from buildLambdaInitCaptureInitialization
   // rather than reconstructing it here.
   TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType, Loc);
-  if (auto PETL = TSI->getTypeLoc().getAs<PackExpansionTypeLoc>())
+  bool IsInitCapturePack = false;
+  if (auto PETL = TSI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
     PETL.setEllipsisLoc(EllipsisLoc);
+    IsInitCapturePack = true;
+  }
 
   // Create a dummy variable representing the init-capture. This is not actually
   // used as a variable, and only exists as a way to name and refer to the
@@ -839,6 +842,8 @@ VarDecl *Sema::createLambdaInitCaptureVa
   NewVD->setInitStyle(static_cast<VarDecl::InitializationStyle>(InitStyle));
   NewVD->markUsed(Context);
   NewVD->setInit(Init);
+  if (NewVD->isParameterPack())
+    getCurLambda()->LocalPacks.push_back(NewVD);
   return NewVD;
 }
 
@@ -928,12 +933,12 @@ void Sema::ActOnStartOfLambdaDefinition(
 
     // Check for unexpanded parameter packs in the method type.
     if (MethodTyInfo->getType()->containsUnexpandedParameterPack())
-      ContainsUnexpandedParameterPack = true;
+      DiagnoseUnexpandedParameterPack(Intro.Range.getBegin(), MethodTyInfo,
+                                      UPPC_DeclarationType);
   }
 
   CXXRecordDecl *Class = createLambdaClosureType(Intro.Range, MethodTyInfo,
                                                  KnownDependent, Intro.Default);
-
   CXXMethodDecl *Method =
       startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params,
                             ParamInfo.getDeclSpec().getConstexprSpecifier());
@@ -1053,7 +1058,7 @@ void Sema::ActOnStartOfLambdaDefinition(
 
       if (C->Init.get()->containsUnexpandedParameterPack() &&
           !C->InitCaptureType.get()->getAs<PackExpansionType>())
-        ContainsUnexpandedParameterPack = true;
+        DiagnoseUnexpandedParameterPack(C->Init.get(), UPPC_Initializer);
 
       unsigned InitStyle;
       switch (C->InitKind) {
@@ -1184,7 +1189,7 @@ void Sema::ActOnStartOfLambdaDefinition(
   }
   finishLambdaExplicitCaptures(LSI);
 
-  LSI->ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack;
+  LSI->ContainsUnexpandedParameterPack |= ContainsUnexpandedParameterPack;
 
   // Add lambda parameters into scope.
   addLambdaParameters(Intro.Captures, Method, CurScope);

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Mon Aug 26 15:51:28 2019
@@ -1013,6 +1013,10 @@ NamedDecl *Sema::ActOnTypeParameter(Scop
                                    Typename, IsParameterPack);
   Param->setAccess(AS_public);
 
+  if (Param->isParameterPack())
+    if (auto *LSI = getEnclosingLambda())
+      LSI->LocalPacks.push_back(Param);
+
   if (ParamName) {
     maybeDiagnoseTemplateParameterShadow(*this, S, ParamNameLoc, ParamName);
 
@@ -1211,6 +1215,10 @@ NamedDecl *Sema::ActOnNonTypeTemplatePar
   if (Invalid)
     Param->setInvalidDecl();
 
+  if (Param->isParameterPack())
+    if (auto *LSI = getEnclosingLambda())
+      LSI->LocalPacks.push_back(Param);
+
   if (ParamName) {
     maybeDiagnoseTemplateParameterShadow(*this, S, D.getIdentifierLoc(),
                                          ParamName);
@@ -1274,6 +1282,10 @@ NamedDecl *Sema::ActOnTemplateTemplatePa
                                      Name, Params);
   Param->setAccess(AS_public);
 
+  if (Param->isParameterPack())
+    if (auto *LSI = getEnclosingLambda())
+      LSI->LocalPacks.push_back(Param);
+
   // If the template template parameter has a name, then link the identifier
   // into the scope and lookup mechanisms.
   if (Name) {

Modified: cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp?rev=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp Mon Aug 26 15:51:28 2019
@@ -294,44 +294,58 @@ Sema::DiagnoseUnexpandedParameterPacks(S
     return false;
 
   // 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
+  // declared within 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> LambdaParamPackReferences;
-  for (unsigned N = FunctionScopes.size(); N; --N) {
-    sema::FunctionScopeInfo *Func = FunctionScopes[N-1];
-    // We do not permit pack expansion that would duplicate a statement
-    // expression, not even within a lambda.
-    // FIXME: We could probably support this for statement expressions that do
-    // not contain labels, and for pack expansions that expand both the stmt
-    // expr and the enclosing lambda.
-    if (std::any_of(
-            Func->CompoundScopes.begin(), Func->CompoundScopes.end(),
-            [](sema::CompoundScopeInfo &CSI) { return CSI.IsStmtExpr; }))
-      break;
+  if (auto *LSI = getEnclosingLambda()) {
+    for (auto &Pack : Unexpanded) {
+      auto DeclaresThisPack = [&](NamedDecl *LocalPack) {
+        if (auto *TTPT = Pack.first.dyn_cast<const TemplateTypeParmType *>()) {
+          auto *TTPD = dyn_cast<TemplateTypeParmDecl>(LocalPack);
+          return TTPD && TTPD->getTypeForDecl() == TTPT;
+        }
+        return declaresSameEntity(Pack.first.get<NamedDecl *>(), LocalPack);
+      };
+      if (std::find_if(LSI->LocalPacks.begin(), LSI->LocalPacks.end(),
+                       DeclaresThisPack) != LSI->LocalPacks.end())
+        LambdaParamPackReferences.push_back(Pack);
+    }
 
-    if (auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Func)) {
-      if (N == FunctionScopes.size()) {
-        for (auto &Pack : Unexpanded) {
-          auto *VD = dyn_cast_or_null<VarDecl>(
-              Pack.first.dyn_cast<NamedDecl *>());
-          if (VD && VD->getDeclContext() == LSI->CallOperator)
-            LambdaParamPackReferences.push_back(Pack);
+    if (LambdaParamPackReferences.empty()) {
+      // Construct in lambda only references packs declared outside the lambda.
+      // That's OK for now, but the lambda itself is considered to contain an
+      // unexpanded pack in this case, which will require expansion outside the
+      // lambda.
+
+      // We do not permit pack expansion that would duplicate a statement
+      // expression, not even within a lambda.
+      // FIXME: We could probably support this for statement expressions that
+      // do not contain labels.
+      // FIXME: This is insufficient to detect this problem; consider
+      //   f( ({ bad: 0; }) + pack ... );
+      bool EnclosingStmtExpr = false;
+      for (unsigned N = FunctionScopes.size(); N; --N) {
+        sema::FunctionScopeInfo *Func = FunctionScopes[N-1];
+        if (std::any_of(
+                Func->CompoundScopes.begin(), Func->CompoundScopes.end(),
+                [](sema::CompoundScopeInfo &CSI) { return CSI.IsStmtExpr; })) {
+          EnclosingStmtExpr = true;
+          break;
         }
+        // Coumpound-statements outside the lambda are OK for now; we'll check
+        // for those when we finish handling the lambda.
+        if (Func == LSI)
+          break;
       }
 
-      // If we have references to a parameter pack of the innermost enclosing
-      // 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 (!LambdaParamPackReferences.empty()) {
-        Unexpanded = LambdaParamPackReferences;
-        break;
+      if (!EnclosingStmtExpr) {
+        LSI->ContainsUnexpandedParameterPack = true;
+        return false;
       }
-
-      LSI->ContainsUnexpandedParameterPack = true;
-      return false;
+    } else {
+      Unexpanded = LambdaParamPackReferences;
     }
   }
 

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=369985&r1=369984&r2=369985&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp Mon Aug 26 15:51:28 2019
@@ -122,3 +122,16 @@ namespace PR33082 {
     b(Pack<int*, float*>(), 1, 2, 3); // expected-note {{instantiation of}}
   }
 }
+
+void pr42587() {
+  (void)[](auto... args) -> decltype(args) {}; // expected-error {{type contains unexpanded parameter pack}}
+  (void)[](auto... args, int = args) {}; // expected-error {{default argument contains unexpanded parameter pack}}
+  (void)[](auto... args, decltype(args)) {}; // expected-error {{type contains unexpanded parameter pack}}
+  (void)[](auto... args, decltype(args)...) {}; // (ok)
+  (void)[](auto... args, int = [=] { return args; }()) {}; // expected-error {{default argument contains unexpanded parameter pack}}
+  (void)([]<typename ...T> (T t) {} + ...); // expected-error {{contains unexpanded parameter pack 'T'}} expected-error {{does not contain any unexpanded}} expected-warning 0-2{{extension}}
+  (void)([]<int ...N> (int k = N) {} + ...); // expected-error {{contains unexpanded parameter pack 'N'}} expected-error {{does not contain any unexpanded}} expected-warning 0-2{{extension}}
+  (void)([]<template<typename> typename ...T> (T<int>) {} + ...); // expected-error {{contains unexpanded parameter pack 'T'}} expected-error {{does not contain any unexpanded}} expected-warning 0-3{{extension}}
+}
+
+template<typename ...T> int v = {[...x = T()] { int k = x; } ...}; // expected-error {{contains unexpanded parameter pack 'x'}} expected-error {{does not contain any unexpanded}} expected-warning 0-1{{extension}}




More information about the cfe-commits mailing list