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

objcProtocol.Type.self fail to cast to Protocol #62134

Open
miku1958 opened this issue Nov 16, 2022 · 8 comments
Open

objcProtocol.Type.self fail to cast to Protocol #62134

miku1958 opened this issue Nov 16, 2022 · 8 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler itself metatypes Feature → types: Metatypes type checker Area → compiler: Semantic analysis

Comments

@miku1958
Copy link
Contributor

miku1958 commented Nov 16, 2022

Describe the bug
I have a method for manipulating objcProtocol that looks like this

func classConforming<P>(to objcProtocol: P.Type) -> P? {
	let p: Any = objcProtocol
	guard let pp = p as? Protocol else {
		return nil
	}
	return self.objonly_classConformingToProtocol(pp) as? P
}

self.objonly_classConformingToProtocol accepts a Protocol object as parameter and return AnyObject

When I call it like:
classConforming(to: aProtocol.Type.self)
Xcode shows me that aProtocol.Type.self is aProtocol.Type.Protocol and lldb tells me that objcProtocol is "@thick aProtocol.Type.Protocol" and p is "" but print will tell me it is aProtocol.Type and I can't cast it to Protocol, pp will always nil.

I also tried adding where P.Type == Protocol, but the compiler would give me an error:

Generic signature requires types 'P.Type' and 'Protocol' to be the same

I'm not sure how the compiler converts, but I think since Swift lacks a mechanism to determine whether it's a protocol/class/struct/enum/actor, it should at least ensure type consistency when passing parameters

@miku1958 miku1958 added the bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. label Nov 16, 2022
@miku1958
Copy link
Contributor Author

miku1958 commented Nov 16, 2022

Another try:
type(of: objcProtocol) will return me “aProtocol.Type.Protocol”, both lldb and print are, but I still can't cast it to Protocol, then I realized that Protocol is a Objc class, so I cast p as? Protocol.Type but pp is still nil

@miku1958
Copy link
Contributor Author

miku1958 commented Nov 16, 2022

One possible solution I expect is to make the type returned by aProtocol.self (aProtocol.Protocol, aka Protocol) consistent with the one in the parameter:
I can use P.Protocol instead of Protocol, which is still essentially an Objc object, but then I can return P.Type by genericizing it, like this:

func classConforming<P>(to objcProtocol: P.Protocol) -> P.Type? {
	return self.objonly_classConformingToProtocol(objcProtocol) as? P.Type
}

classConforming(to: aProtocol.self)

@miku1958 miku1958 changed the title Protocol.Type.self fail to cast to Protocol objcProtocol.Type.self fail to cast to Protocol Nov 16, 2022
@LucianoPAlmeida LucianoPAlmeida added runtime The Swift Runtime conformances Feature → protocol: protocol conformances labels Nov 18, 2022
@AnthonyLatsis AnthonyLatsis added metatypes Feature → types: Metatypes compiler The Swift compiler itself type checker Area → compiler: Semantic analysis and removed runtime The Swift Runtime conformances Feature → protocol: protocol conformances labels Nov 24, 2022
@AnthonyLatsis
Copy link
Collaborator

AnthonyLatsis commented Nov 24, 2022

What are you trying to do? What is the type signature of objonly_classConformingToProtocol? Casting a metatype value to a non-metatype type will always fail. For example, you cannot cast Array<Int>.self to Collection, because the metatype value itself does not conform to Collection — it is the instances of Array<Int>.self that do.

@AnthonyLatsis AnthonyLatsis added existentials Feature: values of types like `any Collection`, `Any` and `AnyObject`; type-erased values and removed existentials Feature: values of types like `any Collection`, `Any` and `AnyObject`; type-erased values labels Nov 24, 2022
@miku1958
Copy link
Contributor Author

miku1958 commented Nov 26, 2022

Hi @AnthonyLatsis, I know when I use aProtocol.Type.self, the complier will return me a Protocol object, and it will lose all the type information about the aProtocol, and the problem I'm having is that I want to get the Protocol object, and I want the function to be generic, so I can return the object that implements the aProtocol

objonly_classConformingToProtocol takes a Protocol object and returns a Class that can be found in the registry:

- (Class)objonly_classConformingToProtocol:(Protocol *)inProtocol;

@AnthonyLatsis
Copy link
Collaborator

The protocol metatype object (the protocol type as an object) is MyProtocol.self (the type of this object is MyProtocol.Protocol). MyProtocol.Type.self is a higher-level, meta-metatype object; its type is MyProtocol.Type.Protocol. Although these meta-metatypes exist as part of the type system, they have little if any practical value.

Unfortunately, there is no way of expressing a generic parameter that accepts only protocol types, so func foo<T>(_: T.Protocol) will not work.

- (Class)objonly_classConformingToProtocol:(Protocol *)inProtocol;

I wasn’t able to find any documentation on this Protocol type. How do you instantiate it? There is no implicit Swift conversion from a protocol metatype like aProtocol.self to Protocol.

@miku1958
Copy link
Contributor Author

miku1958 commented Nov 28, 2022

We basically only use the Protocol object as a key to the dictionary to find the class in objonly_classConformingToProtocol, and this practice of using objonly_classConformingToProtocol is very widespread in our projects.

What I mean is, can this generic be added at the compiler level. The compiler can know what I mean when I use P.Protocol like this:

func classConforming<P>(to objcProtocol: P.Protocol) -> P.Type? {
	return self.objonly_classConformingToProtocol(objcProtocol) as? P.Type
}

Or allow me to use Protocol(or protocol) as a generic constraint like:

func classConforming<P>(to objcProtocol: P.Type) -> P.Type? where P: Protocol {
	return self.objonly_classConformingToProtocol(objcProtocol.Protocol) as? P.Type
}

@AnthonyLatsis
Copy link
Collaborator

What I mean is, can this generic be added at the compiler level.

No, you cannot constrain a generic parameter to protocols only, Swift currently lacks this kind of expressivity. There is no analogue of : AnyObject for protocols.

func classConforming<P>(to objcProtocol: P.Type) -> P.Type? where P: Protocol {
  return self.objonly_classConformingToProtocol(objcProtocol.Protocol) as? P.Type
}

The question of whether something similar to this will work boils down to the question of how you instantiate an object of this Protocol type in Swift. Have you ever used objonly_classConformingToProtocol in Swift successfully before, without generics? If so, could you provide an example?

@miku1958
Copy link
Contributor Author

Hi @AnthonyLatsis, I can use objonly_classConformingToProtocol in Swift directly, but the "Class" from Objc will become "AnyClass" in Swift, and for historical reasons, compiler allow me to call any Objc method with AnyClass. It's not safe, and it's not what I want

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

No branches or pull requests

3 participants