Casting Bug Swift


(James Campbell) #1

Say I have been passed an array of objects that conform to a protocol
called "Eatable" i.e [Eatable]

If I then try to cast them to an array of classes who happen to implement
that protocol i.e [Burger]

The compiler lets me but in the runtime it corrupts the memory.

···

*___________________________________*

*James⎥iOS Lead*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *


(Radek Pietruszewski) #2

Hey James,

The appropriate place for compiler bugs is https://bugs.swift.org <https://bugs.swift.org/>, not Swift Evolution.

Best,
— Radek

···

On 25 Feb 2016, at 17:44, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

Say I have been passed an array of objects that conform to a protocol called "Eatable" i.e [Eatable]

If I then try to cast them to an array of classes who happen to implement that protocol i.e [Burger]

The compiler lets me but in the runtime it corrupts the memory.
___________________________________

James⎥iOS Lead

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >

10 Finsbury Square

London

> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jordan Rose) #3

I think there are two things going on here:

- The compiler allows a memory-corrupting cast, which should be rejected (a bug).
- But it's a useful thing to do (a language change).

I'm actually surprised about the bug, since we get plenty of questions about why the compiler doesn't allow it, implying that the compiler is indeed rejecting it. The answer is that protocol values and class values don't have the same representation, so converting between [Eatable] and [Burger] is an O(N) operation that requires allocating a new array. But that may not be a good enough reason not to allow it—conversions from NSArray to Array can do the same thing if the NSArray was mutable.

Jordan

···

On Feb 25, 2016, at 8:53 , Radosław Pietruszewski via swift-evolution <swift-evolution@swift.org> wrote:

Hey James,

The appropriate place for compiler bugs is https://bugs.swift.org <https://bugs.swift.org/>, not Swift Evolution.

Best,
— Radek

On 25 Feb 2016, at 17:44, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Say I have been passed an array of objects that conform to a protocol called "Eatable" i.e [Eatable]

If I then try to cast them to an array of classes who happen to implement that protocol i.e [Burger]

The compiler lets me but in the runtime it corrupts the memory.
___________________________________

James⎥iOS Lead

james@supmenow.com <mailto:james@supmenow.com>⎥supmenow.com <http://supmenow.com/>
Sup

Runway East >>

10 Finsbury Square

London

>> EC2A 1AF

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #4

I've overheard discussion of removing the covariant container conversions altogether, since they're inconsistent with the rest of the language, lead to a lot of type-checker and runtime dynamic cast complexity, and have unpredictable performance if generalized, and moving in the direction of encouraging the use of abstract AnyCollection values instead of concrete Arrays, that would free us to make covariant conversions cheaper by wrapping instead of eagerly mapping the array representation.

-Joe

···

On Feb 25, 2016, at 9:07 AM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

I think there are two things going on here:

- The compiler allows a memory-corrupting cast, which should be rejected (a bug).
- But it's a useful thing to do (a language change).

I'm actually surprised about the bug, since we get plenty of questions about why the compiler doesn't allow it, implying that the compiler is indeed rejecting it. The answer is that protocol values and class values don't have the same representation, so converting between [Eatable] and [Burger] is an O(N) operation that requires allocating a new array. But that may not be a good enough reason not to allow it—conversions from NSArray to Array can do the same thing if the NSArray was mutable.


(Austin Zheng) #5

That's a bold move. Is opt-in covariance/contravariance as an alternative, with performance caveats explicitly denoted, still off the table? Would the Any(X)Collection types be automatically covariant, like Array<T> is now, or would it work differently somehow?

Austin

···

On Feb 25, 2016, at 11:12 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 25, 2016, at 9:07 AM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

I think there are two things going on here:

- The compiler allows a memory-corrupting cast, which should be rejected (a bug).
- But it's a useful thing to do (a language change).

I'm actually surprised about the bug, since we get plenty of questions about why the compiler doesn't allow it, implying that the compiler is indeed rejecting it. The answer is that protocol values and class values don't have the same representation, so converting between [Eatable] and [Burger] is an O(N) operation that requires allocating a new array. But that may not be a good enough reason not to allow it—conversions from NSArray to Array can do the same thing if the NSArray was mutable.

I've overheard discussion of removing the covariant container conversions altogether, since they're inconsistent with the rest of the language, lead to a lot of type-checker and runtime dynamic cast complexity, and have unpredictable performance if generalized, and moving in the direction of encouraging the use of abstract AnyCollection values instead of concrete Arrays, that would free us to make covariant conversions cheaper by wrapping instead of eagerly mapping the array representation.

-Joe

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #6

In the actual code, the array with the protocol type i.e [Eatable] is used
as an associated type. So perhaps it normally gets rejected but isn't in
this case ?

