Skip to content

Commit 533de8a

Browse files
committed
Make all internal-or-more @_objcImpl members impls
1 parent eaf12b4 commit 533de8a

File tree

3 files changed

+49
-29
lines changed

3 files changed

+49
-29
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ you replace a native Objective-C `@implementation Foo (Bar)` with a Swift
621621

622622
You create a class with this feature very differently from normal ObjC interop:
623623

624-
1. Hand-write headers that declare the classes' Objective-C interface, just as
624+
1. Hand-write headers that declare the class's Objective-C interface, just as
625625
you would for a native Objective-C class. Since you're handwriting these
626626
headers, you can write them just as you would for an Objective-C class:
627627
splitting them across multiple files, grouping related declarations together,
@@ -643,28 +643,35 @@ You create a class with this feature very differently from normal ObjC interop:
643643
* To implement a category in Swift, use
644644
`@_objcImplementation(CategoryName) extension ClassName`.
645645

646-
* We should think about Objective-C generics.
647-
648-
4. Every member of an `@_objcImplementation` extension should be either `@objc`
649-
or `final`. The `final` members can be made `public` if you want to expose
650-
them to Swift clients. The `@objc` members are implicitly `dynamic`.
646+
The members of an `@_objcImplementation` extension should fall into one of
647+
three categories:
651648

652-
* Eventually, we'd like to allow members which are not explicitly marked
653-
`@objc`. The compiler will check that they match something declared in the
654-
header, and if they do, it will automatically apply appropriate attributes.
655-
If not, it will reject them. Members with an explicit `@objc` will not be
656-
checked, so you can define helpers that are callable by selector but not in
657-
any header.
649+
* **Swift-only members** include any member marked `final`. These are not
650+
`@objc` or `dynamic` and are only callable from Swift. Use these for
651+
Swift-only APIs, random helper methods, etc.
652+
653+
* **ObjC helper members** include any non-`final` member marked `fileprivate`
654+
or `private`. These are implicitly `@objc dynamic`. Use these for action
655+
methods, selector-based callbacks, and other situations where you need a
656+
helper method to be accessible from an Objective-C message.
658657

659-
* Eventually, we want the main `@_objcImplementation` extension to be able to
660-
declare stored properties that aren't in the interface. We also want
661-
`final` stored properties to be allowed to be resilent Swift types, but
662-
it's not clear how to achieve that without boxing them in `__SwiftValue`
663-
(which we might do as a stopgap).
658+
* **Member implementations** include any other non-`final` member. These are
659+
implicitly `@objc dynamic` and must match a member declared in the
660+
Objective-C header. Use these to implement the APIs declared in your
661+
headers. Swift will emit an error if these don't match your headers.
662+
663+
Notes:
664+
665+
* We don't currently plan to support ObjC generics.
666+
667+
* Eventually, we want the main `@_objcImplementation` extension to be able to
668+
declare stored properties that aren't in the interface. We also want
669+
`final` stored properties to be allowed to be resilent Swift types, but
670+
it's not clear how to achieve that without boxing them in `__SwiftValue`
671+
(which we might do as a stopgap).
664672

665-
* We should think about ObjC `direct` members.
666-
667-
* Access control design for ObjC methods TBD.
673+
* We should think about ObjC "direct" members, but that would probably
674+
require a way to spell this in Swift.
668675

669676
## `@_objc_non_lazy_realization`
670677

lib/AST/Decl.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3709,6 +3709,21 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
37093709
VD->getDeclContext()->getParentModule() == useDC->getParentModule();
37103710
}
37113711

3712+
/// Checks if \p VD is an ObjC member implementation:
3713+
///
3714+
/// \li It's in an \c \@_objcImplementation extension
3715+
/// \li It's \c \@objc
3716+
/// \li Its access level is not \c private or \c fileprivate
3717+
static bool
3718+
isObjCMemberImplementation(const ValueDecl *VD,
3719+
llvm::function_ref<AccessLevel()> getAccessLevel) {
3720+
if (auto ED = dyn_cast<ExtensionDecl>(VD->getDeclContext()))
3721+
if (ED->isObjCImplementation())
3722+
return VD->isObjC() && getAccessLevel() >= AccessLevel::Internal;
3723+
3724+
return false;
3725+
}
3726+
37123727
/// Checks if \p VD may be used from \p useDC, taking \@testable and \@_spi
37133728
/// imports into account.
37143729
///
@@ -3723,13 +3738,11 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD,
37233738
bool forConformance,
37243739
bool includeInlineable,
37253740
llvm::function_ref<AccessLevel()> getAccessLevel) {
3726-
// If this is an @_objcImplementation member implementation, forbid access and
3727-
// use the imported decl instead.
3728-
// FIXME: Should just use VD->getObjCInterfaceDecl(), but that doesn't work
3729-
// yet.
3730-
if (auto ED = dyn_cast<ExtensionDecl>(VD->getDeclContext()))
3731-
if (ED->isObjCImplementation() && VD->isObjC())
3732-
return false;
3741+
// If this is an @_objcImplementation member implementation, unconditionally
3742+
// forbid access. Name lookups will instead find and use the matching
3743+
// interface decl.
3744+
if (isObjCMemberImplementation(VD, getAccessLevel))
3745+
return false;
37333746

37343747
if (VD->getASTContext().isAccessControlDisabled())
37353748
return true;

test/decl/ext/objc_implementation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
// expected-note@-4 {{add 'final' to define a Swift instance method that cannot be overridden}} {{3-3=final }}
2323
}
2424

25-
@objc func methodNot(fromHeader1: CInt) {
25+
@objc fileprivate func methodNot(fromHeader1: CInt) {
2626
// OK, declares a new @objc dynamic method.
2727
}
2828

@@ -154,7 +154,7 @@
154154
// OK, provides an implementation for the header's method.
155155
}
156156

157-
@objc func categoryMethodNot(fromHeader1: CInt) {
157+
@objc fileprivate func categoryMethodNot(fromHeader1: CInt) {
158158
// OK, declares a new @objc dynamic method.
159159
}
160160

0 commit comments

Comments
 (0)