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-8357] Warn on methods in protocol extensions that are not defaults for requirements of the protocol but would be defaults of some parent protocol #50885

Open
rudkx opened this issue Jul 24, 2018 · 5 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis

Comments

@rudkx
Copy link
Member

rudkx commented Jul 24, 2018

Previous ID SR-8357
Radar rdar://problem/42550855
Original Reporter @rudkx
Type Bug
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, TypeChecker
Assignee None
Priority Medium

md5: 21345fc9adac7d598e0fbf5e6182e60e

relates to:

  • SR-8022 Unexpected call to parent protocol requirement in protocol extension of conditional conformance

Issue Description:

We should warn if a protocol extension implements a method that would be a default implementation of a method in a protocol that this protocol extends (but for which this protocol does not declare the same requirement).

e.g.

protocol P { func f() } 
extension P { func f() {} } 
protocol Q : P {} 
extension Q { 
  func f() { /* implementation */ } 
  func oops() { f() /* calls P's f, not the f above */ } 
}

See https://bugs.swift.org/browse/SR-8022 for motivation.

@rudkx
Copy link
Member Author

rudkx commented Jul 24, 2018

@swift-ci create

@belkadan
Copy link
Contributor

Q's f can still be used to provide a default implementation, so if we have a warning, we should also have a way to silence it that doesn't involve modifying Q itself.

@rudkx
Copy link
Member Author

rudkx commented Jul 26, 2018

Okay, after realizing the actual issue here is slightly different than I had originally thought, I offer this new description:

We should warn if a concrete type's inheritance and conformances result in surprising behavior with respect to which default method gets called.

e.g.

protocol P {
  func f()
}
extension P {
  func f() { print("P") }
}

protocol Q : P {
  // func f() // uncomment to generate a witness table entry for Q.f
}
extension Q {
  func f() { print("Q") }

  func test() {
    f() // dynamic dispatch through witness
  }
}

class X : P {}
class Y : X, Q {}

Y().test() // prints P rather than Q since test() calls f() through dynamic dispatch rather than statically resolving it to Q’s f()

In this case, since Y inherits from X, we end up satisfying P's requirement of f() with X's default implementation. This is despite the fact that Q also has a default implementation, and Y is declared as conforming to Q.

This is very subtle and could surprise users.

It would be nice to emit a warning on the definition of Y to help catch this issue. The warning text would need to be very easy to search for online because it's likely that this will require a relatively long explanation to help users understand what is going on.

I don't have any great suggestions on what syntax we would use to silence the warning if in fact the author of Y really intends calls to f() to call into P's default implementation of f().

@belkadan
Copy link
Contributor

What can the Y author do about this, even if that's not the desired behavior?

@rudkx
Copy link
Member Author

rudkx commented Jul 26, 2018

That's a good question, and I think it would require language additions. One possible approach would be to:

  • Add a mechanism to make it possible to be explicit about which default implementation is used for Y

  • Potentially also change the default so that Y gets Q's f() unless the author of Y uses the aforementioned mechanism to make it explicit

The very painful alternative without language changes would be to use composition instead of inheritance (Y holds an X and has all the boilerplate required to select which behaviors it wants from X vs. those it gets via Q or Y).

@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 type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

2 participants