@@ -183,6 +183,13 @@ namespace {
183
183
// / often enormous.
184
184
SmallSetVector<ImportedModuleDesc, 64 > crossImportableModules;
185
185
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
+
186
193
// / The index of the next module in \c visibleModules that should be
187
194
// / cross-imported.
188
195
size_t nextModuleToCrossImport = 0 ;
@@ -230,13 +237,21 @@ namespace {
230
237
// / then adds them to \c unboundImports using source locations from \p I.
231
238
void crossImport (ModuleDecl *M, UnboundImport &I);
232
239
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
+
233
248
// / Discovers any cross-imports between \p declaringImport and
234
249
// / \p bystandingImport and adds them to \c unboundImports, using source
235
250
// / locations from \p I.
236
251
void findCrossImports (UnboundImport &I,
237
252
const ImportedModuleDesc &declaringImport,
238
253
const ImportedModuleDesc &bystandingImport,
239
- SmallVectorImpl<Identifier> &overlayNames );
254
+ bool shouldDiagnoseRedundantCrossImports );
240
255
241
256
// / Load a module referenced by an import statement.
242
257
// /
@@ -852,55 +867,72 @@ void NameBinder::crossImport(ModuleDecl *M, UnboundImport &I) {
852
867
// FIXME: Should we warn if M doesn't reexport underlyingModule?
853
868
SF.addSeparatelyImportedOverlay (M, I.getUnderlyingModule ().get ());
854
869
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
-
860
870
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
+
862
878
for (auto &newImport : newImports) {
863
879
if (!canCrossImport (newImport))
864
880
continue ;
865
881
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.
867
894
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))
871
924
continue ;
872
925
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);
894
928
}
895
929
}
896
-
897
- nextModuleToCrossImport = crossImportableModules.size ();
898
930
}
899
931
900
932
void NameBinder::findCrossImports (UnboundImport &I,
901
933
const ImportedModuleDesc &declaringImport,
902
934
const ImportedModuleDesc &bystandingImport,
903
- SmallVectorImpl<Identifier> &names ) {
935
+ bool shouldDiagnoseRedundantCrossImports ) {
904
936
assert (&declaringImport != &bystandingImport);
905
937
906
938
LLVM_DEBUG (
@@ -912,9 +944,17 @@ void NameBinder::findCrossImports(UnboundImport &I,
912
944
ctx.Stats ->getFrontendCounters ().NumCrossImportsChecked ++;
913
945
914
946
// Find modules we need to import.
947
+ SmallVector<Identifier, 4 > names;
915
948
declaringImport.module .second ->findDeclaredCrossImportOverlays (
916
949
bystandingImport.module .second ->getName (), names, I.importLoc );
917
950
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
+
918
958
if (ctx.Stats && !names.empty ())
919
959
ctx.Stats ->getFrontendCounters ().NumCrossImportsFound ++;
920
960
@@ -928,6 +968,11 @@ void NameBinder::findCrossImports(UnboundImport &I,
928
968
unboundImports.emplace_back (declaringImport.module .second ->getASTContext (),
929
969
I, name, declaringImport, bystandingImport);
930
970
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
+
931
976
LLVM_DEBUG ({
932
977
auto &crossImportOptions = unboundImports.back ().options ;
933
978
llvm::dbgs () << " " ;
0 commit comments