[Review] SE-0160: Limiting @objc inference


(Chris Lattner) #1

Hello Swift community,

The review of "SE-0160: Limiting @objc inference" begins now and runs through March 28. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at:

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

* What is your evaluation of the proposal?
* Is the problem being addressed significant enough to warrant a change to Swift?
* Does this proposal fit well with the feel and direction of Swift?
* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at:
  https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager


(Rien) #2

+1

* What is your evaluation of the proposal?

Seems to remove magic, which is something I can support, even if it means refactoring existing code.

* Is the problem being addressed significant enough to warrant a change to Swift?

Neutral

* Does this proposal fit well with the feel and direction of Swift?

Neutral

* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

None

* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Glance

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

···

On 22 Mar 2017, at 07:03, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0160: Limiting @objc inference" begins now and runs through March 28. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at:

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

* What is your evaluation of the proposal?
* Is the problem being addressed significant enough to warrant a change to Swift?
* Does this proposal fit well with the feel and direction of Swift?
* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at:
  https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #3

* What is your evaluation of the proposal?

+1. This clarifies the interaction between Swift and Objective-C and reduces the magic that exists for bridging. It’s one more step on the road of Swift relying less on the Objective-C runtime - decoupling the semantics of `dynamic` from `@objc` is a great thing.

This has potential to be the rockiest feature to migrate to Swift 4 thus far. I like Michel’s idea of an option in the migrator to apply `@objc` everywhere it is inferred today. This would help teams that rely on dynamic interactions with the Objective-C runtime to migrate safely.

* Is the problem being addressed significant enough to warrant a change to Swift?

Yes. It cleans up semantics that can be confusing today and makes Objective-C interactions more explicit.

* Does this proposal fit well with the feel and direction of Swift?

Yes.

* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

There is a long history of bridging in the Apple ecosystem. Annotation for bridged declarations is customary.

* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick read.

···

More information about the Swift evolution process is available at:
  https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michel Fortin) #4

* What is your evaluation of the proposal?

Good. I'll certainly appreciate the added clarity of knowing which methods are exposed to Objective-C.

Currently, Swift-only apps are bloated by unnecessary Objective-C thunks. The motivation section says that this will reduce the reduce the binary sizes, which is good, but should we expect not emitting those thunks will also reduce by a bit the compilation times? Perhaps not by much, but I do think it will.

I'm already littering some projects with plenty of `@nonobjc` annotations because I don't need those thunks. This proposal would allow me to make this cleaner. Perhaps the migrator should remove the `@nonobjc` attributes when they become unnecessary. Actually, would the changes in this proposal make the `@nonobjc` attribute irrelevant?

I'm a bit worried about the migration though.

I think it would be reasonable for the migrator to have two settings. A conservative one that adds `@objc ` to all currently implicit `@objc` declarations, to be used when you need to be absolutely certain everything still works. Pruning of extra `@objc` would have to be done manually later, if desired. And the default one that only adds `@objc` when the migrator sees it as necessary.

I know I would always want to use the default migration setting. But it'd be reassuring to have a safe fallback in case the migration breaks things.

* Is the problem being addressed significant enough to warrant a change to Swift?

The more Swift code there is in a project, the more important this change will be.

* Does this proposal fit well with the feel and direction of Swift?

It will make semantics more predictable.

* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

A few years ago I wrote a bridge between D and Objective-C. I actually made two versions: one was based on D templates and generated a lot of thunks, and the second was integrated in the compiler and generated no thunks at all. The first one was creating bloated binaries in addition to being very slow to compile, while the second was very lean as it was mostly an Objective-C compiler using the D syntax.

I'm not expecting improvements of the same magnitude here, but emitting less unnecessary glue code is always a good thing.

* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading of the proposal.

···

--
Michel Fortin
https://michelf.ca


(David Beck) #5

* What is your evaluation of the proposal?
In theory, it seems like a great idea, but I fear that it will be one of
those small changes that have disastrous effects on migrating code to Swift
4. There will likely be a ton of confusing errors and warnings that amount
to "just add @objc to everything". Where there won't be errors or warnings,
I can imagine that behavior could change in ways that most people can't
predict.