···

*___________________________________*

*James⎥Head of Clown*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Thu, Feb 25, 2016 at 5:07 PM, Jordan Rose <jordan_rose@apple.com> wrote:

I think there are two things going on here:

- The compiler allows a memory-corrupting cast, which should be rejected
(a bug).
- But it's a useful thing to do (a language change).

I'm actually surprised about the bug, since we get plenty of questions
about why the compiler doesn't allow it, implying that the compiler is
indeed rejecting it. The answer is that protocol values and class values
don't have the same representation, so converting between [Eatable] and
[Burger] is an O(N) operation that requires allocating a new array. But
that may not be a good enough reason not to allow it—conversions from
NSArray to Array can do the same thing if the NSArray was mutable.

Jordan

On Feb 25, 2016, at 8:53 , Radosław Pietruszewski via swift-evolution < > swift-evolution@swift.org> wrote:

Hey James,

The appropriate place for compiler bugs is https://bugs.swift.org, not
Swift Evolution.

Best,
— Radek

On 25 Feb 2016, at 17:44, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

Say I have been passed an array of objects that conform to a protocol
called "Eatable" i.e [Eatable]

If I then try to cast them to an array of classes who happen to implement
that protocol i.e [Burger]

The compiler lets me but in the runtime it corrupts the memory.

*___________________________________*

*James⎥iOS Lead*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com/>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Radek Pietruszewski) #7

Interesting. Two Qs:

- when you say "moving in the direction of encouraging the use of abstract AnyCollection values", do you mean in general? To encourage people to use AnyCollection<T> instead of [T] in their code?
- how could covariant collection casting look like in this world?

···

Sent from my iPhone

On 25 Feb 2016, at 20:12, Joe Groff <jgroff@apple.com> wrote:

On Feb 25, 2016, at 9:07 AM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

I think there are two things going on here:

- The compiler allows a memory-corrupting cast, which should be rejected (a bug).
- But it's a useful thing to do (a language change).

I'm actually surprised about the bug, since we get plenty of questions about why the compiler doesn't allow it, implying that the compiler is indeed rejecting it. The answer is that protocol values and class values don't have the same representation, so converting between [Eatable] and [Burger] is an O(N) operation that requires allocating a new array. But that may not be a good enough reason not to allow it—conversions from NSArray to Array can do the same thing if the NSArray was mutable.

I've overheard discussion of removing the covariant container conversions altogether, since they're inconsistent with the rest of the language, lead to a lot of type-checker and runtime dynamic cast complexity, and have unpredictable performance if generalized, and moving in the direction of encouraging the use of abstract AnyCollection values instead of concrete Arrays, that would free us to make covariant conversions cheaper by wrapping instead of eagerly mapping the array representation.

-Joe


(Joe Groff) #8

Interesting. Two Qs:

- when you say "moving in the direction of encouraging the use of abstract AnyCollection values", do you mean in general? To encourage people to use AnyCollection<T> instead of [T] in their code?

Yeah. In Cocoa, the NSArray and NSDictionary class clusters serve the purpose not only of standard collections but as abstract interfaces for ordered and keyed collections, after all.

- how could covariant collection casting look like in this world?

A no-language-support-needed approach might be to give the CollectionType protocol a `mapToType(T.self)` method which can be given specialized implementations for cases that can be efficiently implemented (such as mapping an array of class references to another class type), and/or fall back to wrapping with a lazy adapter in cases like going from a collection of ConcreteType to a collection of ProtocolType.

-Joe

···

On Feb 25, 2016, at 11:45 AM, Radek Pietruszewski <radexpl@gmail.com> wrote:


(Radek Pietruszewski) #9

Hm. Obviously, you have far more insight into current implementation and performance difficulties, but purely from a programmer's perspective, going from [Foo] to AnyCollection<Foo> and from 'xs as? [Bar]' to 'xs.mapAsType(Bar.self)' seems like a step back.

Regarding the latter, wouldn't it be better to somehow generalize covariant collection casts? Perhaps a special protocol different collection types can conform to to define their cast implementation?

···

Sent from my iPhone

On 25 Feb 2016, at 20:50, Joe Groff <jgroff@apple.com> wrote:

On Feb 25, 2016, at 11:45 AM, Radek Pietruszewski <radexpl@gmail.com> wrote:

Interesting. Two Qs:

- when you say "moving in the direction of encouraging the use of abstract AnyCollection values", do you mean in general? To encourage people to use AnyCollection<T> instead of [T] in their code?

Yeah. In Cocoa, the NSArray and NSDictionary class clusters serve the purpose not only of standard collections but as abstract interfaces for ordered and keyed collections, after all.

