Skip to content

Commit ef04f10

Browse files
committed
Make all internal-or-more @_objcImpl members impls
1 parent 12f1762 commit ef04f10

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
@@ -698,7 +698,7 @@ you replace a native Objective-C `@implementation Foo (Bar)` with a Swift
698698

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

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

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

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

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

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

746753
## `@_objc_non_lazy_realization`
747754

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
@@ -14,7 +14,7 @@
1414
// FIXME: Should complain about the wrong category
1515
}
1616

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

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

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

0 commit comments

Comments
 (0)