* Is the problem being addressed significant enough to warrant a change to
Swift?
A 5% reduction in binary size is significant, and an issue that Swift is
certainly running into. However I suspect that there are other ways to
address this.

* Does this proposal fit well with the feel and direction of Swift?
Yes.

* If you have you used other languages or libraries with a similar feature,
how do you feel that this proposal compares to those?
N/A

* How much effort did you put into your review? A glance, a quick reading,
or an in-depth study?
Read through the proposal.

···

On Tue, Mar 21, 2017 at 11:03 PM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0160: Limiting @objc inference" begins now and runs
through March 28. The proposal is available here:

        https://github.com/apple/swift-evolution/blob/master/
proposals/0160-objc-inference.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at:

        https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

* What is your evaluation of the proposal?
* Is the problem being addressed significant enough to warrant a change to
Swift?
* Does this proposal fit well with the feel and direction of Swift?
* If you have you used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?
* How much effort did you put into your review? A glance, a quick reading,
or an in-depth study?

More information about the Swift evolution process is available at:
        https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jordan Rose) #6

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md]

I'm definitely in favor of this. Apart from the various motivations discussed in the proposal, this also allows some changes that could improve incremental builds: the generated header ("MyApp-Swift.h") wouldn't need to be regenerated nearly as often with fewer methods exposed to Objective-C. (There's some nuance here that I don't need to go into right now, and there are alternate solutions to that problem, but it's nice that the common case will just put fewer declarations into the header and therefore it would change less often.)

The migration aspect is a little scary. #selector's Objective-C equivalent is @selector, which is easy to search for, but #keyPath maps to plain old strings. For those with Mac apps, properties might even be referenced in Interface Builder using Cocoa Bindings. But limiting the change to Swift 4 mode seems acceptable to me—at least it's a known change that people will be able to point to when things stop working.

Jordan


(Brian King) #7

* What is your evaluation of the proposal?

+1 in intent and -1 in detail.

I think reducing the amount of magic helps make swift more powerful and
easier to understand. This will force developers to add a few more `@objc`
tokens, but the consistency will help in the long run.

The last section in "Source Compatibility" calls out a few
problems, presents some possible solutions, but states that they are "Out
of scope for this proposal". Given the depth of migration issues that this
proposal is expected to incur, I think leaving out these details feels
short-sighted. If this proposal is approved, when are the 'out of scope'
behaviors decided?

A few specific opinions on the possibilities presented:
- I'd like a clearer explanation on how the inferred `dynamic` behavior
would change. I would prefer @objc to always infer dynamic or never infer
dynamic. Currently it's context dependent and confusing.
- I don't like forcing extension methods that are not `@objc` be flagged
`final`. That seems heavy handed.
- I'd like to get over-rides in extensions working!

That said, I'm new here, and I'm a net +0. I trust a good choice will be
made.

* Is the problem being addressed significant enough to warrant a change to
Swift?

Yes

* Does this proposal fit well with the feel and direction of Swift?

Yes

* If you have you used other languages or libraries with a similar feature,

how do you feel that this proposal compares to those?

No Experience

* How much effort did you put into your review? A glance, a quick reading,
or an in-depth study?

In-depth study

···

More information about the Swift evolution process is available at:
        https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #8

* What is your evaluation of the proposal?

In favour.

Like others I can foresee there being a bit of pain for some developers, but I think it's worth it to be more explicit about what's going on, and to clean up a feature that's just for supporting a specific other language.

My main concern is on whether things could be made a bit easier; specifically I wonder whether we could introduce an option (to the compiler?) to trigger warnings anywhere there is a possible missing @objc attribute. Basically on any code that produces bridging headers this would give a warning anywhere that @objc would have been inferred in the past, but will no longer be. Of course this will generate a lot of warnings, but it'll be an easier way for developers to go through and make sure they didn't miss something. Xcode could offer this automatically during migration, and the developer can turn it off when they're done. Not perfect, but it may be a little extra help for those most affected?

* Is the problem being addressed significant enough to warrant a change to Swift?

I believe so, I think we need to cut down on anything that's too platform or legacy specific in favour of Swift being as clean and functional as possible with limited magic features.

* Does this proposal fit well with the feel and direction of Swift?

Yes, as I believe it tidies up the language and removes something caters too much towards a specific, hopefully increasingly legacy, use-case.

* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Following discussion, quick re-read.

···

On 22 Mar 2017, at 06:03, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:


(Brent Royal-Gordon) #9

* What is your evaluation of the proposal?

I'm going to have to come down on the "no" side on this one.

I'm actually not worried about methods so much as properties. KVC is completely untyped on the Objective-C side, and there are several different mechanisms there which use KVC with poorly validated external strings, like bindings, sort descriptors, and predicates. Tons of migration errors are going to escape into production if we do this, and undetectable mistakes are going to continue on an ongoing basis.

Have we explored alternate implementations? For instance, when the compiler can statically determine all of the call sites for an `@objc` member, could we emit *only* an implementation with the Objective-C calling convention and call that directly from Swift?

Could members with binary-compatible signatures share a thunk? The Objective-C calling convention includes a selector parameter, so you can still determine the desired message.

Could we give implicit methods and properties a different, slower implementation that leverages Objective-C's dynamic features? Imagine, for instance, mapping in `imp_implementationWithBlock()`-style pages of general-purpose thunks at runtime to reduce code size, then installing them lazily with `+resolveClassMethod:`. (Maybe this could actually be made fast enough, amortized over all calls, that we could get rid of the thunks entirely—I don't know.)

