[MS] Fix late-parsed template infinite loop in eager instantiation

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

llvm-svn: 328567
This commit is contained in:
Reid Kleckner 2018-03-26 18:22:47 +00:00
parent f33d905293
commit 24bd88c0b0
5 changed files with 96 additions and 2 deletions

View 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)

View 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);

View 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;
}

View File

@ -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

View File

@ -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}}
}