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

Protocol types with class adoption limitation in generics #61296

Closed
francuim-d opened this issue Sep 26, 2022 · 7 comments
Closed

Protocol types with class adoption limitation in generics #61296

francuim-d opened this issue Sep 26, 2022 · 7 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler itself generics Feature: generic declarations and types type checker Area → compiler: Semantic analysis

Comments

@francuim-d
Copy link

Seems like in Swift 5.7 protocol types with class adoption limitation no longer acts like actual class type in generics

protocol Colorable: UIView { }

extension UIView {
    func subviews<T: UIView>(ofType type: T.Type) -> [T] {
        subviews.compactMap { $0 as? T }
    }
}

let view = UIView()
let colorableViews = view. subviews(ofType: Colorable.self)

Actial behavior
The code does not compiles with error: Instance method 'subviews(ofType:)' requires that 'any Colorable' inherit from 'AnyObject'

Expected behavior
The code compiles and correctly finds the subviews of the specified type.

Environment

  • macOS 12.6
  • Xcode 14.0.1 (14A400)

Additional context
That exact code compiles and works fine with Xcode 13.3.1

@francuim-d francuim-d added the bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. label Sep 26, 2022
@theblixguy theblixguy added type checker Area → compiler: Semantic analysis generics Feature: generic declarations and types labels Sep 27, 2022
@theblixguy
Copy link
Collaborator

theblixguy commented Sep 27, 2022

I think you just need to change the type of the argument from T.Type to Any.Type. Also, since the argument is not actually used, you can drop the type label and just do ofType _: Any.Type.

With this small change, your code should compile as expected.

Ignore, I misunderstood the question.

@francuim-d
Copy link
Author

You just need to change the type of the argument from T.Type to Any.Type. Also, since the argument is not actually used, you can drop the type label and just do ofType _: Any.Type.

With this small change, your code should compile as expected.

If I replace T.Type with Any.Type then I will lose the ability to specify a concrete type using the function parameter.
The method of specifying a concrete type with a function parameter is used here because this function is sometimes called in a context in which it cannot infer T by the return type.

And again, what confuses me is that with swift 5.6 the code is compiled and works exactly as it should.

@francuim-d
Copy link
Author

In this thread on the Swift forum, the author faced the same problem.
https://forums.swift.org/t/weird-error-from-the-compiler-when-using-xcode-14/60119

@theblixguy
Copy link
Collaborator

Ah okay, I should've looked at it more closely, sorry.

@theblixguy
Copy link
Collaborator

theblixguy commented Sep 27, 2022

So I think this error is happening because you have a T: UIView constraint on the function, which any Colorable (which is an existential box) does not satisfy as it does not inherit from UIView (it's the type that it contains that inherits from it).

We do have an implicit mechanism to "open" the existential box, but I am unsure if it applies here.

@francuim-d
Copy link
Author

Thanks for your reply. I studied the proposals. I understood the concept of existential types, but I wonder why this code works in swift 5.6, which already contains existential types. And is it possible to somehow write the same code that would work in swift 5.7+

@AnthonyLatsis
Copy link
Collaborator

AnthonyLatsis commented Sep 29, 2022

This was a type safety hole. You should not be able to pass a protocol metatype P.Protocol instance P.self to this function, because a protocol metatype value is not associated with any concrete type T that conforms to the protocol or inherits from the class. Remember, the protocol itself does not inherit from the class, : C is just a requirement on Self, the conforming type. For example, the 5.5.2 compiler lets the following code through, resulting in a run-time crash.

class C {
  class func test() {
    print("Hello, World!")
  }
}
protocol P: C {}

func f1<T: C>(_: T.Type) {
  T.test()
}

f1(P.self)

What you can pass is an existential metatype P.Type instance, a run-time box that encapsulates a metatype value of a concrete type that actually conforms to the protocol:

func f2(_ p: P.Type) {
  f1(p)
}

class Sub: C, P {}
f2(Sub.self) // Hello, World!

Note
What really happens here is that p gets implicitly upcasted to C.Type before being passed to f1.


This is also why you cannot do let _: P.Protocol = Sub.self, unlike let _: P.Type = Sub.self.

@AnthonyLatsis AnthonyLatsis closed this as not planned Won't fix, can't repro, duplicate, stale Sep 29, 2022
@AnthonyLatsis AnthonyLatsis added the compiler The Swift compiler itself label Dec 13, 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 itself generics Feature: generic declarations and types type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

3 participants