Skip to content

Commit b230339

Browse files
authored
[clang][include-tree] Load even spurious modular dependencies (#8011)
With implicit modules, some modular dependencies can be "spurious". By that I mean that `#include "Header.h"` results in a PCM file load, but the header ends up being included textually anyways. (This can happen in situations that would trigger the `-Wincomplete-umbrella` warning.) This patch ensures this behavior is preserved with include-tree. Previously, include-tree would simply textually include the header file and skip the PCM file load. This now ensures consistent behavior in edge-cases like the one in the attached test case (with `-fmodule-name=X`). rdar://121220983
1 parent ce1677f commit b230339

File tree

8 files changed

+315
-68
lines changed

8 files changed

+315
-68
lines changed

clang/include/clang/CAS/IncludeTree.h

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class IncludeTree : public IncludeTreeBase<IncludeTree> {
5656
class Node;
5757
class Module;
5858
class ModuleImport;
59+
class SpuriousImport;
5960
class ModuleMap;
6061
class APINotes;
6162

@@ -106,6 +107,7 @@ class IncludeTree : public IncludeTreeBase<IncludeTree> {
106107
enum class NodeKind : uint8_t {
107108
Tree,
108109
ModuleImport,
110+
SpuriousImport,
109111
};
110112

111113
/// The kind of node included at the given index.
@@ -337,7 +339,8 @@ class IncludeTree::ModuleImport : public IncludeTreeBase<ModuleImport> {
337339
/// Whether this module should only be "marked visible" rather than imported.
338340
bool visibilityOnly() const { return (bool)getData()[0]; }
339341

340-
llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0);
342+
llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0,
343+
char End = '\n');
341344

342345
static bool isValid(const ObjectProxy &Node) {
343346
if (!IncludeTreeBase::isValid(Node))
@@ -356,13 +359,66 @@ class IncludeTree::ModuleImport : public IncludeTreeBase<ModuleImport> {
356359

357360
private:
358361
friend class IncludeTreeBase<ModuleImport>;
362+
friend class SpuriousImport;
359363
friend class Node;
360364

361365
explicit ModuleImport(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) {
362366
assert(isValid(*this));
363367
}
364368
};
365369

370+
class IncludeTree::SpuriousImport
371+
: public IncludeTreeBase<SpuriousImport> {
372+
public:
373+
static Expected<SpuriousImport>
374+
create(ObjectStore &DB, ObjectRef ImportRef, ObjectRef TreeRef);
375+
376+
static constexpr StringRef getNodeKind() { return "SpIm"; }
377+
378+
Expected<ModuleImport> getModuleImport() {
379+
std::optional<ObjectProxy> Proxy;
380+
if (llvm::Error Err = getCAS().getProxy(getReference(0)).moveInto(Proxy))
381+
return Err;
382+
return ModuleImport(*Proxy);
383+
}
384+
385+
Expected<IncludeTree> getIncludeTree() {
386+
std::optional<ObjectProxy> Proxy;
387+
if (llvm::Error Err = getCAS().getProxy(getReference(1)).moveInto(Proxy))
388+
return Err;
389+
return IncludeTree(*Proxy);
390+
}
391+
392+
llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0);
393+
394+
static bool isValid(const ObjectProxy &Node) {
395+
if (!IncludeTreeBase::isValid(Node))
396+
return false;
397+
IncludeTreeBase Base(Node);
398+
if (Base.getNumReferences() != 2 && Base.getData().size() != 0)
399+
return false;
400+
return ModuleImport::isValid(Base.getCAS(), Base.getReference(0)) &&
401+
IncludeTree::isValid(Base.getCAS(), Base.getReference(1));
402+
}
403+
404+
static bool isValid(ObjectStore &DB, ObjectRef Ref) {
405+
auto Node = DB.getProxy(Ref);
406+
if (!Node) {
407+
llvm::consumeError(Node.takeError());
408+
return false;
409+
}
410+
return isValid(*Node);
411+
}
412+
413+
private:
414+
friend class IncludeTreeBase;
415+
friend class Node;
416+
417+
explicit SpuriousImport(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) {
418+
assert(isValid(*this));
419+
}
420+
};
421+
366422
/// Represents an \c IncludeTree or \c ModuleImport.
367423
class IncludeTree::Node {
368424
public:
@@ -374,6 +430,10 @@ class IncludeTree::Node {
374430
assert(K == NodeKind::ModuleImport);
375431
return ModuleImport(N);
376432
}
433+
SpuriousImport getSpuriousImport() const {
434+
assert(K == NodeKind::SpuriousImport);
435+
return SpuriousImport(N);
436+
}
377437
NodeKind getKind() const { return K; }
378438

379439
llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0);

clang/include/clang/Lex/PPCachedActions.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ class PPCachedActions {
4343
// Whether this module should only be "marked visible" rather than imported.
4444
bool VisibilityOnly;
4545
};
46+
/// The module that is loaded and discarded for an \c #include directive, and
47+
/// the file that is included instead.
48+
struct SpuriousImport {
49+
IncludeModule IM;
50+
IncludeFile IF;
51+
};
4652

4753
virtual ~PPCachedActions() = default;
4854

@@ -56,7 +62,8 @@ class PPCachedActions {
5662
/// \returns the file that should be entered or module that should be imported
5763
/// for an \c #include directive. \c {} indicates that the directive
5864
/// should be skipped.
59-
virtual std::variant<std::monostate, IncludeFile, IncludeModule>
65+
virtual std::variant<std::monostate, IncludeFile, IncludeModule,
66+
SpuriousImport>
6067
handleIncludeDirective(Preprocessor &PP, SourceLocation IncludeLoc,
6168
SourceLocation AfterDirectiveLoc) = 0;
6269

clang/lib/CAS/IncludeTree.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ Expected<NodeT> IncludeTreeBase<NodeT>::create(ObjectStore &DB,
3636
return NodeT(*Proxy);
3737
}
3838

39+
Expected<IncludeTree::SpuriousImport>
40+
IncludeTree::SpuriousImport::create(ObjectStore &DB, ObjectRef ImportRef,
41+
ObjectRef TreeRef) {
42+
return IncludeTreeBase::create(DB, {ImportRef, TreeRef}, {});
43+
}
44+
3945
Expected<IncludeTree::File> IncludeTree::File::create(ObjectStore &DB,
4046
StringRef Filename,
4147
ObjectRef Contents) {
@@ -138,7 +144,9 @@ Expected<IncludeTree> IncludeTree::create(
138144
assert((Include.Kind == NodeKind::Tree &&
139145
IncludeTree::isValid(DB, Include.Ref)) ||
140146
(Include.Kind == NodeKind::ModuleImport &&
141-
ModuleImport::isValid(DB, Include.Ref)));
147+
ModuleImport::isValid(DB, Include.Ref)) ||
148+
(Include.Kind == NodeKind::SpuriousImport &&
149+
SpuriousImport::isValid(DB, Include.Ref)));
142150
Refs.push_back(Include.Ref);
143151
Writer.write(Include.Offset);
144152
static_assert(sizeof(uint8_t) == sizeof(Kind));
@@ -671,12 +679,28 @@ llvm::Error IncludeTree::FileList::print(llvm::raw_ostream &OS,
671679
}
672680

673681
llvm::Error IncludeTree::ModuleImport::print(llvm::raw_ostream &OS,
674-
unsigned Indent) {
682+
unsigned Indent, char End) {
675683
if (visibilityOnly())
676684
OS << "(Module for visibility only) ";
677685
else
678686
OS << "(Module) ";
679-
OS << getModuleName() << '\n';
687+
OS << getModuleName() << End;
688+
return llvm::Error::success();
689+
}
690+
691+
llvm::Error IncludeTree::SpuriousImport::print(llvm::raw_ostream &OS,
692+
unsigned Indent) {
693+
auto MI = getModuleImport();
694+
if (!MI)
695+
return MI.takeError();
696+
auto IT = getIncludeTree();
697+
if (!IT)
698+
return IT.takeError();
699+
OS << "(Spurious import) ";
700+
if (llvm::Error E = MI->print(OS, Indent, /*End=*/' '))
701+
return E;
702+
if (llvm::Error E = IT->print(OS, Indent))
703+
return E;
680704
return llvm::Error::success();
681705
}
682706

@@ -686,6 +710,8 @@ llvm::Error IncludeTree::Node::print(llvm::raw_ostream &OS, unsigned Indent) {
686710
return getIncludeTree().print(OS, Indent);
687711
case NodeKind::ModuleImport:
688712
return getModuleImport().print(OS, Indent);
713+
case NodeKind::SpuriousImport:
714+
return getSpuriousImport().print(OS, Indent);
689715
}
690716
}
691717

clang/lib/Frontend/IncludeTreePPActions.cpp

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class IncludeTreePPActions final : public PPCachedActions {
8484
return IncludeInfo.Tree.getCheckResult(Index);
8585
}
8686

87-
std::variant<std::monostate, IncludeFile, IncludeModule>
87+
std::variant<std::monostate, IncludeFile, IncludeModule, SpuriousImport>
8888
handleIncludeDirective(Preprocessor &PP, SourceLocation IncludeLoc,
8989
SourceLocation AfterDirectiveLoc) override {
9090
if (HasCASErrorOccurred)
@@ -116,65 +116,89 @@ class IncludeTreePPActions final : public PPCachedActions {
116116
if (!Node)
117117
return reportError(Node.takeError());
118118

119-
if (Node->getKind() == cas::IncludeTree::NodeKind::ModuleImport) {
120-
cas::IncludeTree::ModuleImport Import = Node->getModuleImport();
119+
auto MakeModuleImport = [&](cas::IncludeTree::ModuleImport Import) {
121120
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
122121
SmallVector<StringRef, 2> ModuleComponents;
123122
Import.getModuleName().split(ModuleComponents, '.');
124123
for (StringRef Component : ModuleComponents)
125124
Path.emplace_back(PP.getIdentifierInfo(Component), IncludeLoc);
126125
return IncludeModule{std::move(Path), Import.visibilityOnly()};
127-
}
128-
129-
assert(Node->getKind() == cas::IncludeTree::NodeKind::Tree);
130-
131-
cas::IncludeTree EnteredTree = Node->getIncludeTree();
132-
auto File = EnteredTree.getBaseFile();
133-
if (!File)
134-
return reportError(File.takeError());
135-
auto FilenameBlob = File->getFilename();
136-
if (!FilenameBlob)
137-
return reportError(FilenameBlob.takeError());
126+
};
138127

139-
SourceManager &SM = PP.getSourceManager();
140-
Expected<FileEntryRef> FE =
141-
SM.getFileManager().getFileRef(FilenameBlob->getData(),
142-
/*OpenFile=*/true);
143-
if (!FE)
144-
return reportError(FE.takeError());
145-
FileID FID =
146-
SM.createFileID(*FE, IncludeLoc, EnteredTree.getFileCharacteristic());
147-
PP.markIncluded(*FE);
148-
IncludeStack.push_back(
149-
{std::move(EnteredTree), SM.getLocForStartOfFile(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);
128+
auto MakeIncludeTree = [&](cas::IncludeTree EnteredTree)
129+
-> std::variant<std::monostate, IncludeFile> {
130+
auto File = EnteredTree.getBaseFile();
131+
if (!File)
132+
return reportError(File.takeError());
133+
auto FilenameBlob = File->getFilename();
134+
if (!FilenameBlob)
135+
return reportError(FilenameBlob.takeError());
136+
137+
SourceManager &SM = PP.getSourceManager();
138+
Expected<FileEntryRef> FE =
139+
SM.getFileManager().getFileRef(FilenameBlob->getData(),
140+
/*OpenFile=*/true);
141+
if (!FE)
142+
return reportError(FE.takeError());
143+
FileID FID =
144+
SM.createFileID(*FE, IncludeLoc, EnteredTree.getFileCharacteristic());
145+
PP.markIncluded(*FE);
146+
IncludeStack.push_back(
147+
{std::move(EnteredTree), SM.getLocForStartOfFile(FID)});
148+
149+
Module *M = nullptr;
150+
auto SubmoduleName = EnteredTree.getSubmoduleName();
151+
if (!SubmoduleName)
152+
return reportError(SubmoduleName.takeError());
153+
if (*SubmoduleName) {
154+
SmallVector<StringRef> ModuleComponents;
155+
(*SubmoduleName)->split(ModuleComponents, '.');
156+
M = PP.getHeaderSearchInfo().lookupModule(
157+
ModuleComponents[0], IncludeLoc,
158+
/*AllowSearch=*/false, /*AllowExtraModuleMapSearch=*/false);
166159
if (!M)
167-
return reportErrorTwine(
168-
llvm::Twine("failed to find or infer submodule '") + Sub + "'");
160+
return reportErrorTwine(llvm::Twine("failed to find module '") +
161+
ModuleComponents[0] + "'");
162+
for (StringRef Sub : ArrayRef(ModuleComponents).drop_front()) {
163+
M = M->findOrInferSubmodule(Sub);
164+
if (!M)
165+
return reportErrorTwine(
166+
llvm::Twine("failed to find or infer submodule '") + Sub + "'");
167+
}
168+
169+
// Add to known headers for the module.
170+
ModuleMap &MMap = PP.getHeaderSearchInfo().getModuleMap();
171+
Module::Header H{"", "", *FE};
172+
MMap.addHeader(M, std::move(H), ModuleMap::NormalHeader);
169173
}
170174

171-
// Add to known headers for the module.
172-
ModuleMap &MMap = PP.getHeaderSearchInfo().getModuleMap();
173-
Module::Header H{"", "", *FE};
174-
MMap.addHeader(M, std::move(H), ModuleMap::NormalHeader);
175-
}
175+
return IncludeFile{FID, M};
176+
};
176177

177-
return IncludeFile{FID, M};
178+
switch (Node->getKind()) {
179+
case cas::IncludeTree::NodeKind::ModuleImport:
180+
return MakeModuleImport(Node->getModuleImport());
181+
case cas::IncludeTree::NodeKind::Tree: {
182+
auto IncludeTree = MakeIncludeTree(Node->getIncludeTree());
183+
if (std::holds_alternative<std::monostate>(IncludeTree))
184+
return std::monostate{};
185+
return std::get<IncludeFile>(IncludeTree);
186+
}
187+
case cas::IncludeTree::NodeKind::SpuriousImport: {
188+
auto SpuriousImportNode = Node->getSpuriousImport();
189+
auto ModuleImportNode = SpuriousImportNode.getModuleImport();
190+
if (!ModuleImportNode)
191+
return reportError(ModuleImportNode.takeError());
192+
auto IncludeTreeNode = SpuriousImportNode.getIncludeTree();
193+
if (!IncludeTreeNode)
194+
return reportError(IncludeTreeNode.takeError());
195+
auto ModuleImport = MakeModuleImport(*ModuleImportNode);
196+
auto IncludeTree = MakeIncludeTree(*IncludeTreeNode);
197+
if (std::holds_alternative<std::monostate>(IncludeTree))
198+
return std::monostate{};
199+
return SpuriousImport{ModuleImport, std::get<IncludeFile>(IncludeTree)};
200+
}
201+
}
178202
}
179203

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

0 commit comments

Comments
 (0)