Have you evaluated applying different rules to methods and properties? Have you considered a deprecation cycle (for instance, having Swift 4 thunks log a warning that they're going away in Swift 5)?

Or is the real motivation that, code size issues aside, you think these members ought to be explicitly marked `@objc` for philosophical reasons? If so, how many times do you want people to say so? You already have to explicitly inherit from an `@objc` base class; you already have to specify `dynamic` to avoid optimizations; now you also have to mark individual members `@objc`? Would you like the request for bridging notarized and filed in triplicate?

I can understand the impulse to require it explicitly on `public` members, but on the other hand, if you *do* accidentally publish a member as `@objc`, what's the harm? Is there any non-breaking change you can make to a `@nonobjc` method which wouldn't be legal on an `@objc` one? Or can you just deprecate the `@objc` version and move on?

Overall, I think this is far too bureaucratic, and I'm not convinced that either the technical or the theoretical justification is compelling. Maybe you've eliminated some of the possibilities I've suggested or there are negative effects I don't understand, but right now, I don't see it.

* Is the problem being addressed significant enough to warrant a change to Swift?

I'm uncertain.

* Does this proposal fit well with the feel and direction of Swift?

It fits with certain goals, like making public APIs explicit, but not with others, like avoiding boilerplate.

* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I haven't used anything with such deep foreign-language bridging as Swift.

* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading during a hockey game, so please forgive any oversights.

···

On Mar 21, 2017, at 11:03 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPad


(Slava Pestov) #10

Here’s an idea for working around the problem of the lack of static knowledge during migration. Probably it’s kind of tacky and won’t get much traction in it’s current form, but it might start some useful discussion at least.

Right now, @objc when applied to a _class_ is completely useless; if a class ultimately inherits from NSObject, it is always implicitly @objc, and applying @objc to a class not rooted in NSObject is always an error. (I think at some point in the past we allowed @objc classes that _don’t_ inherit from NSObject, but I don’t know if that even made it into any released version of Swift, so it’s totally vestigial at this point.) We can keep this behavior in Swift 3 mode, but in Swift 4 mode, change things so that @objc applied to a class enables @objc inference for the members of the class, and the absence of @objc enables the new, more limited inference behavior outlined in this proposal.

Then the migration story can just be “slap @objc on every NSObject-derived class and you’re good”. Existing mixed source bases, KVC, and so on would just work. We could also say that in Swift 4 mode, @objc on an NSObject-derived class produces a warning asking the developer to consider making individual members @objc as necessary instead. This would allow a Swift 4 migration to proceed in two phases — first fix any fallout from SE-0110 or new string stuff or whatever, and get a working app that builds and runs in Swift 4 mode, albeit with some warnings. Then they can deal with marking individual class members as @objc later. We could still have the option of making it an error to apply @objc to an entire class in a future release of Swift, if we decide it is beneficial to do so.

Based on feedback, the all-or-nothing nature of the Swift 2->3 migration was rather painful — mixing and matching 3 and 4 modules will definitely help us do better the next time around, and allowing a complex change such as this one to be done piecemeal could be a further step in the right direction.

Slava

···

On Mar 21, 2017, at 11:03 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0160: Limiting @objc inference" begins now and runs through March 28. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at:

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

* What is your evaluation of the proposal?
* Is the problem being addressed significant enough to warrant a change to Swift?
* Does this proposal fit well with the feel and direction of Swift?
* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at:
  https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #11

Hey all,

Separate from other feedback, we’ve noticed another minor wrinkle: XCTest uses the Objective-C runtime to discover tests in XCTestCase subclasses. While in the long term there is probably a better discovery method that works cross-platform, a more near-term solution would continue to infer @objc for methods in XCTestCase subclasses that look like tests, e.g., methods that have the right signature, start with “test”, and appear in XCTestCase subclasses.

  - Doug

···

On Mar 21, 2017, at 11:03 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0160: Limiting @objc inference" begins now and runs through March 28. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at:

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

* What is your evaluation of the proposal?
* Is the problem being addressed significant enough to warrant a change to Swift?
* Does this proposal fit well with the feel and direction of Swift?
* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at:
  https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #12

* What is your evaluation of the proposal?

Good. I'll certainly appreciate the added clarity of knowing which methods are exposed to Objective-C.

Currently, Swift-only apps are bloated by unnecessary Objective-C thunks. The motivation section says that this will reduce the reduce the binary sizes, which is good, but should we expect not emitting those thunks will also reduce by a bit the compilation times? Perhaps not by much, but I do think it will.

I don’t think the effect on compilation time will be significant or noticeable. Although numerous, the thunks are fairly small and straightforward to produce/optimize/code-gen.

I'm already littering some projects with plenty of `@nonobjc` annotations because I don't need those thunks. This proposal would allow me to make this cleaner. Perhaps the migrator should remove the `@nonobjc` attributes when they become unnecessary.

Yes, it could do that.

Actually, would the changes in this proposal make the `@nonobjc` attribute irrelevant?

In practice, it would probably become mostly irrelevant. I suspect it would still find occasional use to suppress a warning about, e.g., a method that “almost matches” an optional requirement in an Objective-C protocol but that is not intended to match.

I'm a bit worried about the migration though.

I think it would be reasonable for the migrator to have two settings. A conservative one that adds `@objc ` to all currently implicit `@objc` declarations, to be used when you need to be absolutely certain everything still works. Pruning of extra `@objc` would have to be done manually later, if desired. And the default one that only adds `@objc` when the migrator sees it as necessary.

I know I would always want to use the default migration setting. But it'd be reassuring to have a safe fallback in case the migration breaks things.

I expect it’ll take some experimentation to get it right.

  - Doug

···

On Mar 22, 2017, at 7:50 AM, Michel Fortin via swift-evolution <swift-evolution@swift.org> wrote:


(Douglas Gregor) #13

The source compatibility section of the proposal

  https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md#source-compatibility

describes the use of NS_DEPRECATED in the generated header for Swift 3 compatibility mode to get warnings on uses of entities that are implicitly @objc but will no longer be in Swift 4. Does that address your concern?

I’ve also heard the idea of putting the same warnings into the generated Objective-C thunks by NSLog’ing the same information as an opt-in, pre-Swift-4-migration step to help catch the tricky cases where an Objective-C entrypoint is getting called.

  - Doug

···

On Mar 22, 2017, at 3:22 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 22 Mar 2017, at 06:03, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:
* What is your evaluation of the proposal?

In favour.

Like others I can foresee there being a bit of pain for some developers, but I think it's worth it to be more explicit about what's going on, and to clean up a feature that's just for supporting a specific other language.

My main concern is on whether things could be made a bit easier; specifically I wonder whether we could introduce an option (to the compiler?) to trigger warnings anywhere there is a possible missing @objc attribute. Basically on any code that produces bridging headers this would give a warning anywhere that @objc would have been inferred in the past, but will no longer be. Of course this will generate a lot of warnings, but it'll be an easier way for developers to go through and make sure they didn't miss something. Xcode could offer this automatically during migration, and the developer can turn it off when they're done. Not perfect, but it may be a little extra help for those most affected?


(Slava Pestov) #14

* What is your evaluation of the proposal?

I'm going to have to come down on the "no" side on this one.

I'm actually not worried about methods so much as properties. KVC is completely untyped on the Objective-C side, and there are several different mechanisms there which use KVC with poorly validated external strings, like bindings, sort descriptors, and predicates. Tons of migration errors are going to escape into production if we do this, and undetectable mistakes are going to continue on an ongoing basis.

Have we explored alternate implementations? For instance, when the compiler can statically determine all of the call sites for an `@objc` member, could we emit *only* an implementation with the Objective-C calling convention and call that directly from Swift?

This would incur bridging overhead for collections I believe. We really do want to use the Swift calling convention whenever we know the method is defined in Swift, even if happens to be exposed to Objective-C. It would be unfortunate if there was an unpredictable performance cliff encountered because of @objc inference.

Have you evaluated applying different rules to methods and properties? Have you considered a deprecation cycle (for instance, having Swift 4 thunks log a warning that they're going away in Swift 5)?

I’m not sure how this could work. If we emit an unconditional warning on every method that has an Objective-C compatible signature but is not marked as @objc, how would the user acknowledge the warning and say “OK, I don’t really want inference here?” Would the only way to opt out of the warning be to write @nonobjc everywhere?

Or is the real motivation that, code size issues aside, you think these members ought to be explicitly marked `@objc` for philosophical reasons? If so, how many times do you want people to say so? You already have to explicitly inherit from an `@objc` base class; you already have to specify `dynamic` to avoid optimizations; now you also have to mark individual members `@objc`? Would you like the request for bridging notarized and filed in triplicate?

The ‘accidental selector name clash’ argument is a good one I think; if you’re designing a pure Swift class that happens to inherit from NSObject, it might be confusing to a user if they define methods named foo(_: Int) and foo(_: String) and get a compiler error.

It would be interesting to get some more data on the size overhead of @objc thunks from real codebases, but I think the difficulty there is ascertaining which thunks are ‘required’ and which ones are not (which of course is also the core of your objection to the feature, if I understand; if we could perfectly determine which thunks were needed statically, the migration story would be far less of a concern).

Slava

···

On Mar 22, 2017, at 10:55 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 21, 2017, at 11:03 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

I can understand the impulse to require it explicitly on `public` members, but on the other hand, if you *do* accidentally publish a member as `@objc`, what's the harm? Is there any non-breaking change you can make to a `@nonobjc` method which wouldn't be legal on an `@objc` one? Or can you just deprecate the `@objc` version and move on?

Overall, I think this is far too bureaucratic, and I'm not convinced that either the technical or the theoretical justification is compelling. Maybe you've eliminated some of the possibilities I've suggested or there are negative effects I don't understand, but right now, I don't see it.

* Is the problem being addressed significant enough to warrant a change to Swift?

I'm uncertain.

* Does this proposal fit well with the feel and direction of Swift?

It fits with certain goals, like making public APIs explicit, but not with others, like avoiding boilerplate.

* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I haven't used anything with such deep foreign-language bridging as Swift.

* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading during a hockey game, so please forgive any oversights.

Sent from my iPad
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Slava Pestov) #15

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md]

