diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index eb3a271fa59d0..aa1c02d04f7ca 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -316,6 +316,8 @@ C++23 Feature Support C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Implemented module level lookup for C++20 modules. (#GH90154) + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index a6b07dc07e25a..573b46a2321c5 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -836,6 +836,10 @@ class alignas(8) Decl { return isFromASTFile() ? getImportedOwningModule() : getLocalOwningModule(); } + /// Get the top level owning named module that owns this declaration if any. + /// \returns nullptr if the declaration is not owned by a named module. + Module *getTopLevelOwningNamedModule() const; + /// Get the module that owns this declaration for linkage purposes. /// There only ever is such a standard C++ module. Module *getOwningModuleForLinkage() const; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index aac165130b719..40dae25f7b54b 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -738,6 +738,8 @@ enum ASTRecordTypes { CXX_ADDED_TEMPLATE_SPECIALIZATION = 74, CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION = 75, + + UPDATE_MODULE_LOCAL_VISIBLE = 76, }; /// Record types used within a source manager block. @@ -1334,6 +1336,10 @@ enum DeclCode { /// into a DeclContext via DeclContext::lookup. DECL_CONTEXT_VISIBLE, + /// A record containing the set of declarations that are + /// only visible from DeclContext in the same module. + DECL_CONTEXT_MODULE_LOCAL_VISIBLE, + /// A LabelDecl record. DECL_LABEL, diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 6479a81189f90..d77bb01c5aa59 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -353,6 +353,7 @@ class ASTIdentifierLookupTrait; /// The on-disk hash table(s) used for DeclContext name lookup. struct DeclContextLookupTable; +struct ModuleLocalLookupTable; /// The on-disk hash table(s) used for specialization decls. struct LazySpecializationInfoLookupTable; @@ -523,9 +524,14 @@ class ASTReader /// in the chain. DeclUpdateOffsetsMap DeclUpdateOffsets; + struct LookupBlockOffsets { + uint64_t LexicalOffset; + uint64_t VisibleOffset; + uint64_t ModuleLocalOffset; + }; + using DelayedNamespaceOffsetMapTy = - llvm::DenseMap>; + llvm::DenseMap; /// Mapping from global declaration IDs to the lexical and visible block /// offset for delayed namespace in reduced BMI. @@ -631,6 +637,9 @@ class ASTReader /// Map from a DeclContext to its lookup tables. llvm::DenseMap Lookups; + llvm::DenseMap + ModuleLocalLookups; using SpecLookupTableTy = llvm::DenseMap PendingVisibleUpdates; + llvm::DenseMap + PendingModuleLocalVisibleUpdates; using SpecializationsUpdate = SmallVector; using SpecializationsUpdateMap = @@ -696,7 +707,8 @@ class ASTReader /// Read the record that describes the visible contents of a DC. bool ReadVisibleDeclContextStorage(ModuleFile &M, llvm::BitstreamCursor &Cursor, - uint64_t Offset, GlobalDeclID ID); + uint64_t Offset, GlobalDeclID ID, + bool IsModuleLocal); bool ReadSpecializations(ModuleFile &M, llvm::BitstreamCursor &Cursor, uint64_t Offset, Decl *D, bool IsPartial); @@ -1132,6 +1144,10 @@ class ASTReader /// Number of visible decl contexts read/total. unsigned NumVisibleDeclContextsRead = 0, TotalVisibleDeclContexts = 0; + /// Number of module local visible decl contexts read/total. + unsigned NumModuleLocalVisibleDeclContexts = 0, + TotalModuleLocalVisibleDeclContexts = 0; + /// Total size of modules, in bits, currently loaded uint64_t TotalModulesSizeInBits = 0; @@ -1444,6 +1460,9 @@ class ASTReader const serialization::reader::DeclContextLookupTable * getLoadedLookupTables(DeclContext *Primary) const; + const serialization::reader::ModuleLocalLookupTable * + getModuleLocalLookupTables(DeclContext *Primary) const; + /// Get the loaded specializations lookup tables for \p D, /// if any. serialization::reader::LazySpecializationInfoLookupTable * @@ -2608,6 +2627,10 @@ inline bool shouldSkipCheckingODR(const Decl *D) { (D->isFromGlobalModule() || D->isFromHeaderUnit()); } +/// Calculate a hash value for the primary module name of the given module. +/// \returns std::nullopt if M is not a C++ standard module. +std::optional getPrimaryModuleHash(const Module *M); + } // namespace clang #endif // LLVM_CLANG_SERIALIZATION_ASTREADER_H diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index adb7cce522a80..53b09cc914392 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -492,6 +492,10 @@ class ASTWriter : public ASTDeserializationListener, /// file. unsigned NumVisibleDeclContexts = 0; + /// The number of module local visible declcontexts written to the AST + /// file. + unsigned NumModuleLocalDeclContexts = 0; + /// A mapping from each known submodule to its ID number, which will /// be a positive integer. llvm::DenseMap SubmoduleIDs; @@ -587,11 +591,15 @@ class ASTWriter : public ASTDeserializationListener, uint64_t WriteSpecializationInfoLookupTable( const NamedDecl *D, llvm::SmallVectorImpl &Specializations, bool IsPartial); - void GenerateNameLookupTable(ASTContext &Context, const DeclContext *DC, - llvm::SmallVectorImpl &LookupTable); + void + GenerateNameLookupTable(ASTContext &Context, const DeclContext *DC, + llvm::SmallVectorImpl &LookupTable, + llvm::SmallVectorImpl &ModuleLocalLookupTable); uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, const DeclContext *DC); - uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC); + void WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC, + uint64_t &VisibleBlockOffset, + uint64_t &ModuleLocalBlockOffset); void WriteTypeDeclOffsets(); void WriteFileDeclIDsMap(); void WriteComments(ASTContext &Context); @@ -624,7 +632,9 @@ class ASTWriter : public ASTDeserializationListener, unsigned DeclParmVarAbbrev = 0; unsigned DeclContextLexicalAbbrev = 0; unsigned DeclContextVisibleLookupAbbrev = 0; + unsigned DeclModuleLocalVisibleLookupAbbrev = 0; unsigned UpdateVisibleAbbrev = 0; + unsigned ModuleLocalUpdateVisibleAbbrev = 0; unsigned DeclRecordAbbrev = 0; unsigned DeclTypedefAbbrev = 0; unsigned DeclVarAbbrev = 0; diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 7c2dcf95e3792..2886aebdf52e9 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -130,6 +130,14 @@ void Decl::setOwningModuleID(unsigned ID) { *IDAddress |= (uint64_t)ID << 48; } +Module *Decl::getTopLevelOwningNamedModule() const { + if (getOwningModule() && + getOwningModule()->getTopLevelModule()->isNamedModule()) + return getOwningModule()->getTopLevelModule(); + + return nullptr; +} + Module *Decl::getOwningModuleSlow() const { assert(isFromASTFile() && "Not from AST file?"); return getASTContext().getExternalSource()->getModule(getOwningModuleID()); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 8794a0b028787..202227b195585 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -1235,7 +1235,7 @@ unsigned DeclarationNameKey::getHash() const { } ModuleFile * -ASTDeclContextNameLookupTrait::ReadFileRef(const unsigned char *&d) { +ASTDeclContextNameLookupTraitBase::ReadFileRef(const unsigned char *&d) { using namespace llvm::support; uint32_t ModuleFileID = @@ -1244,12 +1244,12 @@ ASTDeclContextNameLookupTrait::ReadFileRef(const unsigned char *&d) { } std::pair -ASTDeclContextNameLookupTrait::ReadKeyDataLength(const unsigned char *&d) { +ASTDeclContextNameLookupTraitBase::ReadKeyDataLength(const unsigned char *&d) { return readULEBKeyDataLength(d); } -ASTDeclContextNameLookupTrait::internal_key_type -ASTDeclContextNameLookupTrait::ReadKey(const unsigned char *d, unsigned) { +DeclarationNameKey +ASTDeclContextNameLookupTraitBase::ReadKeyBase(const unsigned char *&d) { using namespace llvm::support; auto Kind = (DeclarationName::NameKind)*d++; @@ -1283,10 +1283,13 @@ ASTDeclContextNameLookupTrait::ReadKey(const unsigned char *d, unsigned) { return DeclarationNameKey(Kind, Data); } -void ASTDeclContextNameLookupTrait::ReadDataInto(internal_key_type, - const unsigned char *d, - unsigned DataLen, - data_type_builder &Val) { +ASTDeclContextNameLookupTrait::internal_key_type +ASTDeclContextNameLookupTrait::ReadKey(const unsigned char *d, unsigned) { + return ReadKeyBase(d); +} + +void ASTDeclContextNameLookupTraitBase::ReadDataIntoImpl( + const unsigned char *d, unsigned DataLen, data_type_builder &Val) { using namespace llvm::support; for (unsigned NumDecls = DataLen / sizeof(DeclID); NumDecls; --NumDecls) { @@ -1296,6 +1299,47 @@ void ASTDeclContextNameLookupTrait::ReadDataInto(internal_key_type, } } +void ASTDeclContextNameLookupTrait::ReadDataInto(internal_key_type, + const unsigned char *d, + unsigned DataLen, + data_type_builder &Val) { + ReadDataIntoImpl(d, DataLen, Val); +} + +ModuleLocalNameLookupTrait::hash_value_type +ModuleLocalNameLookupTrait::ComputeHash(const internal_key_type &Key) { + llvm::FoldingSetNodeID ID; + ID.AddInteger(Key.first.getHash()); + ID.AddInteger(Key.second); + return ID.computeStableHash(); +} + +ModuleLocalNameLookupTrait::internal_key_type +ModuleLocalNameLookupTrait::GetInternalKey(const external_key_type &Key) { + DeclarationNameKey Name(Key.first); + + std::optional ModuleHash = getPrimaryModuleHash(Key.second); + if (!ModuleHash) + return {Name, 0}; + + return {Name, *ModuleHash}; +} + +ModuleLocalNameLookupTrait::internal_key_type +ModuleLocalNameLookupTrait::ReadKey(const unsigned char *d, unsigned) { + DeclarationNameKey Name = ReadKeyBase(d); + unsigned PrimaryModuleHash = + llvm::support::endian::readNext(d); + return {Name, PrimaryModuleHash}; +} + +void ModuleLocalNameLookupTrait::ReadDataInto(internal_key_type, + const unsigned char *d, + unsigned DataLen, + data_type_builder &Val) { + ReadDataIntoImpl(d, DataLen, Val); +} + ModuleFile * LazySpecializationInfoLookupTrait::ReadFileRef(const unsigned char *&d) { using namespace llvm::support; @@ -1383,8 +1427,8 @@ bool ASTReader::ReadLexicalDeclContextStorage(ModuleFile &M, bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M, BitstreamCursor &Cursor, - uint64_t Offset, - GlobalDeclID ID) { + uint64_t Offset, GlobalDeclID ID, + bool IsModuleLocal) { assert(Offset != 0); SavedStreamPosition SavedPosition(Cursor); @@ -1408,15 +1452,22 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M, return true; } unsigned RecCode = MaybeRecCode.get(); - if (RecCode != DECL_CONTEXT_VISIBLE) { + if (!IsModuleLocal && RecCode != DECL_CONTEXT_VISIBLE) { Error("Expected visible lookup table block"); return true; } + if (IsModuleLocal && RecCode != DECL_CONTEXT_MODULE_LOCAL_VISIBLE) { + Error("Expected module local visible lookup table block"); + return true; + } // We can't safely determine the primary context yet, so delay attaching the // lookup table until we're done with recursive deserialization. auto *Data = (const unsigned char*)Blob.data(); - PendingVisibleUpdates[ID].push_back(UpdateData{&M, Data}); + if (!IsModuleLocal) + PendingVisibleUpdates[ID].push_back(UpdateData{&M, Data}); + else + PendingModuleLocalVisibleUpdates[ID].push_back(UpdateData{&M, Data}); return false; } @@ -3549,6 +3600,19 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, break; } + case UPDATE_MODULE_LOCAL_VISIBLE: { + unsigned Idx = 0; + GlobalDeclID ID = ReadDeclID(F, Record, Idx); + auto *Data = (const unsigned char *)Blob.data(); + PendingModuleLocalVisibleUpdates[ID].push_back(UpdateData{&F, Data}); + // If we've already loaded the decl, perform the updates when we finish + // loading this block. + if (Decl *D = GetExistingDecl(ID)) + PendingUpdateRecords.push_back( + PendingUpdateRecord(ID, D, /*JustLoaded=*/false)); + break; + } + case CXX_ADDED_TEMPLATE_SPECIALIZATION: { unsigned Idx = 0; GlobalDeclID ID = ReadDeclID(F, Record, Idx); @@ -3652,6 +3716,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, TotalNumMacros += Record[1]; TotalLexicalDeclContexts += Record[2]; TotalVisibleDeclContexts += Record[3]; + TotalModuleLocalVisibleDeclContexts += Record[4]; break; case UNUSED_FILESCOPED_DECLS: @@ -3937,7 +4002,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, break; case DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD: { - if (Record.size() % 3 != 0) + if (Record.size() % 4 != 0) return llvm::createStringError( std::errc::illegal_byte_sequence, "invalid DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD block in AST " @@ -3953,8 +4018,12 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, uint64_t LocalVisibleOffset = Record[I++]; uint64_t VisibleOffset = LocalVisibleOffset ? BaseOffset + LocalVisibleOffset : 0; + uint64_t LocalModuleLocalOffset = Record[I++]; + uint64_t ModuleLocalOffset = + LocalModuleLocalOffset ? BaseOffset + LocalModuleLocalOffset : 0; - DelayedNamespaceOffsetMap[ID] = {LexicalOffset, VisibleOffset}; + DelayedNamespaceOffsetMap[ID] = {LexicalOffset, VisibleOffset, + ModuleLocalOffset}; assert(!GetExistingDecl(ID) && "We shouldn't load the namespace in the front of delayed " @@ -8374,23 +8443,36 @@ bool ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, if (!Name) return false; - auto It = Lookups.find(DC); - if (It == Lookups.end()) - return false; - - Deserializing LookupResults(this); - // Load the list of declarations. SmallVector Decls; llvm::SmallPtrSet Found; - for (GlobalDeclID ID : It->second.Table.find(Name)) { - NamedDecl *ND = cast(GetDecl(ID)); - if (ND->getDeclName() == Name && Found.insert(ND).second) - Decls.push_back(ND); + Deserializing LookupResults(this); + + // FIXME: Clear the redundancy with templated lambda in C++20 when that's + // available. + if (auto It = Lookups.find(DC); It != Lookups.end()) { + ++NumVisibleDeclContextsRead; + for (GlobalDeclID ID : It->second.Table.find(Name)) { + NamedDecl *ND = cast(GetDecl(ID)); + if (ND->getDeclName() == Name && Found.insert(ND).second) + Decls.push_back(ND); + } + } + + if (auto *NamedModule = + OriginalDC ? cast(OriginalDC)->getTopLevelOwningNamedModule() + : nullptr) { + if (auto It = ModuleLocalLookups.find(DC); It != ModuleLocalLookups.end()) { + ++NumModuleLocalVisibleDeclContexts; + for (GlobalDeclID ID : It->second.Table.find({Name, NamedModule})) { + NamedDecl *ND = cast(GetDecl(ID)); + if (ND->getDeclName() == Name && Found.insert(ND).second) + Decls.push_back(ND); + } + } } - ++NumVisibleDeclContextsRead; SetExternalVisibleDeclsForName(DC, Name, Decls); return !Decls.empty(); } @@ -8399,18 +8481,25 @@ void ASTReader::completeVisibleDeclsMap(const DeclContext *DC) { if (!DC->hasExternalVisibleStorage()) return; - auto It = Lookups.find(DC); - assert(It != Lookups.end() && - "have external visible storage but no lookup tables"); - DeclsMap Decls; - for (GlobalDeclID ID : It->second.Table.findAll()) { - NamedDecl *ND = cast(GetDecl(ID)); - Decls[ND->getDeclName()].push_back(ND); - } + auto findAll = [&](auto &LookupTables, unsigned &NumRead) { + auto It = LookupTables.find(DC); + if (It == LookupTables.end()) + return; - ++NumVisibleDeclContextsRead; + NumRead++; + + for (GlobalDeclID ID : It->second.Table.findAll()) { + NamedDecl *ND = cast(GetDecl(ID)); + Decls[ND->getDeclName()].push_back(ND); + } + + // FIXME: Why a PCH test is failing if we remove the iterator after findAll? + }; + + findAll(Lookups, NumVisibleDeclContextsRead); + findAll(ModuleLocalLookups, NumModuleLocalVisibleDeclContexts); for (DeclsMap::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) { SetExternalVisibleDeclsForName(DC, I->first, I->second); @@ -8424,6 +8513,12 @@ ASTReader::getLoadedLookupTables(DeclContext *Primary) const { return I == Lookups.end() ? nullptr : &I->second; } +const serialization::reader::ModuleLocalLookupTable * +ASTReader::getModuleLocalLookupTables(DeclContext *Primary) const { + auto I = ModuleLocalLookups.find(Primary); + return I == ModuleLocalLookups.end() ? nullptr : &I->second; +} + serialization::reader::LazySpecializationInfoLookupTable * ASTReader::getLoadedSpecializationsLookupTables(const Decl *D, bool IsPartial) { assert(D->isCanonicalDecl()); @@ -8533,6 +8628,12 @@ void ASTReader::PrintStats() { NumVisibleDeclContextsRead, TotalVisibleDeclContexts, ((float)NumVisibleDeclContextsRead/TotalVisibleDeclContexts * 100)); + if (TotalModuleLocalVisibleDeclContexts) + std::fprintf( + stderr, " %u/%u module local visible declcontexts read (%f%%)\n", + NumModuleLocalVisibleDeclContexts, TotalModuleLocalVisibleDeclContexts, + ((float)NumModuleLocalVisibleDeclContexts / + TotalModuleLocalVisibleDeclContexts * 100)); if (TotalNumMethodPoolEntries) std::fprintf(stderr, " %u/%u method pool entries read (%f%%)\n", NumMethodPoolEntriesRead, TotalNumMethodPoolEntries, @@ -12639,3 +12740,25 @@ void ASTRecordReader::readOpenACCClauseList( for (unsigned I = 0; I < Clauses.size(); ++I) Clauses[I] = readOpenACCClause(); } + +static unsigned getStableHashForModuleName(StringRef PrimaryModuleName) { + // TODO: Maybe it is better to check PrimaryModuleName is a valid + // module name? + llvm::FoldingSetNodeID ID; + ID.AddString(PrimaryModuleName); + return ID.computeStableHash(); +} + +std::optional clang::getPrimaryModuleHash(const Module *M) { + if (!M) + return std::nullopt; + + if (M->isHeaderLikeModule()) + return std::nullopt; + + if (M->isGlobalModule()) + return std::nullopt; + + StringRef PrimaryModuleName = M->getPrimaryModuleInterfaceName(); + return getStableHashForModuleName(PrimaryModuleName); +} diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 95abd75920c8f..1c51a7b5e460f 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -413,7 +413,8 @@ class ASTDeclReader : public DeclVisitor { void VisitEmptyDecl(EmptyDecl *D); void VisitLifetimeExtendedTemporaryDecl(LifetimeExtendedTemporaryDecl *D); - std::pair VisitDeclContext(DeclContext *DC); + void VisitDeclContext(DeclContext *DC, uint64_t &LexicalOffset, + uint64_t &VisibleOffset, uint64_t &ModuleLocalOffset); template RedeclarableResult VisitRedeclarable(Redeclarable *D); @@ -1855,7 +1856,10 @@ void ASTDeclReader::VisitNamespaceDecl(NamespaceDecl *D) { void ASTDeclReader::VisitHLSLBufferDecl(HLSLBufferDecl *D) { VisitNamedDecl(D); - VisitDeclContext(D); + uint64_t LexicalOffset = 0; + uint64_t VisibleOffset = 0; + uint64_t ModuleLocalOffset = 0; + VisitDeclContext(D, LexicalOffset, VisibleOffset, ModuleLocalOffset); D->IsCBuffer = Record.readBool(); D->KwLoc = readSourceLocation(); D->LBraceLoc = readSourceLocation(); @@ -2764,11 +2768,12 @@ void ASTDeclReader::VisitLifetimeExtendedTemporaryDecl( mergeMergeable(D); } -std::pair -ASTDeclReader::VisitDeclContext(DeclContext *DC) { - uint64_t LexicalOffset = ReadLocalOffset(); - uint64_t VisibleOffset = ReadLocalOffset(); - return std::make_pair(LexicalOffset, VisibleOffset); +void ASTDeclReader::VisitDeclContext(DeclContext *DC, uint64_t &LexicalOffset, + uint64_t &VisibleOffset, + uint64_t &ModuleLocalOffset) { + LexicalOffset = ReadLocalOffset(); + VisibleOffset = ReadLocalOffset(); + ModuleLocalOffset = ReadLocalOffset(); } template @@ -3869,6 +3874,7 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) { switch ((DeclCode)MaybeDeclCode.get()) { case DECL_CONTEXT_LEXICAL: case DECL_CONTEXT_VISIBLE: + case DECL_CONTEXT_MODULE_LOCAL_VISIBLE: case DECL_SPECIALIZATIONS: case DECL_PARTIAL_SPECIALIZATIONS: llvm_unreachable("Record cannot be de-serialized with readDeclRecord"); @@ -4176,21 +4182,35 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) { // If this declaration is also a declaration context, get the // offsets for its tables of lexical and visible declarations. if (auto *DC = dyn_cast(D)) { - std::pair Offsets = Reader.VisitDeclContext(DC); + uint64_t LexicalOffset = 0; + uint64_t VisibleOffset = 0; + uint64_t ModuleLocalOffset = 0; + + Reader.VisitDeclContext(DC, LexicalOffset, VisibleOffset, + ModuleLocalOffset); // Get the lexical and visible block for the delayed namespace. // It is sufficient to judge if ID is in DelayedNamespaceOffsetMap. // But it may be more efficient to filter the other cases. - if (!Offsets.first && !Offsets.second && isa(D)) + if (!LexicalOffset && !VisibleOffset && !ModuleLocalOffset && + isa(D)) if (auto Iter = DelayedNamespaceOffsetMap.find(ID); - Iter != DelayedNamespaceOffsetMap.end()) - Offsets = Iter->second; + Iter != DelayedNamespaceOffsetMap.end()) { + LexicalOffset = Iter->second.LexicalOffset; + VisibleOffset = Iter->second.VisibleOffset; + ModuleLocalOffset = Iter->second.ModuleLocalOffset; + } - if (Offsets.first && - ReadLexicalDeclContextStorage(*Loc.F, DeclsCursor, Offsets.first, DC)) + if (LexicalOffset && + ReadLexicalDeclContextStorage(*Loc.F, DeclsCursor, LexicalOffset, DC)) + return nullptr; + if (VisibleOffset && + ReadVisibleDeclContextStorage(*Loc.F, DeclsCursor, VisibleOffset, ID, + /*IsModuleLocal=*/false)) return nullptr; - if (Offsets.second && - ReadVisibleDeclContextStorage(*Loc.F, DeclsCursor, Offsets.second, ID)) + if (ModuleLocalOffset && + ReadVisibleDeclContextStorage(*Loc.F, DeclsCursor, ModuleLocalOffset, + ID, /*IsModuleLocal=*/true)) return nullptr; } assert(Record.getIdx() == Record.size()); @@ -4328,8 +4348,8 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { } // Load the pending visible updates for this decl context, if it has any. - auto I = PendingVisibleUpdates.find(ID); - if (I != PendingVisibleUpdates.end()) { + if (auto I = PendingVisibleUpdates.find(ID); + I != PendingVisibleUpdates.end()) { auto VisibleUpdates = std::move(I->second); PendingVisibleUpdates.erase(I); @@ -4341,6 +4361,21 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { DC->setHasExternalVisibleStorage(true); } + if (auto I = PendingModuleLocalVisibleUpdates.find(ID); + I != PendingModuleLocalVisibleUpdates.end()) { + auto ModuleLocalVisibleUpdates = std::move(I->second); + PendingModuleLocalVisibleUpdates.erase(I); + + auto *DC = cast(D)->getPrimaryContext(); + for (const auto &Update : ModuleLocalVisibleUpdates) + ModuleLocalLookups[DC].Table.add( + Update.Mod, Update.Data, + reader::ModuleLocalNameLookupTrait(*this, *Update.Mod)); + // NOTE: Can we optimize the case that the data being loaded + // is not related to current module? + DC->setHasExternalVisibleStorage(true); + } + // Load any pending related decls. if (D->isCanonicalDecl()) { if (auto IT = RelatedDeclsMap.find(ID); IT != RelatedDeclsMap.end()) { diff --git a/clang/lib/Serialization/ASTReaderInternals.h b/clang/lib/Serialization/ASTReaderInternals.h index be0d22d1f4094..4be2b2323ec40 100644 --- a/clang/lib/Serialization/ASTReaderInternals.h +++ b/clang/lib/Serialization/ASTReaderInternals.h @@ -31,6 +31,7 @@ class FileEntry; struct HeaderFileInfo; class HeaderSearch; class ObjCMethodDecl; +class Module; namespace serialization { @@ -38,9 +39,8 @@ class ModuleFile; namespace reader { -/// Class that performs name lookup into a DeclContext stored -/// in an AST file. -class ASTDeclContextNameLookupTrait { +class ASTDeclContextNameLookupTraitBase { +protected: ASTReader &Reader; ModuleFile &F; @@ -80,11 +80,37 @@ class ASTDeclContextNameLookupTrait { using offset_type = unsigned; using file_type = ModuleFile *; - using external_key_type = DeclarationName; - using internal_key_type = DeclarationNameKey; +protected: + explicit ASTDeclContextNameLookupTraitBase(ASTReader &Reader, ModuleFile &F) + : Reader(Reader), F(F) {} + +public: + static std::pair + ReadKeyDataLength(const unsigned char *&d); + + void ReadDataIntoImpl(const unsigned char *d, unsigned DataLen, + data_type_builder &Val); + + static void MergeDataInto(const data_type &From, data_type_builder &To) { + To.Data.reserve(To.Data.size() + From.size()); + for (GlobalDeclID ID : From) + To.insert(ID); + } + + file_type ReadFileRef(const unsigned char *&d); + + DeclarationNameKey ReadKeyBase(const unsigned char *&d); +}; +/// Class that performs name lookup into a DeclContext stored +/// in an AST file. +class ASTDeclContextNameLookupTrait : public ASTDeclContextNameLookupTraitBase { +public: explicit ASTDeclContextNameLookupTrait(ASTReader &Reader, ModuleFile &F) - : Reader(Reader), F(F) {} + : ASTDeclContextNameLookupTraitBase(Reader, F) {} + + using external_key_type = DeclarationName; + using internal_key_type = DeclarationNameKey; static bool EqualKey(const internal_key_type &a, const internal_key_type &b) { return a == b; @@ -98,25 +124,39 @@ class ASTDeclContextNameLookupTrait { return Name; } - static std::pair - ReadKeyDataLength(const unsigned char *&d); - internal_key_type ReadKey(const unsigned char *d, unsigned); void ReadDataInto(internal_key_type, const unsigned char *d, unsigned DataLen, data_type_builder &Val); +}; - static void MergeDataInto(const data_type &From, data_type_builder &To) { - To.Data.reserve(To.Data.size() + From.size()); - for (GlobalDeclID ID : From) - To.insert(ID); +struct DeclContextLookupTable { + MultiOnDiskHashTable Table; +}; + +class ModuleLocalNameLookupTrait : public ASTDeclContextNameLookupTraitBase { +public: + explicit ModuleLocalNameLookupTrait(ASTReader &Reader, ModuleFile &F) + : ASTDeclContextNameLookupTraitBase(Reader, F) {} + + using external_key_type = std::pair; + using internal_key_type = std::pair; + + static bool EqualKey(const internal_key_type &a, const internal_key_type &b) { + return a == b; } - file_type ReadFileRef(const unsigned char *&d); + static hash_value_type ComputeHash(const internal_key_type &Key); + static internal_key_type GetInternalKey(const external_key_type &Key); + + internal_key_type ReadKey(const unsigned char *d, unsigned); + + void ReadDataInto(internal_key_type, const unsigned char *d, unsigned DataLen, + data_type_builder &Val); }; -struct DeclContextLookupTable { - MultiOnDiskHashTable Table; +struct ModuleLocalLookupTable { + MultiOnDiskHashTable Table; }; using LazySpecializationInfo = GlobalDeclID; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 0ae2157eed4ec..55d3c2bb56f2c 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1088,6 +1088,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(DECL_BLOCK); RECORD(DECL_CONTEXT_LEXICAL); RECORD(DECL_CONTEXT_VISIBLE); + RECORD(DECL_CONTEXT_MODULE_LOCAL_VISIBLE); RECORD(DECL_NAMESPACE); RECORD(DECL_NAMESPACE_ALIAS); RECORD(DECL_USING); @@ -4026,15 +4027,13 @@ void ASTWriter::handleVTable(CXXRecordDecl *RD) { namespace { -// Trait used for the on-disk hash table used in the method pool. -class ASTDeclContextNameLookupTrait { +class ASTDeclContextNameLookupTraitBase { +protected: ASTWriter &Writer; - llvm::SmallVector DeclIDs; + using DeclIDsTy = llvm::SmallVector; + DeclIDsTy DeclIDs; public: - using key_type = DeclarationNameKey; - using key_type_ref = key_type; - /// A start and end index into DeclIDs, representing a sequence of decls. using data_type = std::pair; using data_type_ref = const data_type &; @@ -4042,31 +4041,11 @@ class ASTDeclContextNameLookupTrait { using hash_value_type = unsigned; using offset_type = unsigned; - explicit ASTDeclContextNameLookupTrait(ASTWriter &Writer) : Writer(Writer) {} - - template - data_type getData(const Coll &Decls) { - unsigned Start = DeclIDs.size(); - for (NamedDecl *D : Decls) { - NamedDecl *DeclForLocalLookup = - getDeclForLocalLookup(Writer.getLangOpts(), D); - - if (Writer.getDoneWritingDeclsAndTypes() && - !Writer.wasDeclEmitted(DeclForLocalLookup)) - continue; - - // Try to avoid writing internal decls to reduced BMI. - // See comments in ASTWriter::WriteDeclContextLexicalBlock for details. - if (Writer.isGeneratingReducedBMI() && - !DeclForLocalLookup->isFromExplicitGlobalModule() && - IsInternalDeclFromFileContext(DeclForLocalLookup)) - continue; - - DeclIDs.push_back(Writer.GetDeclRef(DeclForLocalLookup)); - } - return std::make_pair(Start, DeclIDs.size()); - } +protected: + explicit ASTDeclContextNameLookupTraitBase(ASTWriter &Writer) + : Writer(Writer) {} +public: data_type ImportData(const reader::ASTDeclContextNameLookupTrait::data_type &FromReader) { unsigned Start = DeclIDs.size(); DeclIDs.insert( @@ -4076,14 +4055,6 @@ class ASTDeclContextNameLookupTrait { return std::make_pair(Start, DeclIDs.size()); } - static bool EqualKey(key_type_ref a, key_type_ref b) { - return a == b; - } - - hash_value_type ComputeHash(DeclarationNameKey Name) { - return Name.getHash(); - } - void EmitFileRef(raw_ostream &Out, ModuleFile *F) const { assert(Writer.hasChain() && "have reference to loaded module file but no chain?"); @@ -4094,9 +4065,9 @@ class ASTDeclContextNameLookupTrait { llvm::endianness::little); } - std::pair EmitKeyDataLength(raw_ostream &Out, - DeclarationNameKey Name, - data_type_ref Lookup) { + std::pair EmitKeyDataLengthBase(raw_ostream &Out, + DeclarationNameKey Name, + data_type_ref Lookup) { unsigned KeyLen = 1; switch (Name.getKind()) { case DeclarationName::Identifier: @@ -4122,10 +4093,10 @@ class ASTDeclContextNameLookupTrait { // length of DeclIDs. unsigned DataLen = sizeof(DeclID) * (Lookup.second - Lookup.first); - return emitULEBKeyDataLength(KeyLen, DataLen, Out); + return {KeyLen, DataLen}; } - void EmitKey(raw_ostream &Out, DeclarationNameKey Name, unsigned) { + void EmitKeyBase(raw_ostream &Out, DeclarationNameKey Name) { using namespace llvm::support; endian::Writer LE(Out, llvm::endianness::little); @@ -4156,8 +4127,7 @@ class ASTDeclContextNameLookupTrait { llvm_unreachable("Invalid name kind?"); } - void EmitData(raw_ostream &Out, key_type_ref, data_type Lookup, - unsigned DataLen) { + void EmitDataBase(raw_ostream &Out, data_type Lookup, unsigned DataLen) { using namespace llvm::support; endian::Writer LE(Out, llvm::endianness::little); @@ -4168,6 +4138,148 @@ class ASTDeclContextNameLookupTrait { } }; +class ModuleLocalNameLookupTrait : public ASTDeclContextNameLookupTraitBase { +public: + using primary_module_hash_type = unsigned; + + using key_type = std::pair; + using key_type_ref = key_type; + + explicit ModuleLocalNameLookupTrait(ASTWriter &Writer) + : ASTDeclContextNameLookupTraitBase(Writer) {} + + data_type getData(const DeclIDsTy &LocalIDs) { + unsigned Start = DeclIDs.size(); + for (auto ID : LocalIDs) + DeclIDs.push_back(ID); + return std::make_pair(Start, DeclIDs.size()); + } + + static bool EqualKey(key_type_ref a, key_type_ref b) { return a == b; } + + hash_value_type ComputeHash(key_type Key) { + llvm::FoldingSetNodeID ID; + ID.AddInteger(Key.first.getHash()); + ID.AddInteger(Key.second); + return ID.computeStableHash(); + } + + std::pair + EmitKeyDataLength(raw_ostream &Out, key_type Key, data_type_ref Lookup) { + auto [KeyLen, DataLen] = EmitKeyDataLengthBase(Out, Key.first, Lookup); + KeyLen += sizeof(Key.second); + return emitULEBKeyDataLength(KeyLen, DataLen, Out); + } + + void EmitKey(raw_ostream &Out, key_type Key, unsigned) { + EmitKeyBase(Out, Key.first); + llvm::support::endian::Writer LE(Out, llvm::endianness::little); + LE.write(Key.second); + } + + void EmitData(raw_ostream &Out, key_type_ref, data_type Lookup, + unsigned DataLen) { + EmitDataBase(Out, Lookup, DataLen); + } +}; + +static bool isModuleLocalDecl(NamedDecl *D) { + // For decls not in a file context, they should have the same visibility + // with their parent. + if (auto *Parent = dyn_cast(D->getNonTransparentDeclContext()); + Parent && !D->getNonTransparentDeclContext()->isFileContext()) + return isModuleLocalDecl(Parent); + + // Deduction Guide are special here. Since their logical parent context are + // not their actual parent. + if (auto *FTD = dyn_cast(D)) + if (auto *CDGD = dyn_cast(FTD->getTemplatedDecl())) + return isModuleLocalDecl(CDGD->getDeducedTemplate()); + + if (D->getFormalLinkage() == Linkage::Module) + return true; + + return false; +} + +// Trait used for the on-disk hash table used in the method pool. +class ASTDeclContextNameLookupTrait : public ASTDeclContextNameLookupTraitBase { +public: + using ModuleLocalDeclsMapTy = + llvm::DenseMap; + +private: + ModuleLocalDeclsMapTy ModuleLocalDeclsMap; + +public: + using key_type = DeclarationNameKey; + using key_type_ref = key_type; + + explicit ASTDeclContextNameLookupTrait(ASTWriter &Writer) + : ASTDeclContextNameLookupTraitBase(Writer) {} + + template data_type getData(const Coll &Decls) { + unsigned Start = DeclIDs.size(); + for (NamedDecl *D : Decls) { + NamedDecl *DeclForLocalLookup = + getDeclForLocalLookup(Writer.getLangOpts(), D); + + if (Writer.getDoneWritingDeclsAndTypes() && + !Writer.wasDeclEmitted(DeclForLocalLookup)) + continue; + + // Try to avoid writing internal decls to reduced BMI. + // See comments in ASTWriter::WriteDeclContextLexicalBlock for details. + if (Writer.isGeneratingReducedBMI() && + !DeclForLocalLookup->isFromExplicitGlobalModule() && + IsInternalDeclFromFileContext(DeclForLocalLookup)) + continue; + + auto ID = Writer.GetDeclRef(DeclForLocalLookup); + + if (isModuleLocalDecl(D)) { + if (std::optional PrimaryModuleHash = + getPrimaryModuleHash(D->getOwningModule())) { + auto Key = std::make_pair(D->getDeclName(), *PrimaryModuleHash); + auto Iter = ModuleLocalDeclsMap.find(Key); + if (Iter == ModuleLocalDeclsMap.end()) + ModuleLocalDeclsMap.insert({Key, DeclIDsTy{ID}}); + else + Iter->second.push_back(ID); + continue; + } + } + + DeclIDs.push_back(ID); + } + return std::make_pair(Start, DeclIDs.size()); + } + + const ModuleLocalDeclsMapTy &getModuleLocalDecls() { + return ModuleLocalDeclsMap; + } + + static bool EqualKey(key_type_ref a, key_type_ref b) { return a == b; } + + hash_value_type ComputeHash(key_type Name) { return Name.getHash(); } + + std::pair EmitKeyDataLength(raw_ostream &Out, + DeclarationNameKey Name, + data_type_ref Lookup) { + auto [KeyLen, DataLen] = EmitKeyDataLengthBase(Out, Name, Lookup); + return emitULEBKeyDataLength(KeyLen, DataLen, Out); + } + + void EmitKey(raw_ostream &Out, DeclarationNameKey Name, unsigned) { + return EmitKeyBase(Out, Name); + } + + void EmitData(raw_ostream &Out, key_type_ref, data_type Lookup, + unsigned DataLen) { + EmitDataBase(Out, Lookup, DataLen); + } +}; + } // namespace namespace { @@ -4373,7 +4485,8 @@ static bool isLookupResultNotInteresting(ASTWriter &Writer, void ASTWriter::GenerateNameLookupTable( ASTContext &Context, const DeclContext *ConstDC, - llvm::SmallVectorImpl &LookupTable) { + llvm::SmallVectorImpl &LookupTable, + llvm::SmallVectorImpl &ModuleLocalLookupTable) { assert(!ConstDC->hasLazyLocalLexicalLookups() && !ConstDC->hasLazyExternalLexicalLookups() && "must call buildLookups first"); @@ -4555,6 +4668,28 @@ void ASTWriter::GenerateNameLookupTable( // merged table if there is one. auto *Lookups = Chain ? Chain->getLoadedLookupTables(DC) : nullptr; Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr); + + const auto &ModuleLocalDecls = Trait.getModuleLocalDecls(); + if (ModuleLocalDecls.empty()) + return; + + MultiOnDiskHashTableGenerator + ModuleLocalLookupGenerator; + ModuleLocalNameLookupTrait ModuleLocalTrait(*this); + + for (const auto &ModuleLocalIter : ModuleLocalDecls) { + const auto &Key = ModuleLocalIter.first; + const auto &IDs = ModuleLocalIter.second; + ModuleLocalLookupGenerator.insert(Key, ModuleLocalTrait.getData(IDs), + ModuleLocalTrait); + } + + auto *ModuleLocalLookups = + Chain ? Chain->getModuleLocalLookupTables(DC) : nullptr; + ModuleLocalLookupGenerator.emit( + ModuleLocalLookupTable, ModuleLocalTrait, + ModuleLocalLookups ? &ModuleLocalLookups->Table : nullptr); } /// Write the block containing all of the declaration IDs @@ -4562,8 +4697,10 @@ void ASTWriter::GenerateNameLookupTable( /// /// \returns the offset of the DECL_CONTEXT_VISIBLE block within the /// bitstream, or 0 if no block was written. -uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context, - DeclContext *DC) { +void ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context, + DeclContext *DC, + uint64_t &VisibleBlockOffset, + uint64_t &ModuleLocalBlockOffset) { // If we imported a key declaration of this namespace, write the visible // lookup results as an update record for it rather than including them // on this declaration. We will only look at key declarations on reload. @@ -4573,7 +4710,7 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context, for (auto *Prev = cast(DC)->getPreviousDecl(); Prev; Prev = Prev->getPreviousDecl()) if (!Prev->isFromASTFile()) - return 0; + return; // Note that we need to emit an update record for the primary context. UpdatedDeclContexts.insert(DC->getPrimaryContext()); @@ -4622,41 +4759,53 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context, } } - return 0; + return; } if (DC->getPrimaryContext() != DC) - return 0; + return; // Skip contexts which don't support name lookup. if (!DC->isLookupContext()) - return 0; + return; // If not in C++, we perform name lookup for the translation unit via the // IdentifierInfo chains, don't bother to build a visible-declarations table. if (DC->isTranslationUnit() && !Context.getLangOpts().CPlusPlus) - return 0; + return; // Serialize the contents of the mapping used for lookup. Note that, // although we have two very different code paths, the serialized // representation is the same for both cases: a declaration name, // followed by a size, followed by references to the visible // declarations that have that name. - uint64_t Offset = Stream.GetCurrentBitNo(); StoredDeclsMap *Map = DC->buildLookup(); if (!Map || Map->empty()) - return 0; + return; + VisibleBlockOffset = Stream.GetCurrentBitNo(); // Create the on-disk hash table in a buffer. SmallString<4096> LookupTable; - GenerateNameLookupTable(Context, DC, LookupTable); + SmallString<4096> ModuleLocalLookupTable; + GenerateNameLookupTable(Context, DC, LookupTable, ModuleLocalLookupTable); // Write the lookup table RecordData::value_type Record[] = {DECL_CONTEXT_VISIBLE}; Stream.EmitRecordWithBlob(DeclContextVisibleLookupAbbrev, Record, LookupTable); ++NumVisibleDeclContexts; - return Offset; + + if (ModuleLocalLookupTable.empty()) + return; + + ModuleLocalBlockOffset = Stream.GetCurrentBitNo(); + assert(ModuleLocalBlockOffset > VisibleBlockOffset); + // Write the lookup table + RecordData::value_type ModuleLocalRecord[] = { + DECL_CONTEXT_MODULE_LOCAL_VISIBLE}; + Stream.EmitRecordWithBlob(DeclModuleLocalVisibleLookupAbbrev, + ModuleLocalRecord, ModuleLocalLookupTable); + ++NumModuleLocalDeclContexts; } /// Write an UPDATE_VISIBLE block for the given context. @@ -4673,7 +4822,8 @@ void ASTWriter::WriteDeclContextVisibleUpdate(ASTContext &Context, // Create the on-disk hash table in a buffer. SmallString<4096> LookupTable; - GenerateNameLookupTable(Context, DC, LookupTable); + SmallString<4096> ModuleLocalLookupTable; + GenerateNameLookupTable(Context, DC, LookupTable, ModuleLocalLookupTable); // If we're updating a namespace, select a key declaration as the key for the // update record; those are the only ones that will be checked on reload. @@ -4684,6 +4834,15 @@ void ASTWriter::WriteDeclContextVisibleUpdate(ASTContext &Context, RecordData::value_type Record[] = {UPDATE_VISIBLE, getDeclID(cast(DC)).getRawValue()}; Stream.EmitRecordWithBlob(UpdateVisibleAbbrev, Record, LookupTable); + + if (ModuleLocalLookupTable.empty()) + return; + + // Write the module local lookup table + RecordData::value_type ModuleLocalRecord[] = { + UPDATE_MODULE_LOCAL_VISIBLE, getDeclID(cast(DC)).getRawValue()}; + Stream.EmitRecordWithBlob(ModuleLocalUpdateVisibleAbbrev, ModuleLocalRecord, + ModuleLocalLookupTable); } /// Write an FP_PRAGMA_OPTIONS block for the given FPOptions. @@ -5867,7 +6026,8 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema *SemaPtr, StringRef isysroot, // Some simple statistics RecordData::value_type Record[] = { - NumStatements, NumMacros, NumLexicalDeclContexts, NumVisibleDeclContexts}; + NumStatements, NumMacros, NumLexicalDeclContexts, NumVisibleDeclContexts, + NumModuleLocalDeclContexts}; Stream.EmitRecord(STATISTICS, Record); Stream.ExitBlock(); Stream.FlushToWord(); @@ -5944,7 +6104,9 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { RecordData DelayedNamespaceRecord; for (NamespaceDecl *NS : DelayedNamespace) { uint64_t LexicalOffset = WriteDeclContextLexicalBlock(Context, NS); - uint64_t VisibleOffset = WriteDeclContextVisibleBlock(Context, NS); + uint64_t VisibleOffset = 0; + uint64_t ModuleLocalOffset = 0; + WriteDeclContextVisibleBlock(Context, NS, VisibleOffset, ModuleLocalOffset); // Write the offset relative to current block. if (LexicalOffset) @@ -5953,9 +6115,13 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { if (VisibleOffset) VisibleOffset -= DeclTypesBlockStartOffset; + if (ModuleLocalOffset) + ModuleLocalOffset -= DeclTypesBlockStartOffset; + AddDeclRef(NS, DelayedNamespaceRecord); DelayedNamespaceRecord.push_back(LexicalOffset); DelayedNamespaceRecord.push_back(VisibleOffset); + DelayedNamespaceRecord.push_back(ModuleLocalOffset); } // The process of writing lexical and visible block for delayed namespace @@ -6035,6 +6201,12 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)); UpdateVisibleAbbrev = Stream.EmitAbbrev(std::move(Abv)); + Abv = std::make_shared(); + Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_MODULE_LOCAL_VISIBLE)); + Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6)); + Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)); + ModuleLocalUpdateVisibleAbbrev = Stream.EmitAbbrev(std::move(Abv)); + // And a visible updates block for the translation unit. WriteDeclContextVisibleUpdate(Context, TU); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 3b357f3c50dad..7a494cfe1ac64 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -2068,6 +2068,7 @@ void ASTDeclWriter::VisitDeclContext(DeclContext *DC) { uint64_t LexicalOffset = 0; uint64_t VisibleOffset = 0; + uint64_t ModuleLocalOffset = 0; if (Writer.isGeneratingReducedBMI() && isa(DC) && cast(DC)->isFromExplicitGlobalModule()) { @@ -2078,12 +2079,13 @@ void ASTDeclWriter::VisitDeclContext(DeclContext *DC) { } else { LexicalOffset = Writer.WriteDeclContextLexicalBlock(Record.getASTContext(), DC); - VisibleOffset = - Writer.WriteDeclContextVisibleBlock(Record.getASTContext(), DC); + Writer.WriteDeclContextVisibleBlock(Record.getASTContext(), DC, + VisibleOffset, ModuleLocalOffset); } Record.AddOffset(LexicalOffset); Record.AddOffset(VisibleOffset); + Record.AddOffset(ModuleLocalOffset); } const Decl *ASTWriter::getFirstLocalDecl(const Decl *D) { @@ -2438,6 +2440,7 @@ void ASTWriter::WriteDeclAbbrevs() { // DC Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // VisibleOffset + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // ModuleLocalOffset DeclEnumAbbrev = Stream.EmitAbbrev(std::move(Abv)); // Abbreviation for DECL_RECORD @@ -2490,6 +2493,7 @@ void ASTWriter::WriteDeclAbbrevs() { // DC Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // VisibleOffset + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // ModuleLocalOffset DeclRecordAbbrev = Stream.EmitAbbrev(std::move(Abv)); // Abbreviation for DECL_PARM_VAR @@ -2827,6 +2831,11 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); DeclContextVisibleLookupAbbrev = Stream.EmitAbbrev(std::move(Abv)); + Abv = std::make_shared(); + Abv->Add(BitCodeAbbrevOp(serialization::DECL_CONTEXT_MODULE_LOCAL_VISIBLE)); + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + DeclModuleLocalVisibleLookupAbbrev = Stream.EmitAbbrev(std::move(Abv)); + Abv = std::make_shared(); Abv->Add(BitCodeAbbrevOp(serialization::DECL_SPECIALIZATIONS)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); diff --git a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp index d69db40062dae..54ec6aa61ec37 100644 --- a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp +++ b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp @@ -62,8 +62,8 @@ void test_late() { not_exported = 1; #ifndef IMPLEMENTATION - // expected-error@-2 {{declaration of 'not_exported' must be imported from module 'A' before it is required}} - // expected-note@p2.cpp:19 {{declaration here is not visible}} + // expected-error@-2 {{use of undeclared identifier 'not_exported'; did you mean 'exported'?}} + // expected-note@p2.cpp:18 {{'exported' declared here}} #endif internal = 1; diff --git a/clang/test/CXX/module/basic/basic.link/p2.cppm b/clang/test/CXX/module/basic/basic.link/p2.cppm index 19761fb3359ce..5a497304201dc 100644 --- a/clang/test/CXX/module/basic/basic.link/p2.cppm +++ b/clang/test/CXX/module/basic/basic.link/p2.cppm @@ -62,12 +62,11 @@ import M; void use_from_module_impl() { external_linkage_fn(); - module_linkage_fn(); // expected-error {{declaration of 'module_linkage_fn' must be imported}} + module_linkage_fn(); // expected-error {{use of undeclared identifier 'module_linkage_fn'}} internal_linkage_fn(); // expected-error {{declaration of 'internal_linkage_fn' must be imported}} (void)external_linkage_class{}; (void)module_linkage_class{}; // expected-error {{undeclared identifier}} expected-error 0+{{}} (void)internal_linkage_class{}; // expected-error {{undeclared identifier}} expected-error 0+{{}} - // expected-note@M.cppm:9 {{declaration here is not visible}} // expected-note@M.cppm:10 {{declaration here is not visible}} (void)external_linkage_var; (void)module_linkage_var; // expected-error {{undeclared identifier}} diff --git a/clang/test/CXX/module/module.import/p2.cpp b/clang/test/CXX/module/module.import/p2.cpp index 6b8e32f746b62..0ad3bc815beac 100644 --- a/clang/test/CXX/module/module.import/p2.cpp +++ b/clang/test/CXX/module/module.import/p2.cpp @@ -23,10 +23,7 @@ export A f(); //--- Use.cpp import M; void test() { - A a; // expected-error {{definition of 'A' must be imported from module 'M' before it is required}} - // expected-error@-1 {{definition of 'A' must be imported from module 'M' before it is required}} expected-error@-1 {{}} - // expected-note@impl.cppm:2 {{declaration here is not visible}} - // expected-note@impl.cppm:2 {{definition here is not reachable}} expected-note@impl.cppm:2 {{}} + A a; // expected-error {{unknown type name 'A'}} } //--- UseInPartA.cppm @@ -40,10 +37,7 @@ void test() { export module B; import M; void test() { - A a; // expected-error {{declaration of 'A' must be imported from module 'M'}} - // expected-error@-1 {{definition of 'A' must be imported from module 'M'}} expected-error@-1 {{}} - // expected-note@impl.cppm:2 {{declaration here is not visible}} - // expected-note@impl.cppm:2 {{definition here is not reachable}} expected-note@impl.cppm:2 {{}} + A a; // expected-error {{unknown type name 'A'}} } //--- Private.cppm diff --git a/clang/test/CXX/module/module.interface/p7.cpp b/clang/test/CXX/module/module.interface/p7.cpp index 1572390f0d289..cff5df91e43d4 100644 --- a/clang/test/CXX/module/module.interface/p7.cpp +++ b/clang/test/CXX/module/module.interface/p7.cpp @@ -57,12 +57,10 @@ void test() { void test2() { auto a = E1::e1; // OK, namespace-scope name E1 is visible and e1 is reachable auto b = e1; // OK, namespace-scope name e1 is visible - auto c = E2::e2; // expected-error {{declaration of 'E2' must be imported from module}} - // expected-note@* {{declaration here is not visible}} - auto d = e2; // should be error, namespace-scope name e2 is not visible + auto c = E2::e2; // expected-error {{use of undeclared identifier 'E2'}} + auto d = e2; // expected-error {{use of undeclared identifier 'e2'}} auto e = E2U::e2; // OK, namespace-scope name E2U is visible and E2::e2 is reachable - auto f = E3::e3; // expected-error {{declaration of 'E3' must be imported from module 'p7' before it is required}} - // expected-note@* {{declaration here is not visible}} - auto g = e3; // should be error, namespace-scope name e3 is not visible + auto f = E3::e3; // expected-error {{use of undeclared identifier 'E3'}} + auto g = e3; // expected-error {{use of undeclared identifier 'e3'}} auto h = decltype(func())::e3; // OK, namespace-scope name f is visible and E3::e3 is reachable } diff --git a/clang/test/CXX/module/module.reach/p5.cpp b/clang/test/CXX/module/module.reach/p5.cpp index 9c498a260530f..947fd082553ec 100644 --- a/clang/test/CXX/module/module.reach/p5.cpp +++ b/clang/test/CXX/module/module.reach/p5.cpp @@ -14,5 +14,4 @@ export using Y = X; export module B; import A; Y y; // OK, definition of X is reachable -X x; // expected-error {{declaration of 'X' must be imported from module 'A' before it is required}} - // expected-note@* {{declaration here is not visible}} +X x; // expected-error {{unknown type name 'X'}} diff --git a/clang/test/Modules/Reachability-template-default-arg.cpp b/clang/test/Modules/Reachability-template-default-arg.cpp index 35c647d0d344b..a7da86b8cc2d5 100644 --- a/clang/test/Modules/Reachability-template-default-arg.cpp +++ b/clang/test/Modules/Reachability-template-default-arg.cpp @@ -21,6 +21,5 @@ struct A { import template_default_arg; void bar() { A<> a0; - A a1; // expected-error {{declaration of 't' must be imported from module 'template_default_arg' before it is required}} - // expected-note@* {{declaration here is not visible}} + A a1; // expected-error {{use of undeclared identifier 't'}} } diff --git a/clang/test/Modules/cxx20-10-1-ex2.cpp b/clang/test/Modules/cxx20-10-1-ex2.cpp index fc61d89926d44..8611d6d64c851 100644 --- a/clang/test/Modules/cxx20-10-1-ex2.cpp +++ b/clang/test/Modules/cxx20-10-1-ex2.cpp @@ -78,8 +78,7 @@ int &c = n; // OK //--- std10-1-ex2-tu6.cpp import B; // error, n is module-local and this is not a module. -int &c = n; // expected-error {{declaration of 'n' must be imported}} - // expected-note@* {{declaration here is not visible}} +int &c = n; // expected-error {{use of undeclared identifier 'n'}} //--- std10-1-ex2-tu7.cpp // expected-no-diagnostics diff --git a/clang/test/Modules/deduction-guide3.cppm b/clang/test/Modules/deduction-guide3.cppm index 1165dd40bcfb8..f7990004cec7c 100644 --- a/clang/test/Modules/deduction-guide3.cppm +++ b/clang/test/Modules/deduction-guide3.cppm @@ -22,8 +22,6 @@ Templ(T t) -> Templ; //--- Use.cpp import Templ; void func() { - Templ t(5); // expected-error {{declaration of 'Templ' must be imported from module 'Templ' before it is required}} - // expected-error@-1 {{unknown type name 'Templ'}} - // expected-note@Templ.cppm:3 {{declaration here is not visible}} + Templ t(5); // expected-error {{unknown type name 'Templ'}} } diff --git a/clang/test/Modules/module-local-with-templates.cppm b/clang/test/Modules/module-local-with-templates.cppm new file mode 100644 index 0000000000000..87955bdd3f99e --- /dev/null +++ b/clang/test/Modules/module-local-with-templates.cppm @@ -0,0 +1,79 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/use.cc -fmodule-file=a=%t/a.pcm -fsyntax-only -verify +// RUN: %clang_cc1 -std=c++20 %t/a-part.cppm -fmodule-file=a=%t/a.pcm -fsyntax-only -verify +// +// Test again with reduced BMI +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/use.cc -fmodule-file=a=%t/a.pcm -fsyntax-only -verify +// RUN: %clang_cc1 -std=c++20 %t/a-part.cppm -fmodule-file=a=%t/a.pcm -fsyntax-only -verify +// RUN: %clang_cc1 -std=c++20 %t/a.cc -fmodule-file=a=%t/a.pcm -fsyntax-only -verify + + +//--- a.cppm +export module a; + +constexpr int x = 43; + +export constexpr int f() { return x; } + +export template +constexpr T g() { + return x; +} + +namespace nn { + +constexpr int x = 88; + +export constexpr int f() { return x; } + +export template +constexpr T g() { + return x; +} +} + +//--- use.cc +// expected-no-diagnostics +import a; + +static_assert(f() == 43, ""); + +constexpr int x = 99; + +static_assert(g() == 43, ""); + +static_assert(x == 99, ""); + +namespace nn { +static_assert(f() == 88, ""); + +constexpr int x = 1000; + +static_assert(g() == 88, ""); + +static_assert(x == 1000, ""); + +} + +//--- a-part.cppm +module a:impl; +import a; + +static_assert(x == 43, ""); + +constexpr int x = 1000; // expected-error {{redefinition of 'x'}} + // expected-note@* {{previous definition is here}} + +//--- a.cc +module a; + +static_assert(x == 43, ""); + +constexpr int x = 1000; // expected-error {{redefinition of 'x'}} + // expected-note@* {{previous definition is here}} + diff --git a/clang/test/Modules/pr90154.cppm b/clang/test/Modules/pr90154.cppm new file mode 100644 index 0000000000000..d626646fbc488 --- /dev/null +++ b/clang/test/Modules/pr90154.cppm @@ -0,0 +1,25 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/use.cc -fmodule-file=a=%t/a.pcm -fsyntax-only -verify +// +// Test again with reduced BMI +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/use.cc -fmodule-file=a=%t/a.pcm -fsyntax-only -verify + +//--- a.cppm +export module a; +int b = 99; +namespace a { int a = 43; } + +//--- use.cc +// expected-no-diagnostics +import a; + +namespace a { + double a = 43.0; +} + +int b = 883;