Error when Working with Generic Operator Overload

So recently I have been doing some work with an overloaded version of the + operator and Swift has been crashing because it has been expecting the wrong types. Here is the code in question:

protocol B: A where T == Self {
    static var thing: AnyA<Self> { get }
}

protocol A {
    associatedtype T: B
}

struct C<L, R>: A where L: A, R: A, L.T == R.T {
    var l: L
    var r: R
    typealias T = L.T
}
func +<T, U>(lhs: T, rhs: U) -> C<T, U> {
    return C(l: lhs, r: rhs)
}

fileprivate class _AnyABoxBase<T>: A where T: B {
    typealias Base = T
}
fileprivate class _AnyABox<T: A>: _AnyABoxBase<T.T> {
    let base: T
    init(_ base: T) {
        self.base = base
    }
}
final class AnyA<U>: A where U: B {
    typealias T = U
    private let box: _AnyABoxBase<U>
    
    init<P>(_ base: P) where P: A, P.T == U {
        self.box = _AnyABox(base)
    }

}
enum Foo: B {
    typealias T = Self
    case a
    case b
    case c
    
    static var thing: AnyA<Foo> {
        let temp = a + b + c  // Error: Cannot convert value of type 'C<Foo, Foo>' to expected argument type 'Foo'
        return AnyA<Foo>(temp)
    }
}

This code fails in the thing property on Foo when multiple + operators are used on exclusively Foo. Weirdly enough though, if I split it up over multiple lines so that it includes a type other than just Foo, it works.

enum Foo: B {
    typealias T = Self
    case a
    case b
    case c
    
    static var thing: AnyA<Foo> {
        let temp1 = a + b
        let temp2 = temp1 + c
        return AnyA<Foo>(temp2)
    }
}

I am failing to understand why Swift cannot properly understand the first one while it can understand the second one as it is the same just broken up over multiple lines. Is there any reasons why this is the case?

Note: I can also get your example to compile by writing the problematic line as:

let temp = ((a + b) as C) + c

Looks like type inference is having trouble looking 'through' the overloaded operator for some reason. Also, here's a drastically reduced reproducer:

struct C<L, R> {
    var l: L
    var r: R
}

func +<T, U>(lhs: T, rhs: U) -> C<T, U> {
    return C(l: lhs, r: rhs)
}

enum Foo {
    case a

    static var thing: Any {
        let temp = a + a + a  // Error: Cannot convert value of type 'C<Foo, Foo>' to expected argument type 'Foo'
        return temp
    }
}
2 Likes

Your code does a great job of isolating the problem! Thanks. Anyways, I was playing around with it a bit more and I found that when I add another + a at the end of the three shown in your example, Swift produces a new error for the line Referencing operator function '+' on 'RangeReplaceableCollection' requires that 'Foo' conform to 'RangeReplaceableCollection'. Also I found that if I change the operator to || or && for example it does in fact compile. But on the same note, I found that -, *, and / operators all caused it to crash with the same or similar errors. Would you say that this is a bug in the compiler's type inference?

Terms of Service

Privacy Policy

Cookie Policy