Skip to content

Commit aebe9ea

Browse files
committed
Omit @objcImpl from module interfaces
Module interfaces should not include the @objcImplementation attribute, member implementations that are redundant with the ObjC header, or anything that would be invalid in an ordinary extension (e.g. overridden initializers, stored Swift-only properties).
1 parent a327c91 commit aebe9ea

File tree

4 files changed

+151
-6
lines changed

4 files changed

+151
-6
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
137137
return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic();
138138
}
139139

140+
static bool isInObjCImpl(const ValueDecl *VD) {
141+
auto *ED = dyn_cast<ExtensionDecl>(VD->getDeclContext());
142+
return ED && ED->isObjCImplementation();
143+
}
144+
140145
PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
141146
bool preferTypeRepr,
142147
bool printFullConvention,
@@ -208,9 +213,9 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
208213
if (!options.PrintSPIs && D->isSPI())
209214
return false;
210215

211-
// Skip anything that isn't 'public' or '@usableFromInline' or has a
212-
// _specialize attribute with a targetFunction parameter.
213216
if (auto *VD = dyn_cast<ValueDecl>(D)) {
217+
// Skip anything that isn't 'public' or '@usableFromInline' or has a
218+
// _specialize attribute with a targetFunction parameter.
214219
if (!isPublicOrUsableFromInline(VD) &&
215220
!isPrespecilizationDeclWithTarget(VD)) {
216221
// We do want to print private stored properties, without their
@@ -221,6 +226,13 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
221226

222227
return false;
223228
}
229+
230+
// Skip member implementations and @objc overrides in @objcImpl
231+
// extensions.
232+
if (VD->isObjCMemberImplementation()
233+
|| (isInObjCImpl(VD) && VD->getOverriddenDecl() && VD->isObjC())) {
234+
return false;
235+
}
224236
}
225237

