Index: include/clang/Serialization/ASTReader.h =================================================================== --- include/clang/Serialization/ASTReader.h +++ include/clang/Serialization/ASTReader.h @@ -984,14 +984,26 @@ /// \brief The generation number of each identifier, which keeps track of /// the last time we loaded information about this identifier. llvm::DenseMap IdentifierGeneration; - - /// \brief Contains declarations and definitions that will be + + class InterestingDecl { + Decl *D; + bool DeclHasPendingBody; + + public: + InterestingDecl(Decl *D, bool HasBody) + : D(D), DeclHasPendingBody(HasBody) {} + Decl *getDecl() { return D; } + /// Whether the declaration has a pending body. + bool hasPendingBody() { return DeclHasPendingBody; } + }; + + /// \brief Contains declarations and definitions that could be /// "interesting" to the ASTConsumer, when we get that AST consumer. /// /// "Interesting" declarations are those that have data that may /// need to be emitted, such as inline function definitions or /// Objective-C protocols. - std::deque InterestingDecls; + std::deque PotentiallyInterestingDecls; /// \brief The list of redeclaration chains that still need to be /// reconstructed, and the local offset to the corresponding list Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -7191,31 +7191,6 @@ Consumer->HandleInterestingDecl(DeclGroupRef(ImplD)); } -void ASTReader::PassInterestingDeclsToConsumer() { - assert(Consumer); - - if (PassingDeclsToConsumer) - return; - - // Guard variable to avoid recursively redoing the process of passing - // decls to consumer. - SaveAndRestore GuardPassingDeclsToConsumer(PassingDeclsToConsumer, - true); - - // Ensure that we've loaded all potentially-interesting declarations - // that need to be eagerly loaded. - for (auto ID : EagerlyDeserializedDecls) - GetDecl(ID); - EagerlyDeserializedDecls.clear(); - - while (!InterestingDecls.empty()) { - Decl *D = InterestingDecls.front(); - InterestingDecls.pop_front(); - - PassInterestingDeclToConsumer(D); - } -} - void ASTReader::PassInterestingDeclToConsumer(Decl *D) { if (ObjCImplDecl *ImplD = dyn_cast(D)) PassObjCImplDeclToConsumer(ImplD, Consumer); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -3609,12 +3609,37 @@ // AST consumer might need to know about, queue it. // We don't pass it to the consumer immediately because we may be in recursive // loading, and some declarations may still be initializing. - if (isConsumerInterestedIn(Context, D, Reader.hasPendingBody())) - InterestingDecls.push_back(D); + PotentiallyInterestingDecls.push_back( + InterestingDecl(D, Reader.hasPendingBody())); return D; } +void ASTReader::PassInterestingDeclsToConsumer() { + assert(Consumer); + + if (PassingDeclsToConsumer) + return; + + // Guard variable to avoid recursively redoing the process of passing + // decls to consumer. + SaveAndRestore GuardPassingDeclsToConsumer(PassingDeclsToConsumer, + true); + + // Ensure that we've loaded all potentially-interesting declarations + // that need to be eagerly loaded. + for (auto ID : EagerlyDeserializedDecls) + GetDecl(ID); + EagerlyDeserializedDecls.clear(); + + while (!PotentiallyInterestingDecls.empty()) { + InterestingDecl D = PotentiallyInterestingDecls.front(); + PotentiallyInterestingDecls.pop_front(); + if (isConsumerInterestedIn(Context, D.getDecl(), D.hasPendingBody())) + PassInterestingDeclToConsumer(D.getDecl()); + } +} + void ASTReader::loadDeclUpdateRecords(serialization::DeclID ID, Decl *D) { // The declaration may have been modified by files later in the chain. // If this is the case, read the record containing the updates from each file @@ -3625,6 +3650,9 @@ auto UpdateOffsets = std::move(UpdI->second); DeclUpdateOffsets.erase(UpdI); + // FIXME: This call to isConsumerInterestedIn is not safe because + // we could be deserializing declarations at the moment. We should + // delay calling this in the same way as done in D30793. bool WasInteresting = isConsumerInterestedIn(Context, D, false); for (auto &FileAndOffset : UpdateOffsets) { ModuleFile *F = FileAndOffset.first; @@ -3646,7 +3674,8 @@ // we need to hand it off to the consumer. if (!WasInteresting && isConsumerInterestedIn(Context, D, Reader.hasPendingBody())) { - InterestingDecls.push_back(D); + PotentiallyInterestingDecls.push_back( + InterestingDecl(D, Reader.hasPendingBody())); WasInteresting = true; } } Index: test/Modules/Inputs/PR32186/a.h =================================================================== --- /dev/null +++ test/Modules/Inputs/PR32186/a.h @@ -0,0 +1,20 @@ +#pragma once + +template +struct base { + inline static constexpr T max() noexcept { return 10; } +}; + +template +static const constexpr T highest = base::max(); + +template +using cond_t = int; + +template +using dep_t = cond_t<(N < highest)>; + +template +struct foo { + using type = dep_t; +}; Index: test/Modules/Inputs/PR32186/module.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/PR32186/module.modulemap @@ -0,0 +1 @@ +module a { header "a.h" export * } Index: test/Modules/Inputs/delay-emit-check/Module.h =================================================================== --- /dev/null +++ test/Modules/Inputs/delay-emit-check/Module.h @@ -0,0 +1,6 @@ +namespace a { +namespace b { +int foo(); +const int x = foo(); +} +} Index: test/Modules/Inputs/delay-emit-check/module.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/delay-emit-check/module.modulemap @@ -0,0 +1 @@ +module "a" { header "Module.h" } Index: test/Modules/delay-emit-check-PR32186.cpp =================================================================== --- /dev/null +++ test/Modules/delay-emit-check-PR32186.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -x c++ -fmodules-cache-path=%t -std=c++1z -fmodules -I%S/Inputs/PR32186/ -fmodule-map-file=%S/Inputs/PR32186/module.modulemap %s + +#include + +template struct foo<10>; +int main() { return 0; } Index: test/Modules/delay-emit-check.cpp =================================================================== --- /dev/null +++ test/Modules/delay-emit-check.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fmodules-cache-path=%t/cache -fmodules -fimplicit-module-maps -emit-obj -I%S/Inputs/delay-emit-check/ %s -o %t.o + +// Test that we don't make unsafe calls such as to DeclMustBeEmitted during +// deserialization. + +namespace a { +namespace b { +int foo(); +} +} +#include "Module.h" Index: test/PCH/Inputs/empty-def-fwd-struct.modulemap =================================================================== --- /dev/null +++ test/PCH/Inputs/empty-def-fwd-struct.modulemap @@ -0,0 +1,2 @@ +module M { header "../empty-def-fwd-struct.h" } + Index: test/PCH/delay-PCH-emit-check.cpp =================================================================== --- /dev/null +++ test/PCH/delay-PCH-emit-check.cpp @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -x c++ -std=c++14 -fmodules -emit-module -fmodule-name=M %S/Inputs/empty-def-fwd-struct.modulemap -o %t.pcm +// RUN: %clang_cc1 -x c++ -std=c++14 -fmodules -emit-llvm -fmodule-file=%t.pcm %s -o %t.o + +#include "empty-def-fwd-struct.h"