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:
- The demangler grammar (in
lib/Demangling/Demangler.cppand shared sources). - The remangler (
lib/Demangling/Remangler.cpp) so the round-trip works. - The symbolic mangling parser (
lib/Demangling/NodePrinter.cpp). - The compatibility table in
lib/Demangling/OldDemangler.cppif 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
.deffiles 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
ownedvalue must be consumed exactly once on every path; everyguaranteedvalue must end before its scope ends. - Lifetime ends are control-flow-sensitive -- a
destroy_valueafter acond_brmust dominate the correspondingend_borrow. copy_addrvsstore/load--copy_addrcalls value-witnessinit_with_copy;storerequires 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
@frozenenum.
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.