I'm fairly neutral to perhaps slightly negative on this proposal.
Applicability and usefulness
I agree it's useful, but I question whether it's widely useful, because today a deployment target is a fixed property of a module rather than a floating value. Sure, you could dynamically raise the value of a module's deployment target to match that of its downstream dependencies as a sort of "optimization", and I believe that's been proposed for SwiftPM before, but it's not how Apple's developer and build tools have ever worked historically, so it's not a common scenario that you can take advantage of this today.
I would find a #if sdk construct more generally applicable in today's ecosystem since any given module is often built with a wider range of different SDK versions (as opposed to only ever using a single deployment target), and the only way around this is to fall back to C.
Perfect example here: Use pipe2/dup3 O_CLOEXEC atomically where available (#373) · swiftlang/swift-subprocess@8f099c6 · GitHub - I needed both a compile time check (#if defined(__MAC_27_0) in C, or hypothetically #if sdk(...) in Swift) in the case that the code is building with an SDK older than macOS 27, and a runtime check (if (__builtin_available(...)) in C if #available in Swift) in the case that the code is building with an SDK at least macOS 27, but with a lower deployment target.
Usage examples
This section should help explain why I'm slightly negative on this proposal.
This usage example is good:
#if deploymentTargetAtLeast(macOS 15, iOS 18, *)
import Synchronization
typealias Lock<State> = Mutex<State>
#else
import os
typealias Lock<State> = OSAllocatedUnfairLock<State>
#endif
because it's about representing a case where the behavior can't vary based on the runtime OS version (a typealias).
But I'd like to see the proposal also include a second example showing how there are cases where you'd want to combine both #if deploymentTargetAtLeast and #available together, like this:
#if deploymentTargetAtLeast(macOS 15, iOS 18, *)
useNewImplementation()
#else
if #available(macOS 15, iOS 18, *) {
useNewImplementation()
} else {
useFallbackImplementation()
}
#endif
In the above example this is the most correct expression, because if you've compiled with a new-enough SDK, you should be calling the new implementation whenever possible.
The alternative:
#if deploymentTargetAtLeast(macOS 15, iOS 18, *)
useNewImplementation()
#else
useFallbackImplementation()
#endif
would NOT be correct from the perspective of how deployment targets are expected to be used on Apple platforms (you'd always compile the else case until you drop the older deployment target, and then you don't need the condition in the first place), and I worry developers will incorrectly apply the second form above when if #available would be the correct in this case instead.
I think it would be preferable to explore compiler features where:
if #available(macOS 15, iOS 18, *) {
useNewImplementation()
} else {
useFallbackImplementation()
}
could be dynamically optimized to remove the runtime check if the code is compiled with a deployment target where the check becomes a no-op. And in fact, is it not doing that already?
Naming
Also, as several folks have touched on in the thread, I do NOT think we should be changing the spelling of this construct to align with SwiftPM's platforms property.
A user might be using another build system such as CMake or Bazel instead of SwiftPM, and language constructs should be named in a way that continues to make sense in those other contexts, since build tools are independent of the language itself. "deployment target" is the right name for this, since that's what the underlying concept is called on Apple platforms and what Apple developers are familiar with.