I'm definitely in favor of this. Apart from the various motivations discussed in the proposal, this also allows some changes that could improve incremental builds: the generated header ("MyApp-Swift.h") wouldn't need to be regenerated nearly as often with fewer methods exposed to Objective-C. (There's some nuance here that I don't need to go into right now, and there are alternate solutions to that problem, but it's nice that the common case will just put fewer declarations into the header and therefore it would change less often.)

The migration aspect is a little scary. #selector's Objective-C equivalent is @selector, which is easy to search for, but #keyPath maps to plain old strings.

Are these strings usually literal strings that are passed in to some set of mostly-known selector names, or do people pass them around or even construct them dynamically?

Slava

···

On Mar 22, 2017, at 5:51 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

For those with Mac apps, properties might even be referenced in Interface Builder using Cocoa Bindings. But limiting the change to Swift 4 mode seems acceptable to me—at least it's a known change that people will be able to point to when things stop working.

Jordan
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Slava Pestov) #16

A further benefit of this scheme is that it makes the behavior of @objc on class members consistent between NSObject-derived and Swift-native classes. Right now, it is legal to apply @objc to a _member_ of a Swift-native class; this is what allows Swift-native classes to model @objc protocols and so on. But of course we don’t ever implicitly infer @objc on members of Swift-native classes just based on the calling convention of a method.

