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