ElementaryFunctions and ambiguity in extensions

If you call a top-level math function in an extension for a ElementaryFunction-conforming type, there's type checking ambiguity:

import Foundation

extension Float {
    func foo() {
        _ = sin(self)
    }
}
test.swift:5:13: error: static member 'sin' cannot be used on instance of type 'Float'
        _ = sin(self)
            ^~~
            Float.

Maybe users rarely define extensions for Float, but it could be a usability issue for types that a user often wants to extend, e.g. vector types and Tensor. When I tried conforming TensorFlow's Tensor type to ElementaryFunctions, it broke a lot of existing code that called top-level math functions in extensions.

@scanon, any thoughts or suggestions?

2 Likes

I run into this all the time. It would be nice if the compiler could simply keep looking and discover that a global function matches better than the type method.

In the meantime I usually end up defining a forwarding instance method just to make the inconveniences go away:

extension Float {
    func sin(_ angle: Float) -> Float {
        return Float.sin(angle)
    }

    func foo() {
        _ = sin(self) // ✓
    }
}

Fundamentally, this is a typechecker bug, which unfortunately no one has had bandwidth to tackle. I discussed it with the core team at some length in the context of SE-0246, and the conclusion was that this was an annoyance that we're willing to live with in the short term in order to get generic math function support into the stdlib. The earlier proposal with the Math pseudonamespace would have avoided the problem, but ultimately it was decided that this was the less annoying solution.

If someone wants to try fixing the underlying typechecker bug in the near term, have at it.

1 Like

Is there a JIRA bug for this?

https://bugs.swift.org/browse/SR-1772

I think this might be the same as SR–456 “Confusing build error when calling 'max' function within 'extension Int'”

1 Like

Here's a fix: https://github.com/apple/swift/pull/25316.