So now we could say that members of non-@objc classes only infer @objc if necessary to fulfill an override or protocol requirement, independently of whether the class is NSObject-derived or not; and @objc can now be applied to an entire class, as long as its NSObject-derived, to get the implicit inference behavior on members.

Slava

···

On Mar 23, 2017, at 2:06 AM, Slava Pestov <spestov@apple.com> wrote:

Here’s an idea for working around the problem of the lack of static knowledge during migration. Probably it’s kind of tacky and won’t get much traction in it’s current form, but it might start some useful discussion at least.

Right now, @objc when applied to a _class_ is completely useless; if a class ultimately inherits from NSObject, it is always implicitly @objc, and applying @objc to a class not rooted in NSObject is always an error. (I think at some point in the past we allowed @objc classes that _don’t_ inherit from NSObject, but I don’t know if that even made it into any released version of Swift, so it’s totally vestigial at this point.) We can keep this behavior in Swift 3 mode, but in Swift 4 mode, change things so that @objc applied to a class enables @objc inference for the members of the class, and the absence of @objc enables the new, more limited inference behavior outlined in this proposal.

Then the migration story can just be “slap @objc on every NSObject-derived class and you’re good”. Existing mixed source bases, KVC, and so on would just work. We could also say that in Swift 4 mode, @objc on an NSObject-derived class produces a warning asking the developer to consider making individual members @objc as necessary instead. This would allow a Swift 4 migration to proceed in two phases — first fix any fallout from SE-0110 or new string stuff or whatever, and get a working app that builds and runs in Swift 4 mode, albeit with some warnings. Then they can deal with marking individual class members as @objc later. We could still have the option of making it an error to apply @objc to an entire class in a future release of Swift, if we decide it is beneficial to do so.

