Skip to content

Commit 356946f

Browse files
authored
Merge pull request #34476 from scentini/implementation-only-symbol-visibility-fix
[cxx-interop] Fix SR-13785
2 parents 74e2cb6 + 6898aae commit 356946f

32 files changed

+401
-42
lines changed

lib/Sema/TypeCheckAccess.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/ASTWalker.h"
2323
#include "swift/AST/DiagnosticsSema.h"
2424
#include "swift/AST/ExistentialLayout.h"
25+
#include "swift/AST/Import.h"
2526
#include "swift/AST/Pattern.h"
2627
#include "swift/AST/ParameterList.h"
2728
#include "swift/AST/TypeCheckRequests.h"
@@ -1439,7 +1440,6 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
14391440
}
14401441
}
14411442
};
1442-
14431443
} // end anonymous namespace
14441444

14451445
/// Returns the kind of origin, implementation-only import or SPI declaration,
@@ -1459,6 +1459,40 @@ swift::getDisallowedOriginKind(const Decl *decl,
14591459
if (where.isSPI())
14601460
downgradeToWarning = DowngradeToWarning::Yes;
14611461

1462+
// Even if the current module is @_implementationOnly, Swift should
1463+
// not report an error in the cases where the decl is also exported from
1464+
// a non @_implementationOnly module. Thus, we check to see if there is
1465+
// a visible access path to the Clang decl, and only error out in case
1466+
// there is none.
1467+
auto filter = ModuleDecl::ImportFilter(
1468+
{ModuleDecl::ImportFilterKind::Exported,
1469+
ModuleDecl::ImportFilterKind::Default,
1470+
ModuleDecl::ImportFilterKind::SPIAccessControl,
1471+
ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay});
1472+
SmallVector<ImportedModule, 4> sfImportedModules;
1473+
SF->getImportedModules(sfImportedModules, filter);
1474+
if (auto clangDecl = decl->getClangDecl()) {
1475+
for (auto redecl : clangDecl->redecls()) {
1476+
if (auto tagReDecl = dyn_cast<clang::TagDecl>(redecl)) {
1477+
// This is a forward declaration. We ignore visibility of those.
1478+
if (tagReDecl->getBraceRange().isInvalid()) {
1479+
continue;
1480+
}
1481+
}
1482+
auto moduleWrapper =
1483+
decl->getASTContext().getClangModuleLoader()->getWrapperForModule(
1484+
redecl->getOwningModule());
1485+
auto visibleAccessPath =
1486+
find_if(sfImportedModules, [&moduleWrapper](auto importedModule) {
1487+
return importedModule.importedModule == moduleWrapper ||
1488+
!importedModule.importedModule
1489+
->isImportedImplementationOnly(moduleWrapper);
1490+
});
1491+
if (visibleAccessPath != sfImportedModules.end()) {
1492+
return DisallowedOriginKind::None;
1493+
}
1494+
}
1495+
}
14621496
// Implementation-only imported, cannot be reexported.
14631497
return DisallowedOriginKind::ImplementationOnly;
14641498
} else if (decl->isSPI() && !where.isSPI()) {
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
module UserA {
2-
header "user_a.h"
2+
header "user-a.h"
33
export *
44
}
55

66
module UserB {
7-
header "user_b.h"
7+
header "user-b.h"
88
export *
99
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@_exported import UserA
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@_exported import UserB
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USER_A_H
2+
#define TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USER_A_H
3+
4+
#include "helper.h"
5+
6+
#endif // TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USER_A_H
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USER_B_H
2+
#define TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USER_B_H
3+
4+
#include "helper.h"
5+
6+
#endif // TEST_INTEROP_C_IMPLEMENTATION_ONLY_IMPORTS_INPUTS_USER_B_H

test/Interop/C/implementation-only-imports/Inputs/user_a.h

Lines changed: 0 additions & 6 deletions
This file was deleted.

test/Interop/C/implementation-only-imports/Inputs/user_b.h

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: mkdir %t/use_module_a %t/use_module_b
3+
// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/use_module_a/UseModuleA.swiftmodule %S/Inputs/use-module-a.swift -I %S/Inputs
4+
// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/use_module_b/UseModuleB.swiftmodule %S/Inputs/use-module-b.swift -I %S/Inputs
5+
6+
// RUN: %target-swift-frontend -typecheck -swift-version 5 -I %t/use_module_a -I %t/use_module_b -I %S/Inputs %s
7+
8+
// Swift should consider all sources for a decl and recognize that the
9+
// decl is not hidden behind @_implementationOnly in all modules.
10+
11+
// This test, as well as `check-function-transitive-visibility.swift`
12+
// ensures that Swift looks into the transitive visible modules as well
13+
// when looking for the `getFortyTwo()` decl.
14+
15+
import UseModuleA
16+
@_implementationOnly import UseModuleB
17+
18+
@inlinable
19+
public func callFortyTwo() -> CInt {
20+
return getFortyTwo()
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: mkdir %t/use_module_a %t/use_module_b
3+
// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/use_module_a/UseModuleA.swiftmodule %S/Inputs/use-module-a.swift -I %S/Inputs
4+
// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/use_module_b/UseModuleB.swiftmodule %S/Inputs/use-module-b.swift -I %S/Inputs
5+
6+
// RUN: %target-swift-frontend -typecheck -swift-version 5 -I %t/use_module_a -I %t/use_module_b -I %S/Inputs %s
7+
8+
// Swift should consider all sources for a decl and recognize that the
9+
// decl is not hidden behind @_implementationOnly in all modules.
10+
11+
// This test, as well as `check-function-transitive-visibility-inversed.swift`
12+
// ensures that Swift looks into the transitive visible modules as well
13+
// when looking for the `getFortyTwo()` decl.
14+
15+
import UseModuleA
16+
@_implementationOnly import UseModuleB
17+
18+
@inlinable
19+
public func callFortyTwo() -> CInt {
20+
return getFortyTwo()
21+
}

0 commit comments

Comments
 (0)