Skip to content

Commit c710b6d

Browse files
authored
Merge pull request swiftlang#79173 from swiftlang/elsh/rel/pcmo-implOnly
[6.1][Package CMO] Prevent serializing types from SDK/system modules imported as @_implementationOnly.
2 parents b95cca6 + 75d8bab commit c710b6d

File tree

2 files changed

+91
-24
lines changed

2 files changed

+91
-24
lines changed

lib/SILOptimizer/IPO/CrossModuleOptimization.cpp

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -784,11 +784,26 @@ bool CrossModuleOptimization::checkImports(DeclContext *ctxt) const {
784784
ModuleDecl::ImportFilter filter;
785785

786786
if (isPackageCMOEnabled(M.getSwiftModule())) {
787-
// If Package CMO is enabled, decls imported with `package import`
787+
// When Package CMO is enabled, types imported with `package import`
788788
// or `@_spiOnly import` into this module should be allowed to be
789-
// serialized. They are used in decls with `package` or higher
790-
// access level, with or without @_spi; a client of this module
791-
// should be able to access them directly if in the same package.
789+
// serialized. These types may be used in APIs with `package` or
790+
// higher access level, with or without `@_spi`, and such APIs should
791+
// be serializable to allow direct access by another module if it's
792+
// in the same package.
793+
//
794+
// However, types are from modules imported as `@_implementationOnly`
795+
// should not be serialized, even if their defining modules are SDK
796+
// or system modules. Since these types are intended to remain hidden
797+
// from external clients, their metadata (e.g. field offsets) may be
798+
// stripped, making it unavailable for look up at runtime. If serialized,
799+
// the client will attempt to use the serialized accessor and fail
800+
// because the metadata is missing, leading to a linker error.
801+
//
802+
// This issue applies to transitively imported types as well;
803+
// `@_implementationOnly import Foundation` imports `ObjectiveC`
804+
// indirectly, and metadata for types like `NSObject` from `ObjectiveC`
805+
// can also be stripped, thus such types should not be allowed for
806+
// serialization.
792807
filter = { ModuleDecl::ImportFilterKind::ImplementationOnly };
793808
} else {
794809
// See if context is imported in a "regular" way, i.e. not with
@@ -804,17 +819,8 @@ bool CrossModuleOptimization::checkImports(DeclContext *ctxt) const {
804819

805820
auto &imports = M.getSwiftModule()->getASTContext().getImportCache();
806821
for (auto &desc : results) {
807-
if (imports.isImportedBy(moduleOfCtxt, desc.importedModule)) {
808-
// E.g. `@_implementationOnly import QuartzCore_Private.CALayerPrivate`
809-
// imports `Foundation` as its transitive dependency module; use of a
810-
// a `public` decl in `Foundation` such as `IndexSet` in a function
811-
// signature should not block serialization in Package CMO given the
812-
// function has `package` or higher access level.
813-
if (isPackageCMOEnabled(M.getSwiftModule()) &&
814-
moduleOfCtxt->isNonUserModule())
815-
continue;
822+
if (imports.isImportedBy(moduleOfCtxt, desc.importedModule))
816823
return false;
817-
}
818824
}
819825
return true;
820826
}

test/SILOptimizer/package-cmo-import-filter.swift

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
// RUN: %empty-directory(%t)
22
// RUN: split-file %s %t
33

4+
/// 1. Test `@_implementationOnly import`.
5+
// RUN: %target-build-swift-dylib(%t/%target-library-name(Utils)) \
6+
// RUN: %t/UtilsA.swift %t/UtilsB.swift \
7+
// RUN: -module-name Utils -emit-module -package-name Pkg \
8+
// RUN: -Xfrontend -experimental-package-cmo -Xfrontend -experimental-allow-non-resilient-access \
9+
// RUN: -enable-library-evolution -O -wmo
10+
// RUN: %target-sil-opt %t/Utils.swiftmodule -I %t -sil-verify-all -o %t/Utils.sil
11+
// RUN: %FileCheck %s --check-prefix=CHECK-UTILS < %t/Utils.sil
12+
13+
/// Verify accessing PkgKlass.second from a client in a dynamic context does not cause a linker error.
14+
// RUN: %target-build-swift -I %t -L %t %t/Client.swift -package-name Pkg \
15+
// RUN: -O -wmo -enable-library-evolution \
16+
// RUN: %target-rpath(%t) -lUtils -o %t/a.out
17+
18+
// RUN: %target-swift-frontend -emit-sil -I %t -L %t %t/Client.swift \
19+
// RUN: -package-name Pkg -O -wmo -enable-library-evolution \
20+
// RUN: -lUtils -o %t/Client.sil
21+
// RUN: %FileCheck %s --check-prefix=CHECK-CLIENT < %t/Client.sil
22+
23+
/// 2. Test `package import` and `@_spiOnly import`.
424
// RUN: %target-swift-frontend %t/CoreA.swift \
525
// RUN: -module-name=CoreA -package-name Pkg \
626
// RUN: -parse-as-library -emit-module \
@@ -13,32 +33,73 @@
1333
// RUN: -emit-module-path %t/CoreB.swiftmodule -I%t \
1434
// RUN: -O -wmo -enable-library-evolution
1535

16-
// RUN: %target-swift-frontend %t/Lib.swift \
17-
// RUN: -module-name=Lib -package-name Pkg \
36+
// RUN: %target-swift-frontend %t/UI.swift \
37+
// RUN: -module-name=UI -package-name Pkg \
1838
// RUN: -parse-as-library -emit-module \
1939
// RUN: -experimental-spi-only-imports \
20-
// RUN: -emit-module-path %t/Lib.swiftmodule -I %t \
40+
// RUN: -emit-module-path %t/UI.swiftmodule -I %t \
2141
// RUN: -experimental-package-cmo -experimental-allow-non-resilient-access \
22-
// RUN: -O -wmo -enable-library-evolution -Rmodule-loading 2> %t/Lib-result.txt
23-
// RUN: %target-sil-opt %t/Lib.swiftmodule -I %t -sil-verify-all -o %t/Lib.sil
24-
// RUN: %FileCheck %s < %t/Lib.sil
42+
// RUN: -O -wmo -enable-library-evolution -Rmodule-loading 2> %t/UI-result.txt
43+
// RUN: %target-sil-opt %t/UI.swiftmodule -I %t -sil-verify-all -o %t/UI.sil
44+
// RUN: %FileCheck %s < %t/UI.sil
2545

2646
// REQUIRES: swift_in_compiler
47+
// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=maccatalyst
48+
49+
//--- Client.swift
50+
package import Utils
51+
52+
package func clientFunc<T: PkgKlass>(_ list: [T]) {
53+
// closure #1 in clientFunc<A>(_:)
54+
// CHECK-CLIENT: sil private @$s6Client10clientFuncyySayxG5Utils8PkgKlassCRbzlFSo8NSObjectCxXEfU_ : $@convention(thin) <T where T : PkgKlass> (@in_guaranteed T) -> (@out NSObject, @error_indirect Never) {
55+
// CHECK-CLIENT: class_method {{.*}} #PkgKlass.second!getter : (PkgKlass) -> () -> NSObject, $@convention(method) (@guaranteed PkgKlass) -> @owned NSObject
56+
// CHECK-CLIENT: } // end sil function '$s6Client10clientFuncyySayxG5Utils8PkgKlassCRbzlFSo8NSObjectCxXEfU_'
57+
let result = list.map { $0.second }
58+
print(result)
59+
}
60+
61+
62+
//--- UtilsA.swift
63+
public import Foundation // public import to allow `NSObject` in API.
2764

65+
package class PkgKlass: NSObject {
66+
/// Serialized since it does _not_ reference a type from module imported as @_implementationOnly.
67+
// PkgKlass.first.getter
68+
// CHECK-UTILS-DAG: sil package [serialized_for_package] [canonical] @$s5Utils8PkgKlassC5firstSSvg : $@convention(method) (@guaranteed PkgKlass) -> @owned String {
69+
package var first: String
70+
71+
/// NOT serialized since it does reference a type from module imported as @_implementationOnly.
72+
// PkgKlass.second.getter
73+
// CHECK-UTILS-DAG: sil package_external [canonical] @$s5Utils8PkgKlassC6secondSo8NSObjectCvg : $@convention(method) (@guaranteed PkgKlass) -> @owned NSObject
74+
@objc package var second: NSObject
75+
76+
init(first: String, second: NSObject) {
77+
self.first = first
78+
self.second = second
79+
}
80+
}
81+
82+
//--- UtilsB.swift
83+
@_implementationOnly import Foundation
84+
85+
public func utilsFunc() {
86+
let x: NSString = "utilsfunc"
87+
print(x)
88+
}
2889

29-
//--- Lib.swift
90+
//--- UI.swift
3091
package import CoreA
3192
@_spiOnly public import CoreB
3293

3394
/// PkgStruct is imported with `package import` and should be serialized.
34-
// CHECK-DAG: sil package [serialized_for_package] [canonical] @$s3Lib7libFuncyy5CoreA9PkgStructVF : $@convention(thin) (@in_guaranteed PkgStruct) -> () {
95+
// CHECK-DAG: sil package [serialized_for_package] [canonical] @$s2UI7libFuncyy5CoreA9PkgStructVF : $@convention(thin) (@in_guaranteed PkgStruct) -> () {
3596
package func libFunc(_ arg: PkgStruct) {
3697
print(arg.pkgVar)
3798
}
3899

39100
/// PubStruct is imported with `@_spiOnly public import` and should be serialized.
40-
// CHECK-DAG: sil [serialized_for_package] [canonical] @$s3Lib7spiFuncyy5CoreB15PubStructForSPIVF : $@convention(thin) (@in_guaranteed PubStructForSPI) -> () {
41-
@_spi(InCoreB)
101+
// CHECK-DAG: sil [serialized_for_package] [canonical] @$s2UI7spiFuncyy5CoreB15PubStructForSPIVF : $@convention(thin) (@in_guaranteed PubStructForSPI) -> () {
102+
@_spi(GroupB)
42103
public func spiFunc(_ arg: PubStructForSPI) {
43104
print(arg.pubVarForSPI)
44105
}

0 commit comments

Comments
 (0)