Skip to content
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

[SR-9919] Classes marked as @objcMembers don't warn if not inherited from NSObject #52325

Open
AliSoftware opened this issue Feb 13, 2019 · 8 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@AliSoftware
Copy link
Contributor

Previous ID SR-9919
Radar None
Original Reporter @AliSoftware
Type Bug
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: 924803a6681795b962e04f6332511076

Issue Description:

Summary

A pure Swift class non-inheriting NSObject can be marked as @objcMembers

Expected results

The compiler should error when classes not inheriting NSObjects are declared as @objcMembers, the same way it errors about those with @objc

Actual results

No error (and the class is omitted from the generated interface)

Example

When annotating with @objc we get the following expected error:

import Foundation

@objc class Foo {} // error: Only classes that inherit from NSObject can be declared @objc

But when annotating with @objcMembers we don't and this compiles without any error:

import Foundation

@objcMembers class Foo {}
@belkadan
Copy link
Contributor

I think this is correct behavior. A non-NSObject-inheriting class can have @objc members (which means "exposed to the ObjC runtime", not "included in the generated header"); it's just not often that useful to do it. @DougGregor?

@AliSoftware
Copy link
Contributor Author

Ha!
If it is, I'm curious of a use case though… how could the members be exposed to the ObjC runtime if the class isn't? How would the ObjC runtime access and use them then? It's not like if we could class_copyPropertyList to get them if the class itself isn't exposed, right?

@belkadan
Copy link
Contributor

You'd have to pass an instance of the class to Objective-C yourself, but you can certainly do that if you want. (Also, strictly speaking these classes can still be found through NSClassFromString.)

The original motivation for accepting this was to allow pure Swift classes to conform to Objective-C protocols. Given how many Objective-C protocols depend on NSObject-ness in practice, though, I'm not sure how useful it's actually turned out to be.

@DougGregor
Copy link
Member

Swift classes not inheriting NSObject are still valid Objective-C classes, and can have `@objc` methods... they just won't be visible to Objective-C source code because they don't show up in the generated header. In that sense, it's not wrong to allow `@objcMembers` on a Swift class not inheriting from NSObject.

My inclination is to say this is not a bug, and say that we don't want a warning for this case: it's not wrong to have `@objcMembers` without inheriting NSObject, and if we add a warning it would suggest that `@objc` and `@objcMembers` are strongly tied together—but they really aren't in the model. Adding a warning also requires adding a way to suppress that warning (e.g., by adding`@nonobjc`).

@DougGregor
Copy link
Member

@AliSoftware, do you still feel that a warning would be beneficial here?

@AliSoftware
Copy link
Contributor Author

@DougGregor But to be honest, even if @objc and @objcMembers are initially to give exposition to the ObjC runtime more than to add to the generated header, given that 90% of their use cases are to make a Swift class be exposed to the ObjC source code more than just the runtime, I feel like a warning would be beneficial.

But you're also right that if we add a warning (which would be equally triggered by @objcMembers on a non-NSObject class but also on an @objc annotation on a property or method of a class that is itself not exposed to ObjC), we might need a way to disable it as it would apparently be a correct construction for the very rare and unlikely cases that it would be intentional

Maybe a better/ideal solution to the problem would be to add a way (accessible from the IDE like under the 4-squares Ctrl-1 menu (View > Standard Editor > Show Related Item) or something?) to help diagnose why a class isn't exposed to ObjC source code (why it won't appear in the generated header)?

  • That way I'd have been able to select my class, use that "Ctrl-1 > Generated Interface > Diagnose" hypothetical menu, and it would tell me "this class isn't exposed to ObjC source code because it's not inheriting NSObject" or something?

  • And would work similarly when selecting a property or function of a class

  • maybe also emitting an explaination that a function in an {{ @objc enum Foo: Int }} can't be exposed as ObjC don't support functions in enums, and other similar diagnostics?

I realize that might this idea will probably be more work than just adding a warning though, and I might be asking for that just because I lost a lot of time recently because of that, which a diagnostic could have prevented, so was quite frustrating… but it's not the first time I saw situations like that so some help from the compiler to diagnose that (on demand or via a warning) would definitively have helped those times ;-)

@belkadan
Copy link
Contributor

@objc really isn't about the header. There are all sorts of circumstances where it won't show up there:

  • The containing class is generic, or has generic ancestry.

  • The method is private.

  • The containing class is nested (this one's actually inconsistent, which is a bug).

We're not going to have warnings in all those cases.

@AliSoftware
Copy link
Contributor Author

Yeah, that's why I said that a better ideal solution would be something different and separate from just something specifically tied to @objc in the end as it's not that related to the generated header after all, like some on-demand diagnostics using Ctrl-1 > Generated Interface > Analyze" or something to tell us why a class doesn't appear in the generated header, as I admit this was the main reason why I lost a lot of time in the first place, more than the issue related to this.

Maybe you'd prefer we close that bug report related to @objc and @objcMembers, marking it as behaving as expected and not a bug, and I open a different one related to the debugging of why a class wouldn't appear in the generated header (so one to suggest we have a way to diagnose those cases like the ones you mention above to help better understand what reasons would explain a class not to be in the generated header for those cases we intend to make it visible to the ObjC source code but don't understand why it isn't)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

3 participants