Proposal: Enhancing Swift library API stability checker to detect library ABI breakages

(Xi Ge) #1

Swift has an API stability checker that runs as part of the regression test suite for the Swift standard library (stdlib). To ensure client-side source compatibility, the API stability checker warns the stdlib authors about accidental API breakages. Currently, the checker can't detect library ABI-specific breakages, such as reordering members in a fixed-layout struct declaration.

We propose to enhance this API stability checker: (1) to detect Swift library ABI breakages along with API breakages, and (2) to detect breakages introduced by SDK overlays in addition to the stdlib.

How the Existing API Checker Works

The existing API checker performs two tasks: (1) generating a JSON representation of a Swift module's interface; and (2) detecting any source breaking API changes from two JSON representations generated by (1).

To test the API stability of the stdlib, we first generate the JSON representation of a stable version of the stdlib as the baseline. As the stdlib codebase evolves, the test reruns the API checker to generate an updated JSON representation of the stdlib, which is later compared against the checked-in baseline. The comparison generates a list of screening-friendly source-breaking changes, for example, updated function return types or removed variable declarations.

Next, the list of source-breaking changes will be textually diffed against an expected change list. Any diffs will fail the test. To fix the failing test, we can either revert the source-breaking changes committed to the stdlib codebase or update the expected change list.

Scope of the Proposed Swift Library API/ABI Stability Checker

Stable Swift library ABI requires efforts from different levels. At the compiler level, we need to design the code generation in a resilient way, thus benign changes such as adding new members to a type won't break the ABI. Focusing on breakages caused via source changes, the proposed API/ABI checker will be unable to check for ABI breakage that's caused by compiler native code-generation changes, such as changing calling conventions or the emitted metadata. Guarding against compiler code-generation breakages is out of the scope for this proposal.

At the source level, stdlib and SDK overlay authors need to be mindful about the evolution of the Swift library; only those API changes that don’t break API and ABI compatibility should be accepted to the newer versions of the library (proposal). The proposed enhancements for a library API/ABI stability checker will help at this level.

Step 1: Detecting More API Breakages

Most ABI-breaking changes will also manifest as API breaking changes. Thus the existing API stability checker has already covered various illegal evolutions, for example. removing members from a type declaration or updating parameter types in functions. As the first part of the implementation, we plan to detect more kinds of API source breaking changes, including:

Functions

  • Changing functions' generic requirements: this may shrink the applicable scope of the function.
  • Removing functions' default parameter: users' codebase may miss required arguments.

Properties

  • Removing public setter of a computed property: users' change to the property becomes illegal.

Enums

  • Removing raw type of an enum: users' codebase may use the underlying raw type.
  • Removing public protocol conformances.

Classes

  • Removing public super classes.
  • Removing public protocol conformances.

Structs

  • Removing public protocol conformances.

Protocols

  • Adding a non-type requirement without providing default implementation.
  • Adding a new associated type.
  • Removing a default type for an associated type.
  • Removing an unconstrained default implementation
  • Adding/removing inherited protocols

Extensions

  • Changing constraints: this may shrink the applicable scope of the extension's members.
  • Adding/Removing protocol conformance: this is particularly dangerous for protocol extensions.

Step 2: Detecting ABI-specific breakages

Some kinds of library evolution will break ABI stability without breaking API stability. As the second part of the implementation, we plan to implement the checking of these situations, including:

  • Adding/reordering of members in a fixed-layout struct. Unlike regular Swift structs, a struct with fixed layout is similar to a C struct where the change of member ordering will be surfaced to ABI change. We should also warn about adding or removing the @_fixed_layout attribute.

  • Adding members to exhaustive enums. For the similar reason with fixed-layout struts, adding members to exhaustive enums can also break ABI stability without necessarily breaking clients' codebases during compile time.

  • Adding/removing @objc to enums and protocols. Whether exposing a Swift declaration to Objective-C code can also change the underlying binary interface.

  • Removing @objc from classes.

  • Adding/removing dynamic keywords from any Swift functions or variables.

Integrating API/ABI Stability Checker with CI

The proposed changes are just enhancements on top of the existing API stability checker. The way failures are detected and marked as "expected" will remain the same. It will run as part of the Swift regression test suite, and if breakages are detected from changes that are desirable, the changes should be added to the "expected change list" file as part of the PR.

The baseline for checking ABI stability is separately maintained from the one for API stability. This allows us to incorporate more intermediate ABI version bumps for the Swift libraries under development.

14 Likes