Swift 5.8 introduces conditional attributes, using conditional directive hasAttribute(AttributeName), which help simplify code that needs to be conditionally compiled based on the presence or absence of certain attributes.
Now the #if
checks can surround attributes on a declaration wherever they appear, eliminating the need to clone a declaration just to adopt a new attribute.
#if hasAttribute(preconcurrency)
@preconcurrency
#endif
protocol P: Sendable {
func f()
func g()
}
Before this feature, conditional compilation can be used to address this problem, but the result is verbose and unsatisfactory. Moreover, the availability of some attributes can depend not on compiler version, but on platform and configuration flags. Although these are small issues in isolation, they make adopting new attributes in existing code harder than it needs to be.
Using hasAttribute is relatively straightforward, but there are a few rules:
The hasAttribute only considers attributes that are part of the language. While the built-in attribute that enables the feature will be recognized by hasAttribute
(e.g., hasAttribute(propertyWrapper)
will evaluate true
), the custom attribute will not (e.g., hasAttribute(MyWrapper)
will evaluate false
).
An attribute provides additional information about the declaration or type. Some examples of language attributes are:
A conditionally-compiled branch based on #if hasAttribute(UnknownAttributeName)
can still be parsed by an existing compiler, even though it will not be applied to the declaration because it isn’t understood:
#if hasAttribute(UnknownAttributeName)
@UnknownAttributeName
#endif
func f()