"Fossies" - the Fresh Open Source Software Archive

Member "swift-swift-5.1.2-RELEASE/docs/LibraryEvolution.rst" (7 Nov 2019, 75920 Bytes) of package /linux/misc/swift-swift-5.1.2-RELEASE.tar.gz:

As a special service "Fossies" has tried to format the requested source page into HTML format (assuming markdown format). Alternatively you can here view or download the uninterpreted source code file. A member file download can also be achieved by clicking within a package contents listing on the according byte size field. See also the last Fossies "Diffs" side-by-side code changes report for "LibraryEvolution.rst": 5.0.3_vs_5.1.


Library Evolution Support in Swift ("Resilience")


Jordan Rose


John McCall


This document uses some Sphinx-specific features which are not available on GitHub. For proper rendering, download and build the docs yourself. Jordan Rose also posts occasional snapshots at https://jrose-apple.github.io/swift-library-evolution/.

One of Swift's primary design goals is 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:

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.



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:

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.

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.


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 not be required to use any of the annotations described below in order to achieve full performance.


This model may, however, be useful for library authors that want to preserve source compatibility, and it is hoped that the tool for Checking Binary Compatibility described below will also be useful for this purpose. Additionally, we may decide to use some of these annotations as performance hints for non-optimized builds.

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".

Using Versioned API

References to a versioned API must always be guarded with the appropriate availability checks. This means that any client entities that rely on certain APIs from a library must themselves be restricted to contexts in which those APIs are available. This is accomplished using the @available attribute, by specifying the name of the client library along with the required version:

// Client code
@available(Magician 1.5)
class CrystalBallView : MagicView { /*...*/ }

Library versions can also be checked dynamically using #available, allowing for fallback behavior when the requested library version is not present:

