This is a big issue with Swift and SwiftPM's current approach (or lack of one) for libraries that can be distributed with multiple versions, and especially for libraries distributed in either binary or source form.
- Module versioning and/or module aliasing
There's some on-going work in this area. This would allow you to depend on either SDK System or package system, and just call it "System" in your own source code as well as your dependents. This would leave you in an either/or world, though, but is still a big improvement.
- Type conversions between module versions
This is just speculation at this point, but I think it's important for libraries to be able to define conversion functions for their types between module versions. This allows for interoperability, which could be essential for dual-distribution style libraries like System (and perhaps, eventually, for other "Swift Standard Packages" it they can be distributed with a toolchain, SDK, etc).
A type from a source dependency is a different type at run time than the "same" type from a binary dependency (binary dependencies have API and ABI, source just has API). So you could imagine something similar to a cross-import overlay that defines conversion functions.
For cases where the layout is known to be compatible (e.g. via ABI annotation today, but hopefully via other means in the future), it's essentially a bit-cast. You could imagine the compiler even synthesizing this for at least a subset of types. User code doing the conversion might start off being explicit, but you could similarly imagine the compiler inserting conversion calls for you.
- Ability to back-deploy new functionality based on top of old binaries as source
Using points #1 and #2, you could imagine many new features as source that is built on top of existing functionality and interfaces. For example, everything in System is source code built on top of long-established OS interfaces, and this is why a package build has no real SDK version requirements. Something like SwiftNIO would be the same, though they might have newer/better APIs when newer/better interfaces are available (e.g. io_uring), and System might also find itself in this situation. Either way, the vast majority of functionality can be auto-back-deployable given tooling support.
You could back-deploy the new FilePath syntactic APIs on to the old version of System by just distributing the relevant source code. This might not be a case where the layout is equivalent, so there might be an explicit conversion/copy, but that's still dramatically preferable to it not being possible. If System were to acquire file system APIs in the future, then those too should be back-deployable to the extent they use pre-existing OS interfaces.