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-6993] Swift imports @optional ObjC methods from superclass protocol as if implemented #49541

Open
swift-ci opened this issue Feb 13, 2018 · 6 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-6993
Radar rdar://problem/16127954
Original Reporter jalkut@red-sweater.com (JIRA User)
Type Bug

Attachment: Download

Environment

Version 9.3 beta (9Q98q)
macOS 10.13.3.

Additional Detail from JIRA
Votes 1
Component/s Compiler
Labels Bug, ClangImporter
Assignee None
Priority Medium

md5: ebc0717b07092dd0f64cd27c0152f061

is duplicated by:

  • SR-7067 Imported Obj-C class is incorrectly assumed to implement all optional protocol methods

relates to:

  • SR-11470 Swift ignores "unavailable" attribute of initializer declared in an ObjC protocol for a Swift subclass of ObjC class

Issue Description:

If a Swift class inherits from an Objective-C class that conforms to a protocol with @optional methods, Swift assumes that the superclass in fact implements all the optional methods.

This has implications ranging from the unexpected requirement that the subclass label its own implementations of such methods as "override," to the failure of "super.responds(to: Selector)" to work as expected when calling through to a superclass.

Open the attached project to see a concise example: AppDelegate.swift implements a class AppDelegate that inherits from ObjC SuperDelegate. SuperDelegate conforms to the "MaybeSo" protocol but doesn't implement its only optional method: -doSomething.

When you build and run this example, you can see that no warnings or errors are received, and at runtime when AppDelegate.doSomething() attempts to test super's implementation of doSomething, it is assured that it does implement the method, so it calls through and raises an exception.

The real-life scenario I ran into this in was for an AppDelegate class in Swift that inherits from an Objective-C class that conforms to NSApplicationDelegate. In this scenario, the AppDelegate in Swift implemented applicationWillTerminate: and tried to call through to super.applicationDidTerminate only if it is implemented.

To continue experimenting with this bug, try moving NSApplicationDelegate conformance from the Swift AppDelegate to the ObjC SuperDelegate. Now you will be forced by Swift to use the "override" keyword on AppDelegate's implementation of applicationWillTerminate.

@swift-ci
Copy link
Collaborator Author

Comment by Daniel Jalkut (JIRA)

I am working around the issue in my Swift code by checking whether the superclass's instances respond to the selector, rather querying super directly:

if let superClass = self.superclass as? NSObject.Type {
    if superClass.instancesRespond(to: #selector(NSApplicationDelegate.applicationWillTerminate(_:))) {
        super.applicationWillTerminate(notification)
    }
}

@belkadan
Copy link
Contributor

Hmm. This used to go the other way, but I don't remember why we changed it. (Nor did we close the bug that was tracking the old behavior, rdar://problem/16127954.) @DougGregor, do you have the context here?

@swift-ci
Copy link
Collaborator Author

Comment by Daniel Jalkut (JIRA)

I feel like there are still some surprising behaviors here, but FWIW I guess I overlooked some nuances about the behavior of "respondsToSelector". The documentation indicates I should always be calling "instancesRespondToSelector" when checking super for conformance.

@lilyball
Copy link
Mannequin

lilyball mannequin commented Feb 23, 2018

This appears to be a special case of a bug I just filed (SR-7067) wherein it seems that the Swift compiler simply assumes that an Obj-C class implements all optional methods of protocols it conforms to.

That said, you absolutely do need to still provide override when implementing the methods yourself, because you can't know at compile-time if the superclass implements it. However, ideally the Swift compiler would allow you to (nay, require you to¹) say something like super.someMethod?() to invoke it, though unfortunately the Obj-C runtime doesn't actually have a way to do this (the best you can do is query the superclass type if instancesRespondToSelector, but that doesn't help if the method is implemented dynamically at runtime, though it's certainly a lot better than not supporting this at all and requiring the subclass implementor to have to know whether it's safe to call super).

¹I don't know when optional protocol methods changed from optionals to IUOs, but because of that, you can call such methods without checking for their existence. This seems like a really bad idea to me.

@DougGregor
Copy link
Member

@swift-ci create

@belkadan
Copy link
Contributor

Okay, it looks like this is not a recent regression, but more likely a deliberate "least bad choice" from a past release to deal with wanting to call super (as Kevin noted) that for some reason neither I nor Doug remember doing. We could still improve this situation, but at least it's understood.

(Optional protocol methods are not IUOs, as discussed elsewhere. That's a code completion bug.)

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
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