Skip to content

Commit ed84df0

Browse files
committed
[Modules] Add 'no_undeclared_includes' module map attribute
The 'no_undeclared_includes' attribute should be used in a module to tell that only non-modular headers and headers from used modules are accepted. The main motivation behind this is to prevent dep cycles between system libraries (such as darwin) and libc++. Patch by Richard Smith! llvm-svn: 284797
1 parent d15477b commit ed84df0

28 files changed

+196
-27
lines changed

clang/docs/Modules.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ The ``system`` attribute specifies that the module is a system module. When a sy
365365

366366
The ``extern_c`` attribute specifies that the module contains C code that can be used from within C++. When such a module is built for use in C++ code, all of the module's headers will be treated as if they were contained within an implicit ``extern "C"`` block. An import for a module with this attribute can appear within an ``extern "C"`` block. No other restrictions are lifted, however: the module currently cannot be imported within an ``extern "C"`` block in a namespace.
367367

368+
The ``no_undeclared_includes`` attribute specifies that the module can only reach non-modular headers and headers from used modules. Since some headers could be present in more than one search path and map to different modules in each path, this mechanism helps clang to find the right header, i.e., prefer the one for the current module or in a submodule instead of the first usual match in the search paths.
369+
368370
Modules can have a number of different kinds of members, each of which is described below:
369371

370372
.. parsed-literal::

