Here's a protocol:
/// A type with a "swapped dual".
/// ```
/// x.swapped.swapped == x
/// && x.a == x.swapped.b
/// && x.b == x.swapped.a
/// ```
/// Note that `x` and `x.swapped` can be of different types,
/// and so can `x.a` and `x.b`.
protocol Swappable where
Swapped: Swappable,
Swapped.Swapped == Self,
Swapped.A == B, Swapped.B == A
{
associatedtype A
associatedtype B
associatedtype Swapped
var a: A { get }
var b: B { get }
var swapped: Swapped { get }
}
We can make types conform to it, eg:
struct Pair<L, R> {
var left: L
var right: R
}
extension Pair : Swappable{
var a: L { return left }
var b: R { return right }
var swapped: Pair<R, L> { return Pair<R, L>(left: right, right: left) }
}
and it will work as expected:
func foo() {
let op = Pair(left: 123, right: "abc") // (Original Pair)
let sp = op.swapped // (Swapped Pair)
print(op.left, op.right) // 123 abc
print(sp.left, sp.right) // abc 123
}
foo()
But, when writing extensions to Swappable, the compiler has forgotten some of the constraints, Swapped.A == B, Swapped.B == A, and the only way to make it compile is to restate them:
// This should compile according to constraints of Swappable, but does not:
extension Swappable
// WORKAROUND: Uncomment these (restated) constraints:
// where Swapped.A == B, Swapped.B == A
{
func bar() {
let _: Self = swapped.swapped // <- Compiles as expected.
let _: Self.A = swapped.b // <- ERROR: Cannot convert value of type
// 'Self.Swapped.B' to specified
// type 'Self.A'.
let _: Self.B = swapped.a // <- ERROR: Cannot convert value of type
// 'Self.Swapped.A' to specified
// type 'Self.B'.
}
}
I guess this is a bug?
I would expect this to compile without the workaround, and I would kind of expect the workaround to result in a "redundant same type constraint". I think I've read something about this somewhere but couldn't find anything now.
Complete demonstration program here.
//----------------------------------------------------------------------------
// This program demonstrates (what I assume must be) a compiler bug.
// The compiler forgets some of the same type constraints of the protocol in
// protocol extensions. The issue can be worked around by restating those
// constraints at the extension.
//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
// Compiling without the workaround (ie this program, as is):
//----------------------------------------------------------------------------
// $ swiftc --version
// Apple Swift version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15)
// Target: x86_64-apple-darwin19.2.0
// $ swiftc test.swift
// test.swift:43:33: error: cannot convert value of type 'Self.Swapped.B' to specified type 'Self.A'
// let _: Self.A = swapped.b // <- ERROR: Cannot convert value of type
// ~~~~~~~~^
// as! Self.A
// test.swift:46:33: error: cannot convert value of type 'Self.Swapped.A' to specified type 'Self.B'
// let _: Self.B = swapped.a // <- ERROR: Cannot convert value of type
// ~~~~~~~~^
// as! Self.B
//
// Same thing when using dev snapshot 2020-01-31, ie:
// Apple Swift version 5.2-dev (Swift a0fbeb9179)
// Target: x86_64-apple-darwin19.2.0
//
//----------------------------------------------------------------------------
// Compiling and running with the workaround (ie after uncommenting line 62):
//----------------------------------------------------------------------------
// $ swiftc test.swift && ./test
// 123 abc
// abc 123
//----------------------------------------------------------------------------
/// A type with a "swapped dual".
/// ```
/// x.swapped.swapped == x
/// && x.a == x.swapped.b
/// && x.b == x.swapped.a
/// ```
/// Note that `x` and `x.swapped` can be of different types,
/// and so can `x.a` and `x.b`.
protocol Swappable where
Swapped: Swappable,
Swapped.Swapped == Self,
Swapped.A == B, Swapped.B == A
{
associatedtype A
associatedtype B
associatedtype Swapped
var a: A { get }
var b: B { get }
var swapped: Swapped { get }
}
// This should compile according to constraints of Swappable, but does not:
extension Swappable
// WORKAROUND: Uncomment these (restated) constraints:
// where Swapped.A == B, Swapped.B == A
{
func bar() {
let _: Self = swapped.swapped // <- Compiles as expected.
let _: Self.A = swapped.b // <- ERROR: Cannot convert value of type
// 'Self.Swapped.B' to specified
// type 'Self.A'.
let _: Self.B = swapped.a // <- ERROR: Cannot convert value of type
// 'Self.Swapped.A' to specified
// type 'Self.B'.
}
}
struct Pair<L, R> {
var left: L
var right: R
}
extension Pair : Swappable{
var a: L { return left }
var b: R { return right }
var swapped: Pair<R, L> { return Pair<R, L>(left: right, right: left) }
}
func foo() {
// This works as expected (with or without the extension):
let op = Pair(left: 123, right: "abc") // (op = Original Pair)
let sp = op.swapped // (sp = Swapped Pair)
print(op.left, op.right) // 123 abc
print(sp.left, sp.right) // abc 123
}
foo()
Tested with the default toolchain of Xcode 11.3.1 (11C504) and
dev snapshot 2020-01-31.
Filed SR-12120.