I noticed recently that SE-0367 - Conditional Compilation for Attributes only applies to @attributes, and not to declaration modifiers like public. I regret not catching this during review period.
Wanted to gauge interest and see if we think it'd be a good idea to extend this to declaration modifiers as well as attributes, since they're modeled similarly in the compiler anyway and more-or-less parsed together.
I know of at least one project that needs to do something similar to this, and that project is currently using gyb to accomplish this. I'd love to obviate the need for gyb with a small syntactic extension.
I've only started noodling on the implementation here, but it seems straightforward-ish.
Do y'all think this is something that would be desirable? Or are there Reasons™ why I should abandon pursuing this?
Obviously we are quite far from the power of C preprocessor still. Imagine you'd need this prefix for 100 declarations, how easy you'd express it with define, was it available:
Heh, I proposed this in the original pitch, and was convinced that modifiers weren't necessary or couldn't work. I recommend scanning that thread to see if any real problems came up.
Ah! I see that now. I think modifiers would be a nice extension of this, but I definitely agree there's dubious utility for #if hasModifier. I would want to refactor attribute/modifier parsing to allow you to surround both modifiers and attributes in the same #if, and just separately produce a parse error that @attributes must precede modifiers.
There is (are?) preceding thread(s) that discuss solving exactly this problem by a totally different solution, namely declaring public availability; this would allow one to annotate this with an unconditional macOS @available attribute without having to bracket them with compile-time conditionals on separate lines.
Besides access modifiers, I’d imagine most other declaration modifiers would be weird to enable with such a conditional compilation feature—static or lazy for instance—and I’d be wary about making this possible without a careful consideration whether it might be actively undesirable.
For this reason, and because we have been unable to come up with a systematic or overarching vision as to what should be supported with conditional complication given that we actively do not want a C-style preprocessor, suggest to me that maybe the approach focused on availability is the more judiciously tailored one.
The other issue is this type's dependency on one particular framework that is not exposed to clients on one platform, but is exposed to clients on another. We have to conditionally mark this type internal on one platform, it's not sufficient to mark it public-but-unavailable.
Because it is a subclass of a class that comes from a dependency that is exposed to clients on certain platforms, but not exposed to clients on other platforms.
Sorry, I'm entirely too dense to understand here. It is perfectly fine for a public type (even actually available ones) to be a subclass of an internal type, no?
I have just been wondering what to do about this exact problem. I am preparing a swift-collections change to make it easier to build the code in a single-module configuration.
The package comes with an internal module that is shared across its many products, containing common helper functions that I didn't want to duplicate all over.
In the current configuration, these helpers are defined public; I avoid them polluting client namespaces by putting them in a separate module that they won't import.
Unfortunately, in the new build configuration, I have to put everything in a single module, which would leak these internals into clients. So I'd rather have them declared internal in this variant.
It is fine, if the type is internal and part of this module. However, not a type that comes from a dependency that is not available to clients.
// Dependency (available on iOS/tvOS/watchOS, but not exposed to macOS clients)
open class DependencyClass { ... }
MyModule (Dependency is visible to iOS/tvOS/watchOS, but is an internal implementation detail on macOS)
#if os(macOS)
@_implementationOnly import Dependency
#else
import Dependency
#endif
@available(iOS 16.0, *)
@available(tvOS 16.0, *)
@available(watchOS 9.0, *)
#if os(macOS)
@available(macOS 13.0, *)
internal
#else
@available(macOS 13.0, unavailable)
open
#endif
class MyClass: DependencyClass