15
15
//
16
16
// ===----------------------------------------------------------------------===//
17
17
18
+ #define DEBUG_TYPE " swift-name-binding"
18
19
#include " swift/AST/ASTWalker.h"
19
20
#include " swift/AST/DiagnosticsSema.h"
20
21
#include " swift/AST/ModuleLoader.h"
30
31
#include " llvm/ADT/DenseMap.h"
31
32
#include " llvm/ADT/TinyPtrVector.h"
32
33
#include " llvm/ADT/Twine.h"
34
+ #include " llvm/Support/Debug.h"
33
35
#include " llvm/Support/Path.h"
34
36
#include " llvm/Support/SaveAndRestore.h"
35
37
#include < algorithm>
@@ -69,10 +71,17 @@ namespace {
69
71
ModuleDecl::AccessPathTy declPath;
70
72
71
73
NullablePtr<DeclAttributes> attrs;
74
+ NullablePtr<ModuleDecl> underlyingModule;
72
75
73
76
// / Create an UnboundImport for a user-written import declaration.
74
77
explicit UnboundImport (ImportDecl *ID);
75
78
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
+
76
85
// / Make sure the import is not a self-import.
77
86
bool checkNotTautological (const SourceFile &SF);
78
87
@@ -137,6 +146,13 @@ namespace {
137
146
// / Imports which still need their scoped imports validated.
138
147
SmallVector<BoundImport, 16 > unvalidatedImports;
139
148
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
+
140
156
public:
141
157
NameBinder (SourceFile &SF)
142
158
: SF(SF), ctx(SF.getASTContext())
@@ -170,6 +186,22 @@ namespace {
170
186
void addImport (const UnboundImport &I, ModuleDecl *M,
171
187
bool needsScopeValidation);
172
188
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);
173
205
174
206
// / Load a module referenced by an import statement.
175
207
// /
@@ -310,6 +342,8 @@ void NameBinder::bindImport(UnboundImport &&I) {
310
342
addImport (I, M, true );
311
343
}
312
344
345
+ crossImport (M, I);
346
+
313
347
if (I.ID )
314
348
I.ID .get ()->setModule (M);
315
349
}
@@ -318,6 +352,7 @@ void NameBinder::addImport(const UnboundImport &I, ModuleDecl *M,
318
352
bool needsScopeValidation) {
319
353
auto importDesc = I.makeDesc (M);
320
354
355
+ addVisibleModules (importDesc);
321
356
unvalidatedImports.emplace_back (I, importDesc, M, needsScopeValidation);
322
357
}
323
358
@@ -385,7 +420,7 @@ UnboundImport::UnboundImport(ImportDecl *ID)
385
420
: ID(ID), importLoc(ID->getLoc ()), options(), privateImportFileName(),
386
421
importKind(ID->getImportKind (), ID->getKindLoc()),
387
422
modulePath(ID->getModulePath ()), declPath(ID->getDeclPath ()),
388
- attrs(&ID->getAttrs ())
423
+ attrs(&ID->getAttrs ()), underlyingModule()
389
424
{
390
425
if (ID->isExported ())
391
426
options |= ImportFlags::Exported;
@@ -708,3 +743,165 @@ void BoundImport::validateScope(SourceFile &SF) {
708
743
709
744
unbound.ID .get ()->setDecls (ctx.AllocateCopy (decls));
710
745
}
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