Open-Source Wikis

/

Swift

/

Background

/

Pitfalls and danger zones

apple/swift

Pitfalls and danger zones

Things that have surprised contributors. Read before making changes in these areas.

Two demanglers

Changes to the mangler must be applied in both lib/Demangling/ and stdlib/public/Demangling/. The build system runs them independently; symptoms of half-done changes are runtime symbol-lookup failures or swift_demangle returning wrong strings. See docs/ABI/Mangling.rst.

Two demanglers, again, when changing manglings

Even if you only intend to add a new mangling node, you must update:

  1. The demangler grammar (in lib/Demangling/Demangler.cpp and shared sources).
  2. The remangler (lib/Demangling/Remangler.cpp) so the round-trip works.
  3. The symbolic mangling parser (lib/Demangling/NodePrinter.cpp).
  4. The compatibility table in lib/Demangling/OldDemangler.cpp if you're touching pre-Swift-5 mangling.

ABI-breaking mangling changes also require a swift_get_compileTimeMangling (or similar) compatibility table.

Adding a Decl kind touches ~15 files

A new Decl subclass needs to be threaded through:

  • include/swift/AST/DeclNodes.def (the visitor pattern dispatch).
  • lib/AST/Decl.cpp (the implementation).
  • lib/Sema/ (type-checking).
  • lib/Parse/ (parsing).
  • lib/SILGen/ (lowering).
  • lib/Serialization/ (serialization round-trip).
  • lib/PrintAsClang/ (header printing if applicable).
  • lib/IDE/ (completion / index).
  • Various .def files for visitors.

Forgetting one usually shows up as default: llvm_unreachable("...") crashes when the compiler hits the missing case at runtime.

SIL invariants are subtle

The SIL verifier (lib/SIL/Verifier/SILVerifier.cpp) is the canonical reference for what's legal SIL. A few easy traps:

  • OSSA invariants -- every owned value must be consumed exactly once on every path; every guaranteed value must end before its scope ends.
  • Lifetime ends are control-flow-sensitive -- a destroy_value after a cond_br must dominate the corresponding end_borrow.
  • copy_addr vs store/load -- copy_addr calls value-witness init_with_copy; store requires the slot is currently uninitialized. Mixing them is a verifier error.

Build with assertions to catch verifier failures early. CI builds always have assertions.

@frozen is forever

Marking a struct or enum @frozen makes its layout part of the ABI. Once shipped you cannot:

  • Add a stored property.
  • Reorder existing stored properties.
  • Add a case to a @frozen enum.

docs/LibraryEvolution.rst has the rules. ABI checks in CI catch most violations.

Ordering-sensitive serialization

.swiftmodule records are written in the order Sema discovered them. If you change Sema's discovery order, you may produce a different .swiftmodule byte-for-byte. Some downstream tools depend on bit-stable output (incremental builds, content-addressed caching). Run test/Serialization/ with assertions before landing.

Macros and incremental compilation

Macro expansion is currently re-run on every compile -- no caching across invocations. Heavy macro-using code can therefore become noticeably slower. There's ongoing work to memoize expansion based on input syntax.

stdlib/public/runtime/Demangle.cpp and ARC

The runtime is built -fno-exceptions and -fno-rtti. Including the standard library's demangler glue does not pull in C++ STL allocators -- the runtime ships its own minimal std::string-like type. Don't accidentally #include <string> in runtime files.

Thread safety in the AST

ASTContext is not thread-safe in general. Most lookups are protected by single-threaded use of a CompilerInstance. SourceKit serializes per-file requests onto a queue. Don't share an ASTContext across threads without the right synchronization.

TODO/FIXME markers can be very old

The repo has 1,041 TODO/FIXME-bearing files. Some were tagged in 2014 and reference radar IDs that long predate the GitHub migration. If you find one, check git blame and the linked rdar before changing the surrounding code.

C++ interop is moving fast

lib/ClangImporter/, stdlib/public/Cxx/, and test/Interop/ had 1,690 commits in the last year alone. Expect merge conflicts on long-lived C++ interop branches. See C++ interop for context.

Two drivers

There is a C++ driver (lib/Driver/, in this repo) and a Swift driver (swiftlang/swift-driver). Most users run the Swift one. Adding flags must be done in lib/Option/Options.td (consumed by both) but driver-side behavior changes only land in the Swift driver.

Build and test infra are slow

A clean build is ~70 GB of disk and 30+ minutes on a fast laptop. Running the full test suite is hours. Expect the iteration loop to be: edit → ninja check-swift-Sema-only (or whatever is closest to the change). Don't bother running the full suite locally; trust @swift_ci.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Pitfalls and danger zones – Swift wiki | Factory