Based on feedback, the all-or-nothing nature of the Swift 2->3 migration was rather painful — mixing and matching 3 and 4 modules will definitely help us do better the next time around, and allowing a complex change such as this one to be done piecemeal could be a further step in the right direction.

Slava

On Mar 21, 2017, at 11:03 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0160: Limiting @objc inference" begins now and runs through March 28. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at:

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

* What is your evaluation of the proposal?
* Is the problem being addressed significant enough to warrant a change to Swift?
* Does this proposal fit well with the feel and direction of Swift?
* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at:
  https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charlie Monroe) #17

* What is your evaluation of the proposal?

In favour.

Like others I can foresee there being a bit of pain for some developers, but I think it's worth it to be more explicit about what's going on, and to clean up a feature that's just for supporting a specific other language.

My main concern is on whether things could be made a bit easier; specifically I wonder whether we could introduce an option (to the compiler?) to trigger warnings anywhere there is a possible missing @objc attribute. Basically on any code that produces bridging headers this would give a warning anywhere that @objc would have been inferred in the past, but will no longer be. Of course this will generate a lot of warnings, but it'll be an easier way for developers to go through and make sure they didn't miss something. Xcode could offer this automatically during migration, and the developer can turn it off when they're done. Not perfect, but it may be a little extra help for those most affected?

The source compatibility section of the proposal

  https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md#source-compatibility

describes the use of NS_DEPRECATED in the generated header for Swift 3 compatibility mode to get warnings on uses of entities that are implicitly @objc but will no longer be in Swift 4. Does that address your concern?

