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