> On Mar 25, 2017, at 3:46 PM, Brent Royal-Gordon <[email protected]> 
> wrote:
> 
>> On Mar 24, 2017, at 10:09 AM, Douglas Gregor <[email protected]> wrote:
>> 
>>> 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).
> 
> We can do that, but personally, I really hate these kinds of conservative 
> migrations. It might be unavoidable, though.
> 
>>> 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?
> 
> But there was no indication during Swift 3 that this feature was going away. 
> As I understand it, a deprecation cycle introduces advanced warning of a 
> change so you have time to prepare for it; that's not available here.

The deprecation cycle is however long Swift 3 compatibility mode is around: 
Swift 3 compatibility mode can start warning about uses of the deprecated @objc 
inference—both in the compiler proper and at runtime via logging.

> 
> My concern is that, because the tools are not really aware of KVC, we can't 
> count on the compiler to lead developers to missing `@objc` properties. Folks 
> are only going to find those mistakes through testing, and they're inevitably 
> going to miss a few spots. So some poor schmuck is going to migrate their 
> code to Swift 4 without realizing this is an issue at all, accidentally miss 
> a few spots in their testing, ship it, and have to deal with weird crashes 
> out of nowhere. They're going to say, "My code worked just fine before. Swift 
> 4 broke it!" And they won't be wrong.
> 
> I'd be more comfortable with a version-long deprecation cycle that gave 
> developers plenty of time to notice these bugs. Failing that, I'd at least 
> like to see them get backtraces containing a symbol name like 
> `YouCantInvokeASwiftMemberThroughTheObjectiveCRuntimeUnlessItsMarkedWithAtObjc`
>  so the nature of the problem and its solution will be more obvious. 
> (Preferably, this function would log the instance and selector, so if people 
> got both the logs and the backtrace, the diagnosis would be as simple as we 
> can make it.)

We can do logs with a backtrace. This is fantastic suggestion also made earlier.

> (Actually, I wonder if we could install a `-doesNotRecognizeSelector:` 
> override in Swift classes which looked for a matching member in the Swift 
> runtime metadata and, if it found one, called the `YouCantInvoke…` function? 
> That would be lower overhead than generating stubs at compile time, and the 
> slowness of searching the runtime metadata wouldn't matter much since it was 
> going to crash anyway. I'm not sure if it might remove useful information 
> from the backtrace, though. Maybe in Swift 5, when these bugs will be more 
> rare. Or maybe in `SwiftObject`.)

The Objective-C thunks are nontrivial code patterns that we won’t be able to 
generate at runtime without a ton of infrastructure, so we’re stuck with 
statically generating the stubs. Also, we don’t have Swift runtime metadata for 
methods now.

> 
>> 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 do it because you need a particular object to interact with Objective-C. 
> In that circumstance, I don't think the compiler is wrong to assume that you 
> want to expose as many members as possible to Objective-C.

You don’t need it for a particular object to interact with Objective-C; you 
need it so that the class itself is visible to Objective-C source code. Or 
because something forced you to inherit from NSObject (e.g,, the 
NSObjectProtocol, which I mentioned in my reply to Charles).

> 
>>> 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.
> 
> I understand that, but again, I think it's defensible for the compiler to 
> assume that, if you want dynamic behavior in a class where you've already 
> enabled Objective-C interop, you probably want that dynamic behavior to be 
> compatible with Objective-C.
> 
> I guess we just take different standpoints on Objective-C interop. My belief 
> is that, if you state an intention to have a type interoperate with 
> Objective-C, Swift should try to expose as many of its members to Objective-C 
> as possible. I think you believe that Swift should expose as *little* as 
> possible to Objective-C.

I think that exposing as many of its members to Objective-C as possible is both 
hard to reason about (something your idea below would address) and has an 
unacceptable impact on code size.

> Because of that difference, I actually think I'd be *more* likely to support 
> removing inference by requiring an explicit `@nonobjc` on members of 
> Objective-C-compatible classes which aren't compatible with Objective-C. That 
> is, writing:
> 
>       class Foo: NSObject {
>               var bar: Int?
>       }
> 
> Is an error; you have to write:
> 
>       class Foo: NSObject {
>               @nonobjc var bar: Int?
>       }
> 
> I don't really like that answer very much, but I like it more than I would 
> like requiring `@objc` if `bar` were a plain `Int`.

Interesting. I floated the idea of doing this for members of @objc protocol 
extensions

        extension UITableViewController {
            func objCWontSeeMee() { } // error: must have explicit @nonobjc to 
acknowledge that this will not be available in @objc.
        }

but almost nobody liked that idea due to the boilerplate it caused. I think 
they’ll yelp louder if they have to do it in all of their NSObject-derived 
classes.

        - Doug

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to