I’ve also heard the idea of putting the same warnings into the generated Objective-C thunks by NSLog’ing the same information as an opt-in, pre-Swift-4-migration step to help catch the tricky cases where an Objective-C entrypoint is getting called.

I would very much like that, mostly for catching all scenarios with bindings on the Mac.

Otherwise, I agree with the proposal, just am a bit concerned with some of my apps that heavily use bindings...

···

On Mar 23, 2017, at 12:02 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 22, 2017, at 3:22 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 22 Mar 2017, at 06:03, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #18

* What is your evaluation of the proposal?

I'm going to have to come down on the "no" side on this one.

I'm actually not worried about methods so much as properties. KVC is completely untyped on the Objective-C side, and there are several different mechanisms there which use KVC with poorly validated external strings, like bindings, sort descriptors, and predicates. Tons of migration errors are going to escape into production if we do this,

We can avoid these by migrating conservatively (have the migrator add @objc everywhere it’s inferred in Swift 3).

and undetectable mistakes are going to continue on an ongoing basis.

… but what you say above is definitely true: the error of omission of @objc will cause breakage for these cases.

Have we explored alternate implementations? For instance, when the compiler can statically determine all of the call sites for an `@objc` member, could we emit *only* an implementation with the Objective-C calling convention and call that directly from Swift?

Could members with binary-compatible signatures share a thunk? The Objective-C calling convention includes a selector parameter, so you can still determine the desired message.

Could we give implicit methods and properties a different, slower implementation that leverages Objective-C's dynamic features? Imagine, for instance, mapping in `imp_implementationWithBlock()`-style pages of general-purpose thunks at runtime to reduce code size, then installing them lazily with `+resolveClassMethod:`. (Maybe this could actually be made fast enough, amortized over all calls, that we could get rid of the thunks entirely—I don't know.)

(Slava talked through some of these)

Have you evaluated applying different rules to methods and properties?

For me, I’d rather reject the proposal as a whole than further complicate the rules for @objc inference.

