Skip to content

Commit 9b29b3b

Browse files
authored
Merge pull request #21313 from jrose-apple/secret-imports
Early implementation of "implementation-only imports"
2 parents 7b654b3 + e89e66c commit 9b29b3b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+486
-160
lines changed

include/swift/AST/Attr.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,9 @@ DECL_ATTR(_private, PrivateImport,
390390
SIMPLE_DECL_ATTR(_alwaysEmitIntoClient, AlwaysEmitIntoClient,
391391
OnVar | OnSubscript | OnAbstractFunction | UserInaccessible,
392392
83)
393+
SIMPLE_DECL_ATTR(_implementationOnly, ImplementationOnly,
394+
OnImport | UserInaccessible,
395+
84)
393396

394397
#undef TYPE_ATTR
395398
#undef DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,9 @@ ERROR(module_not_testable,Fatal,
765765
ERROR(module_not_compiled_for_private_import,none,
766766
"module %0 was not compiled for private import", (Identifier))
767767

768+
ERROR(import_implementation_cannot_be_exported,none,
769+
"module %0 cannot be both exported and implementation-only", (Identifier))
770+
768771

769772
// Operator decls
770773
ERROR(ambiguous_operator_decls,none,

include/swift/AST/Module.h

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -419,18 +419,23 @@ class ModuleDecl : public DeclContext, public TypeDecl {
419419
SmallVectorImpl<AbstractFunctionDecl *> &results) const;
420420

421421
/// \sa getImportedModules
422-
enum class ImportFilter {
423-
All,
424-
Public,
425-
Private
422+
enum class ImportFilterKind {
423+
/// Include imports declared with `@_exported`.
424+
Public = 1 << 0,
425+
/// Include "regular" imports with no special annotation.
426+
Private = 1 << 1,
427+
/// Include imports declared with `@_implementationOnly`.
428+
ImplementationOnly = 1 << 2
426429
};
430+
/// \sa getImportedModules
431+
using ImportFilter = OptionSet<ImportFilterKind>;
427432

428433
/// Looks up which modules are imported by this module.
429434
///
430435
/// \p filter controls whether public, private, or any imports are included
431436
/// in this list.
432437
void getImportedModules(SmallVectorImpl<ImportedModule> &imports,
433-
ImportFilter filter = ImportFilter::Public) const;
438+
ImportFilter filter = ImportFilterKind::Public) const;
434439

435440
/// Looks up which modules are imported by this module, ignoring any that
436441
/// won't contain top-level decls.
@@ -758,7 +763,7 @@ class FileUnit : public DeclContext {
758763
/// \see ModuleDecl::getImportedModulesForLookup
759764
virtual void getImportedModulesForLookup(
760765
SmallVectorImpl<ModuleDecl::ImportedModule> &imports) const {
761-
return getImportedModules(imports, ModuleDecl::ImportFilter::Public);
766+
return getImportedModules(imports, ModuleDecl::ImportFilterKind::Public);
762767
}
763768

764769
/// Generates the list of libraries needed to link this file, based on its
@@ -891,23 +896,29 @@ class SourceFile final : public FileUnit {
891896
/// This source file has access to private declarations in the imported
892897
/// module.
893898
PrivateImport = 0x4,
899+
900+
/// The imported module is an implementation detail of this file and should
901+
/// not be required to be present if the main module is ever imported
902+
/// elsewhere.
903+
///
904+
/// Mutually exclusive with Exported.
905+
ImplementationOnly = 0x8
894906
};
895907

896908
/// \see ImportFlags
897909
using ImportOptions = OptionSet<ImportFlags>;
898910

899-
typedef std::pair<ImportOptions, StringRef> ImportOptionsAndFilename;
900-
901911
struct ImportedModuleDesc {
902912
ModuleDecl::ImportedModule module;
903913
ImportOptions importOptions;
904914
StringRef filename;
905915

906-
ImportedModuleDesc(ModuleDecl::ImportedModule module, ImportOptions options)
907-
: module(module), importOptions(options) {}
908916
ImportedModuleDesc(ModuleDecl::ImportedModule module, ImportOptions options,
909-
StringRef filename)
910-
: module(module), importOptions(options), filename(filename) {}
917+
StringRef filename = {})
918+
: module(module), importOptions(options), filename(filename) {
919+
assert(!(importOptions.contains(ImportFlags::Exported) &&
920+
importOptions.contains(ImportFlags::ImplementationOnly)));
921+
}
911922
};
912923

913924
private:

include/swift/Basic/OptionSet.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ class OptionSet {
8787
return !static_cast<bool>(set - *this);
8888
}
8989

90+
/// Check if this option set contains the exact same options as the given set.
91+
bool containsOnly(OptionSet set) {
92+
return Storage == set.Storage;
93+
}
94+
95+
// '==' and '!=' are deliberately not defined because they provide a pitfall
96+
// where someone might use '==' but really want 'contains'. If you actually
97+
// want '==' behavior, use 'containsOnly'.
98+
9099
/// Produce the union of two option sets.
91100
friend OptionSet operator|(OptionSet lhs, OptionSet rhs) {
92101
return OptionSet(lhs.Storage | rhs.Storage);
@@ -114,7 +123,7 @@ class OptionSet {
114123
return OptionSet(lhs.Storage & ~rhs.Storage);
115124
}
116125

117-
/// Produce the intersection of two option sets.
126+
/// Produce the difference of two option sets.
118127
friend OptionSet &operator-=(OptionSet &lhs, OptionSet rhs) {
119128
lhs.Storage &= ~rhs.Storage;
120129
return lhs;

include/swift/Serialization/ModuleFile.h

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,27 +135,48 @@ class ModuleFile
135135
const StringRef RawPath;
136136

137137
private:
138-
unsigned IsExported : 1;
138+
using ImportFilterKind = ModuleDecl::ImportFilterKind;
139+
const unsigned RawImportControl : 2;
139140
const unsigned IsHeader : 1;
140141
const unsigned IsScoped : 1;
141142

142-
Dependency(StringRef path, bool isHeader, bool exported, bool isScoped)
143-
: RawPath(path), IsExported(exported), IsHeader(isHeader),
144-
IsScoped(isScoped) {}
143+
static unsigned rawControlFromKind(ImportFilterKind importKind) {
144+
return llvm::countTrailingZeros(static_cast<unsigned>(importKind));
145+
}
146+
ImportFilterKind getImportControl() const {
147+
return static_cast<ImportFilterKind>(1 << RawImportControl);
148+
}
149+
150+
Dependency(StringRef path, bool isHeader, ImportFilterKind importControl,
151+
bool isScoped)
152+
: RawPath(path), RawImportControl(rawControlFromKind(importControl)),
153+
IsHeader(isHeader), IsScoped(isScoped) {
154+
assert(llvm::countPopulation(static_cast<unsigned>(importControl)) == 1 &&
155+
"must be a particular filter option, not a bitset");
156+
assert(getImportControl() == importControl && "not enough bits");
157+
}
145158

146159
public:
147-
Dependency(StringRef path, bool exported, bool isScoped)
148-
: Dependency(path, false, exported, isScoped) {}
160+
Dependency(StringRef path, ImportFilterKind importControl, bool isScoped)
161+
: Dependency(path, false, importControl, isScoped) {}
149162

150163
static Dependency forHeader(StringRef headerPath, bool exported) {
151-
return Dependency(headerPath, true, exported, false);
164+
auto importControl = exported ? ImportFilterKind::Public
165+
: ImportFilterKind::Private;
166+
return Dependency(headerPath, true, importControl, false);
152167
}
153168

154169
bool isLoaded() const {
155170
return Import.second != nullptr;
156171
}
157172

158-
bool isExported() const { return IsExported; }
173+
bool isExported() const {
174+
return getImportControl() == ImportFilterKind::Public;
175+
}
176+
bool isImplementationOnly() const {
177+
return getImportControl() == ImportFilterKind::ImplementationOnly;
178+
}
179+
159180
bool isHeader() const { return IsHeader; }
160181
bool isScoped() const { return IsScoped; }
161182

@@ -638,11 +659,26 @@ class ModuleFile
638659
// Out of line to avoid instantiation OnDiskChainedHashTable here.
639660
~ModuleFile();
640661

641-
/// Associates this module file with an AST module.
642-
///
643-
/// Returns any error that occurred during association, including validation
644-
/// that the module file is compatible with the module it's being loaded as.
645-
Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc);
662+
/// Associates this module file with the AST node representing it.
663+
///
664+
/// Checks that the file is compatible with the AST module it's being loaded
665+
/// into, loads any dependencies needed to understand the module, and updates
666+
/// the ASTContext and ClangImporter with search paths and other information
667+
/// from the module.
668+
///
669+
/// \param file The FileUnit that represents this file's place in the AST.
670+
/// \param diagLoc A location used for diagnostics that occur during loading.
671+
/// This does not include diagnostics about \e this file failing to load,
672+
/// but rather other things that might be imported as part of bringing the
673+
/// file into the AST.
674+
/// \param treatAsPartialModule If true, processes implementation-only
675+
/// information instead of assuming the client won't need it and shouldn't
676+
/// see it.
677+
///
678+
/// \returns any error that occurred during association, such as being
679+
/// compiled for a different OS.
680+
Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
681+
bool treatAsPartialModule);
646682

647683
/// Checks whether this module can be used.
648684
Status getStatus() const {

include/swift/Serialization/ModuleFormat.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 479; // stored property default arg
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 480; // Last change: import control
5656

5757
using DeclIDField = BCFixed<31>;
5858

@@ -452,14 +452,26 @@ enum class EnumElementRawValueKind : uint8_t {
452452
/// TODO: Float, string, char, etc.
453453
};
454454

455+
using EnumElementRawValueKindField = BCFixed<4>;
456+
455457
// These IDs must \em not be renumbered or reordered without incrementing
456458
// the module version.
457459
enum class ResilienceExpansion : uint8_t {
458460
Minimal = 0,
459461
Maximal,
460462
};
461463

462-
using EnumElementRawValueKindField = BCFixed<4>;
464+
// These IDs must \em not be renumbered or reordered without incrementing
465+
// the module version.
466+
enum class ImportControl : uint8_t {
467+
/// `import FooKit`
468+
Normal = 0,
469+
/// `@_exported import FooKit`
470+
Exported,
471+
/// `@_uncheckedImplementationOnly import FooKit`
472+
ImplementationOnly
473+
};
474+
using ImportControlField = BCFixed<2>;
463475

464476
/// The various types of blocks that can occur within a serialized Swift
465477
/// module.
@@ -636,8 +648,8 @@ namespace input_block {
636648

637649
using ImportedModuleLayout = BCRecordLayout<
638650
IMPORTED_MODULE,
639-
BCFixed<1>, // exported?
640-
BCFixed<1>, // scoped?
651+
ImportControlField, // import kind
652+
BCFixed<1>, // scoped?
641653
BCBlob // module name, with submodule path pieces separated by \0s.
642654
// If the 'scoped' flag is set, the final path piece is an access
643655
// path within the module.

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class SerializedModuleLoaderBase : public ModuleLoader {
9494
FileUnit *loadAST(ModuleDecl &M, Optional<SourceLoc> diagLoc,
9595
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer,
9696
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
97-
bool isFramework = false);
97+
bool isFramework, bool treatAsPartialModule);
9898

9999
/// Check whether the module with a given name can be imported without
100100
/// importing it.

lib/AST/Module.cpp

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,21 +1020,18 @@ void
10201020
SourceFile::getImportedModules(SmallVectorImpl<ModuleDecl::ImportedModule> &modules,
10211021
ModuleDecl::ImportFilter filter) const {
10221022
assert(ASTStage >= Parsed || Kind == SourceFileKind::SIL);
1023+
assert(filter && "no imports requested?");
10231024
for (auto desc : Imports) {
1024-
switch (filter) {
1025-
case ModuleDecl::ImportFilter::All:
1026-
break;
1027-
case ModuleDecl::ImportFilter::Public:
1028-
if (!desc.importOptions.contains(ImportFlags::Exported))
1029-
continue;
1030-
break;
1031-
case ModuleDecl::ImportFilter::Private:
1032-
if (desc.importOptions.contains(ImportFlags::Exported))
1033-
continue;
1034-
break;
1035-
}
1025+
ModuleDecl::ImportFilterKind requiredKind;
1026+
if (desc.importOptions.contains(ImportFlags::Exported))
1027+
requiredKind = ModuleDecl::ImportFilterKind::Public;
1028+
else if (desc.importOptions.contains(ImportFlags::ImplementationOnly))
1029+
requiredKind = ModuleDecl::ImportFilterKind::ImplementationOnly;
1030+
else
1031+
requiredKind = ModuleDecl::ImportFilterKind::Private;
10361032

1037-
modules.push_back(desc.module);
1033+
if (filter.contains(requiredKind))
1034+
modules.push_back(desc.module);
10381035
}
10391036
}
10401037

@@ -1277,9 +1274,14 @@ forAllImportedModules(ModuleDecl *topLevel, ModuleDecl::AccessPathTy thisPath,
12771274
llvm::SmallSet<ImportedModule, 32, ModuleDecl::OrderImportedModules> visited;
12781275
SmallVector<ImportedModule, 32> stack;
12791276

1280-
auto filter = respectVisibility ? ModuleDecl::ImportFilter::Public
1281-
: ModuleDecl::ImportFilter::All;
1282-
topLevel->getImportedModules(stack, filter);
1277+
ModuleDecl::ImportFilter filter = ModuleDecl::ImportFilterKind::Public;
1278+
if (!respectVisibility)
1279+
filter |= ModuleDecl::ImportFilterKind::Private;
1280+
1281+
ModuleDecl::ImportFilter topLevelFilter = filter;
1282+
if (!respectVisibility)
1283+
topLevelFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
1284+
topLevel->getImportedModules(stack, topLevelFilter);
12831285

12841286
// Make sure the top-level module is first; we want pre-order-ish traversal.
12851287
AccessPathTy overridingPath;
@@ -1330,9 +1332,11 @@ bool FileUnit::forAllVisibleModules(
13301332

13311333
if (auto SF = dyn_cast<SourceFile>(this)) {
13321334
// Handle privately visible modules as well.
1333-
// FIXME: Should this apply to all FileUnits?
1335+
ModuleDecl::ImportFilter importFilter;
1336+
importFilter |= ModuleDecl::ImportFilterKind::Private;
1337+
importFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
13341338
SmallVector<ModuleDecl::ImportedModule, 4> imports;
1335-
SF->getImportedModules(imports, ModuleDecl::ImportFilter::Private);
1339+
SF->getImportedModules(imports, importFilter);
13361340
for (auto importPair : imports)
13371341
if (!importPair.second->forAllVisibleModules(importPair.first, fn))
13381342
return false;

lib/AST/UnqualifiedLookup.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,8 +1044,12 @@ void UnqualifiedLookupFactory::recordDependencyOnTopLevelName(
10441044
void UnqualifiedLookupFactory::addImportedResults(DeclContext *const dc) {
10451045
// Add private imports to the extra search list.
10461046
SmallVector<ModuleDecl::ImportedModule, 8> extraImports;
1047-
if (auto FU = dyn_cast<FileUnit>(dc))
1048-
FU->getImportedModules(extraImports, ModuleDecl::ImportFilter::Private);
1047+
if (auto FU = dyn_cast<FileUnit>(dc)) {
1048+
ModuleDecl::ImportFilter importFilter;
1049+
importFilter |= ModuleDecl::ImportFilterKind::Private;
1050+
importFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
1051+
FU->getImportedModules(extraImports, importFilter);
1052+
}
10491053

10501054
using namespace namelookup;
10511055
SmallVector<ValueDecl *, 8> CurModuleResults;

0 commit comments

Comments
 (0)