Posted at

Swift3.0でコンパイル出来ない型n選

More than 3 years have passed since last update.

Xcode8、並びにSwift3.0がリリースされました。めでたい🎉

しかし、Swift2.2まではコンパイルに通っていたが、Swift3.0では通らなくなった型があります。

全体的にちゃんと調べてないので、それが仕様なのかバグなのかについては親切な人が教えてくれると思います。


制約付きassociatedtypeを束縛するGenerics関数

制約に他のprotoclを持つassociatedtypeを宣言したprotocolを2つ定義し、Generics関数でassociatedtype同士を束縛します。

protocol A {}

protocol B {
associatedtype X: A
}

protocol C {}
protocol D {
associatedtype Y: C

func hoge<Z: B>(v: Z) where Z.X == Y
}

Generics関数を持つprotocol (D) の具体型を実装すると、コンパイルが通らなくなります。

// 素直に実装してみる。🙅

class O<P: C>: D {
typealias Y = P

func hoge<Z: B>(v: Z) where Z.X == P {

}
}

// protocol extensionに実装を逃がしてみる。🙅
extension D {
func hoge<Z: B>(v: Z) where Z.X == Y {

}
}

class Q<R: C>: D {
typealias Y = R
}


workaround

Genericsの対象となるprotocolのassociatedtypeから束縛を外せば、コンパイルが通るようになります。

// everything is ok! 🎉

protocol A {}
protocol B {
associatedtype X //: A
}

protocol C {}
protocol D {
associatedtype Y: C

func hoge<Z: B>(v: Z) where Z.X == Y
}

class O<P: C>: D {
typealias Y = P

func hoge<Z: B>(v: Z) where Z.X == P {

}
}

extension D {
func hoge<Z: B>(v: Z) where Z.X == Y {

}
}

class Q<R: C>: D {
typealias Y = R
}

或いは、protocolから関数の宣言を取り除けばコンパイルが通るようになります。

protocol A {}

protocol B {
associatedtype X: A
}

protocol C {}
protocol D {
associatedtype Y: C

// func hoge<Z: B>(v: Z) where Z.X == Y
}

class O<P: C>: D {
typealias Y = P

func hoge<Z: B>(v: Z) where Z.X == P {

}
}

extension D {
func hoge<Z: B>(v: Z) where Z.X == Y {

}
}

class Q<R: C>: D {
typealias Y = R
}

protocol extensionに生やした関数を他の型で再実装した時の挙動を考えると、associatedtypeの制約をゆるくしたほうが安全だと思います。

ただ、B.Xの制約がないと困るケースが考えられますが、それについてはBを拡張した別のprotocolで束縛して、そこから下にextensionで実装をぶら下げるしか無さそうです。


同じprotocolを制約に持つassociatedtype

ひとつ前のパターンの凶悪なやつです。workaroundが減ります。

この場合は、protocolにおける関数の宣言の有無に関わらず、Generics関数を持つ具体型が実装出来なくなります。

protocol A {}

protocol B {
associatedtype X: A
}

protocol C {
associatedtype Y: A
}

// 🙅
class O<P: C>: C {
typealias Y = P

func hoge<Z: B>(v: Z) where Z.X == P {

}
}

// 🙅
extension C {
func hoge<Z: B>(v: Z) where Z.X == Y {

}
}

class Q<R: C>: C {
typealias Y = R
}

// global func は 🙆
func hoge<S: B, T: C>(v1: S, v2: T) where S.X == T.Y {

}


workaround

この場合はassociatedtypeからprotocolを取り外す他にないでしょう。

associatedtypeの束縛は、宣言した大元のprotocolのサブセットに任せ、具体的な実装はglobal funcに持たせるしか無さそうです。とてもつらい


以下見つけ次第追記