Skip to content

Commit 8bcfa92

Browse files
committed
Make all internal-or-more @_objcImpl members impls
1 parent 8dbe399 commit 8bcfa92

File tree

3 files changed

+51
-23
lines changed

3 files changed

+51
-23
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

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

700700
You create a class with this feature very differently from normal ObjC interop:
701701

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

724-
* We should think about Objective-C generics.
725-
726-
4. Every member of an `@_objcImplementation` extension should be either `@objc`
727-
or `final`. The `final` members can be made `public` if you want to expose
728-
them to Swift clients. The `@objc` members are implicitly `dynamic`.
724+
The members of an `@_objcImplementation` extension should fall into one of
725+
three categories:
729726

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

737-
* Eventually, we want the main `@_objcImplementation` extension to be able to
738-
declare stored properties that aren't in the interface. We also want
739-
`final` stored properties to be allowed to be resilent Swift types, but
740-
it's not clear how to achieve that without boxing them in `__SwiftValue`
741-
(which we might do as a stopgap).
736+
* **Member implementations** include any other non-`final` member. These are
737+
implicitly `@objc dynamic` and must match a member declared in the
738+
Objective-C header. Use these to implement the APIs declared in your
739+
headers. Swift will emit an error if these don't match your headers.
740+
741+
Notes:
742+
743+
* We don't currently plan to support ObjC generics.
744+
745+
* Eventually, we want the main `@_objcImplementation` extension to be able to
746+
declare stored properties that aren't in the interface. We also want
747+
`final` stored properties to be allowed to be resilent Swift types, but
748+
it's not clear how to achieve that without boxing them in `__SwiftValue`
749+
(which we might do as a stopgap).
742750

743-
* We should think about ObjC `direct` members.
744-
745-
* Access control design for ObjC methods TBD.
751+
* We should think about ObjC "direct" members, but that would probably
752+
require a way to spell this in Swift.
746753

747754
## `@_objc_non_lazy_realization`
748755

lib/AST/Decl.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3742,6 +3742,21 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
37423742
VD->getDeclContext()->getParentModule() == useDC->getParentModule();
37433743
}
37443744

3745+
/// Checks if \p VD is an ObjC member implementation:
3746+
///
3747+
/// \li It's in an \c \@_objcImplementation extension
3748+
/// \li It's \c \@objc
3749+
/// \li Its access level is not \c private or \c fileprivate
3750+
static bool
3751+
isObjCMemberImplementation(const ValueDecl *VD,
3752+
llvm::function_ref<AccessLevel()> getAccessLevel) {
3753+
if (auto ED = dyn_cast<ExtensionDecl>(VD->getDeclContext()))
3754+
if (ED->isObjCImplementation())
3755+
return VD->isObjC() && getAccessLevel() >= AccessLevel::Internal;
3756+
3757+
return false;
3758+
}
3759+
37453760
/// Checks if \p VD may be used from \p useDC, taking \@testable and \@_spi
37463761
/// imports into account.
37473762
///
@@ -3756,6 +3771,12 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD,
37563771
bool forConformance,
37573772
bool includeInlineable,
37583773
llvm::function_ref<AccessLevel()> getAccessLevel) {
3774+
// If this is an @_objcImplementation member implementation, unconditionally
3775+
// forbid access. Name lookups will instead find and use the matching
3776+
// interface decl.
3777+
if (isObjCMemberImplementation(VD, getAccessLevel))
3778+
return false;
3779+
37593780
if (VD->getASTContext().isAccessControlDisabled())
37603781
return true;
37613782

@@ -9552,4 +9573,4 @@ bool ActorIsolation::isDistributedActor() const {
95529573
BuiltinTupleDecl::BuiltinTupleDecl(Identifier Name,
95539574
DeclContext *Parent)
95549575
: NominalTypeDecl(DeclKind::BuiltinTuple, Parent, Name, SourceLoc(),
9555-
ArrayRef<InheritedEntry>(), nullptr) {}
9576+
ArrayRef<InheritedEntry>(), nullptr) {}

test/decl/ext/objc_implementation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// FIXME: Should complain about the wrong category
1616
}
1717

18-
@objc func methodNot(fromHeader1: CInt) {
18+
@objc fileprivate func methodNot(fromHeader1: CInt) {
1919
// OK, declares a new @objc dynamic method.
2020
}
2121

@@ -39,7 +39,7 @@
3939
// OK, provides an implementation for the header's method.
4040
}
4141

42-
@objc func categoryMethodNot(fromHeader1: CInt) {
42+
@objc fileprivate func categoryMethodNot(fromHeader1: CInt) {
4343
// OK, declares a new @objc dynamic method.
4444
}
4545

0 commit comments

Comments
 (0)