[Serialization] Load non-public transitive dependencies on @testable imports

A @testable import allows a client to call internal decls which may
refer to non-public dependencies. To support such a use case, load
non-public transitive dependencies of a module when it's imported
@testable from the main module.

This replaces the previous behavior where we loaded those dependencies
for any modules built for testing. This was risky as we would load more
module for any debug build, opening the door to a different behavior
between debug and release builds. In contrast, applying this logic to
@testable clients will only change the behavior of test targets.

rdar://107329303
This commit is contained in:
Alexis Laferrière
2023-03-28 12:12:36 -07:00
parent 87431a7a66
commit f7f69c6ae1
9 changed files with 72 additions and 13 deletions

View File

@@ -1688,7 +1688,8 @@ ModuleFileSharedCore::getTransitiveLoadingBehavior(
const Dependency &dependency,
bool debuggerMode,
bool isPartialModule,
StringRef packageName) const {
StringRef packageName,
bool forTestable) const {
if (isPartialModule) {
// Keep the merge-module behavior for legacy support. In that case
// we load all transitive dependencies from partial modules and
@@ -1717,7 +1718,7 @@ ModuleFileSharedCore::getTransitiveLoadingBehavior(
// Non-public imports are similar to implementation-only, the module
// loading behavior differs on loading those dependencies
// on testable imports.
if (isTestable() || !moduleIsResilient) {
if (forTestable || !moduleIsResilient) {
return ModuleLoadingBehavior::Required;
} else if (debuggerMode) {
return ModuleLoadingBehavior::Optional;
@@ -1730,6 +1731,7 @@ ModuleFileSharedCore::getTransitiveLoadingBehavior(
// Package dependencies are usually loaded only for import from the same
// package.
if ((!packageName.empty() && packageName == getModulePackageName()) ||
forTestable ||
!moduleIsResilient) {
return ModuleLoadingBehavior::Required;
} else if (debuggerMode) {