Skip to content

Commit d6bccba

Browse files
committed
Load cross-import overlays
1 parent 5d74978 commit d6bccba

File tree

3 files changed

+205
-1
lines changed

3 files changed

+205
-1
lines changed

include/swift/AST/SourceFile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ class SourceFile final : public FileUnit {
272272

273273
bool isImportedImplementationOnly(const ModuleDecl *module) const;
274274

275+
bool shouldCrossImport() const;
276+
275277
/// Register a separately-imported overlay as shadowing the module that
276278
/// declares it.
277279
///

lib/AST/Module.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,6 +1772,11 @@ bool SourceFile::isImportedImplementationOnly(const ModuleDecl *module) const {
17721772
return !imports.isImportedBy(module, getParentModule());
17731773
}
17741774

1775+
bool SourceFile::shouldCrossImport() const {
1776+
return Kind != SourceFileKind::SIL && Kind != SourceFileKind::Interface &&
1777+
getASTContext().LangOpts.EnableCrossImportOverlays;
1778+
}
1779+
17751780
void ModuleDecl::clearLookupCache() {
17761781
getASTContext().getImportCache().clear();
17771782

lib/Sema/NameBinding.cpp

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#define DEBUG_TYPE "swift-name-binding"
1819
#include "swift/AST/ASTWalker.h"
1920
#include "swift/AST/DiagnosticsSema.h"
2021
#include "swift/AST/ModuleLoader.h"
@@ -30,6 +31,7 @@
3031
#include "llvm/ADT/DenseMap.h"
3132
#include "llvm/ADT/TinyPtrVector.h"
3233
#include "llvm/ADT/Twine.h"
34+
#include "llvm/Support/Debug.h"
3335
#include "llvm/Support/Path.h"
3436
#include "llvm/Support/SaveAndRestore.h"
3537
#include <algorithm>
@@ -69,10 +71,17 @@ namespace {
6971
ModuleDecl::AccessPathTy declPath;
7072

7173
NullablePtr<DeclAttributes> attrs;
74+
NullablePtr<ModuleDecl> underlyingModule;
7275

7376
/// Create an UnboundImport for a user-written import declaration.
7477
explicit UnboundImport(ImportDecl *ID);
7578

79+
/// Create an UnboundImport for a cross-import overlay.
80+
explicit UnboundImport(ASTContext &ctx,
81+
const UnboundImport &base, Identifier overlayName,
82+
const ImportedModuleDesc &declaringImport,
83+
const ImportedModuleDesc &bystandingImport);
84+
7685
/// Make sure the import is not a self-import.
7786
bool checkNotTautological(const SourceFile &SF);
7887

@@ -137,6 +146,13 @@ namespace {
137146
/// Imports which still need their scoped imports validated.
138147
SmallVector<BoundImport, 16> unvalidatedImports;
139148

149+
/// All imported modules, including by re-exports.
150+
SmallSetVector<ImportedModuleDesc, 16> visibleModules;
151+
152+
/// The index of the next module in \c visibleModules that should be
153+
/// cross-imported.
154+
size_t nextModuleToCrossImport = 0;
155+
140156
public:
141157
NameBinder(SourceFile &SF)
142158
: SF(SF), ctx(SF.getASTContext())
@@ -170,6 +186,22 @@ namespace {
170186
void addImport(const UnboundImport &I, ModuleDecl *M,
171187
bool needsScopeValidation);
172188

189+
/// Adds \p desc and everything it re-exports to \c visibleModules using
190+
/// the settings from \c desc.
191+
void addVisibleModules(ImportedModuleDesc desc);
192+
193+
/// * If \p I is a cross-import overlay, registers \p M as overlaying
194+
/// \p I.underlyingModule in \c SF.
195+
/// * Discovers any cross-imports between \p I and previously bound imports,
196+
/// then adds them to \c unboundImports using source locations from \p I.
197+
void crossImport(ModuleDecl *M, UnboundImport &I);
198+
199+
/// Discovers any cross-imports between \p declaringImport and
200+
/// \p bystandingImport and adds them to \c unboundImports, using source
201+
/// locations from \p I.
202+
void findCrossImports(UnboundImport &I,
203+
const ImportedModuleDesc &declaringImport,
204+
const ImportedModuleDesc &bystandingImport);
173205

174206
/// Load a module referenced by an import statement.
175207
///
@@ -310,6 +342,8 @@ void NameBinder::bindImport(UnboundImport &&I) {
310342
addImport(I, M, true);
311343
}
312344

345+
crossImport(M, I);
346+
313347
if (I.ID)
314348
I.ID.get()->setModule(M);
315349
}
@@ -318,6 +352,7 @@ void NameBinder::addImport(const UnboundImport &I, ModuleDecl *M,
318352
bool needsScopeValidation) {
319353
auto importDesc = I.makeDesc(M);
320354

355+
addVisibleModules(importDesc);
321356
unvalidatedImports.emplace_back(I, importDesc, M, needsScopeValidation);
322357
}
323358

@@ -385,7 +420,7 @@ UnboundImport::UnboundImport(ImportDecl *ID)
385420
: ID(ID), importLoc(ID->getLoc()), options(), privateImportFileName(),
386421
importKind(ID->getImportKind(), ID->getKindLoc()),
387422
modulePath(ID->getModulePath()), declPath(ID->getDeclPath()),
388-
attrs(&ID->getAttrs())
423+
attrs(&ID->getAttrs()), underlyingModule()
389424
{
390425
if (ID->isExported())
391426
options |= ImportFlags::Exported;
@@ -708,3 +743,165 @@ void BoundImport::validateScope(SourceFile &SF) {
708743

709744
unbound.ID.get()->setDecls(ctx.AllocateCopy(decls));
710745
}
746+
747+
//===----------------------------------------------------------------------===//
748+
// MARK: Cross-import overlays
749+
//===----------------------------------------------------------------------===//
750+
751+
static bool canCrossImport(const ImportedModuleDesc &import) {
752+
if (import.importOptions.contains(ImportFlags::Testable))
753+
return false;
754+
if (import.importOptions.contains(ImportFlags::PrivateImport))
755+
return false;
756+
757+
return true;
758+
}
759+
760+
/// Create an UnboundImport for a cross-import overlay.
761+
UnboundImport::UnboundImport(ASTContext &ctx,
762+
const UnboundImport &base, Identifier overlayName,
763+
const ImportedModuleDesc &declaringImport,
764+
const ImportedModuleDesc &bystandingImport)
765+
: ID(nullptr), importLoc(base.importLoc), options(),
766+
privateImportFileName(), importKind({ ImportKind::Module, SourceLoc() }),
767+
modulePath(),
768+
// If the declaring import was scoped, inherit that scope in the
769+
// overlay's import. Note that we do *not* set importKind; this keeps
770+
// BoundImport::validateScope() from unnecessarily revalidating the
771+
// scope.
772+
declPath(declaringImport.module.first),
773+
attrs(nullptr), underlyingModule(declaringImport.module.second)
774+
{
775+
modulePath = ctx.AllocateCopy(
776+
ModuleDecl::AccessPathTy( { overlayName, base.modulePath[0].Loc }));
777+
778+
// A cross-import is never private or testable, and never comes from a private
779+
// or testable import.
780+
assert(canCrossImport(declaringImport));
781+
assert(canCrossImport(bystandingImport));
782+
783+
auto &declaringOptions = declaringImport.importOptions;
784+
auto &bystandingOptions = bystandingImport.importOptions;
785+
786+
// If both are exported, the cross-import is exported.
787+
if (declaringOptions.contains(ImportFlags::Exported) &&
788+
bystandingOptions.contains(ImportFlags::Exported))
789+
options |= ImportFlags::Exported;
790+
791+
// If either are implementation-only, the cross-import is
792+
// implementation-only.
793+
if (declaringOptions.contains(ImportFlags::ImplementationOnly) ||
794+
bystandingOptions.contains(ImportFlags::ImplementationOnly))
795+
options |= ImportFlags::ImplementationOnly;
796+
}
797+
798+
void NameBinder::crossImport(ModuleDecl *M, UnboundImport &I) {
799+
if (!SF.shouldCrossImport())
800+
return;
801+
802+
if (I.underlyingModule)
803+
// FIXME: Should we warn if M doesn't reexport underlyingModule?
804+
SF.addSeparatelyImportedOverlay(M, I.underlyingModule.get());
805+
806+
auto newImports = visibleModules.getArrayRef().slice(nextModuleToCrossImport);
807+
for (auto &newImport : newImports) {
808+
if (!canCrossImport(newImport))
809+
continue;
810+
811+
// Search imports up to, but not including or after, `newImport`.
812+
auto oldImports =
813+
make_range(visibleModules.getArrayRef().data(), &newImport);
814+
for (auto &oldImport : oldImports) {
815+
if (!canCrossImport(oldImport))
816+
continue;
817+
818+
findCrossImports(I, newImport, oldImport);
819+
findCrossImports(I, oldImport, newImport);
820+
821+
// If findCrossImports() ever changed the visibleModules list, we'd see
822+
// memory smashers here.
823+
assert(newImports.data() == &visibleModules[nextModuleToCrossImport] &&
824+
"findCrossImports() should never mutate visibleModules");
825+
}
826+
}
827+
828+
nextModuleToCrossImport = visibleModules.size();
829+
}
830+
831+
void NameBinder::findCrossImports(UnboundImport &I,
832+
const ImportedModuleDesc &declaringImport,
833+
const ImportedModuleDesc &bystandingImport) {
834+
assert(&declaringImport != &bystandingImport);
835+
836+
LLVM_DEBUG(
837+
llvm::dbgs() << "Discovering cross-imports for '"
838+
<< declaringImport.module.second->getName() << "' -> '"
839+
<< bystandingImport.module.second->getName() << "'\n");
840+
841+
// Find modules we need to import.
842+
SmallVector<Identifier, 2> names;
843+
declaringImport.module.second->findDeclaredCrossImportOverlays(
844+
bystandingImport.module.second->getName(), names, I.importLoc);
845+
846+
// Add import statements.
847+
for (auto &name : names) {
848+
// If we are actually compiling part of this overlay, don't try to load the
849+
// overlay.
850+
if (name == SF.getParentModule()->getName())
851+
continue;
852+
853+
unboundImports.emplace_back(declaringImport.module.second->getASTContext(),
854+
I, name, declaringImport, bystandingImport);
855+
856+
LLVM_DEBUG({
857+
auto &crossImportOptions = unboundImports.back().options;
858+
llvm::dbgs() << " ";
859+
if (crossImportOptions.contains(ImportFlags::Exported))
860+
llvm::dbgs() << "@_exported ";
861+
if (crossImportOptions.contains(ImportFlags::ImplementationOnly))
862+
llvm::dbgs() << "@_implementationOnly ";
863+
llvm::dbgs() << "import " << name << "\n";
864+
});
865+
}
866+
}
867+
868+
void NameBinder::addVisibleModules(ImportedModuleDesc importDesc) {
869+
// FIXME: namelookup::getAllImports() doesn't quite do what we need (mainly
870+
// w.r.t. scoped imports), but it seems like we could extend it to do so, and
871+
// then eliminate most of this.
872+
873+
SmallVector<ImportedModule, 16> importsWorklist = { importDesc.module };
874+
875+
while (!importsWorklist.empty()) {
876+
auto nextImport = importsWorklist.pop_back_val();
877+
878+
// If they are both scoped, and they are *differently* scoped, this import
879+
// cannot possibly expose anything new. Skip it.
880+
if (!importDesc.module.first.empty() && !nextImport.first.empty() &&
881+
importDesc.module.first != nextImport.first)
882+
continue;
883+
884+
// Drop this module into the ImportDesc so we treat it as imported with the
885+
// same options and scope as `I`.
886+
importDesc.module.second = nextImport.second;
887+
888+
// If we've already imported it, we've also already imported its
889+
// imports.
890+
if (!visibleModules.insert(importDesc))
891+
continue;
892+
893+
// Add the module's re-exports to worklist.
894+
nextImport.second->getImportedModules(importsWorklist,
895+
ModuleDecl::ImportFilterKind::Public);
896+
}
897+
}
898+
899+
LLVM_ATTRIBUTE_USED static void dumpCrossImportOverlays(ModuleDecl* M) {
900+
llvm::dbgs() << "'" << M->getName() << "' declares cross-imports with bystanders:\n";
901+
902+
SmallVector<Identifier, 4> secondaries;
903+
M->getDeclaredCrossImportBystanders(secondaries);
904+
905+
for (auto secondary : secondaries)
906+
llvm::dbgs() << " " << secondary << "\n";
907+
}

0 commit comments

Comments
 (0)