Skip to content

Commit bd61c2c

Browse files
committed
[clang][cas] include-tree support for submodule visibility
Track the submodule of each modular header and enter the submodule via PPCachedActions. This fixes declaration and macro header visibility.
1 parent 55b697d commit bd61c2c

File tree

7 files changed

+281
-21
lines changed

7 files changed

+281
-21
lines changed

clang/include/clang/CAS/IncludeTree.h

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,31 @@ class IncludeTree : public IncludeTreeBase<IncludeTree> {
6969
Expected<FileInfo> getBaseFileInfo();
7070

7171
SrcMgr::CharacteristicKind getFileCharacteristic() const {
72-
return (SrcMgr::CharacteristicKind)dataSkippingIncludes().front();
72+
return (SrcMgr::CharacteristicKind)(getData().front() & ~IsSubmoduleBit);
7373
}
7474

75-
size_t getNumIncludes() const { return getNumReferences() - 1; }
75+
bool isSubmodule() const { return (getData().front() & IsSubmoduleBit) != 0; }
76+
77+
std::optional<ObjectRef> getSubmoduleNameRef() const {
78+
if (isSubmodule())
79+
return getReference(getNumReferences() - 1);
80+
return std::nullopt;
81+
}
82+
83+
/// If \c getBaseFile() is a modular header, get its submodule name.
84+
Expected<std::optional<StringRef>> getSubmoduleName() {
85+
auto Ref = getSubmoduleNameRef();
86+
if (!Ref)
87+
return std::nullopt;
88+
auto Node = getCAS().getProxy(*Ref);
89+
if (!Node)
90+
return Node.takeError();
91+
return Node->getData();
92+
}
93+
94+
size_t getNumIncludes() const {
95+
return getNumReferences() - (isSubmodule() ? 2 : 1);
96+
};
7697

7798
ObjectRef getIncludeRef(size_t I) const {
7899
assert(I < getNumIncludes());
@@ -131,7 +152,7 @@ class IncludeTree : public IncludeTreeBase<IncludeTree> {
131152
static Expected<IncludeTree>
132153
create(ObjectStore &DB, SrcMgr::CharacteristicKind FileCharacteristic,
133154
ObjectRef BaseFile, ArrayRef<IncludeInfo> Includes,
134-
llvm::SmallBitVector Checks);
155+
std::optional<ObjectRef> SubmoduleName, llvm::SmallBitVector Checks);
135156

136157
static Expected<IncludeTree> get(ObjectStore &DB, ObjectRef Ref);
137158

@@ -141,6 +162,8 @@ class IncludeTree : public IncludeTreeBase<IncludeTree> {
141162
friend class IncludeTreeBase<IncludeTree>;
142163
friend class IncludeTreeRoot;
143164

165+
static constexpr char IsSubmoduleBit = 0x80;
166+
144167
explicit IncludeTree(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) {
145168
assert(isValid(*this));
146169
}
@@ -154,8 +177,10 @@ class IncludeTree : public IncludeTreeBase<IncludeTree> {
154177

155178
Expected<Node> getIncludeNode(ObjectRef Ref, NodeKind Kind);
156179

180+
StringRef dataSkippingFlags() const { return getData().drop_front(); }
157181
StringRef dataSkippingIncludes() const {
158-
return getData().drop_front(getNumIncludes() * (sizeof(uint32_t) + 1));
182+
return dataSkippingFlags().drop_front(getNumIncludes() *
183+
(sizeof(uint32_t) + 1));
159184
}
160185

161186
static bool isValid(const ObjectProxy &Node);

clang/include/clang/Lex/PPCachedActions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
namespace clang {
2121

2222
class IdentifierInfo;
23+
class Module;
2324
class Preprocessor;
2425

2526
/// This interface provides a way to override the actions of the preprocessor as
@@ -33,6 +34,7 @@ class PPCachedActions {
3334
/// The file that is included by an \c #include directive.
3435
struct IncludeFile {
3536
FileID FID;
37+
Module *Submodule;
3638
};
3739
/// The module that is imported by an \c #include directive or \c @import.
3840
struct IncludeModule {

clang/lib/CAS/IncludeTree.cpp

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,15 @@ Expected<IncludeTree::FileInfo> IncludeTree::getBaseFileInfo() {
6060
llvm::Error IncludeTree::forEachInclude(
6161
llvm::function_ref<llvm::Error(std::pair<Node, uint32_t>)> Callback) {
6262
size_t RefI = 0;
63+
const size_t IncludeEnd = getNumIncludes();
6364
return forEachReference([&](ObjectRef Ref) -> llvm::Error {
6465
if (RefI == 0) {
6566
++RefI;
6667
return llvm::Error::success();
6768
}
6869
size_t IncludeI = RefI - 1;
70+
if (IncludeI >= IncludeEnd)
71+
return llvm::Error::success();
6972
++RefI;
7073
auto Include = getIncludeNode(Ref, getIncludeKind(IncludeI));
7174
if (!Include)
@@ -74,25 +77,30 @@ llvm::Error IncludeTree::forEachInclude(
7477
});
7578
}
7679

77-
Expected<IncludeTree>
78-
IncludeTree::create(ObjectStore &DB,
79-
SrcMgr::CharacteristicKind FileCharacteristic,
80-
ObjectRef BaseFile, ArrayRef<IncludeInfo> Includes,
81-
llvm::SmallBitVector Checks) {
80+
Expected<IncludeTree> IncludeTree::create(
81+
ObjectStore &DB, SrcMgr::CharacteristicKind FileCharacteristic,
82+
ObjectRef BaseFile, ArrayRef<IncludeInfo> Includes,
83+
std::optional<ObjectRef> SubmoduleName, llvm::SmallBitVector Checks) {
8284
// The data buffer is composed of
83-
// 1. `uint32_t` offset and `uint8_t` kind for each includes
84-
// 2. 1 byte for `CharacteristicKind`
85+
// 1. 1 byte for `CharacteristicKind` and IsSubmodule
86+
// 2. `uint32_t` offset and `uint8_t` kind for each includes
8587
// 3. variable number of bitset bytes for `Checks`.
8688

8789
char Kind = FileCharacteristic;
88-
assert(Kind == FileCharacteristic && "SrcMgr::CharacteristicKind too big!");
90+
assert(Kind == FileCharacteristic && (Kind & IsSubmoduleBit) == 0 &&
91+
"SrcMgr::CharacteristicKind too big!");
92+
if (SubmoduleName)
93+
Kind |= IsSubmoduleBit;
94+
8995
assert(File::isValid(DB, BaseFile));
9096
SmallVector<ObjectRef, 16> Refs;
91-
Refs.reserve(Includes.size() + 1);
97+
Refs.reserve(Includes.size() + 2);
9298
Refs.push_back(BaseFile);
9399
SmallString<64> Buffer;
94100
Buffer.reserve(Includes.size() * sizeof(uint32_t) + 1);
95101

102+
Buffer += Kind;
103+
96104
llvm::raw_svector_ostream BufOS(Buffer);
97105
llvm::support::endian::Writer Writer(BufOS, llvm::support::little);
98106

@@ -107,7 +115,8 @@ IncludeTree::create(ObjectStore &DB,
107115
Writer.write(static_cast<uint8_t>(Include.Kind));
108116
}
109117

110-
Buffer += Kind;
118+
if (SubmoduleName)
119+
Refs.push_back(*SubmoduleName);
111120

112121
uintptr_t Store;
113122
ArrayRef<uintptr_t> BitWords = Checks.getData(Store);
@@ -146,15 +155,15 @@ Expected<IncludeTree> IncludeTree::get(ObjectStore &DB, ObjectRef Ref) {
146155

147156
IncludeTree::NodeKind IncludeTree::getIncludeKind(size_t I) const {
148157
assert(I < getNumIncludes());
149-
StringRef Data = getData();
158+
StringRef Data = dataSkippingFlags();
150159
assert(Data.size() >= (I + 1) * (sizeof(uint32_t) + 1));
151160
uint8_t K = *(Data.data() + I * (sizeof(uint32_t) + 1) + sizeof(uint32_t));
152161
return NodeKind(K);
153162
}
154163

155164
uint32_t IncludeTree::getIncludeOffset(size_t I) const {
156165
assert(I < getNumIncludes());
157-
StringRef Data = getData();
166+
StringRef Data = dataSkippingFlags();
158167
assert(Data.size() >= (I + 1) * sizeof(uint32_t));
159168
uint32_t Offset =
160169
llvm::support::endian::read<uint32_t, llvm::support::little>(
@@ -164,7 +173,7 @@ uint32_t IncludeTree::getIncludeOffset(size_t I) const {
164173

165174
bool IncludeTree::getCheckResult(size_t I) const {
166175
// Skip include offsets and CharacteristicKind.
167-
StringRef Data = dataSkippingIncludes().drop_front();
176+
StringRef Data = dataSkippingIncludes();
168177
unsigned ByteIndex = I / CHAR_BIT;
169178
size_t RemainingIndex = I % CHAR_BIT;
170179
uint8_t Bits = Data[ByteIndex];
@@ -187,9 +196,11 @@ bool IncludeTree::isValid(const ObjectProxy &Node) {
187196
if (!IncludeTreeBase::isValid(Node))
188197
return false;
189198
IncludeTreeBase Base(Node);
190-
if (Base.getNumReferences() == 0)
199+
if (Base.getNumReferences() == 0 || Base.getData().empty())
191200
return false;
192201
unsigned NumIncludes = Base.getNumReferences() - 1;
202+
if (Base.getData().front() & IsSubmoduleBit)
203+
NumIncludes -= 1;
193204
return Base.getData().size() >= NumIncludes * (sizeof(uint32_t) + 1) + 1;
194205
}
195206

@@ -321,6 +332,12 @@ llvm::Error IncludeTree::print(llvm::raw_ostream &OS, unsigned Indent) {
321332
if (llvm::Error E = IncludeBy->print(OS))
322333
return E;
323334

335+
auto Submodule = getSubmoduleName();
336+
if (!Submodule)
337+
return Submodule.takeError();
338+
if (*Submodule)
339+
OS.indent(Indent) << "Submodule: " << **Submodule << '\n';
340+
324341
llvm::SourceMgr SM;
325342
auto Blob = IncludeBy->getContents();
326343
if (!Blob)

clang/lib/Frontend/IncludeTreePPActions.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ class IncludeTreePPActions final : public PPCachedActions {
106106
return {};
107107
};
108108

109+
auto reportErrorTwine = [&](const llvm::Twine &T) -> std::monostate {
110+
return reportError(
111+
llvm::createStringError(llvm::inconvertibleErrorCode(), T));
112+
};
113+
109114
Expected<cas::IncludeTree::Node> Node =
110115
IncludeInfo.Tree.getIncludeNode(IncludeInfo.CurIncludeIndex++);
111116
if (!Node)
@@ -142,7 +147,29 @@ class IncludeTreePPActions final : public PPCachedActions {
142147
PP.markIncluded(*FE);
143148
IncludeStack.push_back(
144149
{std::move(EnteredTree), SM.getLocForStartOfFile(FID)});
145-
return IncludeFile{FID};
150+
151+
Module *M = nullptr;
152+
auto SubmoduleName = EnteredTree.getSubmoduleName();
153+
if (!SubmoduleName)
154+
return reportError(SubmoduleName.takeError());
155+
if (*SubmoduleName) {
156+
SmallVector<StringRef> ModuleComponents;
157+
(*SubmoduleName)->split(ModuleComponents, '.');
158+
M = PP.getHeaderSearchInfo().lookupModule(
159+
ModuleComponents[0], IncludeLoc,
160+
/*AllowSearch=*/false, /*AllowExtraModuleMapSearch=*/false);
161+
if (!M)
162+
return reportErrorTwine(llvm::Twine("failed to find module '") +
163+
ModuleComponents[0] + "'");
164+
for (StringRef Sub : ArrayRef(ModuleComponents).drop_front()) {
165+
M = M->findOrInferSubmodule(Sub);
166+
if (!M)
167+
return reportErrorTwine(
168+
llvm::Twine("failed to find or infer submodule '") + Sub + "'");
169+
}
170+
}
171+
172+
return IncludeFile{FID, M};
146173
}
147174

148175
void exitedFile(Preprocessor &PP, FileID FID) override {

clang/lib/Lex/PPDirectives.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,6 +2081,15 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
20812081
}
20822082
EnterSourceFile(File->FID, nullptr, FilenameTok.getLocation(),
20832083
/*IsFirstIncludeOfFile*/ true);
2084+
2085+
if (Module *SM = File->Submodule) {
2086+
assert(!CurLexerSubmodule &&
2087+
"should not have marked this as a module yet");
2088+
CurLexerSubmodule = SM;
2089+
EnterSubmodule(SM, EndLoc, /*ForPragma=*/false);
2090+
EnterAnnotationToken(SourceRange(HashLoc, EndLoc),
2091+
tok::annot_module_begin, SM);
2092+
}
20842093
return;
20852094
}
20862095
if (auto *Import = std::get_if<PPCachedActions::IncludeModule>(&Include)) {

clang/lib/Tooling/DependencyScanning/IncludeTreeActionController.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,17 @@ class IncludeTreeBuilder {
8585

8686
void moduleImport(Preprocessor &PP, const Module *M, SourceLocation EndLoc);
8787

88+
void enteredSubmodule(Preprocessor &PP, Module *M, SourceLocation ImportLoc,
89+
bool ForPragma);
90+
void exitedSubmodule(Preprocessor &PP, Module *M, SourceLocation ImportLoc,
91+
bool ForPragma);
92+
8893
private:
8994
struct FilePPState {
9095
SrcMgr::CharacteristicKind FileCharacteristic;
9196
cas::ObjectRef File;
9297
SmallVector<cas::IncludeTree::IncludeInfo, 6> Includes;
98+
std::optional<cas::ObjectRef> SubmoduleName;
9399
llvm::SmallBitVector HasIncludeChecks;
94100
};
95101

@@ -201,6 +207,15 @@ struct IncludeTreePPCallbacks : public PPCallbacks {
201207

202208
Builder.moduleImport(PP, Imported, EndLoc);
203209
}
210+
211+
void EnteredSubmodule(Module *M, SourceLocation ImportLoc,
212+
bool ForPragma) override {
213+
Builder.enteredSubmodule(PP, M, ImportLoc, ForPragma);
214+
}
215+
void LeftSubmodule(Module *M, SourceLocation ImportLoc,
216+
bool ForPragma) override {
217+
Builder.exitedSubmodule(PP, M, ImportLoc, ForPragma);
218+
}
204219
};
205220
} // namespace
206221

@@ -354,7 +369,7 @@ void IncludeTreeBuilder::enteredInclude(Preprocessor &PP, FileID FID) {
354369
return;
355370
const SrcMgr::FileInfo &FI =
356371
PP.getSourceManager().getSLocEntry(FID).getFile();
357-
IncludeStack.push_back({FI.getFileCharacteristic(), *FileRef, {}, {}});
372+
IncludeStack.push_back({FI.getFileCharacteristic(), *FileRef, {}, {}, {}});
358373
}
359374

360375
void IncludeTreeBuilder::exitedInclude(Preprocessor &PP, FileID IncludedBy,
@@ -395,6 +410,23 @@ void IncludeTreeBuilder::moduleImport(Preprocessor &PP, const Module *M,
395410
cas::IncludeTree::NodeKind::ModuleImport});
396411
}
397412

413+
void IncludeTreeBuilder::enteredSubmodule(Preprocessor &PP, Module *M,
414+
SourceLocation ImportLoc,
415+
bool ForPragma) {
416+
if (ForPragma)
417+
return; // Will be parsed as normal.
418+
if (hasErrorOccurred())
419+
return;
420+
assert(!IncludeStack.back().SubmoduleName && "repeated enteredSubmodule");
421+
auto Ref = check(DB.storeFromString({}, M->getFullModuleName()));
422+
IncludeStack.back().SubmoduleName = Ref;
423+
}
424+
void IncludeTreeBuilder::exitedSubmodule(Preprocessor &PP, Module *M,
425+
SourceLocation ImportLoc,
426+
bool ForPragma) {
427+
// Submodule exit is handled automatically when leaving a modular file.
428+
}
429+
398430
Expected<cas::IncludeTreeRoot>
399431
IncludeTreeBuilder::finishIncludeTree(CompilerInstance &ScanInstance,
400432
CompilerInvocation &NewInvocation) {
@@ -615,7 +647,8 @@ IncludeTreeBuilder::addToFileList(FileManager &FM, const FileEntry *FE) {
615647
Expected<cas::IncludeTree>
616648
IncludeTreeBuilder::getCASTreeForFileIncludes(FilePPState &&PPState) {
617649
return cas::IncludeTree::create(DB, PPState.FileCharacteristic, PPState.File,
618-
PPState.Includes, PPState.HasIncludeChecks);
650+
PPState.Includes, PPState.SubmoduleName,
651+
PPState.HasIncludeChecks);
619652
}
620653

621654
Expected<cas::IncludeTree::File>

0 commit comments

Comments
 (0)