Operator to avoid "Left side of nil coalescing operator '?' has non-optional type"

(Note that the title should actually be: Operator to avoid "Left side of nil coalescing operator '??' has non-optional type" but one ? disappears when saving the topic.)

To avoid the warning "Left side of nil coalescing operator '??' has non-optional type" in a macro, I defined the following operator (cf. Is there any less verbose way to customize nil-valued optional types in string interpolation?):

infix operator ???

public func ???<T>(inLHS: T?, inRHS: T) -> T {
    inLHS ?? inRHS
}

So if something might be an optional, but maybe is not, I can use something like the following without the warning:

print(stringThatMightBeOptional ??? "")

Questions:

  • Is there a method to achieve this without an additional definition like the operator above? (I can't think of any.)
  • Might the above operator be something of general use that should become part of the language?

You could cast the non-optional back into the optional type. For example, for the warning here:

func foo(x: Int) -> Int? {
    return x ?? 0 // ⚠️ Left side of nil coalescing operator '??' has non-optional type 'Int', so the right side is never used
}

Can be worked around by casting the non-optional to an optional:

func foo(x: Int) -> Int? {
    let x = x as Int?
    return x ?? 0 // ✅
}

Ideally you'd have access to the unwrapped type in your macro, but the workaround can be made to work even without that:

func foo(x: Int) -> Int? {
    let x = x as Optional<_>
    return x ?? 0 // ✅
}

I can't think of any example outside of macros where the compiler can prove statically that the value is non-optional in which I'd also want to provide a default value.

This warnings problem with macros isn't unique to optionals either. I've been facing a very similar thing this week with a macro that expected a function to be async, but could also be applied to synchronous functions. So I got a "No 'async' operations occur within 'await' expression" warning in those expansions, and I had to cast the function to an async type before using it.

What I'd want in the language is a way to silence certain kind of warnings in macros as 'expected', instead of having to come up with convoluted ways to suppress the warning.

3 Likes

So I can use:

print(stringThatMightBeOptional as String? ?? "")

Strange that I didn't think of that.

The most implicit versions are:

print(stringThatMightBeOptional as _? ?? "")
print(stringThatMightBeOptional as Optional ?? "")

The only other one I can think of that promotes similarly is

print(stringThatMightBeOptional as AnyHashable? ?? "")