Skip to content

Commit d842fa3

Browse files
authored
Merge pull request #29582 from brentdax/the-most-ambitious-crossover-event-in-history
Cross-Import Overlays
2 parents a61d220 + 9a09eb7 commit d842fa3

File tree

113 files changed

+2196
-283
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+2196
-283
lines changed

docs/Lexicon.rst

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,10 +264,32 @@ source code, tests, and commit messages. See also the `LLVM lexicon`_.
264264
compiler can do something with it.
265265

266266
overlay
267-
A library that is imported whenever a C library or framework by the same
268-
name is imported. The purpose of an overlay is to augment and extend a
269-
library on the system when the library on the system cannot be modified.
270-
Apple has a number of overlays for its own SDKs in stdlib/public/SDK/.
267+
A wrapper library that is implicitly imported "on top of" another library.
268+
It contains an @_exported import of the underlying library, but it augments
269+
it with additional APIs which, for one reason or another, are not included
270+
in the underlying library directly.
271+
272+
There are two kinds of overlays:
273+
274+
A "clang overlay" (the older kind, so it's often just called an "overlay")
275+
is a Swift library that adds Swift-specific functionality to a C-family
276+
library or framework. Clang overlays are used with system libraries that
277+
cannot be modified to add Swift features. A clang overlay has the same
278+
module name as the underlying library and can do a few special things that
279+
normal modules can't, like adding required initializers to classes. If a
280+
module has a clang overlay, the Clang Importer will always load it unless it
281+
is actually compiling the overlay itself. Apple has a number of clang
282+
overlays for its own SDKs in stdlib/public/Darwin/.
283+
284+
A "separately-imported overlay" is a separate module with its own name.
285+
Unlike a clang overlay, it can be imported in some SourceFiles and not
286+
others. When the compiler processes import declarations, it determines which
287+
separately-imported overlays need to be imported and then adds them to the
288+
list of imports for that file; name lookup also knows to look through the
289+
overlay when it looks for declarations in the underlying module.
290+
Separately-imported overlays are used to implement the "cross-import
291+
overlays" feature, which is used to augment a module with additional
292+
functionality when it is imported alongside another module.
271293

272294
PCH
273295
Precompiled header, a type of file ending in .pch. A precompiled header is

include/swift/AST/Attr.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,6 +2103,15 @@ class DeclAttributes {
21032103
return nullptr;
21042104
}
21052105

2106+
/// Retrieve the first attribute with the given kind.
2107+
DeclAttribute *getAttribute(DeclAttrKind DK,
2108+
bool AllowInvalid = false) {
2109+
for (auto Attr : *this)
2110+
if (Attr->getKind() == DK && (Attr->isValid() || AllowInvalid))
2111+
return Attr;
2112+
return nullptr;
2113+
}
2114+
21062115
private:
21072116
/// Predicate used to filter MatchingAttributeRange.
21082117
template <typename ATTR, bool AllowInvalid> struct ToAttributeKind {

include/swift/AST/DeclContext.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,14 @@ class alignas(1 << DeclContextAlignInBits) DeclContext {
578578
SmallVectorImpl<ConformanceDiagnostic> *diagnostics
579579
= nullptr) const;
580580

581+
/// Retrieves a list of separately imported overlays which are shadowing
582+
/// \p declaring. If any \p overlays are returned, qualified lookups into
583+
/// \p declaring should be performed into \p overlays instead; since they
584+
/// are overlays, they will re-export \p declaring, but will also augment it
585+
/// with additional symbols.
586+
void getSeparatelyImportedOverlays(
587+
ModuleDecl *declaring, SmallVectorImpl<ModuleDecl *> &overlays) const;
588+
581589
/// Retrieve the syntactic depth of this declaration context, i.e.,
582590
/// the number of non-module-scoped contexts.
583591
///

include/swift/AST/DiagnosticsCommon.def

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ WARNING(warn_property_wrapper_module_scope,none,
165165
"wrapper %0; please qualify the reference with %1",
166166
(DeclNameRef, Identifier))
167167

168+
//------------------------------------------------------------------------------
169+
// MARK: Cross-import overlay loading diagnostics
170+
//------------------------------------------------------------------------------
171+
ERROR(cannot_load_swiftoverlay_file, none,
172+
"cannot load cross-import overlay for %0 and %1: %2 (declared by '%3')",
173+
(Identifier, Identifier, StringRef, StringRef))
174+
ERROR(cannot_list_swiftcrossimport_dir, none,
175+
"cannot list cross-import overlays for %0: %1 (declared in '%2')",
176+
(Identifier, StringRef, StringRef))
177+
WARNING(cross_imported_by_both_modules, none,
178+
"modules %0 and %1 both declare module %2 as a cross-import overlay, "
179+
"which may cause paradoxical behavior when looking up names in them; "
180+
"please report this bug to the maintainers of these modules",
181+
(Identifier, Identifier, Identifier))
182+
168183
#ifndef DIAG_NO_UNDEF
169184
# if defined(DIAG)
170185
# undef DIAG

include/swift/AST/FileUnit.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ class FileUnit : public DeclContext {
214214
virtual void
215215
collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const {}
216216

217+
/// Returns the path of the file or directory that defines the module
218+
/// represented by this \c FileUnit, or empty string if there is none.
219+
/// Cross-import overlay specifiers are found relative to this path.
220+
virtual StringRef getModuleDefiningPath() const {
221+
return "";
222+
}
223+
217224
/// True if this file contains the main class for the module.
218225
bool hasMainClass() const {
219226
return getMainClass();

include/swift/AST/Module.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ enum class ResilienceStrategy : unsigned {
124124
Resilient
125125
};
126126

127+
class OverlayFile;
128+
127129
/// The minimum unit of compilation.
128130
///
129131
/// A module is made up of several file-units, which are all part of the same
@@ -203,6 +205,9 @@ class ModuleDecl : public DeclContext, public TypeDecl {
203205

204206
SmallVector<FileUnit *, 2> Files;
205207

208+
llvm::SmallDenseMap<Identifier, SmallVector<OverlayFile *, 1>>
209+
declaredCrossImports;
210+
206211
std::unique_ptr<SourceLookupCache> Cache;
207212
SourceLookupCache &getSourceLookupCache() const;
208213

@@ -252,6 +257,22 @@ class ModuleDecl : public DeclContext, public TypeDecl {
252257
void addFile(FileUnit &newFile);
253258
void removeFile(FileUnit &existingFile);
254259

260+
/// Add a file declaring a cross-import overlay.
261+
void addCrossImportOverlayFile(StringRef file);
262+
263+
/// Append to \p overlayNames the names of all modules that this module
264+
/// declares should be imported when \p bystanderName is imported.
265+
///
266+
/// This operation is asymmetric: you will get different results if you
267+
/// reverse the positions of the two modules involved in the cross-import.
268+
void findDeclaredCrossImportOverlays(
269+
Identifier bystanderName, SmallVectorImpl<Identifier> &overlayNames,
270+
SourceLoc diagLoc) const;
271+
272+
/// Get the list of all modules this module declares a cross-import with.
273+
void getDeclaredCrossImportBystanders(
274+
SmallVectorImpl<Identifier> &bystanderNames);
275+
255276
/// Convenience accessor for clients that know what kind of file they're
256277
/// dealing with.
257278
SourceFile &getMainSourceFile(SourceFileKind expectedKind) const;
@@ -444,7 +465,11 @@ class ModuleDecl : public DeclContext, public TypeDecl {
444465
/// Include "regular" imports with no special annotation.
445466
Private = 1 << 1,
446467
/// Include imports declared with `@_implementationOnly`.
447-
ImplementationOnly = 1 << 2
468+
ImplementationOnly = 1 << 2,
469+
/// Include imports shadowed by a separately-imported overlay (i.e. a
470+
/// cross-import overlay). Unshadowed imports are included whether or not
471+
/// this flag is specified.
472+
ShadowedBySeparateOverlay = 1 << 4
448473
};
449474
/// \sa getImportedModules
450475
using ImportFilter = OptionSet<ImportFilterKind>;

include/swift/AST/ModuleLoader.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ namespace swift {
3838
class AbstractFunctionDecl;
3939
class ClangImporterOptions;
4040
class ClassDecl;
41+
class FileUnit;
4142
class ModuleDecl;
4243
class NominalTypeDecl;
4344
class TypeDecl;
@@ -154,6 +155,10 @@ class ModuleLoader {
154155

155156
/// Verify all modules loaded by this loader.
156157
virtual void verifyAllModules() { }
158+
159+
/// Discover overlays declared alongside this file and add infomation about
160+
/// them to it.
161+
void findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module, FileUnit *file);
157162
};
158163

159164
} // namespace swift

include/swift/AST/SourceFile.h

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ class SourceFile final : public FileUnit {
5454
/// elsewhere.
5555
///
5656
/// Mutually exclusive with Exported.
57-
ImplementationOnly = 0x8
57+
ImplementationOnly = 0x8,
58+
59+
/// Used for DenseMap.
60+
Reserved = 0x80
5861
};
5962

6063
/// \see ImportFlags
@@ -69,7 +72,8 @@ class SourceFile final : public FileUnit {
6972
StringRef filename = {})
7073
: module(module), importOptions(options), filename(filename) {
7174
assert(!(importOptions.contains(ImportFlags::Exported) &&
72-
importOptions.contains(ImportFlags::ImplementationOnly)));
75+
importOptions.contains(ImportFlags::ImplementationOnly)) ||
76+
importOptions.contains(ImportFlags::Reserved));
7377
}
7478
};
7579

@@ -128,6 +132,17 @@ class SourceFile final : public FileUnit {
128132
/// The list of top-level declarations in the source file.
129133
std::vector<Decl *> Decls;
130134

135+
using SeparatelyImportedOverlayMap =
136+
llvm::SmallDenseMap<ModuleDecl *, llvm::SmallPtrSet<ModuleDecl *, 1>>;
137+
138+
/// Keys are modules which are shadowed by one or more separately-imported
139+
/// overlays; values are the list of overlays shadowing them.
140+
///
141+
/// This is used by cross-import overlays to make their members appear to
142+
/// be part of the underlying module. (ClangImporter overlays use a different
143+
/// mechanism which is not SourceFile-dependent.)
144+
SeparatelyImportedOverlayMap separatelyImportedOverlays;
145+
131146
friend ASTContext;
132147
friend Impl;
133148

@@ -257,6 +272,31 @@ class SourceFile final : public FileUnit {
257272

258273
bool isImportedImplementationOnly(const ModuleDecl *module) const;
259274

275+
bool shouldCrossImport() const;
276+
277+
/// Register a separately-imported overlay as shadowing the module that
278+
/// declares it.
279+
///
280+
/// \returns true if the overlay was added; false if it already existed.
281+
bool addSeparatelyImportedOverlay(ModuleDecl *overlay,
282+
ModuleDecl *declaring) {
283+
return std::get<1>(separatelyImportedOverlays[declaring].insert(overlay));
284+
}
285+
286+
/// Retrieves a list of separately imported overlays which are shadowing
287+
/// \p declaring. If any \p overlays are returned, qualified lookups into
288+
/// \p declaring should be performed into \p overlays instead; since they
289+
/// are overlays, they will re-export \p declaring, but will also augment it
290+
/// with additional symbols.
291+
void getSeparatelyImportedOverlays(
292+
ModuleDecl *declaring, SmallVectorImpl<ModuleDecl *> &overlays) {
293+
auto i = separatelyImportedOverlays.find(declaring);
294+
if (i == separatelyImportedOverlays.end()) return;
295+
296+
auto &value = std::get<1>(*i);
297+
overlays.append(value.begin(), value.end());
298+
}
299+
260300
void cacheVisibleDecls(SmallVectorImpl<ValueDecl *> &&globals) const;
261301
const SmallVectorImpl<ValueDecl *> &getCachedVisibleDecls() const;
262302

@@ -508,4 +548,58 @@ inline void simple_display(llvm::raw_ostream &out, const SourceFile *SF) {
508548
}
509549
} // end namespace swift
510550

551+
namespace llvm {
552+
553+
template<>
554+
struct DenseMapInfo<swift::SourceFile::ImportOptions> {
555+
using ImportOptions = swift::SourceFile::ImportOptions;
556+
557+
using UnsignedDMI = DenseMapInfo<uint8_t>;
558+
559+
static inline ImportOptions getEmptyKey() {
560+
return ImportOptions(UnsignedDMI::getEmptyKey());
561+
}
562+
static inline ImportOptions getTombstoneKey() {
563+
return ImportOptions(UnsignedDMI::getTombstoneKey());
564+
}
565+
static inline unsigned getHashValue(ImportOptions options) {
566+
return UnsignedDMI::getHashValue(options.toRaw());
567+
}
568+
static bool isEqual(ImportOptions a, ImportOptions b) {
569+
return UnsignedDMI::isEqual(a.toRaw(), b.toRaw());
570+
}
571+
};
572+
573+
template<>
574+
struct DenseMapInfo<swift::SourceFile::ImportedModuleDesc> {
575+
using ImportedModuleDesc = swift::SourceFile::ImportedModuleDesc;
576+
577+
using ImportedModuleDMI = DenseMapInfo<swift::ModuleDecl::ImportedModule>;
578+
using ImportOptionsDMI = DenseMapInfo<swift::SourceFile::ImportOptions>;
579+
using StringRefDMI = DenseMapInfo<StringRef>;
580+
581+
static inline ImportedModuleDesc getEmptyKey() {
582+
return ImportedModuleDesc(ImportedModuleDMI::getEmptyKey(),
583+
ImportOptionsDMI::getEmptyKey(),
584+
StringRefDMI::getEmptyKey());
585+
}
586+
static inline ImportedModuleDesc getTombstoneKey() {
587+
return ImportedModuleDesc(ImportedModuleDMI::getTombstoneKey(),
588+
ImportOptionsDMI::getTombstoneKey(),
589+
StringRefDMI::getTombstoneKey());
590+
}
591+
static inline unsigned getHashValue(const ImportedModuleDesc &import) {
592+
return combineHashValue(ImportedModuleDMI::getHashValue(import.module),
593+
combineHashValue(ImportOptionsDMI::getHashValue(import.importOptions),
594+
StringRefDMI::getHashValue(import.filename)));
595+
}
596+
static bool isEqual(const ImportedModuleDesc &a,
597+
const ImportedModuleDesc &b) {
598+
return ImportedModuleDMI::isEqual(a.module, b.module) &&
599+
ImportOptionsDMI::isEqual(a.importOptions, b.importOptions) &&
600+
StringRefDMI::isEqual(a.filename, b.filename);
601+
}
602+
};
603+
}
604+
511605
#endif

include/swift/Basic/FileTypes.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ TYPE("module-trace", ModuleTrace, "trace.json", "")
7373
TYPE("index-data", IndexData, "", "")
7474
TYPE("opt-record", OptRecord, "opt.yaml", "")
7575

76+
// Overlay files declare wrapper modules, called "separately-imported overlays",
77+
// that should be automatically imported when a particular module is imported.
78+
// Cross-import directories conditionalize overlay files so they only take
79+
// effect when certain other modules are also loaded.
80+
TYPE("swiftcrossimport", SwiftCrossImportDir, "swiftcrossimport","")
81+
TYPE("swiftoverlay", SwiftOverlayFile, "swiftoverlay", "")
82+
7683
// Misc types
7784
TYPE("pcm", ClangModuleFile, "pcm", "")
7885
TYPE("pch", PCH, "pch", "")

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ namespace swift {
109109
/// human-readable string.
110110
bool EnableConcisePoundFile = false;
111111

112+
/// Detect and automatically import modules' cross-import overlays.
113+
bool EnableCrossImportOverlays = false;
114+
112115
///
113116
/// Support for alternate usage modes
114117
///

include/swift/Basic/OptionSet.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class OptionSet {
8888
}
8989

9090
/// Check if this option set contains the exact same options as the given set.
91-
bool containsOnly(OptionSet set) {
91+
bool containsOnly(OptionSet set) const {
9292
return Storage == set.Storage;
9393
}
9494

include/swift/Basic/Statistics.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ FRONTEND_STATISTIC(Parse, NumIterableDeclContextParsed)
187187
/// Number of conformances that were deserialized by this frontend job.
188188
FRONTEND_STATISTIC(Sema, NumConformancesDeserialized)
189189

190+
/// Number of pairs of modules we've checked for cross-imports.
191+
FRONTEND_STATISTIC(Sema, NumCrossImportsChecked)
192+
193+
/// Number of pairs of modules we've actually found cross-imports for.
194+
FRONTEND_STATISTIC(Sema, NumCrossImportsFound)
195+
190196
/// Number of constraint-solving scopes created in the typechecker, while
191197
/// solving expression type constraints. A rough proxy for "how much work the
192198
/// expression typechecker did".

include/swift/ClangImporter/ClangModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ class ClangModuleUnit final : public LoadedFile {
118118
Optional<clang::ExternalASTSource::ASTSourceDescriptor>
119119
getASTSourceDescriptor() const;
120120

121+
virtual StringRef getModuleDefiningPath() const override;
122+
121123
static bool classof(const FileUnit *file) {
122124
return file->getKind() == FileUnitKind::ClangModule;
123125
}

include/swift/Option/FrontendOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ def verify_syntax_tree : Flag<["-"], "verify-syntax-tree">,
126126
def show_diagnostics_after_fatal : Flag<["-"], "show-diagnostics-after-fatal">,
127127
HelpText<"Keep emitting subsequent diagnostics after a fatal error">;
128128

129+
def enable_cross_import_overlays : Flag<["-"], "enable-cross-import-overlays">,
130+
HelpText<"Automatically import declared cross-import overlays.">;
131+
def disable_cross_import_overlays : Flag<["-"], "disable-cross-import-overlays">,
132+
HelpText<"Do not automatically import declared cross-import overlays.">;
133+
129134
def enable_descriptive_diagnostics : Flag<["-"], "enable-descriptive-diagnostics">,
130135
HelpText<"Show descriptive diagnostic information, if available.">;
131136

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,8 @@ class SerializedASTFile final : public LoadedFile {
393393

394394
virtual StringRef getFilename() const override;
395395

396+
virtual StringRef getModuleDefiningPath() const override;
397+
396398
ClassDecl *getMainClass() const override;
397399

398400
bool hasEntryPoint() const override;

lib/AST/DeclContext.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ DeclContext *DeclContext::getModuleScopeContext() const {
287287
}
288288
}
289289

290+
void DeclContext::getSeparatelyImportedOverlays(
291+
ModuleDecl *declaring, SmallVectorImpl<ModuleDecl *> &overlays) const {
292+
if (auto SF = getParentSourceFile())
293+
SF->getSeparatelyImportedOverlays(declaring, overlays);
294+
}
295+
290296
/// Determine whether the given context is generic at any level.
291297
bool DeclContext::isGenericContext() const {
292298
auto dc = this;

0 commit comments

Comments
 (0)