@@ -183,6 +183,13 @@ namespace {
183183 // / often enormous.
184184 SmallSetVector<ImportedModuleDesc, 64 > crossImportableModules;
185185
186+ // / The subset of \c crossImportableModules which may declare cross-imports.
187+ // /
188+ // / This is a performance optimization. Since most modules do not register
189+ // / any cross-imports, we can usually compare against this list, which is
190+ // / much, much smaller than \c crossImportableModules.
191+ SmallVector<ImportedModuleDesc, 16 > crossImportDeclaringModules;
192+
186193 // / The index of the next module in \c visibleModules that should be
187194 // / cross-imported.
188195 size_t nextModuleToCrossImport = 0 ;
@@ -230,13 +237,21 @@ namespace {
230237 // / then adds them to \c unboundImports using source locations from \p I.
231238 void crossImport (ModuleDecl *M, UnboundImport &I);
232239
240+ // / Discovers any cross-imports between \p newImport and
241+ // / \p oldImports and adds them to \c unboundImports, using source
242+ // / locations from \p I.
243+ void findCrossImportsInLists (UnboundImport &I,
244+ ArrayRef<ImportedModuleDesc> declaring,
245+ ArrayRef<ImportedModuleDesc> bystanding,
246+ bool shouldDiagnoseRedundantCrossImports);
247+
233248 // / Discovers any cross-imports between \p declaringImport and
234249 // / \p bystandingImport and adds them to \c unboundImports, using source
235250 // / locations from \p I.
236251 void findCrossImports (UnboundImport &I,
237252 const ImportedModuleDesc &declaringImport,
238253 const ImportedModuleDesc &bystandingImport,
239- SmallVectorImpl<Identifier> &overlayNames );
254+ bool shouldDiagnoseRedundantCrossImports );
240255
241256 // / Load a module referenced by an import statement.
242257 // /
@@ -852,55 +867,72 @@ void NameBinder::crossImport(ModuleDecl *M, UnboundImport &I) {
852867 // FIXME: Should we warn if M doesn't reexport underlyingModule?
853868 SF.addSeparatelyImportedOverlay (M, I.getUnderlyingModule ().get ());
854869
855- // FIXME: Most of the comparisons we do here are probably unnecessary. We
856- // only need to findCrossImports() on pairs where at least one of the two
857- // modules declares cross-imports, and most modules don't. This is low-hanging
858- // performance fruit.
859-
860870 auto newImports = crossImportableModules.getArrayRef ()
861- .slice (nextModuleToCrossImport);
871+ .drop_front (nextModuleToCrossImport);
872+
873+ if (newImports.empty ())
874+ // Nothing to do except crash when we read past the end of
875+ // crossImportableModules in that assert at the bottom.
876+ return ;
877+
862878 for (auto &newImport : newImports) {
863879 if (!canCrossImport (newImport))
864880 continue ;
865881
866- // Search imports up to, but not including or after, `newImport`.
882+ // First we check if any of the imports of modules that have declared
883+ // cross-imports have declared one with this module.
884+ findCrossImportsInLists (I, crossImportDeclaringModules, {newImport},
885+ /* shouldDiagnoseRedundantCrossImports=*/ false );
886+
887+ // If this module doesn't declare any cross-imports, we're done with this
888+ // import.
889+ if (!newImport.module .second ->mightDeclareCrossImportOverlays ())
890+ continue ;
891+
892+ // Fine, we need to do the slow-but-rare thing: check if this import
893+ // declares a cross-import with any previous one.
867894 auto oldImports =
868- make_range (crossImportableModules.getArrayRef ().data (), &newImport);
869- for (auto &oldImport : oldImports) {
870- if (!canCrossImport (oldImport))
895+ // Slice from the start of crossImportableModules up to newImport.
896+ llvm::makeArrayRef (crossImportableModules.getArrayRef ().data (),
897+ &newImport);
898+ findCrossImportsInLists (I, {newImport}, oldImports,
899+ /* shouldDiagnoseRedundantCrossImports=*/ true );
900+
901+ // Add this to the list of imports everyone needs to check against.
902+ crossImportDeclaringModules.push_back (newImport);
903+ }
904+
905+ // Catch potential memory smashers
906+ assert (newImports.data () ==
907+ &crossImportableModules[nextModuleToCrossImport] &&
908+ " findCrossImports() should never mutate visibleModules" );
909+
910+ nextModuleToCrossImport = crossImportableModules.size ();
911+ }
912+
913+ void
914+ NameBinder::findCrossImportsInLists (UnboundImport &I,
915+ ArrayRef<ImportedModuleDesc> declaring,
916+ ArrayRef<ImportedModuleDesc> bystanding,
917+ bool shouldDiagnoseRedundantCrossImports) {
918+ for (auto &declaringImport : declaring) {
919+ if (!canCrossImport (declaringImport))
920+ continue ;
921+
922+ for (auto &bystandingImport : bystanding) {
923+ if (!canCrossImport (bystandingImport))
871924 continue ;
872925
873- SmallVector<Identifier, 2 > newImportOverlays;
874- findCrossImports (I, newImport, oldImport, newImportOverlays);
875-
876- SmallVector<Identifier, 2 > oldImportOverlays;
877- findCrossImports (I, oldImport, newImport, oldImportOverlays);
878-
879- // If both sides of the cross-import declare some of the same overlays,
880- // this will cause some strange name lookup behavior; let's warn about it.
881- for (auto name : newImportOverlays) {
882- if (llvm::is_contained (oldImportOverlays, name)) {
883- ctx.Diags .diagnose (I.importLoc , diag::cross_imported_by_both_modules,
884- newImport.module .second ->getName (),
885- oldImport.module .second ->getName (), name);
886- }
887- }
888-
889- // If findCrossImports() ever changed the visibleModules list, we'd see
890- // memory smashers here.
891- assert (newImports.data () ==
892- &crossImportableModules[nextModuleToCrossImport] &&
893- " findCrossImports() should never mutate visibleModules" );
926+ findCrossImports (I, declaringImport, bystandingImport,
927+ shouldDiagnoseRedundantCrossImports);
894928 }
895929 }
896-
897- nextModuleToCrossImport = crossImportableModules.size ();
898930}
899931
900932void NameBinder::findCrossImports (UnboundImport &I,
901933 const ImportedModuleDesc &declaringImport,
902934 const ImportedModuleDesc &bystandingImport,
903- SmallVectorImpl<Identifier> &names ) {
935+ bool shouldDiagnoseRedundantCrossImports ) {
904936 assert (&declaringImport != &bystandingImport);
905937
906938 LLVM_DEBUG (
@@ -912,9 +944,17 @@ void NameBinder::findCrossImports(UnboundImport &I,
912944 ctx.Stats ->getFrontendCounters ().NumCrossImportsChecked ++;
913945
914946 // Find modules we need to import.
947+ SmallVector<Identifier, 4 > names;
915948 declaringImport.module .second ->findDeclaredCrossImportOverlays (
916949 bystandingImport.module .second ->getName (), names, I.importLoc );
917950
951+ // If we're diagnosing cases where we cross-import in both directions, get the
952+ // inverse list. Otherwise, leave the list empty.
953+ SmallVector<Identifier, 4 > oppositeNames;
954+ if (shouldDiagnoseRedundantCrossImports)
955+ bystandingImport.module .second ->findDeclaredCrossImportOverlays (
956+ declaringImport.module .second ->getName (), oppositeNames, I.importLoc );
957+
918958 if (ctx.Stats && !names.empty ())
919959 ctx.Stats ->getFrontendCounters ().NumCrossImportsFound ++;
920960
@@ -928,6 +968,11 @@ void NameBinder::findCrossImports(UnboundImport &I,
928968 unboundImports.emplace_back (declaringImport.module .second ->getASTContext (),
929969 I, name, declaringImport, bystandingImport);
930970
971+ if (llvm::is_contained (oppositeNames, name))
972+ ctx.Diags .diagnose (I.importLoc , diag::cross_imported_by_both_modules,
973+ declaringImport.module .second ->getName (),
974+ bystandingImport.module .second ->getName (), name);
975+
931976 LLVM_DEBUG ({
932977 auto &crossImportOptions = unboundImports.back ().options ;
933978 llvm::dbgs () << " " ;
0 commit comments