Enum case value must be literal: is there a macro that does literal expression evaluation and fill in the case value?

For many cases, yes. It's not perfect.
Don't write global operator functions when you don't have to, and you won't have the problems you illustrate with operator ambiguity.

1 Like

Alternatively, you can use the attached extension macro EnumRawValues

@EnumRawValues
enum Direction: Double, CaseIteratable {
  case north = 0.0 - 45
  case east = 90.0 - 45
  case south = 180.0 - 45
  case west = 270.0 - 45
}

It can convert the code to a valid format, and verify raw value type and case/argument count match:

@EnumRawValues(
  0.0 - 45,
  90.0 - 45,
  180.0 - 45,
  270.0 - 45
)
enum Direction: Double, CaseIteratable {
  case north
  case east
  case south
  case west
}

The example above shows a situation when global operator + ambiguity error is a better outcome... If I introduce my ** operator I want my app to fail to compile when standard library introduces its own ** operator – that gives me an opportunity to revise my code and change my ** to something else. And if I make ** operator via a static method (instead of a global function) – I am oblivious and unaware of the change – the code continues to compile which is in this case unwanted.

I have no idea what we were talking about a year ago, but you should not rely on the standard library breaking your code: we already go to great lengths with special-case rules so that standard library additions break user code as little as possible, and we will likely invent new ones.

Your excellent example upthread highlights the issue… Just imagine something less ridiculous than overriding == operator. Say I’m making an operator of my own like **, or ^^, etc. At the time of writing that operator is not defined by the standard library. Some time later that operator is introduced by the standard library and it so happens it has a different behaviour compared to mine, at which point when I recompile my code I would prefer the code to no longer compile (“breaking of kind 1”) rather than to silently work unreliably (“breaking of kind 2”) as your example above brilliantly shows. If I am not mistaken for the “breaking of kind 1” behaviour I would need to use a global operator instead of a static operator, and to be honest I don’t see why would anyone want to prefer the “breaking of kind 2” behaviour, given that runtime errors are much more costly to catch and fix compared to compile time errors.

Shadowing means your existing code continues to compile and work exactly as before. This is what the example illustrates.

To emphasize again, there is no spelling you can choose today for an operator which is guaranteed to stop compiling when a future standard library defines the same operator.

1 Like

To emphasize again, there is no spelling you can choose today for an operator which is guaranteed to stop compiling when a future standard library defines the same operator.

Hmm. Not even if I declare it as both global and static operator?

infix operator ^^

extension Double {
    public static func ^^(lhs: Double, rhs: Double) -> Double { 42 }
}

public func ^^(lhs: Double, rhs: Double) -> Double { 42 }

1.0+2.0
// Expecting to get an ambiguity error here if std library
// defines its own ^^ operator for doubles somehow.

Nope. The compiler is free to adopt new special-case hacks for standard library overload resolution to avoid user code ambiguity (and absolutely will do so if there is evidence of user code that uses such an operator). It can even do so on an operator-by-operator basis.

1 Like

Good to know. Is this specific to operators only?

No.

1 Like

Could you please expand on this. Say I have a property:

extension Double {
    var foo: Double { 42 }
}
print(3.14.foo) // 42

and one year later Swift standard library introduces foo property on Double (it so happens that it matches the "signature" of mine but the implementation and the outcome is different). I recompile my code. I am not guaranteed to get a compilation error, correct?

If so, could third party libraries do the same or is this only a superpower of swift compiler / standard library?

1 Like

No, in the specific example given where you both declare and use a property in the same module, it's just ordinary shadowing.

1 Like