What is name mangling in Swift, how do you read a symbol such as $s4Main3FooV3baryyF, and where do you meet mangled names in practice?

5.8.1 · L5 · Learning

Predict first

Which two symbols appear in the generated IR for this module (compiled with -module-name Main)?

import Swift
public struct Foo {
    public func bar() {}
}
public func baz(_ x: Int) -> Int { x }
  • $s4Main3FooV3baryyF and $s4Main3bazyS2iF
  • $s4Main3FooC3baryyF and $s4Main3bazSiF
  • _Foo_bar and _baz
  • $sMain.Foo.bar and $sMain.baz

Mental model. A mangled name is a postal address written in full: country, city, street and house number packed into one line. It looks unreadable until you know the fields, and it never clashes with another address.

Swift has overloading, generics, modules and nested types, so a plain name cannot identify a function in an object file. The compiler builds a mangled name that encodes everything that distinguishes the declaration. The prefix $s marks the current Swift scheme, then come length-prefixed identifiers (4Main the module, 3Foo the type, 3bar the member), a kind letter (V struct, C class, O enum), and finally the signature and entity: yy is “no arguments, returns Void” and F means a function, so $s4Main3FooV3baryyF is Main.Foo.bar() -> (). In $s4Main3bazyS2iF the part S2i is a compressed “Int, twice” (argument and result). The same scheme names the generated entities: Ma is the type-metadata accessor, Mn the nominal type descriptor, Mf the full metadata and fC an allocating initialiser, as seen in the IR below.

You meet these names in crash logs and backtraces, in linker errors (“duplicate symbol” or “undefined symbol” show the mangled form), in nm and otool output, and in Instruments. swift demangle '$s4Main3FooV3baryyF' (or xcrun swift-demangle) turns them back into Main.Foo.bar() -> (), and you can pipe a whole log through it. Two consequences matter: the module name is part of every symbol, so renaming a module renames everything it exports (and two modules with the same name collide), and changing a signature changes the symbol, which is why the ABI of a binary framework is its set of mangled names and layouts. Names are not mangled for @objc members (the selector is used) or for @_cdecl functions (the C name you give).

Length-prefixed module, type, member names with V for a struct and F for a function; swift demangle gives Main.Foo.bar() -> () and Main.baz(Swift.Int) -> Swift.Int.

Common traps

  • Treating a mangled name as stable across compiler versions. The scheme has been stable since Swift 5 on Apple platforms, but you should rely on the demangler, not on parsing it yourself.
  • Assuming the module name does not matter. It is the first component of every symbol, so two copies of a module with the same name in one process collide.

They might follow up with

  • How do you symbolicate a crash with only addresses? Use the dSYM that matches the build UUID; mangled names come out demangled by the symbolicator.
  • Can I give a Swift function a fixed C name? Yes, @_cdecl("name") (underscored, so not an official promise) or @objc for Objective-C selectors.

Related questions

  • 5.1.1 What are the stages from Swift source to machine code, and why does Swift have SIL?
  • 5.1.3 How do you look at what the compiler produced at each stage?
  • 1.10.4 What is ABI stability vs source compatibility vs module stability?
  • 23.5.1 How do you read a crash report, what do the exception types and Swift runtime messages mean, and how does…
  • 5.8.2 How do access levels become linkage in the object file, and why can an unused internal function disappear in…

How do access levels become linkage in the object file, and why can… →