The proposal document is at the moment the most detailed documentation for this feature that exists. The attribute probably ought to be covered in The Swift Programming Language, where the intended use case could be clarified officially.
In hindsight, the proposal ought to have been a lot clearer about this analogy because this confusion is probably inevitable. @backDeployed
allows ABI stable libraries to extend the effective availability of their own public APIs to older OSes, where the API isn't actually present in the library. This resembles a polyfill mechanically, but it doesn't support the same patterns as folks may be used to from other languages. It does not help a module to overlay a polyfill on top of an API from another module.
The attribute does not affect how this aspect of the language works. In many contexts, Swift allows you to introduce declarations that shadow other declarations. For example, function parameters can be shadowed by local variables:
func foo(x: Int) {
let x = 2
print(x)
}
foo(x: 1) // "2"
Swift also allows members of extensions to shadow members of a type declared in another module. For example, in the program below I've declared my own count
property on the Swift.String
type that always returns 1 instead of the real length:
extension String {
var count: Int { return 1 }
}
print("string".count) // 1
print(("string" as any Collection).count) // 6
I suppose you could use this technique to transparently redirect calls to APIs from the SDK in your module if you really wanted to implement a shim without changing any of the existing call sites, but it's important to understand exactly what's happening and what the limitations of the approach are. As demonstrated by the output from the second print()
, the count
declaration I wrote hasn't somehow dynamically replaced String.count
in the entire program; it's just an alternative function that the compiler may statically resolve specific calls to. The witness of the count
requirement of Collection
still calls the count
implementation in the standard library, since that's the one the compiler resolved the witness to when compiling String
's conformance to Collection
in the standard library, and so the second .count
invokes the standard library's implementation.
If you follow how shadowing operates in the example above, then you can take it a step further to understand what adding the @backDeployed
attribute to the count
declaration in the example does: it introduces a thunk every time that specific instance of count
would be called. For callers in the same module this accomplishes basically nothing at all (and the call to the thunk would actually be optimized away). For callers in another module, it would do something, but still not something very useful since the original implementation of the function doesn't ship with the OS and therefore the thunk is still just dispatching between two copies of a function that doesn't live in the OS.
It would be interesting to discuss how a feature that allows modules to provide polyfills for declarations in other modules would work. But that would be a separate language feature to pitch and discuss, and it isn't the problem we set out to solve by introducing @backDeployed
into the language. I encourage anyone who wants to see such a feature to start a discussion thread in the Evolution section of the forum.
If there are specific examples of code that ought to be considered invalid that the compiler can diagnose to help alleviate some of the confusion, I'm happy to implement them. Unfortunately, though, the original code example in this thread is a perfectly valid application of the attribute; you could copy-paste that code into the implementation of some framework in Apple's SDK and it would be a 100% valid API, if a bit weird. I think the crux of the problem is that Swift happens to allow you to use extensions to shadow the members of types, and therefore if you have this misunderstanding about the purpose of @backDeployed
then nothing will stop you from writing this code and thinking it does something that it doesn't. I think the obvious next step is to ensure there's really clear official documentation for the feature, though, and I'll try to get the ball rolling on that.