id-as-Any and ObjC generic parameters


(John McCall) #1

Suppose we are calling a function that is generic over T, where T: AnyObject. This comes up when e.g. calling an initializer for an ObjC generic class.

Today we allow conversions from [String] to [NSString], String to NSString, and [String] to [T], but we do not allow a conversion from String to T. The concrete conversions are allowed because there is a bridging conversion from String to NSString. The generic "scalar" conversion is not allowed because the type-checker's enumeration of possible supertypes does not consider bridged types. The generic array conversion is allowed because the special-case code that governs collection up-casting in the type-checker immediately turns the generic argument into its bridged type and so bypasses that flaw.

One goal of the id-as-Any import change is to remove the implicit bridging conversions and the widespread use of AnyObject constraints. As part of this, [U] will convert to [V] only when U is convertible to V. This implies that both of the generic conversions above would be rejected.

With no other changes, this means that when calling an ObjC generic initializer, e.g.:
  @interface Generic<T>
  - (id) initWithArray: (NSArray<T> *) array;
  @end
it will not be possible to pass a [String] (inferring T == NSObject). This is technically a regression.

Well, maybe we don't care.

If we do care, one option is to try to bridge generic parameters and their constraints. Effectively, this would mean removing the implicitly AnyObject constraint on all ObjC generic parameters. If we did this, it would be possible to pass a [String] to the initializer of Generic, and inferring T == String; SILGen would then recognize the need to bridge to NSArray<NSString> when passing the argument. But this is a fair amount of work that I think is new to the plan.

Thoughts?

John.


(Jordan Rose) #2

I don’t think it’s the end of the world to have to say this explicitly. We’re not removing the explicit ‘as’ conversion, and the type of Generic won’t be Generic<String> anyway; it’ll be Generic<NSString>.

Jordan

···

On Jul 7, 2016, at 17:26, John McCall via swift-dev <swift-dev@swift.org> wrote:

Suppose we are calling a function that is generic over T, where T: AnyObject. This comes up when e.g. calling an initializer for an ObjC generic class.

Today we allow conversions from [String] to [NSString], String to NSString, and [String] to [T], but we do not allow a conversion from String to T. The concrete conversions are allowed because there is a bridging conversion from String to NSString. The generic "scalar" conversion is not allowed because the type-checker's enumeration of possible supertypes does not consider bridged types. The generic array conversion is allowed because the special-case code that governs collection up-casting in the type-checker immediately turns the generic argument into its bridged type and so bypasses that flaw.

One goal of the id-as-Any import change is to remove the implicit bridging conversions and the widespread use of AnyObject constraints. As part of this, [U] will convert to [V] only when U is convertible to V. This implies that both of the generic conversions above would be rejected.

With no other changes, this means that when calling an ObjC generic initializer, e.g.:
@interface Generic<T>
- (id) initWithArray: (NSArray<T> *) array;
@end
it will not be possible to pass a [String] (inferring T == NSObject). This is technically a regression.

Well, maybe we don't care.

If we do care, one option is to try to bridge generic parameters and their constraints. Effectively, this would mean removing the implicitly AnyObject constraint on all ObjC generic parameters. If we did this, it would be possible to pass a [String] to the initializer of Generic, and inferring T == String; SILGen would then recognize the need to bridge to NSArray<NSString> when passing the argument. But this is a fair amount of work that I think is new to the plan.

Thoughts?


(Joe Groff) #3

I tried to subset handling generic parameters out. While it's definitely a possibility, it seems like something we could tackle in an additive fashion later, since removing the 'AnyObject' constraint from generics is "just" removing requirements. I think we can live with the lack of conversion in ObjC generics. It's already the case that ObjC generic covariance doesn't carry over to Swift, and I don't think we have bandwidth to make that work for this release either.

-Joe

···

On Jul 7, 2016, at 5:26 PM, John McCall <rjmccall@apple.com> wrote:

Suppose we are calling a function that is generic over T, where T: AnyObject. This comes up when e.g. calling an initializer for an ObjC generic class.

Today we allow conversions from [String] to [NSString], String to NSString, and [String] to [T], but we do not allow a conversion from String to T. The concrete conversions are allowed because there is a bridging conversion from String to NSString. The generic "scalar" conversion is not allowed because the type-checker's enumeration of possible supertypes does not consider bridged types. The generic array conversion is allowed because the special-case code that governs collection up-casting in the type-checker immediately turns the generic argument into its bridged type and so bypasses that flaw.

One goal of the id-as-Any import change is to remove the implicit bridging conversions and the widespread use of AnyObject constraints. As part of this, [U] will convert to [V] only when U is convertible to V. This implies that both of the generic conversions above would be rejected.

With no other changes, this means that when calling an ObjC generic initializer, e.g.:
@interface Generic<T>
- (id) initWithArray: (NSArray<T> *) array;
@end
it will not be possible to pass a [String] (inferring T == NSObject). This is technically a regression.

Well, maybe we don't care.

If we do care, one option is to try to bridge generic parameters and their constraints. Effectively, this would mean removing the implicitly AnyObject constraint on all ObjC generic parameters. If we did this, it would be possible to pass a [String] to the initializer of Generic, and inferring T == String; SILGen would then recognize the need to bridge to NSArray<NSString> when passing the argument. But this is a fair amount of work that I think is new to the plan.

Thoughts?


(John McCall) #4

Okay. Between Jordan's response and yours, I'm convinced that we can just regress here.

John.

···

On Jul 7, 2016, at 8:17 PM, Joe Groff <jgroff@apple.com> wrote:

On Jul 7, 2016, at 5:26 PM, John McCall <rjmccall@apple.com> wrote:

Suppose we are calling a function that is generic over T, where T: AnyObject. This comes up when e.g. calling an initializer for an ObjC generic class.

Today we allow conversions from [String] to [NSString], String to NSString, and [String] to [T], but we do not allow a conversion from String to T. The concrete conversions are allowed because there is a bridging conversion from String to NSString. The generic "scalar" conversion is not allowed because the type-checker's enumeration of possible supertypes does not consider bridged types. The generic array conversion is allowed because the special-case code that governs collection up-casting in the type-checker immediately turns the generic argument into its bridged type and so bypasses that flaw.

One goal of the id-as-Any import change is to remove the implicit bridging conversions and the widespread use of AnyObject constraints. As part of this, [U] will convert to [V] only when U is convertible to V. This implies that both of the generic conversions above would be rejected.

With no other changes, this means that when calling an ObjC generic initializer, e.g.:
@interface Generic<T>
- (id) initWithArray: (NSArray<T> *) array;
@end
it will not be possible to pass a [String] (inferring T == NSObject). This is technically a regression.

Well, maybe we don't care.

If we do care, one option is to try to bridge generic parameters and their constraints. Effectively, this would mean removing the implicitly AnyObject constraint on all ObjC generic parameters. If we did this, it would be possible to pass a [String] to the initializer of Generic, and inferring T == String; SILGen would then recognize the need to bridge to NSArray<NSString> when passing the argument. But this is a fair amount of work that I think is new to the plan.

Thoughts?

I tried to subset handling generic parameters out. While it's definitely a possibility, it seems like something we could tackle in an additive fashion later, since removing the 'AnyObject' constraint from generics is "just" removing requirements. I think we can live with the lack of conversion in ObjC generics. It's already the case that ObjC generic covariance doesn't carry over to Swift, and I don't think we have bandwidth to make that work for this release either.