#if someCompilationSwitch
extension SomeType: @retroactive SomeProtocol {
... many lines here
}
#else
extension SomeType: SomeProtocol {
... many lines here
}
#endif
I tried putting #if brackets around @retroactive or around @retroactive SomeProtocol, or around the first line - but nothing works short of duplicating the whole thing.
Just an idea, but if the requirements of SomeProtocol are the same in either case, how about this:
extension SomeType { // note it's a general extension, not for the protocol!
... all those many lines
}
#if someCompilationSwitch
extension SomeType: @retroactive SomeProtocol { } // implementation empty, as the type already has it
#else
extension SomeType: SomeProtocol { } // same
#endif
It's not ideal, but would at least spare you most of the redundant lines, right?
We specifically allow fully qualifying SomeProtocol with the module name (that is, SomeModule.SomeProtocol) as a synonym for @retroactive so that you don't have to use a compile-time switch for Swift language version
So I could suppress retroactive warnings by merely qualifying the protocol with the module name? But why this feature? Looks non-obvious, unsafe and makes it harder to spot retroactive conformances (e.g. during PR reviews, etc).
For the exact purpose that you're asking about in the first place--that is, for source compatibility so you don't have to write the same thing twice to support Swift versions that pre-date @retroactive.
To clarify, you need to qualify both the type and the protocol, and in this way it is explicit that both come from outside modules (i.e., that the conformance is retroactive).
qualifying Equatable with Swift. Equatable gets rid of the warning... Qualifying CMVideoDimensions with CoreMedia.CMVideoDimensions doesn't seem to have any effect.
The fix-it suggests add @retroactive without mentioning the "qualification" trick, hence it wasn't obvious to me. Beyond that it could be a footgun, IMHO, as I routinely see module qualifications which are unrelated to retroactive silencing, like these:
Yet, is this supposed to be a temporary trick, and once users switch to Swift 6 that supports retroactive the "qualification" trick is going to be deprecated in favour of @retroactive ? It just feels odd otherwise, to be honest.
Swift 5: Swift.Equatable, etc - ok, you meant @retroactive, will treat it like that
Swift 6: Swift.Equatable, etc - is deprecated, please change it to explicit @retroctive.
The implementation looks at each inheritance clause entry for a @retroactive attribute or full qualification. The way that the extended type itself is written is not considered. I don’t think this is a bug, it seems like a defensible model. I don’t know if the intent is to deprecate the fully qualified syntax at some point. It would be nice though, from the viewpoint of deleting unnecessary code.
It isn’t the approved model described in the proposal, which spells out explicitly that the type itself is to be qualified as well to silence the warning.
That defeats the point. How would you devise a single spelling that is valid in all versions of Swift so that you don’t have to use #if, the precise thing that you asked for?
Interesting. The logic is implemented here. You can see it's only looking at the qualification on the protocol name itself:
Another apparent limitation is that isModuleQualified() is not checked for protocol compositions, eg this will not have the intended effect:
extension Foo: P & Swift.Equatable {}
Actually implementing this would require adding even more one-off logic and I think it would be better to just remove the 'fully qualified' thing entirely at some point.
Apologies; I vaguely remember that the original intent for how to silence the warning was that you have to module-qualify every item in the extension, but then during the reshuffle to @retroactive I think I lost that part of the fallback. I also agree that just removing this fallback syntax would be preferred in the fullness of time.
No need to apologize, I think like I said doing the proper TypeRepr walk here that handles all permutations would be awkward anyway.
In fact, we can’t really avoid the reality that proposals will occasionally diverge from the implemented behavior - for reasons intentional and unintentional - so having an accepted process for revision of existing proposals, specifically to account for experience gained during and after the implementation, would help sort out issues like this.
There are many things that are incompatible across various versions of Swift (e.g. Swift 1 vs 2, or Swift 3 vs 4) so that would be just one of those things.
Just to clarify, since this often a point of confusion and I think this conversation may be suffering from that confusion: Swift language modes and Swift compiler versions are independent versions. The alternative syntax is not being offered in order to make the code compatible with multiple language modes (e.g. Swift 5 vs. Swift 6). It is offered to allow the code to compile without diagnostics using multiple versions of the compiler. The Swift 6.0 compiler introduced support for the @retroactive attribute and also began diagnosing its absence in existing code, even in the Swift 5 language mode. As a result, a package that wants to both suppress the warning when building with recent versions of the compiler but also remain source compatible with older versions of the toolchain would have an awkward time doing so without this alternative syntax.
There are many things that are incompatible across various versions of Swift (e.g. Swift 1 vs 2, or Swift 3 vs 4) so that would be just one of those things.
Hopefully my explanation above was clear enough to establish why this is different than source breaking changes between language modes. Adopting either a new language mode or a new syntax is typically an opt-in activity. You're free to just not use new syntax if you're concerned about source compatibility with older toolchains. The introduction of the requirement to specify @retroactive on retroactive conformances is different, though. Just compiling existing code with the new toolchain can result in a warning. While it's just a warning, it might be important to you to address it regardless. If you also wanted to continue to maintain source compatibility with older toolchains, without the alternative syntax you'd be required to duplicate code like this:
#if compiler(>=6.0)
extension Foo: @retroactive Bar {
// ... conformance implementation ...
}
#else
extension Foo: Bar {
// ... same conformance implementation ...
}
#endif