Skip to content

Commit 5993468

Browse files
committed
[Serialization] Differentiate module loading behavior for non-public imports
Differentiate `internal` and `fileprivate` imports from implementation-only imports at the module-wide level to offer a different module loading strategy. The main difference is for non-public imports from a module with testing enabled to be loaded by transitive clients. Ideally, we would only load transitive non-public dependencies on testable imports of the middle module. The current module loading logic doesn't allow for this behavior easily as a module may be first loaded for a normal import and extra dependencies would have to be loaded on later imports. We may want to refactor the module loading logic to allow this if needed. rdar://106514965
1 parent d7cd65c commit 5993468

File tree

10 files changed

+91
-20
lines changed

10 files changed

+91
-20
lines changed

include/swift/AST/Module.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -857,16 +857,20 @@ class ModuleDecl
857857
Exported = 1 << 0,
858858
/// Include "regular" imports with an access-level of `public`.
859859
Default = 1 << 1,
860-
/// Include imports declared with `@_implementationOnly` or with an
861-
/// access-level of `package` or less.
860+
/// Include imports declared with `@_implementationOnly`.
862861
ImplementationOnly = 1 << 2,
863862
/// Include imports declared with `package import`.
864863
PackageOnly = 1 << 3,
864+
/// Include imports marked `internal` or lower. These differs form
865+
/// implementation-only imports by stricter type-checking and loading
866+
/// policies. At this moment, we can group them under the same category
867+
/// as they have the same loading behavior.
868+
InternalOrBelow = 1 << 4,
865869
/// Include imports declared with `@_spiOnly`.
866-
SPIOnly = 1 << 4,
870+
SPIOnly = 1 << 5,
867871
/// Include imports shadowed by a cross-import overlay. Unshadowed imports
868872
/// are included whether or not this flag is specified.
869-
ShadowedByCrossImportOverlay = 1 << 5
873+
ShadowedByCrossImportOverlay = 1 << 6
870874
};
871875
/// \sa getImportedModules
872876
using ImportFilter = OptionSet<ImportFilterKind>;
@@ -877,6 +881,7 @@ class ModuleDecl
877881
ImportFilterKind::Default,
878882
ImportFilterKind::ImplementationOnly,
879883
ImportFilterKind::PackageOnly,
884+
ImportFilterKind::InternalOrBelow,
880885
ImportFilterKind::SPIOnly,
881886
ImportFilterKind::ShadowedByCrossImportOverlay};
882887
}
@@ -890,6 +895,7 @@ class ModuleDecl
890895
ImportFilterKind::Default,
891896
ImportFilterKind::ImplementationOnly,
892897
ImportFilterKind::PackageOnly,
898+
ImportFilterKind::InternalOrBelow,
893899
ImportFilterKind::SPIOnly};
894900
}
895901

lib/AST/ImportCache.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ ImportSet &ImportCache::getImportSet(const DeclContext *dc) {
195195
file->getImportedModules(imports,
196196
{ModuleDecl::ImportFilterKind::Default,
197197
ModuleDecl::ImportFilterKind::ImplementationOnly,
198+
ModuleDecl::ImportFilterKind::InternalOrBelow,
198199
ModuleDecl::ImportFilterKind::PackageOnly,
199200
ModuleDecl::ImportFilterKind::SPIOnly});
200201
}
@@ -280,6 +281,7 @@ ImportCache::getAllAccessPathsNotShadowedBy(const ModuleDecl *mod,
280281
file->getImportedModules(stack,
281282
{ModuleDecl::ImportFilterKind::Default,
282283
ModuleDecl::ImportFilterKind::ImplementationOnly,
284+
ModuleDecl::ImportFilterKind::InternalOrBelow,
283285
ModuleDecl::ImportFilterKind::PackageOnly,
284286
ModuleDecl::ImportFilterKind::SPIOnly});
285287
}