- how could covariant collection casting look like in this world?

A no-language-support-needed approach might be to give the CollectionType protocol a `mapToType(T.self)` method which can be given specialized implementations for cases that can be efficiently implemented (such as mapping an array of class references to another class type), and/or fall back to wrapping with a lazy adapter in cases like going from a collection of ConcreteType to a collection of ProtocolType.

-Joe


(Howard Lovatt) #10

I think it would be a good idea to remove the covariance of Array since it
is compiler magic and that would clear the way for a more general solution.

···

On Friday, 26 February 2016, Radek Pietruszewski via swift-evolution < swift-evolution@swift.org> wrote:

Interesting. Two Qs:

- when you say "moving in the direction of encouraging the use of abstract
AnyCollection values", do you mean in general? To encourage people to use
AnyCollection<T> instead of [T] in their code?
- how could covariant collection casting look like in this world?

Sent from my iPhone

> On 25 Feb 2016, at 20:12, Joe Groff <jgroff@apple.com <javascript:;>> > wrote:
>
>
>> On Feb 25, 2016, at 9:07 AM, Jordan Rose via swift-evolution < > swift-evolution@swift.org <javascript:;>> wrote:
>>
>> I think there are two things going on here:
>>
>> - The compiler allows a memory-corrupting cast, which should be
rejected (a bug).
>> - But it's a useful thing to do (a language change).
>>
>> I'm actually surprised about the bug, since we get plenty of questions
about why the compiler doesn't allow it, implying that the compiler is
indeed rejecting it. The answer is that protocol values and class values
don't have the same representation, so converting between [Eatable] and
[Burger] is an O(N) operation that requires allocating a new array. But
that may not be a good enough reason not to allow it—conversions from
NSArray to Array can do the same thing if the NSArray was mutable.
>
> I've overheard discussion of removing the covariant container
conversions altogether, since they're inconsistent with the rest of the
language, lead to a lot of type-checker and runtime dynamic cast
complexity, and have unpredictable performance if generalized, and moving
in the direction of encouraging the use of abstract AnyCollection values
instead of concrete Arrays, that would free us to make covariant
conversions cheaper by wrapping instead of eagerly mapping the array
representation.
>
> -Joe
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <javascript:;>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-- Howard.


(Joe Groff) #11

Hm. Obviously, you have far more insight into current implementation and performance difficulties, but purely from a programmer's perspective, going from [Foo] to AnyCollection<Foo> and from 'xs as? [Bar]' to 'xs.mapAsType(Bar.self)' seems like a step back.

True. This is all just ideas at this point. If we were serious about changing the currency type for collections, we'd probably want to change the type sugar to match.

-Joe

···

On Feb 25, 2016, at 12:07 PM, Radek Pietruszewski <radexpl@gmail.com> wrote:

Regarding the latter, wouldn't it be better to somehow generalize covariant collection casts? Perhaps a special protocol different collection types can conform to to define their cast implementation?

Sent from my iPhone

On 25 Feb 2016, at 20:50, Joe Groff <jgroff@apple.com> wrote:

On Feb 25, 2016, at 11:45 AM, Radek Pietruszewski <radexpl@gmail.com> wrote:

Interesting. Two Qs:

- when you say "moving in the direction of encouraging the use of abstract AnyCollection values", do you mean in general? To encourage people to use AnyCollection<T> instead of [T] in their code?

Yeah. In Cocoa, the NSArray and NSDictionary class clusters serve the purpose not only of standard collections but as abstract interfaces for ordered and keyed collections, after all.

- how could covariant collection casting look like in this world?

A no-language-support-needed approach might be to give the CollectionType protocol a `mapToType(T.self)` method which can be given specialized implementations for cases that can be efficiently implemented (such as mapping an array of class references to another class type), and/or fall back to wrapping with a lazy adapter in cases like going from a collection of ConcreteType to a collection of ProtocolType.

-Joe


(Joe Groff) #12

An explicit `mapAsType` operation might also serve as the basis for a compiler-blessed protocol that allows for user-defined covariant container types.

-Joe

···

On Feb 25, 2016, at 12:14 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 25, 2016, at 12:07 PM, Radek Pietruszewski <radexpl@gmail.com <mailto:radexpl@gmail.com>> wrote:

Hm. Obviously, you have far more insight into current implementation and performance difficulties, but purely from a programmer's perspective, going from [Foo] to AnyCollection<Foo> and from 'xs as? [Bar]' to 'xs.mapAsType(Bar.self)' seems like a step back.

True. This is all just ideas at this point. If we were serious about changing the currency type for collections, we'd probably want to change the type sugar to match.