The peculiar case of `(-)(0)`

Found a small gotcha when using prefix - operator as a function:

_ = (+)(0.0)        // ✅
_ = (-)(0.0)        // ✅

_ = (+)(0)          // ✅
_ = (-)(0)          // ❌
// Ambiguous use of operator '-'
// - Found this candidate in module 'Swift' (Swift.Float16.-)
// - Found this candidate in module 'Swift' (Swift.Float.-)
// - Found this candidate in module 'Swift' (Swift.Double.-)

_ = (+)(0 as Int)   // ✅
_ = (-)(0 as Int)   // ✅

_ = (-)(0, 0)       // ✅

Obviously minor as it's highly unlikely anyone will be hurt or blocked by this.

4 Likes

This is caused by a performance hack we’d like to remove. The hack kicks in with operators only and not functions, skipping generic overload choices if a concrete overload matched.

In this case the generic overload (for integers) yields a better solution, because it does not involve a non-default literal type. However we don’t consider that overload at all, because the three float overloads matched first.

3 Likes

Investigating, I didn't know this would compile. Is it useful?

-0 as UInt

-0 is just a plain old literal and not the application of an operator to a literal, you have to write -(0) if you want to join the party.

1 Like

Oh, it's not that related. I just wondered as e.g.

-1 as Uint

doesn't compile.

It’s because -0 and 0 have the same binary representation, so the former is still a valid value for an unsigned type.

I guess to be really consistent we’d also need to define a unary overload of - for unsigned values which traps if the value is non-zero, and otherwise returns zero, but that seems like a pointless footgun in practice.

4 Likes