[swift-evolution] [Proposal draft] Limiting @objc inference

Douglas Gregor dgregor at apple.com
Fri Jan 6 12:30:57 CST 2017


> On Jan 6, 2017, at 3:46 AM, Rod Brown <rodney.brown6 at icloud.com> wrote:
> 
>> 
>> On 5 Jan 2017, at 11:50 am, Douglas Gregor via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> Hi all,
>> 
>> Here’s a draft proposal to limit inference of @objc to only those places where we need it for consistency of the semantic model. It’s in the realm of things that isn’t *needed* for ABI stability, but if we’re going to make the source-breaking change here we’d much rather do it in the Swift 4 time-frame than later. Proposal is at:
>> 
>> 	https://github.com/DougGregor/swift-evolution/blob/objc-inference/proposals/NNNN-objc-inference.md <https://github.com/DougGregor/swift-evolution/blob/objc-inference/proposals/NNNN-objc-inference.md>
>> 
> 
>>  <https://github.com/DougGregor/swift-evolution/tree/objc-inference#source-compatibility>Source compatibility
>> 
>> The two changes that remove inference of @objc are both source-breaking in different ways. The dynamic change mostly straightforward:
>> 
>> In Swift 4 mode, introduce an error that when a dynamic declaration does not explicitly state @objc, with a Fix-It to add the @objc.
>> 
>> In Swift 3 compatibility mode, continue to infer @objc for dynamic methods. However, introduce a warning that such code will be ill-formed in Swift 4, along with a Fix-It to add the @objc. This
>> 
>> A Swift 3-to-4 migrator could employ the same logic as Swift 3 compatibility mode to update dynamic declarations appropriately.
>> 
>> The elimination of inference of @objc for declarations in NSObject subclasses is more complicated. Considering again the three cases:
>> 
>> In Swift 4 mode, do not infer @objc for such declarations. Source-breaking changes that will be introduced include:
>> 
>> If #selector or #keyPath refers to one such declaration, an error will be produced on previously-valid code that the declaration is not @objc. In most cases, a Fix-It will suggest the addition of @objc.
>> 
>> The lack of @objc means that Objective-C code in mixed-source projects won't be able to call these declarations. Most problems caused by this will result in warnings or errors from the Objective-C compiler (due to unrecognized selectors), but some might only be detected at runtime. These latter cases will be hard-to-detect.
>> 
>> Other tools and frameworks that rely on the presence of Objective-C entrypoints but do not make use of Swift's facilities for referring to them will fail. This case is particularly hard to diagnose well, and failures of this sort are likely to cause runtime failures that only the developer can diagnose and correct.
>> 
>> In Swift 3 compatibility mode, continue to infer @objc for these declarations. When @objc is inferred based on this rule, modify the generated header (i.e., the header used by Objective-C code to call into Swift code) so that the declaration contains a "deprecated" attribute indicating that the Swift declaration should be explicitly marked with @objc. For example:
>> 
>> class MyClass : NSObject {
>>   func foo() { }
>> }
>> will produce a generated header that includes:
>> 
>> @interface MyClass : NSObject
>> -(void)foo NS_DEPRECATED("MyClass.foo() requires an explicit `@objc` in Swift 4");
>> @end
>> This way, any reference to that declaration from Objective-C code will produce a warning about the deprecation. Users can silence the warning by adding an explicit @objc.
>> 
>> A Swift 3-to-4 migrator is the hardest part of the story. Ideally, the migrator to only add @objc in places where it is needed, so that we see some of the expected benefits of code-size reduction. However, there are two problems with doing so:
>> 
>> Some of the uses that imply the need to add @objc come from Objective-C code, so a Swift 3-to-4 migrator would also need to compile the Objective-C code (possibly with a modified version of the Objective-C compiler) and match up the "deprecated" warnings mentioned in the Swift 3 compatibility mode bullet with Swift declarations.
>> 
>> The migrator can't reason about dynamically-constructed selectors or the behavior of other tools that might directly use the Objective-C runtime, so failing to add a @objc will lead to migrated programs that compile but fail to execute correctly.
>> 
>> 
> Yeah, this is definitely a point of concern. I’m not sure that the first part deals with stuff like frameworks and libraries that currently expose functionality to external users in Obj-C. How could you be sure which API was designed to be used by Obj-C when it comes to libraries?

It is *very* hard to know.

> As others have mentioned, perhaps we could have options in the converter? I’m not generally a fan of this type of preference selection but in this case it seems reasonable.

Yeah, maybe. Designing the migrator itself isn’t really in the scope of the Swift evolution process, but we do appreciate hearing people’s ideas here. It’s a tough thing to migrate well.

> The dynamically constructed selector case seems rare, especially considering that using string-based selectors has been deprecated since Swift 2.2.

I suspect that you are right that failures would be rare. It’s the 
>>  <https://github.com/DougGregor/swift-evolution/tree/objc-inference#overriding-of-declarations-introduced-in-class-extensions>Overriding of declarations introduced in class extensions
>> 
>> There are several potential solutions to this problem, but both are out-of-scope for this particular proposal:
>> 
>> Require that a non- at objc declaration in a class extension by explicitly declared final so that it is clear from the source that this declaration cannot be overridden.
>> 
>> Extend Swift's class model to permit overriding of declarations introduced in extensions.
>> 
>> 
> I will definitely be running into this issue if/when this occurs. A lot of users use extensions to break up their code into clear elements for organisational reasons, and also adding functionality to external libraries. Both here would end up with odd behaviour and I really hope we go with Option 2 here.

Either option requires a proposal; Option 2 can certainly have ABI impact because it could effect the ABI of Swift classes.

As an aside, we/I realized belatedly that this proposal obviously doesn’t fit Swift 4 stage 1, so we’ll hold off on the review until we open up stage 2.

	- Doug

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170106/4948adb8/attachment.html>


More information about the swift-evolution mailing list