3

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.

1
  • Lol this also works: { a * a }() * B() or { $0 * $0 }(a) * B() :p Commented Jan 7, 2018 at 16:22

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I think your observation about this bug not occurring when a custom operator is used is very interesting! Using intermediate casts seems like an okay-ish workaround for the use-cases I have. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.