`@unknown default` produces a warning on Linux with non-frozen enum

TL;DR: this

import Dispatch

func foo(_ interval: DispatchTimeInterval) {
    switch interval {
    case .seconds(let n):
        print("\(n) seconds")
    case .milliseconds(let n):
        print("\(n) milliseconds")
    case .microseconds(let n):
        print("\(n) microseconds")
    case .nanoseconds(let n):
        print("\(n) nanoseconds")
    case .never:
        print("never")
    @unknown default:
        print("unknown")
    }
}

results in a warning on non-Darwin platforms:

<source>:15:5: warning: default will never be executed
    @unknown default:
    ^

Why? DispatchTimeInterval is not marked as @frozen neither in swift-corelibs-libdispatch, nor in libdispatch overlays for Darwin. So, if I want so fix the warning, I either have to conditionally remove the default case on non-Darwin platforms (and the clients of my library will stop compiling if a new case is added), or powerlessly complain on the Swift forums hoping that this issue will be fixed. It'll probably be a breaking change to fix this :(

On Darwin, OTOH, the @unknown default is enforced by the compiler.

This is really unfortunate.

The difference is due to the core libraries (including Dispatch) using library evolution mode on Darwin (because they are a stable part of the operating system) whereas everywhere else they use the normal mode, because their ABIs are not yet stable.

Differences found in the core libraries between platforms are usually considered bugs to be fixed, but this isnā€™t really Dispatchā€™s fault. The ultimate fix is for other platforms to reach ABI stability.

the clients of my library will stop compiling if a new case is added

That is already the case on all platforms. Itā€™s what you are asking the compiler to do when you use @unknown. Youā€™re saying, ā€œIf there is another known, I want to be warned that Iā€™m forgetting it.ā€

This will compile without warnings on all platforms in the present, and will start errorring (during compilation) on all platforms if something is added:

// ...
case .nanoseconds(let n):
  print("\(n) nanoseconds")
case .never:
  print("never")
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
  @unknown default:
    print("unknown")
#endif
}

If you really do want to evade any future warnings on all platforms in the event of a new case being added, you can do it like this:


// ...
case .nanoseconds(let n):
  print("\(n) nanoseconds")
default:
  if case let .never = interval {
    print("never")
  } else {
    print("unknown")
  }
}

I respectfully disagree. This has nothing to do with ABI stability. If a new case is added to the enum, and the @unknown default case is present, the compiler will issue a warning rather than an error. SE-0192 explicitly says:

It is now a source-compatible change to add a case to a non-frozen enum (whether imported from C or defined in the standard library).

It never mentions that it is only applicable to libraries that are built with library evolution mode enabled. In fact, the proposal that introduced the notion of the "library evolution mode" (SE-0260) was pitched a year after SE-0192.

Thanks, I think I'll go this way. I do not, however want to evade warnings, I want to avoid breaking my clients. In fact, I do want a warning if a new enum case is added so I can quickly spot it and fix my code to handle it.

The second paragraph of the introduction in SEā€0192 says:

A key note: in this version of the proposal, nothing changes for user-defined Swift enums. This only affects C enums and enums in the standard library and overlays today. (This refers to libraries that Apple could hypothetically ship with its OSs, as it does with Foundation.framework and the Objective-C runtime.) The features described here may be used by third-party libraries in the future.

That is why it only applies to code importing stable libraries. At first the only stable libraries were those Apple wanted to embed in their operating systems. The future generalization of the stability features for anyone to use mentioned in that last sentence is precisely what later became known as library evolution mode.


You are correct. Since your original question was about how to get rid of a warning, I was not being particular about the difference between the two, and treating the whole situation in a warningsā€areā€asā€badā€asā€errors sort of way. I figured that if that warning was intolerable to you, other warnings would be too. I apologize for my imprecision.


If you would like, you could pitch the idea of exempting @unknown default from exhaustivity checks, so that it could be used without warnings even when referring to nonā€stable enumerations where it would do absolutely nothing. That would ease the source differences when importing libraries which are stable under some compilation conditions but not under others.

1 Like

I kind of thought that swift-corelibs-foundation and friends are pretty similar to the overlays with respect to source compatibility concerns. Apparently, I was wrong, and this is sad.

No need to apologize, it was me who wasn't precise enough. To elaborate: I (and I'm sure many language users out there) only want to see warnings that indicate a problem, so that I could fix it right away. If a warning suggests something potentially dangerous, I want there to be no warning.

This is a good idea! I'll do it.


I went ahead with your workaround, but decided to combine both options. For anyone interested, this is likely the least harmful way to work around this warning. If a new case is added, you'll get a warning on Darwin, and at the same time nothing will break on Linux.

According to docs:

When the compiler isnā€™t in library evolution mode, all structures and enumerations are implicitly frozen

@broadway_lamb did you end up pitching this?

1 Like