Skip to content

Commit 4b5d885

Browse files
[NFC] Clarify semantics of getImportedModules.
The lack of clarity manifested as unexpected behavior when using getImportedModules to create the module import graph. The new behavior makes SPI-ness and Shadowing-ness behave similarly in terms of filtering. We also check if a filter is well-formed to avoid accidental empty import lists.
1 parent d3369f7 commit 4b5d885

File tree

6 files changed

+57
-7
lines changed

6 files changed

+57
-7
lines changed

include/swift/AST/Module.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,8 @@ class ModuleDecl : public DeclContext, public TypeDecl {
653653
Default = 1 << 1,
654654
/// Include imports declared with `@_implementationOnly`.
655655
ImplementationOnly = 1 << 2,
656-
/// Include imports of SPIs declared with `@_spi`
656+
/// Include imports of SPIs declared with `@_spi`. Non-SPI imports are
657+
/// included whether or not this flag is specified.
657658
SPIAccessControl = 1 << 3,
658659
/// Include imports shadowed by a cross-import overlay. Unshadowed imports
659660
/// are included whether or not this flag is specified.
@@ -664,8 +665,33 @@ class ModuleDecl : public DeclContext, public TypeDecl {
664665

665666
/// Looks up which modules are imported by this module.
666667
///
667-
/// \p filter controls whether public, private, or any imports are included
668-
/// in this list.
668+
/// \p filter controls which imports are included in the list.
669+
///
670+
/// There are three axes for categorizing imports:
671+
/// 1. Privacy: Exported/Private/ImplementationOnly (mutually exclusive).
672+
/// 2. SPI/non-SPI: An import of any privacy level may be @_spi("SPIName").
673+
/// 3. Shadowed/Non-shadowed: An import of any privacy level may be shadowed
674+
/// by a cross-import overlay.
675+
///
676+
/// It is also possible for SPI imports to be shadowed by a cross-import
677+
/// overlay.
678+
///
679+
/// If \p filter contains multiple privacy levels, modules at all the privacy
680+
/// levels are included.
681+
///
682+
/// If \p filter contains \c ImportFilterKind::SPIAccessControl, then both
683+
/// SPI and non-SPI imports are included. Otherwise, only non-SPI imports are
684+
/// included.
685+
///
686+
/// If \p filter contains \c ImportFilterKind::ShadowedByCrossImportOverlay,
687+
/// both shadowed and non-shadowed imports are included. Otherwise, only
688+
/// non-shadowed imports are included.
689+
///
690+
/// Clang modules have some additional complexities; see the implementation of
691+
/// \c ClangModuleUnit::getImportedModules for details.
692+
///
693+
/// \pre \p filter must contain at least one privacy level, i.e. one of
694+
/// \c Exported or \c Private or \c ImplementationOnly.
669695
void getImportedModules(SmallVectorImpl<ImportedModule> &imports,
670696
ImportFilter filter = ImportFilterKind::Exported) const;
671697

include/swift/Basic/OptionSet.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "llvm/ADT/None.h"
2121

22+
#include <cassert>
2223
#include <type_traits>
2324
#include <cstdint>
2425
#include <initializer_list>
@@ -98,6 +99,14 @@ class OptionSet {
9899
return Storage == set.Storage;
99100
}
100101

102+
/// Check if this option set contains any options from \p set.
103+
///
104+
/// \pre \p set must be non-empty.
105+
bool containsAny(OptionSet set) const {
106+
assert((bool)set && "argument must be non-empty");
107+
return (bool)((*this) & set);
108+
}
109+
101110
// '==' and '!=' are deliberately not defined because they provide a pitfall
102111
// where someone might use '==' but really want 'contains'. If you actually
103112
// want '==' behavior, use 'containsOnly'.

lib/AST/ImportCache.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ ImportSet &ImportCache::getImportSet(const DeclContext *dc) {
175175
ModuleDecl::ImportedModule{ImportPath::Access(), mod});
176176

177177
if (file) {
178+
// Should include both SPI & non-SPI.
178179
file->getImportedModules(imports,
179180
{ModuleDecl::ImportFilterKind::Default,
180181
ModuleDecl::ImportFilterKind::ImplementationOnly,
@@ -260,6 +261,7 @@ ImportCache::getAllAccessPathsNotShadowedBy(const ModuleDecl *mod,
260261
ModuleDecl::ImportedModule{ImportPath::Access(), currentMod});
261262

262263
if (auto *file = dyn_cast<FileUnit>(dc)) {
264+
// Should include both SPI & non-SPI
263265
file->getImportedModules(stack,
264266
{ModuleDecl::ImportFilterKind::Default,
265267
ModuleDecl::ImportFilterKind::ImplementationOnly,

lib/AST/Module.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,13 @@ void SourceFile::lookupPrecedenceGroupDirect(
11631163

11641164
void ModuleDecl::getImportedModules(SmallVectorImpl<ImportedModule> &modules,
11651165
ModuleDecl::ImportFilter filter) const {
1166+
assert(filter.containsAny(ImportFilter({
1167+
ModuleDecl::ImportFilterKind::Exported,
1168+
ModuleDecl::ImportFilterKind::Default,
1169+
ModuleDecl::ImportFilterKind::ImplementationOnly}))
1170+
&& "filter should have at least one of Exported|Private|ImplementationOnly"
1171+
);
1172+
11661173
FORWARD(getImportedModules, (modules, filter));
11671174
}
11681175

@@ -1187,11 +1194,12 @@ SourceFile::getImportedModules(SmallVectorImpl<ModuleDecl::ImportedModule> &modu
11871194
requiredFilter |= ModuleDecl::ImportFilterKind::Exported;
11881195
else if (desc.importOptions.contains(ImportFlags::ImplementationOnly))
11891196
requiredFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
1190-
else if (desc.importOptions.contains(ImportFlags::SPIAccessControl))
1191-
requiredFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl;
11921197
else
11931198
requiredFilter |= ModuleDecl::ImportFilterKind::Default;
11941199

1200+
if (desc.importOptions.contains(ImportFlags::SPIAccessControl))
1201+
requiredFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl;
1202+
11951203
if (!separatelyImportedOverlays.lookup(desc.module.importedModule).empty())
11961204
requiredFilter |= ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay;
11971205

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ static void printImports(raw_ostream &out,
116116

117117
SmallVector<ModuleDecl::ImportedModule, 4> ioiImport;
118118
M->getImportedModules(ioiImport,
119-
ModuleDecl::ImportFilterKind::ImplementationOnly);
119+
{ModuleDecl::ImportFilterKind::ImplementationOnly,
120+
ModuleDecl::ImportFilterKind::SPIAccessControl});
120121
ioiImportSet.insert(ioiImport.begin(), ioiImport.end());
121122
}
122123

lib/Serialization/Serialization.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,11 @@ void Serializer::writeInputBlock(const SerializationOptions &options) {
10391039
ImportSet privateImportSet =
10401040
getImportsAsSet(M, ModuleDecl::ImportFilterKind::Default);
10411041
ImportSet spiImportSet =
1042-
getImportsAsSet(M, ModuleDecl::ImportFilterKind::SPIAccessControl);
1042+
getImportsAsSet(M, {
1043+
ModuleDecl::ImportFilterKind::Exported,
1044+
ModuleDecl::ImportFilterKind::Default,
1045+
ModuleDecl::ImportFilterKind::SPIAccessControl
1046+
});
10431047

10441048
auto clangImporter =
10451049
static_cast<ClangImporter *>(M->getASTContext().getClangModuleLoader());

0 commit comments

Comments
 (0)