Feuermurmel
Feuermurmel

Reputation: 9952

Strange type error with basic operator overloading

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

Answers (1)

Martin R
Martin R

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

Related Questions