I know from the title this probably sounds like it's going to be rather complex and confusing, so I'll try keep the code and explanation as simple as possible. Basically I have a class acting as a data structure that can be queried using a custom operator. The query is an enum that can be created using a String literal. The result of the query is optional, but when I use the custom operator and the nil coalescing operator to provide a default value, a compiler error occurs.
Here is a greatly simplified implementation of the structure and other code:
class Structure {
private var values: [String: String] = [:]
func get(query: Query) -> String? {
switch query {
case .key(let key):
return values[key]
}
}
}
enum Query: ExpressibleByStringLiteral {
case key(String)
init(stringLiteral string: String) {
self = .key(string)
}
}
precedencegroup QueryPrecedence {
associativity: left
higherThan: NilCoalescingPrecedence
}
infix operator % : QueryPrecedence
func % (structure: Structure, query: Query) -> String? {
return structure.get(query: query)
}
(If you're wondering why the enum only has one case, that's just because of the way I've simplified the code - the actual code uses more cases, which is why I'm not just querying with Strings directly)
In most cases, this code works perfectly fine. The following lines of code all compile without any issues:
let string = structure.get(query: "test")
let string = structure.get(query: "test") ?? "Default value"
let string = structure % "test"
However, the using both the custom operator and the nil coalescing operator like this:
let string = structure % "test" ?? "Default value"
Results in a compiler error: Cannot convert value of type 'Query' to expected argument type 'String'
As far as I can tell, the compiler is attempting to evaluate the "Default value"
literal as a Query, ignoring the precedence that states that the custom operator should be evaluated first. Even using brackets to create an explicit order - (structure % "test") ?? "Default value"
- still produces the same error. Forcing the literal to be treated as a String - structure % "test" ?? "Default value" as String
- does compile normally.
My guess is that this is some very minor bug in the compiler, but maybe there is some other explanation for it - I'm curious to know if that's the case. It's definitely not a scenario that's likely to be encountered often.