]> granicus.if.org Git - clang/commitdiff
[MS] Fix late-parsed template infinite loop in eager instantiation
authorReid Kleckner <rnk@google.com>
Mon, 26 Mar 2018 18:22:47 +0000 (18:22 +0000)
committerReid Kleckner <rnk@google.com>
Mon, 26 Mar 2018 18:22:47 +0000 (18:22 +0000)
Summary:
This fixes PR33561 and PR34185.

Don't store pending template instantiations for late-parsed templates in
the normal PendingInstantiations queue. Instead, use a separate list
that will only be parsed and instantiated at end of TU when late
template parsing actually works and doesn't infinite loop.

Reviewers: rsmith, thakis, hans

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D44846

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@328567 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/Sema.h
lib/Sema/Sema.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/PCH/late-parsed-instantiations.cpp [new file with mode: 0644]
test/SemaTemplate/late-parsing-eager-instantiation.cpp [new file with mode: 0644]

index 705b579998e69115ceb38a988a703427d7c92607..2e907562faaf493aee12edf03282bbe2ffb33e26 100644 (file)
@@ -7550,6 +7550,10 @@ public:
   /// but have not yet been performed.
   std::deque<PendingImplicitInstantiation> PendingInstantiations;
 
+  /// Queue of implicit template instantiations that cannot be performed
+  /// eagerly.
+  SmallVector<PendingImplicitInstantiation, 1> LateParsedInstantiations;
+
   class GlobalEagerInstantiationScope {
   public:
     GlobalEagerInstantiationScope(Sema &S, bool Enabled)
index 171b3ec5f8a479c3aa46d7ed4f18069cbdfdf5ae..5c0026cd3747d7aa131a89fb4b96db1579d94d07 100644 (file)
@@ -850,6 +850,20 @@ void Sema::ActOnEndOfTranslationUnit() {
   if (PP.isCodeCompletionEnabled())
     return;
 
+  // Transfer late parsed template instantiations over to the pending template
+  // instantiation list. During normal compliation, the late template parser
+  // will be installed and instantiating these templates will succeed.
+  //
+  // If we are building a TU prefix for serialization, it is also safe to
+  // transfer these over, even though they are not parsed. The end of the TU
+  // should be outside of any eager template instantiation scope, so when this
+  // AST is deserialized, these templates will not be parsed until the end of
+  // the combined TU.
+  PendingInstantiations.insert(PendingInstantiations.end(),
+                               LateParsedInstantiations.begin(),
+                               LateParsedInstantiations.end());
+  LateParsedInstantiations.clear();
+
   // Complete translation units and modules define vtables and perform implicit
   // instantiations. PCH files do not.
   if (TUKind != TU_Prefix) {
@@ -879,8 +893,13 @@ void Sema::ActOnEndOfTranslationUnit() {
       PendingInstantiations.insert(PendingInstantiations.begin(),
                                    Pending.begin(), Pending.end());
     }
+
     PerformPendingInstantiations();
 
+    assert(LateParsedInstantiations.empty() &&
+           "end of TU template instantiation should not create more "
+           "late-parsed templates");
+
     if (LateTemplateParserCleanup)
       LateTemplateParserCleanup(OpaqueParser);
 
index ebfad36d3656e6a978f536baa8b7e31018cc6063..9e09a6aa3569f7966189427d29c387fd8f037dd7 100644 (file)
@@ -3837,8 +3837,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
   if (PatternDecl->isLateTemplateParsed() &&
       !LateTemplateParser) {
     Function->setInstantiationIsPending(true);
-    PendingInstantiations.push_back(
-      std::make_pair(Function, PointOfInstantiation));
+    LateParsedInstantiations.push_back(
+        std::make_pair(Function, PointOfInstantiation));
     return;
   }
 
diff --git a/test/PCH/late-parsed-instantiations.cpp b/test/PCH/late-parsed-instantiations.cpp
new file mode 100644 (file)
index 0000000..f1fe8bb
--- /dev/null
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -fdelayed-template-parsing -std=c++14 -emit-pch %s -o %t.pch -verify
+// RUN: %clang_cc1 -fdelayed-template-parsing -std=c++14 -include-pch %t.pch %s -verify
+
+#ifndef HEADER_INCLUDED
+
+#define HEADER_INCLUDED
+
+// pr33561
+class ArrayBuffer;
+template <typename T> class Trans_NS_WTF_RefPtr {
+public:
+  ArrayBuffer *operator->() { return nullptr; }
+};
+Trans_NS_WTF_RefPtr<ArrayBuffer> get();
+template <typename _Visitor>
+constexpr void visit(_Visitor __visitor) {
+  __visitor(get()); // expected-note {{in instantiation}}
+}
+class ArrayBuffer {
+  char data() {
+    visit([](auto buffer) -> char { // expected-note {{in instantiation}}
+      buffer->data();
+    }); // expected-warning {{control reaches end of non-void lambda}}
+  } // expected-warning {{control reaches end of non-void function}}
+};
+
+#else
+
+// expected-no-diagnostics
+
+#endif
diff --git a/test/SemaTemplate/late-parsing-eager-instantiation.cpp b/test/SemaTemplate/late-parsing-eager-instantiation.cpp
new file mode 100644 (file)
index 0000000..412f026
--- /dev/null
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -std=c++14 -verify %s
+
+// pr33561
+class ArrayBuffer;
+template <typename T> class Trans_NS_WTF_RefPtr {
+public:
+  ArrayBuffer *operator->() { return nullptr; }
+};
+Trans_NS_WTF_RefPtr<ArrayBuffer> get();
+template <typename _Visitor>
+constexpr void visit(_Visitor __visitor) {
+  __visitor(get()); // expected-note {{in instantiation}}
+}
+class ArrayBuffer {
+  char data() {
+    visit([](auto buffer) -> char { // expected-note {{in instantiation}}
+      buffer->data();
+    }); // expected-warning {{control reaches end of non-void lambda}}
+  } // expected-warning {{control reaches end of non-void function}}
+};
+
+// pr34185
+template <typename Promise> struct coroutine_handle {
+  Promise &promise() const { return
+    *static_cast<Promise *>(nullptr); // expected-warning {{binding dereferenced null}}
+  }
+};
+
+template <typename Promise> auto GetCurrenPromise() {
+  struct Awaiter { // expected-note {{in instantiation}}
+    void await_suspend(coroutine_handle<Promise> h) {
+      h.promise(); // expected-note {{in instantiation}}
+    }
+  };
+  return Awaiter{};
+}
+
+void foo() {
+  auto &&p = GetCurrenPromise<int>(); // expected-note {{in instantiation}}
+}