Skip to content

Commit 7a73648

Browse files
benlangmuirakyrtzi
authored andcommitted
[clang][cas] Fall back to textual include for missing inferred submodules
When an inferred submodule is missing, because the umbrella header does not actually include it, we need to fall back to textual include. This was not working when included via PCH, because we were not serializing the inference flag(s) in the include-tree. rdar://107281193 (cherry picked from commit ecf7946)
1 parent 91b39ea commit 7a73648

File tree

5 files changed

+145
-14
lines changed

5 files changed

+145
-14
lines changed

clang/include/clang/CAS/IncludeTree.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,9 +398,13 @@ class IncludeTree::Module : public IncludeTreeBase<Module> {
398398
bool IsExplicit : 1;
399399
bool IsExternC : 1;
400400
bool IsSystem : 1;
401+
bool InferSubmodules : 1;
402+
bool InferExplicitSubmodules : 1;
403+
bool InferExportWildcard : 1;
401404
ModuleFlags()
402405
: IsFramework(false), IsExplicit(false), IsExternC(false),
403-
IsSystem(false) {}
406+
IsSystem(false), InferSubmodules(false),
407+
InferExplicitSubmodules(false), InferExportWildcard(false) {}
404408
};
405409

406410
ModuleFlags getFlags() const;
@@ -449,7 +453,7 @@ class IncludeTree::Module : public IncludeTreeBase<Module> {
449453
if (!IncludeTreeBase::isValid(Node))
450454
return false;
451455
IncludeTreeBase Base(Node);
452-
return Base.getData().size() > 1;
456+
return Base.getData().size() > 2;
453457
}
454458
static bool isValid(ObjectStore &DB, ObjectRef Ref) {
455459
auto Node = DB.getProxy(Ref);
@@ -461,8 +465,8 @@ class IncludeTree::Module : public IncludeTreeBase<Module> {
461465
}
462466

463467
private:
464-
char rawFlags() const { return getData()[0]; }
465-
StringRef dataAfterFlags() const { return getData().drop_front(); }
468+
uint16_t rawFlags() const;
469+
StringRef dataAfterFlags() const { return getData().drop_front(2); }
466470
bool hasExports() const;
467471
bool hasLinkLibraries() const;
468472
std::optional<unsigned> getExportsIndex() const;

clang/lib/CAS/IncludeTree.cpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -316,20 +316,26 @@ bool IncludeTree::FileList::isValid(const ObjectProxy &Node) {
316316
Data.size() == sizeof(uint32_t) + NumFiles * sizeof(FileSizeTy);
317317
}
318318

319-
static constexpr char ModuleFlagFramework = 1 << 0;
320-
static constexpr char ModuleFlagExplicit = 1 << 1;
321-
static constexpr char ModuleFlagExternC = 1 << 2;
322-
static constexpr char ModuleFlagSystem = 1 << 3;
323-
static constexpr char ModuleFlagHasExports = 1 << 4;
324-
static constexpr char ModuleFlagHasLinkLibraries = 1 << 5;
319+
static constexpr uint16_t ModuleFlagFramework = 1 << 0;
320+
static constexpr uint16_t ModuleFlagExplicit = 1 << 1;
321+
static constexpr uint16_t ModuleFlagExternC = 1 << 2;
322+
static constexpr uint16_t ModuleFlagSystem = 1 << 3;
323+
static constexpr uint16_t ModuleFlagInferSubmodules = 1 << 4;
324+
static constexpr uint16_t ModuleFlagInferExplicitSubmodules = 1 << 5;
325+
static constexpr uint16_t ModuleFlagInferInferExportWildcard = 1 << 6;
326+
static constexpr uint16_t ModuleFlagHasExports = 1 << 7;
327+
static constexpr uint16_t ModuleFlagHasLinkLibraries = 1 << 8;
325328

326329
IncludeTree::Module::ModuleFlags IncludeTree::Module::getFlags() const {
327-
char Raw = rawFlags();
330+
uint16_t Raw = rawFlags();
328331
ModuleFlags Flags;
329332
Flags.IsFramework = Raw & ModuleFlagFramework;
330333
Flags.IsExplicit = Raw & ModuleFlagExplicit;
331334
Flags.IsExternC = Raw & ModuleFlagExternC;
332335
Flags.IsSystem = Raw & ModuleFlagSystem;
336+
Flags.InferSubmodules = Raw & ModuleFlagInferSubmodules;
337+
Flags.InferExplicitSubmodules = Raw & ModuleFlagInferExplicitSubmodules;
338+
Flags.InferExportWildcard = Raw & ModuleFlagInferInferExportWildcard;
333339
return Flags;
334340
}
335341

@@ -362,14 +368,14 @@ IncludeTree::Module::create(ObjectStore &DB, StringRef ModuleName,
362368
std::optional<ObjectRef> ExportList,
363369
std::optional<ObjectRef> LinkLibraries) {
364370
// Data:
365-
// - 1 byte for Flags
371+
// - 2 bytes for Flags
366372
// - ModuleName (String)
367373
// Refs:
368374
// - Submodules (IncludeTreeModule)
369375
// - (optional) ExportList
370376
// - (optional) LinkLibaryList
371377

372-
char RawFlags = 0;
378+
uint16_t RawFlags = 0;
373379
if (Flags.IsFramework)
374380
RawFlags |= ModuleFlagFramework;
375381
if (Flags.IsExplicit)
@@ -378,13 +384,22 @@ IncludeTree::Module::create(ObjectStore &DB, StringRef ModuleName,
378384
RawFlags |= ModuleFlagExternC;
379385
if (Flags.IsSystem)
380386
RawFlags |= ModuleFlagSystem;
387+
if (Flags.InferSubmodules)
388+
RawFlags |= ModuleFlagInferSubmodules;
389+
if (Flags.InferExplicitSubmodules)
390+
RawFlags |= ModuleFlagInferExplicitSubmodules;
391+
if (Flags.InferExportWildcard)
392+
RawFlags |= ModuleFlagInferInferExportWildcard;
381393
if (ExportList)
382394
RawFlags |= ModuleFlagHasExports;
383395
if (LinkLibraries)
384396
RawFlags |= ModuleFlagHasLinkLibraries;
385397

386398
SmallString<64> Buffer;
387-
Buffer.push_back(RawFlags);
399+
llvm::raw_svector_ostream BufOS(Buffer);
400+
llvm::support::endian::Writer Writer(BufOS, llvm::support::little);
401+
Writer.write(RawFlags);
402+
388403
Buffer.append(ModuleName);
389404

390405
SmallVector<ObjectRef> Refs(Submodules);
@@ -396,6 +411,11 @@ IncludeTree::Module::create(ObjectStore &DB, StringRef ModuleName,
396411
return IncludeTreeBase::create(DB, Refs, Buffer);
397412
}
398413

414+
uint16_t IncludeTree::Module::rawFlags() const {
415+
return llvm::support::endian::read<uint16_t, llvm::support::little>(
416+
getData().data());
417+
}
418+
399419
bool IncludeTree::Module::hasExports() const {
400420
return rawFlags() & ModuleFlagHasExports;
401421
}
@@ -670,6 +690,15 @@ llvm::Error IncludeTree::Module::print(llvm::raw_ostream &OS, unsigned Indent) {
670690
if (Flags.IsSystem)
671691
OS << " (system)";
672692
OS << '\n';
693+
if (Flags.InferSubmodules) {
694+
if (Flags.InferExplicitSubmodules)
695+
OS << " explicit module *";
696+
else
697+
OS << " module *";
698+
if (Flags.InferExportWildcard)
699+
OS << " { export * }";
700+
OS << '\n';
701+
}
673702
auto ExportList = getExports();
674703
if (!ExportList)
675704
return ExportList.takeError();

clang/lib/Frontend/FrontendAction.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,9 @@ static Expected<Module *> makeIncludeTreeModule(CompilerInstance &CI,
559559
M->Kind = Module::IncludeTreeModuleMap;
560560
M->IsExternC = Flags.IsExternC;
561561
M->IsSystem = Flags.IsSystem;
562+
M->InferSubmodules = Flags.InferSubmodules;
563+
M->InferExplicitSubmodules = Flags.InferExplicitSubmodules;
564+
M->InferExportWildcard = Flags.InferExportWildcard;
562565

563566
auto ExportList = Mod.getExports();
564567
if (!ExportList)

clang/lib/Tooling/DependencyScanning/IncludeTreeActionController.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,9 @@ getIncludeTreeModule(cas::ObjectStore &DB, Module *M) {
517517
Flags.IsExplicit = M->IsExplicit;
518518
Flags.IsExternC = M->IsExternC;
519519
Flags.IsSystem = M->IsSystem;
520+
Flags.InferSubmodules = M->InferSubmodules;
521+
Flags.InferExplicitSubmodules = M->InferExplicitSubmodules;
522+
Flags.InferExportWildcard = M->InferExportWildcard;
520523

521524
bool GlobalWildcardExport = false;
522525
SmallVector<ITModule::ExportList::Export> Exports;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Ensure we fallback to textual inclusion for headers in incomplete umbrellas.
2+
3+
// REQUIRES: ondisk_cas
4+
5+
// RUN: rm -rf %t
6+
// RUN: split-file %s %t
7+
// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
8+
// RUN: sed "s|DIR|%/t|g" %t/cdb_pch.json.template > %t/cdb_pch.json
9+
10+
// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json \
11+
// RUN: -cas-path %t/cas -module-files-dir %t/outputs \
12+
// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \
13+
// RUN: > %t/deps_pch.json
14+
15+
// RUN: %deps-to-rsp %t/deps_pch.json --module-name Foo > %t/Foo.rsp
16+
// RUN: %deps-to-rsp %t/deps_pch.json --tu-index 0 > %t/pch.rsp
17+
// RUN: %clang @%t/Foo.rsp
18+
// RUN: %clang @%t/pch.rsp
19+
20+
// RUN: clang-scan-deps -compilation-database %t/cdb.json \
21+
// RUN: -cas-path %t/cas -module-files-dir %t/outputs \
22+
// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \
23+
// RUN: > %t/deps.json
24+
25+
// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp
26+
27+
// Extract include-tree casids
28+
// RUN: cat %t/Foo.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Foo.casid
29+
// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid
30+
31+
// RUN: echo "MODULE Foo" > %t/result.txt
32+
// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Foo.casid >> %t/result.txt
33+
// RUN: echo "TRANSLATION UNIT" >> %t/result.txt
34+
// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid >> %t/result.txt
35+
// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%/t
36+
37+
// CHECK-LABEL: MODULE Foo
38+
// CHECK: <module-includes> llvmcas://
39+
// CHECK: 1:1 <built-in> llvmcas://
40+
// CHECK: 2:1 [[PREFIX]]/Foo.framework/Headers/Foo.h llvmcas://
41+
// CHECK: Submodule: Foo
42+
// CHECK-NOT: Bar
43+
// CHECK: Module Map:
44+
// CHECK: Foo (framework)
45+
// CHECK-NOT: Bar
46+
// CHECK: module *
47+
// CHECK-NOT: Bar
48+
49+
// CHECK-LABEL: TRANSLATION UNIT
50+
// CHECK: (PCH) llvmcas://
51+
// CHECK: [[PREFIX]]/tu.c llvmcas://
52+
// CHECK: 1:1 <built-in> llvmcas://
53+
// CHECK: 2:1 [[PREFIX]]/Foo.framework/Headers/Bar.h llvmcas://
54+
55+
// RUN: %clang @%t/tu.rsp
56+
57+
//--- cdb_pch.json.template
58+
[{
59+
"file": "DIR/prefix.h",
60+
"directory": "DIR",
61+
"command": "clang -x c-header DIR/prefix.h -o DIR/prefix.h.pch -F DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache"
62+
}]
63+
64+
//--- cdb.json.template
65+
[{
66+
"file": "DIR/tu.c",
67+
"directory": "DIR",
68+
"command": "clang -fsyntax-only DIR/tu.c -include prefix.h -F DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache"
69+
}]
70+
71+
//--- Foo.framework/Modules/module.modulemap
72+
framework module Foo {
73+
umbrella header "Foo.h"
74+
module * { export * }
75+
}
76+
77+
//--- Foo.framework/Headers/Foo.h
78+
// Do not import Bar.h
79+
void foo(void);
80+
81+
//--- Foo.framework/Headers/Bar.h
82+
void bar(void);
83+
84+
//--- prefix.h
85+
#include <Foo/Foo.h>
86+
87+
//--- tu.c
88+
#include <Foo/Bar.h>
89+
// FIXME: -Wincomplete-umbrella warning
90+
void tu(void) {
91+
bar();
92+
}

0 commit comments

Comments
 (0)