func scareMySiblings() {
  if #available(Magician 1.2) {
  } else {


Possible implementations include generating a hidden symbol into a library, or putting the version number in some kind of metadata, like the Info.plist in a framework bundle on Darwin platforms.

This is essentially the same model as the availability checking released in Swift 2.0, but generalized for checking library versions instead of just OS versions.

Declaring Library Version Dependencies

Swift's current availability model includes the notion of a minimum deployment target, the version of an OS that must be present for the program being compiled to run at all. For example, a program compiled with a minimum deployment target of iOS 9.2 will not launch on iOS 9.0.

The generalized model above suggests being able to make similar guarantees for individual libraries. For example, a client program may depend on version 1.1 of the "Magician" library; trying to run using version 1.0 will result in errors. By declaring this at compile-time, the client code can omit @available and #available checks that are satisfied by the minimum library version.

Both the syntax and enforcement of this feature are not covered by this document.

Publishing Versioned API

A library's API is already marked with the public modifier, but if a client wants to work with multiple releases of the library, the API needs versioning information as well. A versioned entity represents anything with a run-time presence that a client may rely on; its version records when the entity was first exposed publicly in its library. Put another way, it is the oldest version of the library where the entity may be used.

In a versioned library, any top-level public entity from the list above may not be made public (or open) without an appropriate version. A public entity declared within a versioned type (or an extension of a versioned type) will default to having the same version as the type.

In this document, the term "public" includes classes and members marked open.

Code within a library may generally use all other entities declared within the library (barring their own availability checks), since the entire library is shipped as a unit. That is, even if a particular API was introduced in v1.0, its (non-public) implementation may refer to APIs introduced in later versions.

Certain uses of internal entities require them to be part of a library's binary interface, which means they need to be versioned as well. See Versioning Internal Declarations below.

In addition to versioned entities, there are also attributes that are safe to add to declarations when releasing a new version of a library. In most cases, clients can only take advantage of the attributes when using the new release of the library, and therefore the attributes also need to record the version in which they were introduced; these are called versioned attributes. If the version is omitted, it is assumed to be the version of the declaration to which the attribute is attached.

The syntax for marking an entity as versioned has not yet been decided, but the rest of this document will use syntax #1 described below.

Syntax #1: Attributes

public func summonDemons()

@available(1.0) @inlinable(1.2)
public func summonElves()

Using the same attribute for both publishing and using versioned APIs helps tie the feature together and enforces a consistent set of rules. However, there are several other annotations described later in this document that also need versioning information, and it may not be obvious what the version number means outside the context of available.

Syntax #2: Version Blocks

public func summonDemons()

#version(1.0) {}
#version(1.2) { @inlinable }
public func summonElves()

Since there are potentially many annotations on a declaration that need versioning information, it may make sense to group them together in some way. Only certain annotations would support being versioned in this way.

Syntax #3: The public modifier

public(1.2) func summonDemons()

/* @inlinable ?? */
public(1.0) func summonElves()

Putting the version on the public modifier is the most concise option. However, there's no obvious syntax here for adding versions to other annotations that may apply to a declaration.

(Also, at one point there was a proposal to tag API only intended for certain clients using a similar syntax: public("Foundation"), for example, for APIs only meant to be used by Foundation. These could then be stripped out of the public interface for a framework before being widely distributed. But that could easily use an alternate syntax.)

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.

Anything not listed in this document should be assumed unsafe.

Top-Level Functions

A versioned top-level function is fairly restricted in how it can be changed. The following changes are permitted:

No other changes are permitted; the following are particularly of note:

Inlinable Functions

Functions are a very common example of resilience: 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:

A versioned function marked with the @inlinable attribute makes its body available to clients as part of the module's public interface. @inlinable is a versioned attribute; clients may not assume that the body of the function is suitable when deploying against older versions of the library.

Clients are not required to inline a function marked @inlinable.


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 themselves treated as @inlinable, and 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; existing clients should not be depending on internal details of the previous implementation.

Removing the @inlinable attribute completely---say, to reference private implementation details that should not be versioned <versioned entity>---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, transparent functions are implicitly marked @inlinable.

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:

Default Argument Expressions

Default argument expressions for functions that are public, versioned, or inlinable are implemented very similar to inlinable functions and thus are subject to similar restrictions:

A default argument implicitly has the same availability as the function it is attached to. Because default argument expressions can be added and removed, a client that uses one must always emit its own copy of the implementation.

Top-Level Variables and Constants

Given a versioned module-scope variable declared with var, the following changes are permitted:

If a public setter is added after the property is first exposed (whether the property is stored or computed), it must be versioned independently of the property itself.


This needs syntax.

Additionally, for a module-scope constant declared with let, the following changes are permitted:

It is not safe to change a let constant into a variable or vice versa. Top-level constants are assumed not to change for the entire lifetime of the program once they have been initialized.


We could make it safe to turn a read-only var into a let, but do we want to? We would have to come up with syntax for declaring when it changed, at least.

Giving Up Flexibility

Both top-level constants and variables can be marked @inlinableAccess to allow clients to access them more efficiently. This restricts changes a fair amount:


It Would Be Nice(tm) to allow marking the getter of a top-level variable inlinable while still allowing the setter to change. This would need syntax, though.

Any inlinable accessors must follow the rules for inlinable functions, as described above.

Note that if a constant's initial value expression has any observable side effects, including the allocation of class instances, it must not be treated as inlinable. A constant must always behave as if it is initialized exactly once.


Is this a condition we can detect at compile-time? Do we have to be restricted to things that can be lowered to compile-time constants?


@inlinableAccess isn't implemented yet, but for computed properties we already allow putting @inlinable on the accessors individually. That doesn't support all the use cases, like promising that a stored property will remain stored, but it also provides flexibility in only making one accessor inlinable. Is that important?


Swift structs are a little more flexible than their C counterparts. By default, the following changes are permitted:

The important most aspect of a Swift struct is its value semantics, not its layout.

It is not safe to add or remove mutating or nonmutating from a member or accessor within a struct. These modifiers are not versioned attributes <versioned attribute> and as such there is no safety guarantee for a client deploying against an earlier version of the library.

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.


Struct properties behave largely the same as top-level bindings. They permit all of the same modifications, and also allow adding or removing an initial value entirely.

Struct properties can also be marked @inlinableAccess, with the same restrictions as for top-level bindings. An inlinable stored property may not become computed, but the offset of its storage within the struct is not necessarily fixed.

Like top-level constants, it is not safe to change a let property into a variable or vice versa. Properties declared with let are assumed not to change for the entire lifetime of the program once they have been initialized.


Subscripts behave largely the same as properties, except that there are no stored subscripts. This means that the following changes are permitted:

Like properties, subscripts can be marked @inlinableAccess, 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.

New Conformances

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 is implied if the protocol itself was introduced in version 1.1, but needs special handling if both the protocol and the type were available earlier. In this case, the conformance itself needs to be labeled as being introduced in version 1.1, so that the compiler can enforce its safe use.


This may feel like a regression from Objective-C, where duck typing would allow a Wand to be passed as an id <MagicType> without ill effects. However, Wand would still fail a -conformsToProtocol: check in version 1.0 of the library, and so whether or not the client code will work is dependent on what should be implementation details of the library.

We've considered two possible syntaxes for this:

extension Wand : MagicType {/*...*/}


extension Wand : @available(1.1) MagicType {/*...*/}

The former requires fewer changes to the language grammar, but the latter could also be used on the declaration of the type itself (i.e. the struct declaration).

If we went with the former syntax, applying @available to an extension would override the default availability of entities declared within the extension; unlike access control, entities within the extension may freely declare themselves to be either more or less available than what the extension provides.

Fixed-Contents Structs

To opt out of this flexibility, a struct may be marked @fixedContents. This promises that no stored properties will be added to or removed from the struct, even non-public ones. Additionally, all versioned instance stored properties in a @fixedContents struct are implicitly declared @inlinable (as described above for top-level variables). In effect:

Additionally, if the type of any stored instance property includes a struct or enum, that struct or enum must be versioned <versioned entity>. This includes generic parameters and members of tuples.


The above restrictions do not apply to static properties of @fixedContents structs. Static members effectively behave as top-level functions and variables.


The name @fixedContents is intentionally awful to encourage us to come up with a better one.

While adding or removing stored properties is forbidden, existing properties may still be modified in limited ways:

An initializer of a fixed-contents struct may be declared @inlinable even if it does not delegate to another initializer, as long as the @inlinable attribute, or the initializer itself, is not introduced earlier than the @fixedContents attribute and the struct has no non-versioned stored properties.

A @fixedContents 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.


We can 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. At the very least, Swift structs don't guarantee the same tail padding that C structs do.


Hypothetically, we could use a different model where a @fixedContents struct only guarantees the "shape" of the struct, so to speak, while leaving all property accesses to go through function calls. This would allow stored properties to change their accessors, or (with the Behaviors proposal) to change a behavior's implementation, or change from one behavior to another. However, the most common case here is probably just a simple C-like struct that groups together simple values, with only public stored properties and no observing accessors, and having to opt into direct access to those properties seems unnecessarily burdensome. The struct is being declared @fixedContents for a reason, after all: it's been discovered that its use is causing performance issues.

Consequently, as a first pass we may just require all stored properties in a @fixedContents struct, public or non-public, to have trivial accessors, i.e. no observing accessors and no behaviors.

@fixedContents is a versioned attribute. This is so that clients can deploy against older versions of the library, which may have a different layout for the struct. (In this case the client must manipulate the struct as if the @fixedContents attribute were absent.)


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:


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.


Non-public cases in public enums don't exist at the moment, but they can be useful, and they require essentially the same implementation work as cases added in future versions of a library.

Adding or removing the @objc attribute from an enum is not permitted; this affects the enum's memory representation and is not backwards-compatible.


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 a versioned enum as @frozen. A "frozen" enum may not have any cases with less access than the enum itself, and may not add new cases in the future. This guarantees to clients that the enum cases are exhaustive. In particular:


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.

@frozen is a versioned attribute. This is so that clients can deploy against older versions of the library, which may have non-public cases in the enum. (In this case the client must manipulate the enum as if the @frozen attribute were absent.) All cases that are not versioned become implicitly versioned with this number.

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.


One possibility would be a way to map new cases to older ones on older clients. This would only be useful for certain kinds of enums, though, and adds a lot of additional complexity, all of which would be tied up in versions. Our generalized switch patterns probably make it hard to nail down the behavior here.


There are very few safe changes to make to protocols and their members:

New requirements can be added to a protocol. However, restrictions around existential types mean that adding new associated types or non-type requirements involving Self can break source compatibility. For this reason, the following are `binary-compatible source-breaking changes <binary-compatible source-breaking change>`:

All other changes to the protocol itself are forbidden, including:

Protocol extensions may be more freely modified; see below.


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:

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:

Finally, classes allow the following changes that do not apply to structs:


This last is very tricky to get right. We've seen it happen a few times in Apple's SDKs, but at least one of them, NSCollectionViewItem becoming a subclass of NSViewController instead of the root class NSObject, doesn't strictly follow the rules. While NSViewController was introduced in the same version of the OS, its superclass, NSResponder, was already present. If a client app was deploying to an earlier version of the OS, would NSCollectionViewItem be a subclass of NSResponder or not? How would the compiler be able to enforce this?


Both final and open may be applied to a declaration after it has been made public. However, these need to be treated as versioned attributes <versioned attribute>. It's not clear what syntax should be used for this.

Other than those detailed above, no other changes to a class or its members are permitted. In particular:


@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


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.


This implies a different rule for inheriting required convenience initializers than non-required convenience initializers, which is not currently implemented.

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.


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:

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.


A previous draft of this document allowed non-final methods to be marked @inlinable, permitting inlining based on speculative devirtualization. This was removed because of the added complexity for users.


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 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 variable and constant properties (on both instances and classes) may be marked @inlinableAccess. This behaves as described for struct properties.


Subscripts behave much like properties; they inherit the rules of their struct counterparts with a few small changes:

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 @inlinableAccess, which behaves as described for struct subscripts.

Possible Restrictions on Classes

In addition to final, it may be useful to restrict the stored properties of a class instance, like Fixed-Contents Structs. However, there are open questions about how this would actually work, and the compiler still wouldn't be able to make much use of the information, because classes from other libraries must almost always be allocated on the heap.

The design of this annotation is not covered by this document. As a purely additive feature, it can be added to the model at any time.


Extensions largely follow the same rules as the types they extend. The following changes are permitted:

Additionally, non-protocol extensions allow a few additional changes:


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:

Any other change counts as a binary-compatible source-breaking change.

Operator and precedence group declarations are not versioned.


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 versioned entity 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 are versioned <versioned entity> despite being compile-time constructs in order to verify the availability of their underlying types.

A Unifying Theme

So far this document has talked about ways to give up flexibility for several different kinds of declarations: @inlinable for functions, @fixedContents for structs, etc. Each of these has a different set of constraints it enforces on the library author and promises it makes to clients. However, they all follow a common theme of giving up the flexibility of future changes in exchange for improved performance and perhaps some semantic guarantees. Therefore, all of these attributes are informally referred to as "fragility attributes".

Given that these attributes share several characteristics, we could consider converging on a single common attribute, say @fixed, @inline, or @fragile. However, this may be problematic if the same declaration has multiple kinds of flexibility.

Versioning Internal Declarations

The initial discussion on versioning focused on public APIs, making sure that a client knows what features they can use when a specific version of a library is present. Inlinable functions have much the same constraints, except the inlinable function is the client and the entities being used may not be public.

Adding a versioning annotation 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. If the entity is ever made public or open, its availability should not be changed; not only is it safe for new clients to rely on it, but existing clients require its presence as well.


Why isn't this a special form of public? Because we don't want it to imply everything that public does, such as requiring overrides to be public.

In libraries without binary compatibility concerns, the equivalent annotation is @usableFromInline, since inlinable functions are the only way that a non-public entity can be referenced from outside of a module.

Because a versioned class member may eventually be made open, it must be assumed that new overrides may eventually appear from outside the module if the class is marked open unless the member is marked final.

Non-public conformances are never considered versioned, even if both the conforming type and the protocol are versioned. A conformance is considered public if and only if both the conforming type and protocol are public.

Entities declared private or fileprivate may not be versioned; the mangled name of such an entity includes an identifier based on the containing file, which means moving the declaration to another file changes the entity's mangled name. This implies that a client would not be able to find the entity at run time if the source code is reorganized, which is unacceptable.


There are ways around this limitation, the most simple being that versioned private entities are subject to the same cross-file redeclaration rules as internal entities. However, this is a purely additive feature, so to keep things simple we'll stick with the basics.

We could do away with the entire feature if we restricted inlinable functions and fixed-contents structs to only refer to public entities. However, this removes one of the primary reasons to make something inlinable: to allow efficient access to a type while still protecting its invariants.


Backdating refers to releasing a new version of a library that contains changes, but pretending those changes were made in a previous version of the library. For example, you might want to release version 1.2 of the "Magician" library, but pretend that the "SpellIncantation" struct was fixed-contents since its introduction in version 1.0.

This is not safe.

Backdating the availability a versioned entity that was previously non-public is clearly not safe: older versions of the library will not expose the entity as part of their ABI. What may be less obvious is that the fragility attributes likewise are not safe to backdate, even if you know the attributes could have been added in the past. To give one example, the presence of @closed or @fixedContents may affect the layout and calling conventions for an enum or struct.


If we add an "SPI" feature, such that the use of specific public entities is limited to certain clients, it will be safe to change the set of clients, or remove the restriction altogether. In fact, in such cases the library author is required to not change the availability info that was originally presented for the limited set of clients, since as mentioned above this may affect how those existing clients use the entities declared in the library.

The one exception is @inlinable, which does not change how a function is called or otherwise used at the ABI level. If the implementation being provided is compatible with a previous version of a library, and the function was present and public (or versioned <versioned entity>) there, then the library author may choose to backdate the @inlinable annotation.


Allowing a library to evolve inhibits the optimization of client code in several ways. For example:

In order to make sure client code doesn't make unsafe assumptions, queries about properties that may change between library versions must be parameterized with the availability context that is using the entity. An availability context is a set of minimum platform and library versions that can be assumed present for code executing within the context. (See Declaring Library Version Dependencies.) This allows the compiler to answer the question, "Given what I know about where this code will be executed, what can I assume about a particular entity being used?".

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 report itself as computed.

The presence of versioned fragility attributes makes the situation more complicated. Within a client function that requires version 1.5 of a particular library, the compiler should be able to take advantage of any fragility information (and performance assertions) introduced prior to version 1.5.

Inlinable Code

By default, the availability context for a library always includes the latest version of the library itself, since that code is always distributed as a unit. However, this is not true for functions that have been marked inlinable (see Inlinable Functions above). Inlinable code must be treated as if it is outside the current module, since once it's inlined it will be.

For inlinable code, the availability context is exactly the same as the equivalent non-inlinable code except that the assumed version of the containing library is the version attached to the @inlinable attribute, or the version of the library in which the entity was introduced, and any library version dependencies or minimum deployment target must be specified explicitly using @available. Code within this context must be treated as if the containing library were just a normal dependency.

A versioned inlinable function still has an exported symbol in the library binary, which may be used when the function is referenced from a client rather than called. This version of the function is not subject to the same restrictions as the version that may be inlined, and so it may be desirable to compile a function twice: once for inlining, once for maximum performance.

If the body of an inlinable function is used in any way by a client module (say, to determine that it does not read any global variables), that module must take care to emit and use its own copy of the function. This is because analysis of the function body may not apply to the version of the function currently in the library.

Local Availability Contexts

Swift availability contexts aren't just at the declaration level; they also cover specific regions of code inside function bodies as well. These "local" constructs are formed using the #available construct, which performs a dynamic check.

In theory, it would be legal to allow code dominated by a #available check to take advantage of additional fragility information introduced by the more restrictive dependencies that were checked for. However, this is an additional optimization that may be complicated to implement (and even to represent properly in SIL), and so it is not a first priority.

Other Promises About Types

Advanced users may want to promise more specific things about various types. These are similar to the internal effects attribute we have for functions, except that they can be enforced by the compiler.

Collectively these features are known as "performance assertions", to underscore the fact that they do not affect how a type is used at the source level, but do allow for additional optimizations. We may also expose some of these qualities to static or dynamic queries for performance-sensitive code.


Previous revisions of this document contained a noPayload assertion for enums. However, this doesn't actually offer any additional optimization opportunities over combining trivial with maximumFootprint, and the latter is more flexible.


None of these names / spellings are final. The name "trivial" comes from C++, though Swift's trivial is closer to C++'s "trivially copyable".

All of these features need to be versioned, just like the more semantic fragility attributes above. The exact spelling is not proposed by this document.

Resilience Domains

As described in the Introduction, the features and considerations discussed in this document do not apply to libraries distributed in a bundle with their clients. In this case, a client can rely on all the current implementation details of its libraries when compiling, since the same version of the library is guaranteed to be present at run time. This allows more optimization than would otherwise be possible.

In some cases, a collection of libraries may be built and delivered together, even though their clients may be packaged separately. (For example, the ICU project is usually built into several library binaries, but these libraries are always distributed together.) While the clients cannot rely on a particular version of any library being present, the various libraries in the collection should be able to take advantage of the implementations of their dependencies also in the collection---that is, it should treat all entities as if marked with the appropriate fragility attributes. Modules in this sort of collection are said to be in the same resilience domain.

Exactly how resilience domains are specified is not covered by this document, and indeed they are an additive feature. One possibility is that a library's resilience domain defaults to the name of the module, but can be overridden. If a client has the same resilience domain name as a library it is using, it may assume that version of the library will be present at run time.


Related to the concept of a resilience domain is a deployment. While a resilience domain allows related libraries to be compiled more efficiently, a deployment groups related libraries together to present semantic version information to clients. The simplest example of this might be an OS release: OS X 10.10.0 contains Foundation version 1151.16 and AppKit version 1343. A deployment thus acts as a "virtual dependency": clients that depend on OS X 10.10 can rely on the presence of both of the library versions above.

The use of deployments allows clients to only have to think about aggregate dependencies, instead of listing every library they might depend on. It also allows library authors to build many versions of a library within a larger release cycle, as well as allowing a vendor to bundle together many libraries with uncoordinated release schedules and release them as a logical unit.

There are lots of details to figure out here, including how to distribute this information. In particular, just like libraries publish the history of their own APIs, a deployment must publish the history of their included library versions, i.e. not just that OS X 10.10 contains Foundation 1151.16 and AppKit 1343, but also that OS X 10.9 contains Foundation 1056 and AppKit 1265, and that OS X 10.8 contains Foundation 945.0 and AppKit 1187, and so on, back to the earliest version of the deployment that is supported.

Checking Binary Compatibility

With this many manual controls, it's important that library owners be able to check their work. Therefore, we intend to build a tool that can compare two versions of a library's public interface, and present any suspect differences for verification. Important cases include but are not limited to:

Wherever possible, this tool should also check for binary-compatible source-breaking changes <binary-compatible source-breaking change>, such as changing a default argument from false to true.

Automatic Versioning

A possible extension of this "checker" would be a tool that automatically generates versioning information for entities in a library, given the previous public interface of the library. This would remove the need for versions on any of the fragility attributes, and declaring versioned API would be as simple as marking an entity public. Obviously this would also remove the possibility of human error in managing library versions.

However, making this tool has a number of additional difficulties beyond the simple checker tool:

Because this tool would require a fair amount of additional work, it is not part of this initial model. It is something we may decide to add in the future.

Open Issues

There are still a number of known issues with the model described in this document. We should endeavor to account for each of them, and if we can't come up with a satisfactory implementation we should at least make sure that they will not turn into pitfalls for library or client developers.

Subclass and base both conform to protocol

// Library, version 1
class Elf {}
protocol Summonable {}
// Client, version 1
class ShoemakingElf : Elf, Summonable {}
// Library, version 2
extension Elf : Summonable {}

Now ShoemakingElf conforms to Summonable in two different ways, which may be incompatible (especially if Summonable had associated types or requirements involving Self).

Additionally, the client can't even remove ShoemakingElf's conformance to Summonable, because it may itself be a library with other code depending on it. We could fix that with an annotation to explicitly inherent the conformance of Summonable from the base class, but even that may not be possible if there are incompatible associated types involved (because changing a member typealias is not a safe change).

One solution is to disallow adding a conformance for an existing protocol to an open class.

Recompiling changes a protocol's implementation

// Library, version 1
protocol MagicType {}
protocol Wearable {}
func use<T: MagicType>(_ item: T) {}
// Client, version 1
struct Amulet : MagicType, Wearable {}
// Library, version 2
protocol MagicType {
  func equip() { print("Equipped.") }

extension Wearable where Self: MagicType {
  func equip() { print("You put it on.") }

func use<T: MagicType>(_ item: T) { item.equip() }

Before the client is recompiled, the implementation of equip() used for Amulet instances can only be the default implementation, i.e. the one that prints "Equipped". However, recompiling the client will result in the constrained implementation being considered a "better" match for the protocol requirement, thus changing the behavior of the program.

This should never change the meaning of a program, since the default implementation for a newly-added requirement should always be correct. However, it may have significantly different performance characteristics or side effects that would make the difference in behavior a surprise.

This is similar to adding a new overload to an existing set of functions, which can also change the meaning of client code just by recompiling. However, the difference here is that the before-recompilation behavior was never requested or acknowledged by the client; it's just the best the library can do.

A possible solution here is to require the client to acknowledge the added requirement in some way when it is recompiled.

(We do not want to perform overload resolution at run time to find the best possible default implementation for a given type.)


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. The language guarantees that client code will never accidentally introduce implicit dependencies on specific versions of libraries.

Related Proposals

The following proposals (some currently in the process, some planned) will affect the model described in this document, or concern the parts of this document that affect language semantics:

This does not mean all of these proposals need to be accepted, only that their acceptance or rejection will affect this document.



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".


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".

availability context

The collection of library and platform versions that can be assumed, at minimum, to be present in a certain block of code. Availability contexts are always properly nested, and the global availability context includes the module's minimum deployment target and minimum dependency versions.


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.


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.)


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.


An API that is designed to handle future clients, perhaps allowing certain changes to be made without changing the ABI.

fragility attribute

See A Unifying Theme.


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.

performance assertion

See Other Promises About Types.

resilience domain

A grouping for code that will always be recompiled and distributed together, and can thus take advantage of details about a type even if it changes in the future.


A subset of API that is only available to certain clients. Stands for "System Programming Interface".


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.

versioned entity

See Publishing Versioned API.

versioned attribute

See Publishing Versioned API.