Have you considered a deprecation cycle (for instance, having Swift 4 thunks log a warning that they're going away in Swift 5)?

I think Swift 3 -> Swift 4 is the deprecation cycle, no?

Or is the real motivation that, code size issues aside, you think these members ought to be explicitly marked `@objc` for philosophical reasons?

That’s certainly *a* reason. The @objc inference rules are fairly complicated and even experienced developers can’t easily guess whether something will be exposed to Objective-C or not because (e.g.) minor changes in the parameter/result types of a method can affect it.

If so, how many times do you want people to say so?

Once per API that’s exposed to Objective-C.

You already have to explicitly inherit from an `@objc` base class;

You don’t *need* to inherit from an `@objc` base class to have an @objc member, but @objc inference ties together the notions of

Plus, inheritance from an Objective-C class is often incidental: you do it because you need an NSObjectProtocol conformance, or something else expects NSObject. I haven’t heard of developers inheriting from NSObject solely to get @objc inference for their members.

you already have to specify `dynamic` to avoid optimizations;

Conceptually, ‘dynamic’ is orthogonal to ‘@objc’. In today’s implementation, we can only implement ‘dynamic’ via the Objective-C runtime, hence this proposal’s requirement to write both.

now you also have to mark individual members `@objc`? Would you like the request for bridging notarized and filed in triplicate?

I can understand the impulse to require it explicitly on `public` members, but on the other hand, if you *do* accidentally publish a member as `@objc`, what's the harm? Is there any non-breaking change you can make to a `@nonobjc` method which wouldn't be legal on an `@objc` one?

There probably are; making an Int parameter of a final method Int? isn’t generally source-breaking, but means that you can no longer expose an @objc entrypoint. That said...

Or can you just deprecate the `@objc` version and move on?

it’s easy to leave a deprecated @objc entrypoint in place if this happened to you, so I don’t think “accidentally made an @objc API that I wanted to be @nonobjc and now I have to support it going forward” is strong argument in favor of this proposal.

  - Doug

···

On Mar 22, 2017, at 10:55 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 21, 2017, at 11:03 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:


(Douglas Gregor) #19

Here’s an idea for working around the problem of the lack of static knowledge during migration. Probably it’s kind of tacky and won’t get much traction in it’s current form, but it might start some useful discussion at least.

Right now, @objc when applied to a _class_ is completely useless; if a class ultimately inherits from NSObject, it is always implicitly @objc, and applying @objc to a class not rooted in NSObject is always an error. (I think at some point in the past we allowed @objc classes that _don’t_ inherit from NSObject, but I don’t know if that even made it into any released version of Swift, so it’s totally vestigial at this point.) We can keep this behavior in Swift 3 mode, but in Swift 4 mode, change things so that @objc applied to a class enables @objc inference for the members of the class, and the absence of @objc enables the new, more limited inference behavior outlined in this proposal.

Then the migration story can just be “slap @objc on every NSObject-derived class and you’re good”. Existing mixed source bases, KVC, and so on would just work. We could also say that in Swift 4 mode, @objc on an NSObject-derived class produces a warning asking the developer to consider making individual members @objc as necessary instead. This would allow a Swift 4 migration to proceed in two phases — first fix any fallout from SE-0110 or new string stuff or whatever, and get a working app that builds and runs in Swift 4 mode, albeit with some warnings. Then they can deal with marking individual class members as @objc later. We could still have the option of making it an error to apply @objc to an entire class in a future release of Swift, if we decide it is beneficial to do so.

I don’t think we should repurpose @objc to mean “infer @objc if possible”, but we could add a new attribute—you’d mentioned @objcMembers somewhere before, I’ll refer to it as @implicitobjc” here—that one could added to classes or extensions. The semantics could be:

* @implicitobjc added to a class implicitly makes members of that class *and all of its subclasses* @objc if they can be exposed to Objective-C
* @implicitobjc added to a class extension makes members of that class extension @objc if they can be exposed to Objective-C

This is a more elegant way to handle the XCTestCase issue—we can mark XCTestCase as @implicitobjc through some Clang-side attribute or API note—and makes it easy for class hierarchies that rely heavily on the Objective-C runtime to continue to get @objc inference.

  - Doug

···

On Mar 23, 2017, at 2:06 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

Based on feedback, the all-or-nothing nature of the Swift 2->3 migration was rather painful — mixing and matching 3 and 4 modules will definitely help us do better the next time around, and allowing a complex change such as this one to be done piecemeal could be a further step in the right direction.

Slava

On Mar 21, 2017, at 11:03 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0160: Limiting @objc inference" begins now and runs through March 28. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at:

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

* What is your evaluation of the proposal?
* Is the problem being addressed significant enough to warrant a change to Swift?
* Does this proposal fit well with the feel and direction of Swift?
* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at:
  https://github.com/apple/swift-evolution/blob/master/process.md

Thanks!

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #20

FWIW, I’ve updated the proposal with a much more extensive discussion of the migration story. It incorporates “conservative” and “minimal” approaches, and describes how to do the “minimal” approach well. (It’s backed by a prototype implementation as well; see the Implementation link at the top”)

  - Doug

···

On Mar 22, 2017, at 7:50 AM, Michel Fortin via swift-evolution <swift-evolution@swift.org> wrote:

* What is your evaluation of the proposal?

Good. I'll certainly appreciate the added clarity of knowing which methods are exposed to Objective-C.

Currently, Swift-only apps are bloated by unnecessary Objective-C thunks. The motivation section says that this will reduce the reduce the binary sizes, which is good, but should we expect not emitting those thunks will also reduce by a bit the compilation times? Perhaps not by much, but I do think it will.

I'm already littering some projects with plenty of `@nonobjc` annotations because I don't need those thunks. This proposal would allow me to make this cleaner. Perhaps the migrator should remove the `@nonobjc` attributes when they become unnecessary. Actually, would the changes in this proposal make the `@nonobjc` attribute irrelevant?

I'm a bit worried about the migration though.

I think it would be reasonable for the migrator to have two settings. A conservative one that adds `@objc ` to all currently implicit `@objc` declarations, to be used when you need to be absolutely certain everything still works. Pruning of extra `@objc` would have to be done manually later, if desired. And the default one that only adds `@objc` when the migrator sees it as necessary.