clang/include/clang/Basic/Module.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ class Module {
201201
/// built.
202202
unsigned ConfigMacrosExhaustive : 1;
203203

204+
/// \brief Whether files in this module can only include non-modular headers
205+
/// and headers from used modules.
206+
unsigned NoUndeclaredIncludes : 1;
207+
204208
/// \brief Describes the visibility of the various names within a
205209
/// particular module.
206210
enum NameVisibilityKind {

clang/include/clang/Lex/HeaderSearch.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,10 @@ class HeaderSearch {
523523
/// \brief Retrieve the module that corresponds to the given file, if any.
524524
///
525525
/// \param File The header that we wish to map to a module.
526-
ModuleMap::KnownHeader findModuleForHeader(const FileEntry *File) const;
527-
526+
/// \param AllowTextual Whether we want to find textual headers too.
527+
ModuleMap::KnownHeader findModuleForHeader(const FileEntry *File,
528+
bool AllowTextual = false) const;
529+
528530
/// \brief Read the contents of the given module map file.
529531
///
530532
/// \param File The module map file.

clang/include/clang/Lex/ModuleMap.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ class ModuleMap {
171171

172172
/// \brief The set of attributes that can be attached to a module.
173173
struct Attributes {
174-
Attributes() : IsSystem(), IsExternC(), IsExhaustive() {}
174+
Attributes()
175+
: IsSystem(), IsExternC(), IsExhaustive(), NoUndeclaredIncludes() {}
175176

176177
/// \brief Whether this is a system module.
177178
unsigned IsSystem : 1;
@@ -181,6 +182,10 @@ class ModuleMap {
181182

182183
/// \brief Whether this is an exhaustive set of configuration macros.
183184
unsigned IsExhaustive : 1;
185+
186+
/// \brief Whether files in this module can only include non-modular headers
187+
/// and headers from used modules.
188+
unsigned NoUndeclaredIncludes : 1;
184189
};
185190

186191
/// \brief A directory for which framework modules can be inferred.
@@ -315,10 +320,15 @@ class ModuleMap {
315320
///
316321
/// \param File The header file that is likely to be included.
317322
///
323+
/// \param AllowTextual If \c true and \p File is a textual header, return
324+
/// its owning module. Otherwise, no KnownHeader will be returned if the
325+
/// file is only known as a textual header.
326+
///
318327
/// \returns The module KnownHeader, which provides the module that owns the
319328
/// given header file. The KnownHeader is default constructed to indicate
320329
/// that no module owns this header file.
321-
KnownHeader findModuleForHeader(const FileEntry *File);
330+
KnownHeader findModuleForHeader(const FileEntry *File,
331+
bool AllowTextual = false);
322332

323333
/// \brief Retrieve all the modules that contain the given header file. This
324334
/// may not include umbrella modules, nor information from external sources,

clang/lib/Basic/Module.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,16 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
3333
IsExplicit(IsExplicit), IsSystem(false), IsExternC(false),
3434
IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false),
3535
InferExportWildcard(false), ConfigMacrosExhaustive(false),
36-
NameVisibility(Hidden) {
36+
NoUndeclaredIncludes(false), NameVisibility(Hidden) {
3737
if (Parent) {
3838
if (!Parent->isAvailable())
3939
IsAvailable = false;
4040
if (Parent->IsSystem)
4141
IsSystem = true;
4242
if (Parent->IsExternC)
4343
IsExternC = true;
44+
if (Parent->NoUndeclaredIncludes)
45+
NoUndeclaredIncludes = true;
4446
IsMissingRequirement = Parent->IsMissingRequirement;
4547

4648
Parent->SubModuleIndex[Name] = Parent->SubModules.size();
@@ -181,6 +183,11 @@ bool Module::directlyUses(const Module *Requested) const {
181183
for (auto *Use : Top->DirectUses)
182184
if (Requested->isSubModuleOf(Use))
183185
return true;
186+
187+
// Anyone is allowed to use our builtin stddef.h and its accompanying module.
188+
if (!Requested->Parent && Requested->Name == "_Builtin_stddef_max_align_t")
189+
return true;
190+
184191
return false;
185192
}
186193

clang/lib/Lex/HeaderSearch.cpp

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,12 @@ getTopFrameworkDir(FileManager &FileMgr, StringRef DirName,
413413
return TopFrameworkDir;
414414
}
415415

416+
static bool needModuleLookup(Module *RequestingModule,
417+
bool HasSuggestedModule) {
418+
return HasSuggestedModule ||
419+
(RequestingModule && RequestingModule->NoUndeclaredIncludes);
420+
}
421+
416422
/// DoFrameworkLookup - Do a lookup of the specified file in the current
417423
/// DirectoryLookup, which is a framework directory.
418424
const FileEntry *DirectoryLookup::DoFrameworkLookup(
@@ -508,7 +514,7 @@ const FileEntry *DirectoryLookup::DoFrameworkLookup(
508514
}
509515

510516
// If we found the header and are allowed to suggest a module, do so now.
511-
if (FE && SuggestedModule) {
517+
if (FE && needModuleLookup(RequestingModule, SuggestedModule)) {
512518
// Find the framework in which this header occurs.
513519
StringRef FrameworkPath = FE->getDir()->getName();
514520
bool FoundFramework = false;
@@ -1158,22 +1164,45 @@ bool HeaderSearch::hasModuleMap(StringRef FileName,
11581164
}
11591165

11601166
ModuleMap::KnownHeader
1161-
HeaderSearch::findModuleForHeader(const FileEntry *File) const {
1167+
HeaderSearch::findModuleForHeader(const FileEntry *File,
1168+
bool AllowTextual) const {
11621169
if (ExternalSource) {
11631170
// Make sure the external source has handled header info about this file,
11641171
// which includes whether the file is part of a module.
11651172
(void)getExistingFileInfo(File);
11661173
}
1167-
return ModMap.findModuleForHeader(File);
1174+
return ModMap.findModuleForHeader(File, AllowTextual);
1175+
}
1176+
1177+
static bool suggestModule(HeaderSearch &HS, const FileEntry *File,
1178+
Module *RequestingModule,
1179+
ModuleMap::KnownHeader *SuggestedModule) {
1180+
ModuleMap::KnownHeader Module =
1181+
HS.findModuleForHeader(File, /*AllowTextual*/true);
1182+
if (SuggestedModule)
1183+
*SuggestedModule = (Module.getRole() & ModuleMap::TextualHeader)
1184+
? ModuleMap::KnownHeader()
1185+
: Module;
1186+
1187+
// If this module specifies [no_undeclared_includes], we cannot find any
1188+
// file that's in a non-dependency module.
1189+
if (RequestingModule && Module && RequestingModule->NoUndeclaredIncludes) {
1190+
HS.getModuleMap().resolveUses(RequestingModule, /*Complain*/false);
1191+
if (!RequestingModule->directlyUses(Module.getModule())) {
1192+
return false;
1193+
}
1194+
}
1195+
1196+
return true;
11681197
}
11691198

11701199
bool HeaderSearch::findUsableModuleForHeader(
11711200
const FileEntry *File, const DirectoryEntry *Root, Module *RequestingModule,
11721201
ModuleMap::KnownHeader *SuggestedModule, bool IsSystemHeaderDir) {
1173-
if (File && SuggestedModule) {
1202+
if (File && needModuleLookup(RequestingModule, SuggestedModule)) {
11741203
// If there is a module that corresponds to this header, suggest it.
11751204
hasModuleMap(File->getName(), Root, IsSystemHeaderDir);
1176-
*SuggestedModule = findModuleForHeader(File);
1205+
return suggestModule(*this, File, RequestingModule, SuggestedModule);
11771206
}
11781207
return true;
11791208
}
@@ -1182,7 +1211,7 @@ bool HeaderSearch::findUsableModuleForFrameworkHeader(
11821211
const FileEntry *File, StringRef FrameworkName, Module *RequestingModule,
11831212
ModuleMap::KnownHeader *SuggestedModule, bool IsSystemFramework) {
11841213
// If we're supposed to suggest a module, look for one now.
1185-
if (SuggestedModule) {
1214+
if (needModuleLookup(RequestingModule, SuggestedModule)) {
11861215
// Find the top-level framework based on this framework.
11871216
SmallVector<std::string, 4> SubmodulePath;
11881217
const DirectoryEntry *TopFrameworkDir
@@ -1199,7 +1228,7 @@ bool HeaderSearch::findUsableModuleForFrameworkHeader(
11991228
// important so that we're consistent about whether this header
12001229
// corresponds to a module. Possibly we should lock down framework modules
12011230
// so that this is not possible.
1202-
*SuggestedModule = findModuleForHeader(File);
1231+
return suggestModule(*this, File, RequestingModule, SuggestedModule);
12031232
}
12041233
return true;
12051234
}

clang/lib/Lex/ModuleMap.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,10 @@ static bool isBetterKnownHeader(const ModuleMap::KnownHeader &New,
327327
return false;
328328
}
329329

330-
ModuleMap::KnownHeader ModuleMap::findModuleForHeader(const FileEntry *File) {
330+
ModuleMap::KnownHeader ModuleMap::findModuleForHeader(const FileEntry *File,
331+
bool AllowTextual) {
331332
auto MakeResult = [&](ModuleMap::KnownHeader R) -> ModuleMap::KnownHeader {
332-
if (R.getRole() & ModuleMap::TextualHeader)
333+
if (!AllowTextual && R.getRole() & ModuleMap::TextualHeader)
333334
return ModuleMap::KnownHeader();
334335
return R;
335336
};
@@ -674,6 +675,8 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir,
674675
Attrs.IsSystem |= inferred->second.Attrs.IsSystem;
675676
Attrs.IsExternC |= inferred->second.Attrs.IsExternC;
676677
Attrs.IsExhaustive |= inferred->second.Attrs.IsExhaustive;
678+
Attrs.NoUndeclaredIncludes |=
679+
inferred->second.Attrs.NoUndeclaredIncludes;
677680
ModuleMapFile = inferred->second.ModuleMapFile;
678681
}
679682
}
@@ -711,6 +714,7 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir,
711714
Result->IsSystem |= Attrs.IsSystem;
712715
Result->IsExternC |= Attrs.IsExternC;
713716
Result->ConfigMacrosExhaustive |= Attrs.IsExhaustive;
717+
Result->NoUndeclaredIncludes |= Attrs.NoUndeclaredIncludes;
714718
Result->Directory = FrameworkDir;
715719

716720
// umbrella header "umbrella-header-name"
@@ -1309,7 +1313,9 @@ namespace {
13091313
/// \brief The 'extern_c' attribute.
13101314
AT_extern_c,
13111315
/// \brief The 'exhaustive' attribute.
1312-
AT_exhaustive
1316+
AT_exhaustive,
1317+
/// \brief The 'no_undeclared_includes' attribute.
1318+
AT_no_undeclared_includes
13131319
};
13141320
}
13151321

@@ -1479,6 +1485,9 @@ void ModuleMapParser::parseModuleDecl() {
14791485
ActiveModule->IsSystem = true;
14801486
if (Attrs.IsExternC)
14811487
ActiveModule->IsExternC = true;
1488+
if (Attrs.NoUndeclaredIncludes ||
1489+
(!ActiveModule->Parent && ModuleName == "Darwin"))
1490+
ActiveModule->NoUndeclaredIncludes = true;
14821491
ActiveModule->Directory = Directory;
14831492

14841493
bool Done = false;
@@ -1845,13 +1854,7 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
18451854
// If Clang supplies this header but the underlying system does not,
18461855
// just silently swap in our builtin version. Otherwise, we'll end
18471856
// up adding both (later).
1848-
//
1849-
// For local visibility, entirely replace the system file with our
1850-
// one and textually include the system one. We need to pass macros
1851-
// from our header to the system one if we #include_next it.
1852-
//
1853-
// FIXME: Can we do this in all cases?
1854-
if (BuiltinFile && (!File || Map.LangOpts.ModulesLocalVisibility)) {
1857+
if (BuiltinFile && !File) {
18551858
File = BuiltinFile;
18561859
RelativePathName = BuiltinPathName;
18571860
BuiltinFile = nullptr;
@@ -1877,15 +1880,16 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
18771880
Module::Header H = {RelativePathName.str(), File};
18781881
Map.excludeHeader(ActiveModule, H);
18791882
} else {
1880-
// If there is a builtin counterpart to this file, add it now, before
1881-
// the "real" header, so we build the built-in one first when building
1882-
// the module.
1883+
// If there is a builtin counterpart to this file, add it now as a textual
1884+
// header, so it can be #include_next'd by the wrapper header, and can
1885+
// receive macros from the wrapper header.
18831886
if (BuiltinFile) {
18841887
// FIXME: Taking the name from the FileEntry is unstable and can give
18851888
// different results depending on how we've previously named that file
18861889
// in this build.
18871890
Module::Header H = { BuiltinFile->getName(), BuiltinFile };
1888-
Map.addHeader(ActiveModule, H, Role);
1891+
Map.addHeader(ActiveModule, H, ModuleMap::ModuleHeaderRole(
1892+
Role | ModuleMap::TextualHeader));
18891893
}
18901894

18911895
// Record this header.
@@ -2375,6 +2379,7 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) {
23752379
= llvm::StringSwitch<AttributeKind>(Tok.getString())
23762380
.Case("exhaustive", AT_exhaustive)
23772381
.Case("extern_c", AT_extern_c)
2382+
.Case("no_undeclared_includes", AT_no_undeclared_includes)
23782383
.Case("system", AT_system)
23792384
.Default(AT_unknown);
23802385
switch (Attribute) {
@@ -2394,6 +2399,10 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) {
23942399
case AT_exhaustive:
23952400
Attrs.IsExhaustive = true;
23962401
break;
2402+
2403+
case AT_no_undeclared_includes:
2404+
Attrs.NoUndeclaredIncludes = true;
2405+
break;
23972406
}
23982407
consumeToken();
23992408

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
// Testing hack: does not define bool/true/false.
1+
#include_next <stdbool.h>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include_next <math.h>
2+
template<typename T> T abs(T t) { return (t < 0) ? -t : t; }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module "libc++" {
2+
module math { header "math.h" export * }
3+
module stdlib { header "stdlib.h" export * }
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include_next "stdlib.h"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int abs(int);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module libc [no_undeclared_includes] {
2+
module math { header "math.h" export * }
3+
module stdlib { header "stdlib.h" export * }
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <math.h>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef _LIBCPP_CONFIG
2+
#define _LIBCPP_CONFIG
3+
4+
#define __LIBCXX_CONFIG
5+
6+
#endif
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef LIBCXX_MATH_H
2+
#define LIBCXX_MATH_H
3+
4+
#include_next <math.h>
5+
template<typename T> T abs(T t) { return (t < 0) ? -t : t; }
6+
7+
#endif
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module "libc++" {
2+
module math { header "math.h" export * }
3+
module stdlib { header "stdlib.h" export * }
4+
module stddef { header "stddef.h" export * }
5+
module stdio { textual header "stdio.h" export * }
6+
module __config { header "__config" export * }
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef LIBCXX_STDDEF_H
2+
#define LIBCXX_STDDEF_H
3+
4+
#include <__config>
5+
6+
#endif
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef LIBCXX_STDIO_H
2+
#define LIBCXX_STDIO_H
3+
4+
#include <__config>
5+
6+
#endif
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef LIBCXX_STDLIB_H
2+
#define LIBCXX_STDLIB_H
3+
4+
#include_next "stdlib.h"
5+
6+
#endif
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int abs(int);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module libc [no_undeclared_includes] {
2+
module math { header "math.h" export * }
3+
module stdlib { header "stdlib.h" export * }
4+
module stdatomic { header "stdatomic.h" export * }
5+
module stddef { header "stddef.h" export * }
6+
module stdint { header "stdint.h" export * }
7+
module stdio { header "stdio.h" export * }
8+
module util { header "util.h" export * }
9+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// stddef.h
2+
#include_next "stddef.h"

0 commit comments

Comments
 (0)