blob: dac3495fe893aede6a9ea03f658fff06ef1ddf81 [file] [log] [blame]
:orphan:
.. default-role:: term
.. title:: Library Evolution Support in Swift ("Resilience")
.. note::
This document uses some Sphinx-specific features which are not available on
GitHub. For proper rendering, download and build the docs yourself.
Since Swift 5, ABI-stable platforms have supported `library evolution`_, the
ability to change a library without breaking source or binary compatibility.
This model is intended to serve library designers whose libraries will evolve
over time. Such libraries must be both `backwards-compatible`, meaning that
existing clients should continue to work even when the library is updated, and
`forwards-compatible`, meaning that future clients will be able run using the
current version of the library. In simple terms:
- Last year's apps should work with this year's library.
- Next year's apps should work with this year's library.
This document is intended to be a specification for *which* changes can be made
without breaking binary compatibility. When a library author wants to make a
change, they can jump to the relevant section of this document to see if it's
allowed. Anything *not* listed in this document should be assumed unsafe, i.e.
changing it will break binary compatibility.
Library evolution was formally described in `SE-0260 <SE0260_>`_, but this
document should be kept up to date as new features are added to the language.
.. _library evolution: https://swift.org/blog/abi-stability-and-more/
.. _SE0260: https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md
.. contents:: :local:
Background
==========
One of Swift's primary design goals has always been to allow efficient
execution of code without sacrificing load-time abstraction of implementation.
Abstraction of implementation means that code correctly written against a
published interface will correctly function when the underlying implementation
changes to anything which still satisfies the original interface. There are
many potential reasons to provide this sort of abstraction. Apple's primary
interest is in making it easy and painless for our internal and external
developers to improve the ecosystem of Apple products by creating good and
secure programs and libraries; subtle deployment problems and/or unnecessary
dependencies on the behavior of our implementations would work against these
goals.
Our current design in Swift is to provide opt-out load-time abstraction of
implementation for all language features. Alone, this would either incur
unacceptable cost or force widespread opting-out of abstraction. We intend to
mitigate this primarily by designing the language and its implementation to
minimize unnecessary and unintended abstraction:
* Avoiding unnecessary language guarantees and taking advantage of that
flexibility to limit load-time costs.
* Within the domain that defines an entity, all the details of its
implementation are available.
* When entities are not exposed outside their defining module, their
implementation is not constrained.
* By default, entities are not exposed outside their defining modules. This is
independently desirable to reduce accidental API surface area, but happens to
also interact well with the performance design.
This last point is a specific case of a general tenet of Swift: **the default
behavior is safe**. Where possible, choices made when an entity is first
published should not limit its evolution in the future.
Introduction
============
This document will frequently refer to a *library* which vends public APIs, and
a single *client* that uses them. The same principles apply even when multiple
libraries and multiple clients are involved. It also uses the term `ABI-public`
introduced in `SE-0193 <SE0193_>`_.
This document is primarily concerned with `binary compatibility`, i.e. what
changes can safely be made to a library between releases that will not break
memory-safety or type-safety, or cause clients to fail to run at all. A
secondary concern is identifying `binary-compatible source-breaking changes
<binary-compatible source-breaking change>`, where clients compiled against the
previous version of a library are likely to behave differently than clients
compiled against the new version of the library.
.. note::
These rules do not (and cannot) guarantee that a change is *semantically*
backwards-compatible or forwards-compatible. *Any* change to a library's
existing API that affects its observable behavior may affect clients. It is
the responsibility of a library author to be sure that the changes they are
making are *semantically* correct, preserving the preconditions,
postconditions, and invariants of previously-published APIs.
This model is largely not of interest to libraries that are bundled with their
clients (distribution via source, static library, or embedded/sandboxed dynamic
library, as used by the `Swift Package Manager`_). Because a client always uses
a particular version of such a library, there is no need to worry about
backwards- or forwards-compatibility at the binary level. Just as developers
with a single app target are not forced to think about access control, anyone
writing a bundled library should (ideally) not be required to use any of the
annotations described below in order to achieve full performance.
.. _SE0193: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
.. _Swift Package Manager: https://swift.org/package-manager/
.. note::
This model may, however, be useful for library authors that want to
preserve *source* compatibility, though this document mostly doesn't
discuss that. Additionally, some of these annotations are useful for
performance today, such as ``@inlinable``.
The term "resilience" comes from the occasional use of "fragile" to describe
certain constructs that have very strict binary compatibility rules. For
example, a client's use of a C struct is "fragile" in that if the library
changes the fields in the struct, the client's use will "break". In Swift,
changing the fields in a struct will not automatically cause problems for
existing clients, so we say the struct is "resilient".
Supported Evolution
===================
This section describes the various changes that are safe to make when releasing
a new version of a library, i.e. changes that will not break binary
compatibility. They are organized by declaration type.
Top-Level Functions
~~~~~~~~~~~~~~~~~~~
An ABI-public top-level function is fairly restricted in how it can be changed.
The following changes are permitted:
- Changing the body of the function (as long as it is not ``@inlinable``; see
below).
- Changing *internal* parameter names (i.e. the names used within the function
body, not the labels that are part of the function's full name).
- Reordering generic requirements (but not the generic parameters themselves).
- Adding a default argument expression to a parameter.
- Changing or removing a default argument is a `binary-compatible
source-breaking change`.
- Adding or removing the ``@discardableResult`` and ``@warn_unqualified_access``
attributes.
No other changes are permitted; the following are particularly of note:
- An ABI-public function may not change its parameters or return type.
- An ABI-public function may not change its generic requirements.
- An ABI-public function may not change its external parameter names (labels).
- An ABI-public function may not add, remove, or reorder parameters, whether or
not they have default arguments.
- An ABI-public function that throws may not become non-throwing or vice versa.
- The ``@escaping`` attribute may not be added to or removed from a parameter.
- Adding or removing a function builder from a parameter is a
`binary-compatible source-breaking change`.
Inlinable Functions
-------------------
Functions are a very common example of "abstraction of implementation": the
function's declaration is published as API, but its body may change between
library versions as long as it upholds the same semantic contracts. This
applies to other function-like constructs as well: initializers, accessors, and
deinitializers.
However, sometimes it is useful to provide the body to clients as well. There
are a few common reasons for this:
- The function only performs simple operations, and so inlining it will both
save the overhead of a cross-library function call and allow further
optimization of callers.
- The function accesses a frozen struct with non-public members; this
allows the library author to preserve invariants while still allowing
efficient access to the struct.
- The function is generic and its performance may be greatly increased by
specialization in the client.
An ABI-public function marked with the ``@inlinable`` attribute makes its body
available to clients as part of the module's public interface. Clients are not
required to inline a function marked ``@inlinable``.
.. note::
It is legal to change the implementation of an inlinable function in the
next release of the library. However, any such change must be made with the
understanding that it will not affect existing clients. This is the
standard example of a `binary-compatible source-breaking change`.
Any local functions or closures within an inlinable function are treated as
``@_alwaysEmitIntoClient`` (see below). A client that inlines the containing
function must emit its own copy of the local functions or closures. This is
important in case it is necessary to change the inlinable function later.
Removing the ``@inlinable`` attribute completely---say, to reference private
implementation details that should not be ABI-public---is a safe change.
However, existing clients will of course not be affected by this change, and
any future use of the function must take this into account.
Although they are not a supported feature for arbitrary libraries at this time,
public `transparent`_ functions are implicitly marked ``@inlinable``.
.. _transparent: https://github.com/apple/swift/blob/master/docs/TransparentAttr.md
Restrictions on Inlinable Functions
-----------------------------------
Because the body of an inlinable function (or method, accessor, initializer,
or deinitializer) will be inlined into another module, it must not make any
assumptions that rely on knowledge of the current module. Here is a trivial
example using methods::
public struct Point2D {
var x, y: Double
public init(x: Double, y: Double) { /*...*/ }
}
extension Point2D {
@inlinable public func distance(to other: Point2D) -> Double {
let deltaX = self.x - other.x
let deltaY = self.y - other.y
return sqrt(deltaX*deltaX + deltaY*deltaY)
}
}
As written, this ``distance`` method is not safe to inline. The next release
of the library could very well replace the implementation of ``Point2D`` with a
polar representation::
public struct Point2D {
var r, theta: Double
public init(x: Double, y: Double) { /*...*/ }
}
and the ``x`` and ``y`` properties have now disappeared. To avoid this, the
bodies of inlinable functions have the following restrictions (enforced by the
compiler):
- They may not define any local types.
- They must not reference any ``private`` or ``fileprivate`` entities.
- They must not reference any ``internal`` entities except for those that have
been declared ``@usableFromInline`` or ``@inlinable``.
Always Emit Into Client
-----------------------
A function, computed property or subscript annotated as ``@_alwaysEmitIntoClient``
is similar to an ``@inlinable`` declaration, except the declaration is
not part of the module's ABI, meaning that the client must always emit
their own copy.
As a result, removing a declaration annotated as ``@_alwaysEmitIntoClient``
is a binary-compatible source-breaking change.
.. admonition:: TODO
The implementation of ``@_alwaysEmitIntoClient`` is incomplete and
should probably graduate to having its own evolution proposal.
Default Argument Expressions
----------------------------
Default argument expressions for functions that are ABI-public are implicitly
``@_alwaysEmitIntoClient``. They are subject to the same restrictions as
inlinable functions except that they also must not reference any non-``public``
entities, even if they are ``@usableFromInline`` or ``@inlinable``. This is to
make sure a default argument expression can always be written explicitly by a
caller.
Top-Level Variables and Constants
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given an ABI-public module-scope variable declared with ``var``, the following
changes are permitted:
- Adding (but not removing) a public setter to a computed variable.
- Adding or removing a non-ABI-public setter.
- Changing from a stored variable to a computed variable, or vice versa, as
long as a previously ABI-public setter is not removed.
- As a special case of the above, adding or removing ``lazy`` from a stored
property.
- Changing the body of an accessor, if the property is not marked ``@inlinable``
(see below).
- Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from
an existing variable. This is effectively the same as modifying the body of a
setter.
- Changing the initial value of a stored variable.
- Adding or removing ``weak`` to/from a variable with ``Optional`` type.
- Adding or removing ``unowned`` to/from a variable.
- Adding or removing ``@NSCopying`` to/from a variable.
- If the variable is get-only, or if it has a non-ABI-public setter, it may be
replaced by a ``let`` constant.
- Adding a property wrapper to a variable, or changing from one property
wrapper to another, as long as an ABI-public setter or projected value
(``$foo``) is not removed
- Removing a property wrapper from a variable, as long as the property wrapper
didn't have a projected value (``$foo``).
For an ABI-public module-scope constant declared with ``let``, the following
changes are permitted:
- Changing the value of the constant.
- Replacing the constant with a variable.
Giving Up Flexibility
---------------------
Top-level computed variables can be marked ``@inlinable`` just like functions.
This restricts changes a fair amount:
- Adding an ABI-public setter to a computed variable is still permitted.
- Adding or removing a non-ABI-public setter is still permitted.
- Changing the body of an accessor is a `binary-compatible source-breaking
change`.
Any inlinable accessors must follow the rules for `inlinable functions`_, as
described above.
Structs
~~~~~~~
Swift structs are a little more flexible than their C counterparts. By default,
the following changes are permitted:
- Reordering any existing members, including stored properties (unless the
struct is marked ``@frozen``; see below).
- Adding any new members, including stored properties.
- Changing existing properties from stored to computed or vice versa (unless the
struct is marked ``@frozen``; see below).
- As a special case of the above, adding or removing ``lazy`` from a stored
property.
- Changing the body of any methods, initializers, or accessors.
- Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from
an existing property (unless the struct is marked ``@frozen``; see below).
This is effectively the same as modifying the body of a setter.
- Removing any non-ABI-public members, including stored properties.
- Adding a conformance to an ABI-public protocol *that was introduced in the
same release* (see below).
- Adding or removing a conformance to a non-ABI-public protocol.
- Adding ``@dynamicCallable`` to the struct.
The important most aspect of a Swift struct is its value semantics, not its
layout. Note that adding a stored property to a struct is *not* a breaking
change even with Swift's synthesis of memberwise and no-argument initializers;
these initializers are always ``internal`` and thus not exposed to clients
outside the module.
It is not safe to add or remove ``mutating`` or ``nonmutating`` from a member
or accessor within a struct.
If a conformance is added to a type in version 1.1 of a library, it's important
that it isn't accessed in version 1.0. This means that it is only safe to add
new conformances to ABI-public protocols when the protocol is introduced, and
not after. If the protocol comes from a separate module, there is no safe way
to conform to it.
.. admonition:: TODO
Coming up with a way to do this, either with availability annotations for
protocol conformances or a way to emit a fallback copy of the conformance
for clients on older library versions to use, is highly desired.
Methods and Initializers
------------------------
For the most part struct methods and initializers are treated exactly like
top-level functions. They permit all of the same modifications and can also be
marked ``@inlinable``, with the same restrictions.
Inlinable initializers must always delegate to another initializer or assign an
entire value to ``self``, since new properties may be added between new
releases. For the same reason, initializers declared outside of the struct's
module must always delegate to another initializer or assign to ``self``. This
is enforced by the compiler.
Properties
----------
Struct properties behave largely the same as top-level variables and constants.
They permit all of the same modifications, and also allow adding or removing an
initial value entirely.
Subscripts
----------
Subscripts behave largely the same as properties, except that there are no
stored subscripts. This means that the following changes are permitted:
- Adding (but not removing) a public setter.
- Adding or removing a non-ABI-public setter.
- Changing the body of an accessor.
- Changing index parameter internal names (i.e. the names used within the
accessor bodies, not the labels that are part of the subscript's full name).
- Reordering generic requirements (but not the generic parameters themselves).
- Adding a default argument expression to an index parameter.
- Changing or removing a default argument is a `binary-compatible
source-breaking change`.
Like properties, subscripts can be marked ``@inlinable``, which makes
changing the body of an accessor a `binary-compatible source-breaking change`.
Any inlinable accessors must follow the rules for `inlinable functions`_, as
described above.
Frozen Structs
--------------
To opt out of this flexibility, a struct may be marked ``@frozen``. This
promises that no stored properties will be added to or removed from the struct,
even non-ABI-public ones, and allows the compiler to optimize as such. These
stored properties also must not have any observing accessors. In effect:
- Reordering stored instance properties (public or non-public) is not permitted.
Reordering all other members is still permitted.
- Adding new stored instance properties (public or non-public) is not permitted.
Adding any other new members is still permitted.
- Changing existing instance properties from stored to computed or
vice versa is not permitted.
- Similarly, adding or removing ``lazy`` from a stored property is not
permitted.
- Changing the body of any *existing* methods, initializers, or computed
property accessors is still permitted.
- Adding observing accessors to any stored instance properties (public or
non-public) is not permitted (and is checked by the compiler).
- Removing stored instance properties is not permitted. Removing any other
non-ABI-public members is still permitted.
- Adding a new protocol conformance is still permitted, per the usual
restrictions.
- Removing conformances to non-ABI-public protocols is still permitted.
- Adding, changing, or removing property wrappers is not permitted.
Additionally, if the type of any stored instance property includes a struct or
enum, that struct or enum must be ABI-public. This includes generic parameters,
members of tuples, and property wrappers for stored instance properties.
.. note::
The above restrictions do not apply to ``static`` properties of
``@frozen`` structs. Static members effectively behave as top-level
functions and variables.
While adding or removing stored properties is forbidden, existing properties may
still be modified in limited ways:
- An existing non-ABI-public property may change its access level to any other
non-public access level.
- ``@usableFromInline`` may be added to an ``internal`` property (with the
current availability version, if necessary).
- A ``@usableFromInline`` property may be made ``public``.
Adding or removing ``@frozen`` from an existing struct is forbidden.
An initializer of a frozen struct may be declared ``@inlinable`` even
if it does not delegate to another initializer.
A ``@frozen`` struct is *not* guaranteed to use the same layout as a C
struct with a similar "shape". If such a struct is necessary, it should be
defined in a C header and imported into Swift.
.. note::
We may add a *different* feature to control layout some day, or something
equivalent, but this feature should not restrict Swift from doing useful
things like minimizing member padding. While the layout of ``@frozen``
structs is part of the stable ABI on Apple platforms now, it's not
something that can't be revised in the future (with appropriate
compatibility considerations). At the very least, Swift structs don't
guarantee the same tail padding that C structs do.
Enums
~~~~~
By default, a library owner may add new cases to a public enum between releases
without breaking binary compatibility. As with structs, this results in a fair
amount of indirection when dealing with enum values, in order to potentially
accommodate new values. More specifically, the following changes are permitted:
- Adding a new case (unless the enum is marked ``@frozen``; see below).
- Reordering existing cases is a `binary-compatible source-breaking change`
(unless the struct is marked ``@frozen``; see below). In particular, both
CaseIterable and RawRepresentable default implementations may affect client
behavior.
- Adding a raw type to an enum that does not have one.
- Adding any other members.
- Removing any non-ABI-public members.
- Adding a new protocol conformance, with the same restrictions as for structs.
- Removing conformances to non-ABI-public protocols.
- Adding ``@dynamicCallable`` to the enum.
.. note::
If an enum value has a known case, or can be proven to belong to a set of
known cases, the compiler is of course free to use a more efficient
representation for the value, just as it may discard fields of structs that
are provably never accessed.
Adding or removing the ``@objc`` attribute from an enum is not permitted; this
affects the enum's memory representation and is not backwards-compatible.
Initializers
------------
For the most part enum initializers are treated exactly like top-level
functions. They permit all of the same modifications and can also be marked
``@inlinable``, with the same restrictions.
Methods and Subscripts
----------------------
The rules for enum methods and subscripts are identical to those for struct
members.
Frozen Enums
------------
A library owner may opt out of this flexibility by marking an ABI-public enum
as ``@frozen``. A "frozen" enum may not add new cases in the future,
guaranteeing to clients that the current set of enum cases is exhaustive. In
particular:
- Adding new cases is not permitted.
- Reordering existing cases is not permitted.
- Adding a raw type is still permitted.
- Adding any other members is still permitted.
- Removing any non-ABI-public members is still permitted.
- Adding a new protocol conformance is still permitted.
- Removing conformances to non-ABI-public protocols is still permitted.
.. note::
Were a public "frozen" enum allowed to have non-public cases, clients of
the library would still have to treat the enum as opaque and would still
have to be able to handle unknown cases in their ``switch`` statements.
Adding or removing ``@frozen`` from an existing enum is forbidden.
Even for default "non-frozen" enums, adding new cases should not be done
lightly. Any clients attempting to do an exhaustive switch over all enum cases
will likely not handle new cases well.
Protocols
~~~~~~~~~
There are very few safe changes to make to protocols and their members:
- A default may be added to an associated type.
- A new optional requirement may be added to an ``@objc`` protocol.
- All members may be reordered, including associated types.
- Changing *internal* parameter names of function and subscript requirements
is permitted.
- Reordering generic requirements is permitted (but not the generic parameters
themselves).
- The ``@discardableResult`` and ``@warn_unqualified_access`` attributes may
be added to or removed from a function requirement.
- A new ``associatedtype`` requirement may be added (with the appropriate
availability), as long as it has a default implementation. If the protocol
did not have one or more ``associatedtype`` requirements before the change,
then this is a `binary-compatible source-breaking change`.
- A new non-type requirement may be added (with the appropriate availability),
as long as it has an unconstrained default implementation. If the requirement
uses ``Self`` and the protocol has no other requirements using ``Self`` and
no associated types, this is a `binary-compatible source-breaking change` due
to restrictions on protocol value types.
All other changes to the protocol itself are forbidden, including:
- Adding or removing refined protocols.
- Removing any existing requirements (type or non-type).
- Removing the default type of an associated type.
- Making an existing requirement optional.
- Making a non-``@objc`` protocol ``@objc`` or vice versa.
- Adding or removing protocols and superclasses from the inheritance
clause of an associated type.
- Adding or removing constraints from the ``where`` clause of
the protocol or an associated type.
Protocol extensions may be more freely modified; `see below`__.
__ #protocol-extensions
Classes
~~~~~~~
Because class instances are always accessed through references, they are very
flexible and can change in many ways between releases. Like structs, classes
support all of the following changes:
- Reordering any existing members, including stored properties.
- Changing existing properties from stored to computed or vice versa.
- As a special case of the above, adding or removing ``lazy`` from a stored
property.
- Changing the body of any methods, initializers, accessors, or deinitializers.
- Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from
an existing property. This is effectively the same as modifying the body of a
setter.
- Removing any non-ABI-public members, including stored properties.
- Adding a new protocol conformance (subject to the same restrictions as for
structs).
- Removing conformances to non-ABI-public protocols.
- Adding ``@dynamicCallable`` to the class.
Omitted from this list is the free addition of new members. Here classes are a
little more restrictive than structs; they only allow the following changes:
- Adding a new convenience initializer.
- Adding a new designated initializer, if the class is not ``open`` and any
``open`` subclasses that previously inherited convenience initializers
continue to do so.
- Adding a deinitializer.
- Adding new, non-overriding method, subscript, or property.
- Adding a new overriding member, though if the class is ``open`` the type of
the member may not deviate from the member it overrides. Changing the type
could be incompatible with existing overrides in subclasses.
Finally, classes allow the following changes that do not apply to structs:
- Removing an explicit deinitializer. (A class with no declared deinitializer
effectively has an implicit deinitializer.)
- "Moving" a method, subscript, or property up to its superclass. The
declaration of the original member must remain along with its original
availability, but its body may consist of simply calling the new superclass
implementation.
- A non-final override of a method, subscript, property, or initializer may be
removed as long as the generic parameters, formal parameters, and return type
*exactly* match the overridden declaration. Any existing callers should
automatically use the superclass implementation.
- ``final`` can be added to or removed from any non-ABI-public class, or any
non-ABI-public member of a class.
- ``@IBOutlet``, ``@IBAction``, ``@IBInspectable``, and ``@GKInspectable`` may
be added to a member that is already exposed to Objective-C (either explicitly
with ``@objc`` or implicitly through overriding or protocol requirements).
Removing any of these is a `binary-compatible source-breaking change` if the
member remains ``@objc``, and disallowed if not.
- ``@IBDesignable`` may be added to a class; removing it is considered a
`binary-compatible source-breaking change`.
- Changing a class's superclass ``A`` to another class ``B``, *if* class ``B``
is a subclass of ``A`` *and* class ``B``, along with any superclasses between
it and class ``A``, were introduced in the latest version of the library.
Other than those detailed above, no other changes to a class or its members
are permitted. In particular:
- ``open`` cannot be added to or removed from an ABI-public class or member.
- ``final`` may not be added to or removed from an ABI-public class or its
ABI-public members. (The presence of ``final`` enables optimization.)
- ``dynamic`` may not be added to *or* removed from any ABI-public members.
Existing clients would not know to invoke the member dynamically.
- A ``final`` override of a member may *not* be removed, even if the type
matches exactly; existing clients may be performing a direct call to the
implementation instead of using dynamic dispatch.
- ``@objc`` and ``@nonobjc`` may not be added to or removed from the class or
any existing members, except if the member already was or was not exposed to
Objective-C.
- ``@NSManaged`` may not be added to or removed from any existing
ABI-public members.
.. admonition:: TODO
``@NSManaged`` as it is in Swift 4.2 exposes implementation details to
clients in a bad way. If we want to use ``@NSManaged`` in frameworks with
binary compatibility concerns, we need to fix this. rdar://problem/20829214
Initializers
------------
New designated initializers may not be added to an ``open`` class. This would
change the inheritance of convenience initializers, which existing subclasses
may depend on. An ``open`` class also may not change a convenience initializer
into a designated initializer or vice versa.
A new ``required`` initializer may be added to a class only if it is a
convenience initializer; that initializer may only call existing ``required``
initializers. An existing initializer may not be marked ``required``.
All of the modifications permitted for top-level functions are also permitted
for class initializers. Convenience initializers may be marked ``@inlinable``,
with the same restrictions as top-level functions; designated initializers may
not.
Methods
-------
Both class and instance methods allow all of the modifications permitted for
top-level functions, but the potential for overrides complicates things a
little. They allow the following changes:
- Changing the body of the method.
- Changing *internal* parameter names (i.e. the names used within the method
body, not the labels that are part of the method's full name).
- Reordering generic requirements (but not the generic parameters themselves).
- Adding a default argument expression to a parameter.
- Changing or removing a default argument is a `binary-compatible
source-breaking change`.
- Adding or removing the ``@discardableResult`` and ``@warn_unqualified_access``
attributes.
Class and instance methods may be marked ``@inlinable``, with the same
restrictions as struct methods. Additionally, only non-overriding ``final``
methods may be marked ``@inlinable``.
Properties
----------
Class and instance properties allow *most* of the modifications permitted for
struct properties, but the potential for overrides complicates things a little.
Variable properties (those declared with ``var``) allow the following changes:
- Adding (but not removing) a computed setter to a non-``open`` property.
- Adding or removing a non-ABI-public setter.
- Changing from a stored property to a computed property, or vice versa, as
long as a previously ABI-public setter is not removed.
- Changing the body of an accessor.
- Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from
an existing variable. This is effectively the same as modifying the body of a
setter.
- Adding, removing, or changing the initial value of a stored variable.
- Adding or removing ``weak`` from a variable with ``Optional`` type.
- Adding or removing ``unowned`` from a variable.
- Adding or removing ``@NSCopying`` from a variable.
- Adding a property wrapper to a non-``open`` variable, or changing from one
property wrapper to another, as long as an ABI-public setter or projected
value (``$foo``) is not removed.
- Adding a property wrapper to an ``open`` variable, or changing from one
property wrapper to another, as long as an ABI-public setter or projected
value (``$foo``) is not added or removed.
- Removing a property wrapper from a variable, as long as the property wrapper
didn't have a projected value (``$foo``).
Adding a public setter to an ``open`` property is a
`binary-compatible source-breaking change`; any existing overrides will not
know what to do with the setter and will likely not behave correctly.
Constant properties (those declared with ``let``) still permit changing their
value, as well as adding or removing an initial value entirely.
Non-overriding ``final`` computed properties (on both instances and classes)
may be marked ``@inlinable``. This behaves as described for struct properties.
Subscripts
----------
Subscripts behave much like properties; they inherit the rules of their struct
counterparts with a few small changes:
- Adding (but not removing) a public setter to a non-``open`` subscript is
permitted.
- Adding or removing a non-ABI-public setter is permitted.
- Changing the body of an accessor is permitted.
- Changing index parameter internal names is permitted.
- Reordering generic requirements (but not the generic parameters themselves)
is permitted.
- Adding a default argument expression to an index parameter is permitted.
- Changing or removing a default argument is a `binary-compatible
source-breaking change`.
Adding a public setter to an ``open`` subscript is a
`binary-compatible source-breaking change`; any existing overrides will not
know what to do with the setter and will likely not behave correctly.
Non-overriding ``final`` class subscripts may be marked ``@inlinable``,
which behaves as described for struct subscripts.
Extensions
~~~~~~~~~~
Extensions largely follow the same rules as the types they extend.
The following changes are permitted:
- Adding new extensions and removing empty extensions (that is, extensions that
declare neither members nor protocol conformances).
- Moving a member from one extension to another within the same module, as long
as both extensions have the exact same constraints.
- Adding any new member.
- Reordering members.
- Removing any non-ABI-public member.
- Changing the body of any methods, initializers, or accessors.
Additionally, non-protocol extensions allow a few additional changes:
- Moving a member from an unconstrained extension to the declaration of the
base type, provided that the declaration is in the same module. The reverse
is permitted for all members that would be valid to declare in an extension,
although note that moving all initializers out of a type declaration may
cause a new one to be implicitly synthesized.
- Adding a new protocol conformance (subject to the same restrictions discussed
for structs).
- Removing conformances to non-ABI-public protocols.
.. note::
Although it is not related to evolution, it is worth noting that members of
protocol extensions that do *not* satisfy protocol requirements are not
overridable, even when the conforming type is a class.
Operators and Precedence Groups
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Operator and precedence group declarations are entirely compile-time
constructs, so changing them does not have any effect on binary compatibility.
However, they do affect *source* compatibility, so it is recommended that
existing operators are not changed at all except for the following:
- Making a non-associative precedence group left- or right-associative.
Any other change counts as a `binary-compatible source-breaking change`.
Typealiases
~~~~~~~~~~~
Public typealiases within structs, enums, and protocols may be used for
protocol conformances (to satisfy associated type requirements), not only
within the library but within client modules as well. Therefore, changing a
member typealias in any way is not permitted; while it will not break existing
clients, they cannot recompile their code and get correct behavior.
Top-level typealiases only exist at compile-time, so changing the underlying
type of one is a `binary-compatible source-breaking change`. However, if the
typealias is *used* in the type of any ABI-public declaration in a library, it
may be an actual breaking change and would not be permitted.
It is always permitted to change the *use* of a public typealias to its
underlying type, and vice versa, at any location in the program.
Typealiases require availability annotations despite being compile-time
constructs in order to verify the availability of their underlying types.
``@usableFromInline``
=====================
Adding ``@usableFromInline`` to an ``internal`` entity promises that the entity
will be available at link time in the containing module's binary. This makes it
safe to refer to such an entity from an inlinable function or in the stored
properties of a frozen struct. ``@usableFromInline`` declarations shipped as
part of an OS should have availability just like ``public`` declarations; if
the entity is ever made ``public`` or ``open``, its availability should not be
changed.
.. note::
Why isn't ``@usableFromInline`` a special form of ``public``? Because we
don't want it to imply everything that ``public`` does, such as requiring
overrides to be ``public``.
Because a ``@usableFromInline`` class member may eventually be made ``open``,
the compiler must assume that new overrides may eventually appear from outside
the module if the class is marked ``open`` unless the member is marked
``final``.
For more information, see `SE-0193 <SE0193_>`_.
Optimization
============
Allowing a library to evolve inhibits the optimization of client code in
several ways. For example:
- A function that currently does not access global memory might do so in the
future, so calls to it cannot be freely reordered in client code.
- A stored property may be replaced by a computed property in the future, so
client code must not try to access the storage directly.
- A struct may have additional members in the future, so client code must not
assume it fits in any fixed-sized allocation.
If the entity is declared within the same module as the code that's using it,
then the code is permitted to know all the details of how the entity is
declared. After all, if the entity is changed, the code that's using it will be
recompiled. However, if the entity is declared in another module, then the code
using it must be more conservative, and will therefore receive more
conservative answers to its queries. (For example, a stored property may be
treated as computed.)
As a special case, inlinable code must be treated as if it is outside the
current module, since once it's inlined it will be.
Summary
=======
When possible, Swift gives library authors freedom to evolve their code without
breaking binary compatibility. This has implications for both the semantics and
performance of client code, and so library owners also have tools to waive the
ability to make certain future changes. When shipping libraries as part of the
OS, the availability model guarantees that client code will never accidentally
introduce implicit dependencies on specific versions of libraries.
Glossary
========
.. glossary::
ABI
The run-time contract for using a particular API (or for an entire library),
including things like symbol names, calling conventions, and type layout
information. Stands for "Application Binary Interface".
ABI-public
Describes entities that are part of a library's `ABI`. Marked ``public``,
``open``, ``@usableFromInline``, or ``@inlinable`` in Swift. See
`SE-0193 <SE0193_>`_ for more information.
API
An `entity` in a library that a `client` may use, or the collection of all
such entities in a library. (If contrasting with `SPI`, only those entities
that are available to arbitrary clients.) Marked ``public`` or ``open`` in
Swift. Stands for "Application Programming Interface".
backwards-compatible
A modification to an API that does not break existing clients. May also
describe the API in question.
binary compatibility
A general term encompassing both backwards- and forwards-compatibility
concerns. Also known as "ABI compatibility".
binary-compatible source-breaking change
A change that does not break `binary compatibility`, but which is known to
either change the behavior of existing clients or potentially result in
errors when a client is recompiled. In most cases, a client that *hasn't*
been recompiled may use the new behavior or the old behavior, or even a
mix of both; however, this will always be deterministic (same behavior when
a program is re-run) and will not break Swift's memory-safety and
type-safety guarantees. It is recommended that these kinds of changes are
avoided just like those that break binary compatibility.
client
A target that depends on a particular library. It's usually easiest to
think of this as an application, but it could be another library.
(In certain cases, the "library" is itself an application, such as when
using Xcode's unit testing support.)
duck typing
In Objective-C, the ability to treat a class instance as having an
unrelated type, as long as the instance handles all messages sent to it.
(Note that this is a dynamic constraint.)
entity
A type, function, member, or global in a Swift program. Occasionally the
term "entities" also includes conformances, since these have a run-time
presence and are depended on by clients.
forwards-compatible
An API that is designed to handle future clients, perhaps allowing certain
changes to be made without changing the ABI.
module
The primary unit of code sharing in Swift. Code in a module is always built
together, though it may be spread across several source files.
SPI
A subset of `API` that is only available to certain clients. Stands for
"System Programming Interface".
target
In this document, a collection of code in a single Swift module that is
built together; a "compilation unit". Roughly equivalent to a target in
Xcode.