r310972 - Do not look through pack expansions when looking for unexpanded parameter packs.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 15 15:58:45 PDT 2017


Author: rsmith
Date: Tue Aug 15 15:58:45 2017
New Revision: 310972

URL: http://llvm.org/viewvc/llvm-project?rev=310972&view=rev
Log:
Do not look through pack expansions when looking for unexpanded parameter packs.

Fixes a selection of rejects-valids when pack-expanding a lambda that itself
contains a pack expansion.

Modified:
    cfe/trunk/include/clang/AST/RecursiveASTVisitor.h
    cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/p4.cpp
    cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp

Modified: cfe/trunk/include/clang/AST/RecursiveASTVisitor.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RecursiveASTVisitor.h?rev=310972&r1=310971&r2=310972&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/RecursiveASTVisitor.h (original)
+++ cfe/trunk/include/clang/AST/RecursiveASTVisitor.h Tue Aug 15 15:58:45 2017
@@ -267,6 +267,12 @@ public:
   bool TraverseTemplateArguments(const TemplateArgument *Args,
                                  unsigned NumArgs);
 
+  /// \brief Recursively visit a base specifier. This can be overridden by a
+  /// subclass.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier &Base);
+
   /// \brief Recursively visit a constructor initializer.  This
   /// automatically dispatches to another visitor for the initializer
   /// expression, but not for the name of the initializer, so may
@@ -1769,12 +1775,19 @@ bool RecursiveASTVisitor<Derived>::Trave
 }
 
 template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseCXXBaseSpecifier(
+    const CXXBaseSpecifier &Base) {
+  TRY_TO(TraverseTypeLoc(Base.getTypeSourceInfo()->getTypeLoc()));
+  return true;
+}
+
+template <typename Derived>
 bool RecursiveASTVisitor<Derived>::TraverseCXXRecordHelper(CXXRecordDecl *D) {
   if (!TraverseRecordHelper(D))
     return false;
   if (D->isCompleteDefinition()) {
     for (const auto &I : D->bases()) {
-      TRY_TO(TraverseTypeLoc(I.getTypeSourceInfo()->getTypeLoc()));
+      TRY_TO(TraverseCXXBaseSpecifier(I));
     }
     // We don't traverse the friends or the conversions, as they are
     // already in decls_begin()/decls_end().

Modified: cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp?rev=310972&r1=310971&r2=310972&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp Tue Aug 15 15:58:45 2017
@@ -161,7 +161,7 @@ namespace {
       return true;
     }
 
-    /// \brief Suppress traversel into types with location information
+    /// \brief Suppress traversal into types with location information
     /// that do not contain unexpanded parameter packs.
     bool TraverseTypeLoc(TypeLoc TL) {
       if ((!TL.getType().isNull() && 
@@ -172,19 +172,48 @@ namespace {
       return true;
     }
 
-    /// \brief Suppress traversal of non-parameter declarations, since
-    /// they cannot contain unexpanded parameter packs.
+    /// \brief Suppress traversal of parameter packs.
     bool TraverseDecl(Decl *D) { 
-      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())
+      // an unexpanded parameter pack. Likewise for a template parameter
+      // pack that contains any references to other packs.
+      if (D->isParameterPack())
         return true;
 
-      if (PVD || InLambda)
-        return inherited::TraverseDecl(D);
+      return inherited::TraverseDecl(D);
+    }
 
-      return true;
+    /// \brief Suppress traversal of pack-expanded attributes.
+    bool TraverseAttr(Attr *A) {
+      if (A->isPackExpansion())
+        return true;
+
+      return inherited::TraverseAttr(A);
+    }
+
+    /// \brief Suppress traversal of pack expansion expressions and types.
+    ///@{
+    bool TraversePackExpansionType(PackExpansionType *T) { return true; }
+    bool TraversePackExpansionTypeLoc(PackExpansionTypeLoc TL) { return true; }
+    bool TraversePackExpansionExpr(PackExpansionExpr *E) { return true; }
+    bool TraverseCXXFoldExpr(CXXFoldExpr *E) { return true; }
+
+    ///@}
+
+    /// \brief Suppress traversal of using-declaration pack expansion.
+    bool TraverseUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D) {
+      if (D->isPackExpansion())
+        return true;
+
+      return inherited::TraverseUnresolvedUsingValueDecl(D);
+    }
+
+    /// \brief Suppress traversal of using-declaration pack expansion.
+    bool TraverseUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D) {
+      if (D->isPackExpansion())
+        return true;
+
+      return inherited::TraverseUnresolvedUsingTypenameDecl(D);
     }
 
     /// \brief Suppress traversal of template argument pack expansions.
@@ -203,6 +232,22 @@ namespace {
       return inherited::TraverseTemplateArgumentLoc(ArgLoc);
     }
 
+    /// \brief Suppress traversal of base specifier pack expansions.
+    bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier &Base) {
+      if (Base.isPackExpansion())
+        return true;
+
+      return inherited::TraverseCXXBaseSpecifier(Base);
+    }
+
+    /// \brief Suppress traversal of mem-initializer pack expansions.
+    bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
+      if (Init->isPackExpansion())
+        return true;
+
+      return inherited::TraverseConstructorInitializer(Init);
+    }
+
     /// \brief Note whether we're traversing a lambda containing an unexpanded
     /// parameter pack. In this case, the unexpanded pack can occur anywhere,
     /// including all the places where we normally wouldn't look. Within a
