-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add @_objcImplementation #60630
Add @_objcImplementation #60630
Conversation
86a8951
to
eaf12b4
Compare
aec03c1
to
451f2e9
Compare
Part of swiftlang/swift#60630, but safe to merge before the compiler-side implementation.
451f2e9
to
97bf476
Compare
41770b0
to
76beb1c
Compare
Part of swiftlang/swift#60630, but safe to merge before the compiler-side implementation.
c5953fc
to
cd72652
Compare
@swift-ci please test |
5557ebd
to
6c28360
Compare
Part of swiftlang/swift#60630, but safe to merge before the compiler-side implementation.
With swiftlang/swift-syntax#798 @swift-ci please test |
6c28360
to
bc1b030
Compare
With swiftlang/swift-syntax#798 @swift-ci please test |
With swiftlang/swift-syntax#798 @swift-ci please test |
@swift-ci build toolchain |
With swiftlang/swift-syntax#798 @swift-ci please build toolchain macOS platform |
Does not validate members yet; nor does it emit different metadata.
They either must be @objc dynamic (the dynamic is implicit) or final.
Stored properties are only allowed in the extension implementing the class's main interface, not its categories. This also means banning `@objc final`, which is unenforceable anyway when ObjC subclasses are allowed, and therefore allowing `@objc let` and `@objc static` properties to be overridden if they're declared in objcImplementations.
This commit begins to generate correct metadata for @_objcImplementation extensions: • Swift-specific metadata and symbols are not generated. • For main-class @_objcImpls, we visit the class to emit metadata, but visit the extension’s members. • Includes both IR tests and executable tests, including coverage of same-module @objc subclasses, different-module @objc subclasses, and clang subclasses. The test cases do not yet cover stored properties.
Only works for trivial types right now because features related to initialization and deinitialization are seriously busted.
Accessing a stored property from a normal method should send a selector, but accessing it from an initializer or accessor should use the field offset.
This is failing in the iOS simulator, but *only* in Swift CI, not locally (even with the same tools). Disable the test ouside of macOS while we investigate the failure.
5b89f39
to
7f0791e
Compare
Part of swiftlang/swift#60630, but safe to merge before the compiler-side implementation.
With swiftlang/swift-syntax#798 @swift-ci please test |
With swiftlang/swift-syntax#798 @swift-ci please clean test |
With swiftlang/swift-syntax#798 @swift-ci please clean test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great. I appreciate the testing discipline for this tricky feature.
I'm seeing a failure on a local build of
Any ideas? Is |
Ignore that. I just updated all my sources and now it seems to build fine, so whatever this issue was you must have already fixed it. Thanks!! |
This PR was paired with swiftlang/swift-syntax#798 and you needed to pull that PR’s commits to fix your build. |
|
||
Declares an extension that defines an implementation for the Objective-C | ||
category `CategoryName` on the class in question, or for the main `@interface` | ||
if the argument list is omitted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is just internal documentation, but I think I would phrase this as:
## `@_objcImplementation` and `@_objcImplementation(CategoryName)`
Must be written on an `extension` of a non-generic imported Objective-C class.
When written without an argument list, emits an Objective-C class implementation
for the class. When written with a category name argument, emits an Objective-C
category implementation for that category of the class, which must be declared
in the imported headers. In both cases, the emitted implementation will look
exactly like what an Objective-C compiler would have emitted for a corresponding
`@implementation`.
Some part of me really hates that the first use case is done with an extension
.
I don't know that I have a better option, though. Having two ClassDecl
s, or
gutting one for the other, both seem worse.
|
||
bool isCategoryNameInvalid() const { | ||
return Bits.ObjCImplementationAttr.isCategoryNameInvalid; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means that a category name was given but doesn't match anything from the class?
/// | ||
/// If \c this (an otherwise nonsensical value), the value has not yet been | ||
/// computed. | ||
Decl *CachedObjCImplementationDecl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we want to pay for this on literally every declaration we make in the compiler. I doubt this is even hot. Can you use out-of-line storage for it?
@@ -1553,6 +1556,16 @@ class LinkEntity { | |||
bool isAlwaysSharedLinkage() const; | |||
#undef LINKENTITY_GET_FIELD | |||
#undef LINKENTITY_SET_FIELD | |||
|
|||
private: | |||
static bool isObjCImplementation(NominalTypeDecl *NTD) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasObjCImplementation
, maybe? But really I think this assertion can be broader — we should never have a type metadata or descriptor symbol for any imported class. Unless we emit a lazy candidate with the normal symbol name, but I don't think we do? And if we do, it would trigger for your case, too.
@@ -84,7 +84,14 @@ AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const { | |||
if (D->hasInterfaceType() && D->isInvalid()) { | |||
return AccessLevel::Private; | |||
} else { | |||
auto container = cast<NominalTypeDecl>(D->getDeclContext()); | |||
auto container = dyn_cast<NominalTypeDecl>(DC); | |||
if (D->getKind() == DeclKind::Destructor && !container) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isa<DestructorDecl>(D)
?
/// \li It's \c \@objc | ||
/// \li Its access level is not \c private or \c fileprivate | ||
static bool | ||
isObjCMemberImplementation(const ValueDecl *VD, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love this name, or some of the similar ones. "Implementation" is not a word that ambiguously refers to an ObjC @implementation
declaration, and there are a lot of places where ObjC
means e.g. the @objc
attribute. Is there a more distinctive term we can use within the compiler for these things?
@@ -35,7 +35,8 @@ FormalLinkage swift::getDeclLinkage(const ValueDecl *D) { | |||
|
|||
// Clang declarations are public and can't be assured of having a | |||
// unique defining location. | |||
if (isa<ClangModuleUnit>(fileContext)) | |||
if (isa<ClangModuleUnit>(fileContext) && | |||
!D->getObjCImplementationDecl()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you want to change this. Do you remember why you found it necessary?
This PR adds a mechanism to implement "pure" Objective-C classes in Swift. Unlike the existing
@objc
interop mechanism, these classes use traditional handwritten ObjC header files and can be subclassed, swizzled, and otherwise manipulated from ObjC code like any other ObjC class.To do this, you arrange to import the header file via your bridging or umbrella header, as though it were implemented in Objective-C, and then you apply the
@_objcImplementation
attribute to anextension
and implement the methods there. Each@_objcImplementation extension
corresponds to one@interface
in the header; the attribute can specify a category name in parentheses to implement a category, or it can leave the category name out to implement the main body of the class. The extension that implements the main body of the class can declare stored properties.This initial PR includes fairly minimal support sufficient to allow you to implement initializers, methods, stored properties, and accessors/observers for ObjC classes without leaking memory. Sema-level diagnostics are very incomplete and stored resilient properties have not yet been tested. The vast majority of Swift-specific metadata has been eliminated, but there may still be a couple of things that shouldn't be there.
Parser reviewers: There is a matching pull request, swiftlang/swift-syntax#798, which includes a test for the new attribute.
Fixes rdar://70730077.