lib/AST/Module.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,15 +2115,14 @@ SourceFile::getImportedModules(SmallVectorImpl<ImportedModule> &modules,
21152115
if (!Imports)
21162116
return;
21172117

2118-
bool moduleIsResilient = getParentModule()->getResilienceStrategy() ==
2119-
ResilienceStrategy::Resilient;
21202118
for (auto desc : *Imports) {
21212119
ModuleDecl::ImportFilter requiredFilter;
21222120
if (desc.options.contains(ImportFlags::Exported))
21232121
requiredFilter |= ModuleDecl::ImportFilterKind::Exported;
2124-
else if (desc.options.contains(ImportFlags::ImplementationOnly) ||
2125-
(desc.accessLevel <= AccessLevel::Internal && moduleIsResilient))
2122+
else if (desc.options.contains(ImportFlags::ImplementationOnly))
21262123
requiredFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
2124+
else if (desc.accessLevel <= AccessLevel::Internal)
2125+
requiredFilter |= ModuleDecl::ImportFilterKind::InternalOrBelow;
21272126
else if (desc.accessLevel <= AccessLevel::Package)
21282127
requiredFilter |= ModuleDecl::ImportFilterKind::PackageOnly;
21292128
else if (desc.options.contains(ImportFlags::SPIOnly))
@@ -2480,6 +2479,7 @@ SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const
24802479

24812480
ModuleDecl::ImportFilter topLevelFilter = filter;
24822481
topLevelFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
2482+
topLevelFilter |= ModuleDecl::ImportFilterKind::InternalOrBelow;
24832483
topLevelFilter |= ModuleDecl::ImportFilterKind::PackageOnly,
24842484
topLevelFilter |= ModuleDecl::ImportFilterKind::SPIOnly;
24852485
topLevel->getImportedModules(stack, topLevelFilter);

lib/Serialization/ModuleFile.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ void ModuleFile::getImportedModules(SmallVectorImpl<ImportedModule> &results,
468468
continue;
469469
}
470470

471+
} else if (dep.isInternalOrBelow()) {
472+
if (!filter.contains(ModuleDecl::ImportFilterKind::InternalOrBelow))
473+
continue;
474+
471475
} else if (dep.isPackageOnly()) {
472476
if (!filter.contains(ModuleDecl::ImportFilterKind::PackageOnly))
473477
continue;

lib/Serialization/ModuleFile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ class ModuleFile
107107
bool isImplementationOnly() const {
108108
return Core.isImplementationOnly();
109109
}
110+
bool isInternalOrBelow() const {
111+
return Core.isInternalOrBelow();
112+
}
110113
bool isPackageOnly() const {
111114
return Core.isPackageOnly();
112115
}

lib/Serialization/ModuleFileSharedCore.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,8 @@ getActualImportControl(unsigned rawValue) {
10211021
return ModuleDecl::ImportFilterKind::Exported;
10221022
case static_cast<unsigned>(serialization::ImportControl::ImplementationOnly):
10231023
return ModuleDecl::ImportFilterKind::ImplementationOnly;
1024+
case static_cast<unsigned>(serialization::ImportControl::InternalOrBelow):
1025+
return ModuleDecl::ImportFilterKind::InternalOrBelow;
10241026
case static_cast<unsigned>(serialization::ImportControl::PackageOnly):
10251027
return ModuleDecl::ImportFilterKind::PackageOnly;
10261028
default:
@@ -1711,6 +1713,19 @@ ModuleFileSharedCore::getTransitiveLoadingBehavior(
17111713
}
17121714
}
17131715

1716+
if (dependency.isInternalOrBelow()) {
1717+
// Non-public imports are similar to implementation-only, the module
1718+
// loading behavior differs on loading those dependencies
1719+
// on testable imports.
1720+
if (isTestable() || !moduleIsResilient) {
1721+
return ModuleLoadingBehavior::Required;
1722+
} else if (debuggerMode) {
1723+
return ModuleLoadingBehavior::Optional;
1724+
} else {
1725+
return ModuleLoadingBehavior::Ignored;
1726+
}
1727+
}
1728+
17141729
if (dependency.isPackageOnly()) {
17151730
// Package dependencies are usually loaded only for import from the same
17161731
// package.

lib/Serialization/ModuleFileSharedCore.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class ModuleFileSharedCore {
119119

120120
private:
121121
using ImportFilterKind = ModuleDecl::ImportFilterKind;
122-
const unsigned RawImportControl : 2;
122+
const unsigned RawImportControl : 3;
123123
const unsigned IsHeader : 1;
124124
const unsigned IsScoped : 1;
125125

@@ -159,6 +159,9 @@ class ModuleFileSharedCore {
159159
bool isImplementationOnly() const {
160160
return getImportControl() == ImportFilterKind::ImplementationOnly;
161161
}
162+
bool isInternalOrBelow() const {
163+
return getImportControl() == ImportFilterKind::InternalOrBelow;
164+
}
162165
bool isPackageOnly() const {
163166
return getImportControl() == ImportFilterKind::PackageOnly;
164167
}
@@ -573,6 +576,16 @@ class ModuleFileSharedCore {
573576
return ModulePackageName;
574577
}
575578

579+
/// Is the module built with testing enabled?
580+
bool isTestable() const {
581+
return Bits.IsTestable;
582+
}
583+
584+
/// Whether the module is resilient. ('-enable-library-evolution')
585+
ResilienceStrategy getResilienceStrategy() const {
586+
return ResilienceStrategy(Bits.ResilienceStrategy);
587+
}
588+
576589
/// Returns the list of modules this module depends on.
577590
ArrayRef<Dependency> getDependencies() const {
578591
return Dependencies;

lib/Serialization/ModuleFormat.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 754; // allocbox move debug info
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 755; // InternalOrBelow dependency
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -583,12 +583,14 @@ enum class ImportControl : uint8_t {
583583
Normal = 0,
584584
/// `@_exported import FooKit`
585585
Exported,
586-
/// `@_uncheckedImplementationOnly import FooKit`
586+
/// `@_implementationOnly import FooKit`
587587
ImplementationOnly,
588+
/// `internal import FooKit` or more restrictive.
589+
InternalOrBelow,
588590
/// `package import FooKit`
589-
PackageOnly
591+
PackageOnly,
590592
};
591-
using ImportControlField = BCFixed<2>;
593+
using ImportControlField = BCFixed<3>;
592594

593595
// These IDs must \em not be renumbered or reordered without incrementing
594596
// the module version.

lib/Serialization/Serialization.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,8 @@ void Serializer::writeInputBlock(const SerializationOptions &options) {
12181218
getImportsAsSet(M, ModuleDecl::ImportFilterKind::Default);
12191219
ImportSet packageOnlyImportSet =
12201220
getImportsAsSet(M, ModuleDecl::ImportFilterKind::PackageOnly);
1221+
ImportSet internalOrBelowImportSet =
1222+
getImportsAsSet(M, ModuleDecl::ImportFilterKind::InternalOrBelow);
12211223

12221224
auto clangImporter =
12231225
static_cast<ClangImporter *>(M->getASTContext().getClangModuleLoader());
@@ -1267,6 +1269,8 @@ void Serializer::writeInputBlock(const SerializationOptions &options) {
12671269
stableImportControl = ImportControl::Normal;
12681270
else if (packageOnlyImportSet.count(import))
12691271
stableImportControl = ImportControl::PackageOnly;
1272+
else if (internalOrBelowImportSet.count(import))
1273+
stableImportControl = ImportControl::InternalOrBelow;
12701274
else
12711275
stableImportControl = ImportControl::ImplementationOnly;
12721276

test/Serialization/access-level-import-dependencies.swift

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ private import HiddenDep
4242
// RUN: -enable-experimental-feature AccessLevelOnImport
4343

4444
// RUN: %target-swift-frontend -typecheck %t/ClientOfPublic.swift -I %t \
45-
// RUN: -enable-library-evolution -package-name MyOtherPackage \
46-
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=VISIBLE-HIDDEN-DEP %s
47-
// VISIBLE-HIDDEN-DEP: source: '{{.*}}HiddenDep.swiftmodule'
45+
// RUN: -package-name MyOtherPackage \
46+
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=VISIBLE-DEP %s
47+
// VISIBLE-DEP: loaded module 'HiddenDep'
4848
//--- ClientOfPublic.swift
4949
import PublicDep
5050

5151
// RUN: %target-swift-frontend -typecheck %t/ClientOfNonPublic.swift -I %t \
52-
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=HIDDEN-HIDDEN-DEP %s
53-
// HIDDEN-HIDDEN-DEP-NOT: HiddenDep
52+
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=HIDDEN-DEP %s
53+
// HIDDEN-DEP-NOT: loaded module 'HiddenDep'
5454
//--- ClientOfNonPublic.swift
5555
import PackageDep
5656
import InternalDep
@@ -70,7 +70,29 @@ import PrivateDep
7070
// RUN: -enable-experimental-feature AccessLevelOnImport
7171

7272
// RUN: %target-swift-frontend -typecheck %t/ClientOfPublic.swift -I %t \
73-
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=VISIBLE-HIDDEN-DEP %s
73+
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=VISIBLE-DEP %s
7474
// RUN: %target-swift-frontend -typecheck %t/ClientOfNonPublic.swift -I %t \
75-
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=VISIBLE-HIDDEN-DEP %s
75+
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=VISIBLE-DEP %s
7676

77+
/// Even with resilience, all access-level dependencies are visible to clients
78+
/// for modules with testing enabled.
79+
// RUN: %target-swift-frontend -emit-module %t/PublicDep.swift -o %t -I %t \
80+
// RUN: -enable-library-evolution -enable-testing \
81+
// RUN: -enable-experimental-feature AccessLevelOnImport
82+
// RUN: %target-swift-frontend -emit-module %t/PackageDep.swift -o %t -I %t \
83+
// RUN: -enable-library-evolution -enable-testing -package-name MyPackage \
84+
// RUN: -enable-experimental-feature AccessLevelOnImport
85+
// RUN: %target-swift-frontend -emit-module %t/InternalDep.swift -o %t -I %t \
86+
// RUN: -enable-library-evolution -enable-testing \
87+
// RUN: -enable-experimental-feature AccessLevelOnImport
88+
// RUN: %target-swift-frontend -emit-module %t/FileprivateDep.swift -o %t -I %t \
89+
// RUN: -enable-library-evolution -enable-testing \
90+
// RUN: -enable-experimental-feature AccessLevelOnImport
91+
// RUN: %target-swift-frontend -emit-module %t/PrivateDep.swift -o %t -I %t \
92+
// RUN: -enable-library-evolution -enable-testing \
93+
// RUN: -enable-experimental-feature AccessLevelOnImport
94+
95+
// RUN: %target-swift-frontend -typecheck %t/ClientOfPublic.swift -I %t \
96+
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=VISIBLE-DEP %s
97+
// RUN: %target-swift-frontend -typecheck %t/ClientOfNonPublic.swift -I %t \
98+
// RUN: -Rmodule-loading 2>&1 | %FileCheck -check-prefix=VISIBLE-DEP %s

0 commit comments

Comments
 (0)