@@ -233,6 +278,7 @@ namespace {
                                Expr *Init) {
       if (C->isPackExpansion())
         return true;
+
       return inherited::TraverseLambdaCapture(Lambda, C, Init);
     }
   };
@@ -266,25 +312,25 @@ Sema::DiagnoseUnexpandedParameterPacks(S
   // 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;
+  SmallVector<UnexpandedParameterPack, 4> LambdaParamPackReferences;
   for (unsigned N = FunctionScopes.size(); N; --N) {
     if (sema::LambdaScopeInfo *LSI =
           dyn_cast<sema::LambdaScopeInfo>(FunctionScopes[N-1])) {
-      if (LSI->isGenericLambda()) {
+      if (N == FunctionScopes.size()) {
         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);
+            LambdaParamPackReferences.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;
+      // 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;
       }
 

Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/p4.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/p4.cpp?rev=310972&r1=310971&r2=310972&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/p4.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/p4.cpp Tue Aug 15 15:58:45 2017
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -fexceptions -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++2a -fsyntax-only -fexceptions -fcxx-exceptions -verify %s
 
 template<typename... Types> struct tuple;
 template<int I> struct int_c;
@@ -174,6 +175,7 @@ int check_temp_arg_1[is_same<tuple_of_in
                              tuple<int_c<1>, int_c<2>, int_c<3>, int_c<4>, 
                                    int_c<5>>>::value? 1 : -1];
 
+#if __cplusplus < 201703L
 // In a dynamic-exception-specification (15.4); the pattern is a type-id.
 template<typename ...Types>
 struct f_with_except {
@@ -191,3 +193,99 @@ struct check_f_with_except_2 : f_with_ex
 struct check_f_with_except_3 : f_with_except<int, float> {
   virtual void f() throw(int, float, double); // expected-error{{exception specification of overriding function is more lax than base version}}
 };
+#endif
+
+namespace PackExpansionWithinLambda {
+  void swallow(...);
+  template<typename ...T, typename ...U> void f(U ...u) {
+    swallow([=] {
+      // C++17 [temp.variadic]p4:
+      //   Pack expansions can occur in the following contexts:
+
+      //    - in a function parameter pack
+      void g(T...);
+
+#if __cplusplus >= 201703L
+      struct A : T... {
+        //  - in a using-declaration
+        using T::x...;
+        using typename T::U...;
+      };
+#endif
+
+      //    - in a template parameter pack that is a pack expansion
+      // FIXME: We do not support any way to reach this case yet.
+
+      //    - in an initializer-list
+      int arr[] = {T().x...};
+
+      //    - in a base-specifier-list
+      struct B : T... {
+        //  - in a mem-initializer-list
+        B() : T{0}... {}
+      };
+
+      //    - in a template-argument-list
+      f<T...>();
+
+      //    - in an attribute-list
+      // FIXME: We do not support any such attributes yet.
+      
+      //    - in an alignment-specifier
+      alignas(T...) int y;
+
+      //    - in a capture-list
+      [](T ...t) { [t...]{}(); } (T()...);
+
+      //    - in a sizeof... expression
+      const int k1 = sizeof...(T);
+
+#if __cplusplus >= 201703L
+      //    - in a fold-expression
+      const int k2 = ((sizeof(T)/sizeof(T)) + ...);
+
+      static_assert(k1 == k2);
+#endif
+
+      // Trigger clang to look in here for unexpanded packs.
+      U u;
+    } ...);
+  }
+
+  template<typename ...T> void nested() {
+    swallow([=] {
+      [](T ...t) { [t]{}(); } (T()...); // expected-error {{unexpanded parameter pack 't'}}
+    }...); // expected-error {{does not contain any unexpanded}}
+  }
+
+  template <typename ...T> void g() {
+    // Check that we do detect the above cases when the pack is not expanded.
+    swallow([=] { void h(T); }); // expected-error {{unexpanded parameter pack 'T'}}
+    swallow([=] { struct A : T {}; }); // expected-error {{unexpanded parameter pack 'T'}}
+#if __cplusplus >= 201703L
+    swallow([=] { struct A : T... { using T::x; }; }); // expected-error {{unexpanded parameter pack 'T'}}
+    swallow([=] { struct A : T... { using typename T::U; }; }); // expected-error {{unexpanded parameter pack 'T'}}
+#endif
+
+    swallow([=] { int arr[] = {T().x}; }); // expected-error {{unexpanded parameter pack 'T'}}
+    swallow([=] { struct B : T... { B() : T{0} {} }; }); // expected-error {{unexpanded parameter pack 'T'}}
+    swallow([=] { f<T>(); }); // expected-error {{unexpanded parameter pack 'T'}}
+    swallow([=] { alignas(T) int y; }); // expected-error {{unexpanded parameter pack 'T'}}
+    swallow([=] { [](T ...t) {
+          [t]{}(); // expected-error {{unexpanded parameter pack 't'}}
+        } (T()...); });
+  }
+
+  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}}
+  }
+}

Modified: cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp?rev=310972&r1=310971&r2=310972&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp (original)
+++ cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp Tue Aug 15 15:58:45 2017
@@ -490,6 +490,17 @@ namespace {
       OS << "}\n";
     }
 
+    void writeASTVisitorTraversal(raw_ostream &OS) const override {
+      StringRef Name = getUpperName();
+      OS << "  if (A->is" << Name << "Expr()) {\n"
+         << "    if (!getDerived().TraverseStmt(A->get" << Name << "Expr()))\n" 
+         << "      return false;\n" 
+         << "  } else if (auto *TSI = A->get" << Name << "Type()) {\n"
+         << "    if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n"
+         << "      return false;\n" 
+         << "  }\n";
+    }
+
     void writeCloneArgs(raw_ostream &OS) const override {
       OS << "is" << getLowerName() << "Expr, is" << getLowerName()
          << "Expr ? static_cast<void*>(" << getLowerName()
@@ -630,6 +641,10 @@ namespace {
          << "A->" << getLowerName() << "_size()";
     }
 
+    void writeASTVisitorTraversal(raw_ostream &OS) const override {
+      // FIXME: Traverse the elements.
+    }
+
     void writeCtorBody(raw_ostream &OS) const override {
       OS << "    std::copy(" << getUpperName() << ", " << getUpperName()
          << " + " << ArgSizeName << ", " << ArgName << ");\n";
@@ -1153,6 +1168,12 @@ namespace {
       OS << "  }";
     }
 
+    void writeASTVisitorTraversal(raw_ostream &OS) const override {
+      OS << "  if (auto *TSI = A->get" << getUpperName() << "Loc())\n";
+      OS << "    if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n";
+      OS << "      return false;\n";
+    }
+
     void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
       OS << "A->get" << getUpperName() << "Loc()";
     }




More information about the cfe-commits mailing list