Reputation: 9952
I'm writing structs to represent vectors and matrices, with some operators to work with them. But some example usages of those operators produce type errors which I can't explain.
This is a reduced excrept from my library (A
was a matrix type and B
a vector type, for those interested):
struct A { }
struct B { }
func *(left: A, right: A) -> A { return A() }
func *(left: A, right: B) -> B { return B() }
let a: A = A()
let b: B = (a * a) * B()
Running this in a Swift Playground produces the following compiler error:
error: Test.playground:2:20: error: 'A' is not convertible to 'B'
let b: B = (a * a) * B()
~~~~~~~~^~~~~
Inlining some of the variables, extracting a * a
as a separate variable and/or removing type annotations on from the variables in some cases either resolves the error or produces a different error.
I'm not looking for a workaround, I'm trying to understand what's going on here, whether I'm doing something wrong or whether I found a bug in the compiler.
I'm running Xcode 9.2.
Upvotes: 3
Views: 83
Reputation: 540145
This looks like a bug to me. If we omit the type annotation (which should not be necessary anyway):
let a = A()
let b = (a * a) * B()
then the compiler complains about "ambiguous use":
main.swift:14:12: error: ambiguous reference to member '*'
let b = (a * a) * B()
^
main.swift:7:6: note: found this candidate
func *(left: A, right: A) -> A { return A() }
^
main.swift:8:6: note: found this candidate
func *(left: A, right: B) -> B { return B() }
^
Swift.Float:4:24: note: found this candidate
public static func *(lhs: Float, rhs: Float) -> Float
^
Swift.Double:4:24: note: found this candidate
public static func *(lhs: Double, rhs: Double) -> Double
Swift.Float80:4:24: note: found this candidate
public static func *(lhs: Float80, rhs: Float80) -> Float80
^
Swift.UInt8:234:24: note: found this candidate
public static func *(lhs: UInt8, rhs: UInt8) -> UInt8
^
Swift.Int8:234:24: note: found this candidate
public static func *(lhs: Int8, rhs: Int8) -> Int8
// ... and many more ...
which makes no sense, because those overloads are not applicable
to two A
operands.
It seems that the compiler is "confused" because there are so many
overloaded definitions for the *
operator. The problem does not
occur with a custom operator, this compiles without problems:
infix operator <*>: MultiplicationPrecedence
struct A { }
struct B { }
func <*>(left: A, right: A) -> A { return A() }
func <*>(left: A, right: B) -> B { return B() }
let a = A()
let b = (a <*> a) <*> B()
I recommend to file a bug at https://bugs.swift.org, and use intermediate variables:
let a = A()
let a2 = a * a
let b = a2 * B()
or intermediate casts:
let a = A()
let b = ((a * a) as A) * B()
as a workaround.
Upvotes: 4