226238
// Skip extensions that extend things we wouldn't print.
@@ -312,6 +324,7 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
312324
DAK_AccessControl,
313325
DAK_SetterAccess,
314326
DAK_Lazy,
327+
DAK_ObjCImplementation,
315328
DAK_StaticInitializeObjCMetadata,
316329
DAK_RestatedObjCConformance,
317330
DAK_NonSendable,
@@ -1125,8 +1138,10 @@ void PrintAST::printAttributes(const Decl *D) {
11251138

11261139
if (auto vd = dyn_cast<VarDecl>(D)) {
11271140
// Don't print @_hasInitialValue if we're printing an initializer
1128-
// expression or if the storage is resilient.
1129-
if (vd->isInitExposedToClients() || vd->isResilient())
1141+
// expression, if the storage is resilient, or if it's in an
1142+
// @objcImplementation extension (where final properties should appear
1143+
// computed).
1144+
if (vd->isInitExposedToClients() || vd->isResilient() || isInObjCImpl(vd))
11301145
Options.ExcludeAttrList.push_back(DAK_HasInitialValue);
11311146

11321147
if (!Options.PrintForSIL) {
@@ -2105,8 +2120,9 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
21052120
// Don't print accessors for trivially stored properties...
21062121
if (impl.isSimpleStored()) {
21072122
// ...unless we're printing for SIL, which expects a { get set? } on
2108-
// trivial properties
2109-
if (Options.PrintForSIL) {
2123+
// trivial properties, or in an @objcImpl extension, which treats
2124+
// final stored properties as computed.
2125+
if (Options.PrintForSIL || isInObjCImpl(ASD)) {
21102126
Printer << " { get " << (impl.supportsMutation() ? "set }" : "}");
21112127
}
21122128
// ...or you're private/internal(set), at which point we'll print
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module objc_implementation {
2+
header "objc_implementation.h"
3+
export *
4+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface ImplClass: NSObject
4+
5+
- (nonnull instancetype)init;
6+
7+
@property (assign) int implProperty;
8+
9+
- (void)mainMethod:(int)param;
10+
11+
@end
12+
13+
14+
@interface ImplClass (Category1)
15+
16+
- (void)category1Method:(int)param;
17+
18+
@end
19+
20+
21+
@interface ImplClass (Category2)
22+
23+
- (void)category2Method:(int)param;
24+
25+
@end
26+
27+
28+
@interface NoImplClass
29+
30+
- (void)noImplMethod:(int)param;
31+
32+
@end
33+
34+
@interface NoInitImplClass: NSObject
35+
36+
@property (readonly, strong, nonnull) NSString *s1;
37+
@property (strong, nonnull) NSString *s2;
38+
@property (readonly, strong, nonnull) NSString *s3;
39+
@property (strong, nonnull) NSString *s4;
40+
41+
@end
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -I %S/Inputs/objc_implementation -F %clang-importer-sdk-path/frameworks %s -import-underlying-module -swift-version 5 -enable-library-evolution -emit-module-interface-path %t.swiftinterface
2+
// RUN: %FileCheck --input-file %t.swiftinterface %s
3+
// RUN: %FileCheck --input-file %t.swiftinterface --check-prefix NEGATIVE %s
4+
// REQUIRES: objc_interop
5+
6+
// We should never see @_objcImplementation in the header
7+
// NEGATIVE-NOT: @_objcImplementation
8+
9+
//
10+
// @_objcImplementation class
11+
//
12+
13+
// CHECK-LABEL: extension objc_implementation.ImplClass {
14+
@_objcImplementation extension ImplClass {
15+
// CHECK-NOT: init()
16+
@objc public override init() {
17+
implProperty = 42
18+
implProperty2 = NSObject()
19+
super.init()
20+
}
21+
22+
// CHECK-NOT: var implProperty:
23+
@objc public var implProperty: Int32 {
24+
didSet { print(implProperty) }
25+
}
26+
27+
// CHECK-DAG: final public var implProperty2: ObjectiveC.NSObject? { get set }
28+
public final var implProperty2: NSObject?
29+
30+
// CHECK-NOT: func mainMethod
31+
@objc public func mainMethod(_: Int32) { print(implProperty) }
32+
33+
// CHECK-NOT: deinit
34+
}
35+
// CHECK: }
36+
37+
//
38+
// @_objcImplementation category
39+
//
40+
41+
// Empty category should be omitted, so there's only one `extension ImplClass`.
42+
// CHECK-NOT: extension objc_implementation.ImplClass {
43+
@_objcImplementation(Category1) extension ImplClass {
44+
// NEGATIVE-NOT: func category1Method
45+
@objc public func category1Method(_: Int32) {
46+
print("category1Method")
47+
}
48+
}
49+
50+
//
51+
// Second @_objcImplementation class, inherited initializer
52+
//
53+
54+
// NEGATIVE-NOT: extension objc_implementation.NoInitImplClass
55+
@_objcImplementation extension NoInitImplClass {
56+
// NEGATIVE-NOT: var s1:
57+
@objc public let s1 = "s1v"
58+
// NEGATIVE-NOT: var s2:
59+
@objc public var s2 = "s2v"
60+
// NEGATIVE-NOT: var s3:
61+
@objc(s3) public let s3 = "s3v"
62+
// NEGATIVE-NOT: var s4:
63+
@objc(s4) public var s4 = "s4v"
64+
}
65+
66+
//
67+
// @objc subclass of @_objcImplementation class
68+
//
69+
70+
// CHECK-LABEL: @objc @_inheritsConvenienceInitializers open class SwiftSubclass : objc_implementation.ImplClass {
71+
open class SwiftSubclass: ImplClass {
72+
// CHECK-DAG: @objc override dynamic open func mainMethod
73+
override open func mainMethod(_: Int32) {
74+
print("subclass mainMethod")
75+
}
76+
77+
// CHECK-DAG: @objc dynamic public init()
78+
// CHECK-DAG: @objc deinit
79+
}
80+
// CHECK: }
81+
82+
//
83+
// Epilogue
84+
//

0 commit comments

Comments
 (0)