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-55] non-@objc protocol existentials do not conform to their own protocol type #42677

Open
swift-ci opened this issue Dec 4, 2015 · 20 comments
Open
Assignees

Comments

@swift-ci
Copy link
Collaborator

@swift-ci swift-ci commented Dec 4, 2015

Previous ID SR-55
Radar rdar://problem/21341337
Original Reporter siarhei.barysenka (JIRA User)
Type Bug

Attachment: Download

Additional Detail from JIRA
Votes 40
Component/s Compiler
Labels Bug
Assignee @slavapestov
Priority Medium

md5: 4333a622f7ff4546521a5fc9f3702a7d

is duplicated by:

  • SR-1176 Using protocols (inheriting from class) as generic parameter which needs to be of type AnyObject fails
  • SR-1324 Passing a parameter of type protocol P to a generic method with a protocol P constraint fails
  • SR-1581 Can not use protocol to fulfill associatedtype requirement where associatedType has protocol constraint
  • SR-2020 Protocol composition doesn't conform to associated type requirement
  • SR-2580 Call to a generic function that expects argument to conform a protocol fails if argument compose multiple protocols.
  • SR-2704 T: Protocol generic constraint doesn't accept protocol existentials
  • SR-3038 Cannot pass in a protocol type to a constrained generic parameter
  • SR-5188 Class existentials don't conform to protocols that the class conforms to
  • SR-5341 Protocol with associatedtype constrained to another protocol & inheritance does not compile
  • SR-5357 Type-constrained protocol cannot be used as a generic argument for similarly constrained generic parameter
  • SR-5777 Existential Metatype in generics
  • SR-6039 A protocol A: class is not any AnyObject
  • SR-6173 Problems with generics + AnyObject or class protocol conformance
  • SR-6645 EXC_BAD_ACCESS in code using protocol extension for defining default implementation
  • SR-7008 Subclass existential doesn't satisfy generic class constraints
  • SR-7412 Swift crashes when a class conforms to a protocol with associated type as AnyObject
  • SR-7563 Generic class with AnyObject constraint won't except protocol that also has AnyObject constraint
  • SR-8261 Generics don't identify Protocol with class requirement properly
  • SR-8624 Unable to use protocol with class type where AnyObject is expected
  • SR-8637 'Protocol composition type' with generic types/inheritance
  • SR-8893 AnyObject constraint on generic type parameter doesn't work when a protocol type is passed as an argument
  • SR-10031 Constraints involving a Class & Protocol constraint don't type check correctly
  • SR-10053 Protocols extending class types are not recognized as class types

relates to:

  • SR-7332 Conditional conformance over array of heterogenous values that share a protocol conformance fails
  • SR-10991 Function Builders & Opaque return types error Xcode 11 / Swift 5.1
  • SR-10907 Default implementation not picked up when conforming to two protocols at once
  • SR-1581 Can not use protocol to fulfill associatedtype requirement where associatedType has protocol constraint
  • SR-7343 Generic constraint fails to infer type when expecting sub protocol

Issue Description:

Summary:
The compiler throws the error

generic parameter 'T' could not be inferred

at the final line of the following code:

protocol MediaProtocol {}
protocol ImageProtocol: MediaProtocol {}

class Image: ImageProtocol {}

func itemToProcess() -> ImageProtocol {
    return Image()
}

//func process<T>(item: T) -> T { // Uncomment this line and comment out the following one to remove the compile-time error
func process<T: MediaProtocol>(item: T) -> T {
    return item
}

let image = itemToProcess()
let processedImage: ImageProtocol = process(image)

The error is gone when conformance to `MediaProtocol` is removed from the generic type `T` at the `process` method declaration in the given code.

Reproduced on:
Xcode 7.1.1 (7B1005), Swift 2.1, OS X 10.11

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 4, 2015

Comment by Chris Willmore (JIRA)

Reduced case:

protocol P {}
class C: P {}

func process<T: P>(item: T) -> T { return item }
func f(image: P) { let processed: P = process(image) }

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 14, 2015

Comment by Chris Willmore (JIRA)

Doug Gregor and I spoke briefly about this bug and he says making it work is a very deep rabbit hole.

@slavapestov
Copy link
Member

@slavapestov slavapestov commented Dec 17, 2015

This was on my agenda – I've discussed it before with Joe Groff and we have a half-plan. Low priority though.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented May 9, 2017

Comment by nate rivard (JIRA)

This is still an issue on Swift 3.2, is there a plan to tackle what seems like a pretty valid use-case (see dupes for more cases?)

@mattneub
Copy link

@mattneub mattneub commented Sep 7, 2017

This does compile:

@objc protocol P {}
class C: P {}

func process<T: P>(item: T) -> T { return item }
func f(image: P) { let processed: P = process(item:image) }

Adding @objc makes it compile; removing it makes it not compile again.

Some of us over on Stack Overflow find this surprising and would like to know whether that's deliberate or a buggy edge-case.

@slavapestov
Copy link
Member

@slavapestov slavapestov commented Sep 7, 2017

It's deliberate – lifting this restriction is what this bug is about. Like I said it's tricky and we don't have any concrete plans yet.

@mattneub
Copy link

@mattneub mattneub commented Sep 7, 2017

Thank you, @slavapestov !

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Nov 8, 2017

Comment by Ilya Kazakov (JIRA)

Anxiously awaiting a positive resolution of this issue by your team. For all its glory the new Codable protocol is quite limited without it. It is going to be a pain to use it, as every class needs to implement its own boilerplate code to support custom mapping. And custom mapping is what we use in the real world to talk to remote systems.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Jan 22, 2018

Comment by Bridger Maxwell (JIRA)

I wish this worked so I could make an array of weak listeners (like a delegate, but more than one).

This is an example:

https://gist.github.com/bridger/f3818fc187a3b3a17b147de31981f06d

@ffried
Copy link
Contributor

@ffried ffried commented Jan 22, 2018

bridger (JIRA User) I've had the same problem (SR-1176). But there's a workaround (not nice but works). I've removed the :AnyObject restriction from the generic parameter. I've then got initializers that again restrict the parameter to AnyObject. Those are non-failable but of course you can't use them with an existential type. Then there are initializers that take the generic type directly. Those are failable and have to check if the object can be converted to an AnyObject first.

Quick sample:

struct Weak<T> {
    private weak var _object: AnyObject?
    public var object: T? {
        get { return _object as? T }
        set { _object = newValue as AnyObject }
    }

    init?(object: T) {
        self._object = object as AnyObject
        if object == nil {
            return nil
        }
    }
}

extension Weak where T: AnyObject {
    init(object: T) {
        self._object = object
    }
}

Note that the casts to AnyObject always succeed, but the type does not match, so the object == nil will succeed and the initializer will return nil.

@belkadan
Copy link
Contributor

@belkadan belkadan commented Apr 11, 2018

I think we should separate out the AnyObject parts of this. AnyObject is special in that you don't need a witness table to conform to it.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Apr 28, 2018

Comment by Marc Palmer (JIRA)

@belkadan it would be great if we could get progress on this. It is so painful. I want to have a general purpose ObserverSet<T: AnyObject> but this forces all observer types to be be @objc, which has viral effects of de-swifting my code.

I need `T: AnyObject` because my observer set needs observer identity.

@werediver
Copy link

@werediver werediver commented May 1, 2018

marcpalmer (JIRA User) In Swift 4 everything is convertible to AnyObject and for AnyObject instances we have ObjectIdentifier to identify them. This mean that your ObserverSet is most likely possible to implement without AnyObject constraint — it will statically allow value types, but will never perform anything meaningful with them.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented May 1, 2018

Comment by Marc Palmer (JIRA)

Thanks @werediver I'll give it a try. However, I don't think you meant what you wrote i.e. not everything is convertible to `AnyObject`:

struct A {
    let text = "Hello"
}

let value = A()
let x: AnyObject = value

---

error: Test.playground:43:20: error: value of type 'A' does not conform to specified type 'AnyObject'
let x: AnyObject = value
                   ^~~~~
                         as AnyObject

@werediver
Copy link

@werediver werediver commented May 1, 2018

marcpalmer (JIRA User) I did mean what I said, but nothing more — I didn't say "implicitly". Everything is convertible to AnyObject explicitly: anything as AnyObject.

I'd suggest moving this question to Stack Overflow, because it (likely) can be solved in Swift 4 as it is now.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented May 1, 2018

Comment by Marc Palmer (JIRA)

I'm not planning to have a discussion here. The fact is that there is a problem with protocol X: AnyObject being used with generic types and this is very painful and weird.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented May 1, 2018

Comment by Marc Palmer (JIRA)

Try explaining the nature of this problem to a student who is being taught Swift at school on an iPad. "Here's a protocol... constrain it to AnyObject so that only classes can implement it... now let's show you what generics look like... oh wait."

@belkadan
Copy link
Contributor

@belkadan belkadan commented May 1, 2018

Just like a value is not the same thing as an Optional containing that value, neither is a class instance that conforms to a protocol P the same as a value of type P that contains that instance.

@belkadan
Copy link
Contributor

@belkadan belkadan commented Mar 15, 2019

I'm not 100% sure the AnyObject part belongs in this bug…

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Oct 6, 2021

Comment by Maarten Billemont (JIRA)

To add a data-point to this (old) bug, while it can be appreciated that there are difficulties involved with allowing protocol types to satisfy protocol constrained generics, we can also appreciate that a language where this is allowed:

protocol A {}
protocol B : A {}

struct S1<P> {
    var p : P?
}
do {
    let a: A? = nil, b: B? = nil, c = "C"
    S1(p: a)
    S1(p: b)
    S1(p: c)
}

But wanting to specialize S1 so I can protect it from being used with non-A types is not:

struct S2<P : A> {
    var p : P?
}
do {
    let a: A? = nil, b: B? = nil, c = "C"
    S2(p: a) // unexpected: Protocol 'A' as a type cannot conform to the protocol itself
    S2(p: b) // unexpected: Protocol 'B' as a type cannot conform to 'A'
    S2(p: c) // expected: Generic struct 'S2' requires that 'String' conform to 'A'
}

Is not ideal.

This frustrates the ability to create safe specializations, forcing me to go with the "any type goes" (S1) construct and fall back to runtime as? A checks instead if I need specialization (S2).

@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
Projects
None yet
Development

No branches or pull requests

6 participants