Motivation
Swift's C++ interop can represent concrete types on both sides of the boundary. Classes get RefCountedClass wrappers with retain/release lifecycle. Structs get value-type wrappers with VWT-delegated copy and destroy. But there is no C++ representation for Swift protocols, and that gap blocks more than just protocol-typed function signatures.
The deeper problem is generics. Zoe Carver's Bridging C++ Templates with Interop pitch (February 2022) laid out the design for importing C++ function templates into Swift: specialize at the call site, constrain via protocol archetypes, and bridge class templates through protocol conformance extensions. That pitch established protocol conformance as the bridging mechanism between Swift generics and C++ templates, and identified the key limitation that class template extensions "probably can't make it across library boundaries." But it addressed only one direction. C++ libraries express generic interfaces through templates and, increasingly, C++20 concepts. Swift libraries express them through protocols and existentials. There is no representation for Swift protocols in C++, which means a C++ function template constrained by a concept cannot accept a Swift value, and a Swift function returning any P cannot be called from C++ at all. Every any P parameter or return type in a Swift API is unavilable to C++ callers.
The August 2022 workgroup meeting drew the boundary explicitly: "if a generic function has specific constraints, we should respect them in C++ as well and figure out some way of type-checking them from C++." In the same meeting, Zoe noted that C++ code would not be able to create new types conforming to Swift protocols. This framing points to exactly the representation gap that existential wrappers fill. C++ callers do not need to define new conforming types; they need to receive, hold, and operate on values that already conform. That is what existentials are for.
The community has felt this gap as well. Himeshi's Exposing Swift protocols to C++ as pure abstract classes thread (October 2025) asked how protocols should appear in generated headers. The discussion converged on the idea of a SwiftExistential container type rather than pure abstract classes, since abstract class hierarchies cannot represent Swift's value-typed existential semantics (inline buffer, VWT lifecycle) or class-bound existentials (retained pointer, isa-based metadata recovery) without object slicing hazards.
Protocol existential wrappers close this gap from the bottom up: Each Swift protocol exposed to C++ gets a wrapper class that holds the same existential container the Swift runtime uses (type metadata, witness table, inline buffer or heap-allocated value). The wrapper's public methods dispatch through the protocol witness table, so C++ callers get the same type-erased polymorphism Swift callers get. Boxing constructors on each wrapper accept concrete conforming types, which means std::convertible_to<Circle, Drawable> holds automatically. C++ template code can constrain on protocol conformance without any annotation or concept import machinery.
This is the foundation for two follow-on pieces of work. First, the compiler can auto-generate C++20 concepts from Swift protocol declarations (one concept per protocol, structural satisfaction, gated on __cpp_concepts). Those concepts give C++ developers named constraints to write generic code against Swift types. Second, the Swift frontend can resolve concept constraints at call sites and instantiate C++ templates directly, completing the reverse of Zoe's original pitch: greet(myDog) in Swift wraps into the appropriate C++ type and calls the template specialization. Both future features depend on the wrapper types this pitch introduces.
Round-trip import closes the loop in the other direction. When C++ code uses a generated existential wrapper type in a function signature, the ClangImporter recognizes the base class hierarchy and recovers the any Protocol type on re-import. Swift can call back into C++ code that operates on Swift existentials without any manual bridging.
Design
Existential container layout
The wrappers match Swift's runtime containers exactly. Opaque existentials (SwiftExistentialType) are 40 bytes for a single-protocol existential: three words of inline buffer, one word of type metadata, one word for the witness table. Class-bound existentials (SwiftClassExistentialType) are 16 bytes: one retained class pointer plus one witness table. Protocol inheritance does not add witness tables. any Hashable where Hashable: Equatable has exactly one WT; the Hashable table includes entries for all inherited Equatable requirements. Multiple witness tables only arise from ad-hoc compositions (any A & B), which are deferred.
Base classes
SwiftExistentialType lives in _SwiftCxxInteroperability.h alongside RefCountedClass. It delegates all lifecycle operations to the value witness table (copy, move, assign, destroy) and provides a _loadWitness template that indexes into a witness table at a compile-time offset with pointer authentication. SwiftClassExistentialType is the class-bound variant: it uses swift_retain/swift_release for lifecycle and recovers type metadata via swift_getObjectType (isa pointer read) rather than storing a _type field. swift::Any is a non-final subclass of SwiftExistentialType with zero witness tables. Marker protocols inherit from it, and it serves as the default template parameter for unconstrained primary associated types.
Per-protocol wrappers
Each non-marker, non-@objc protocol gets a final class inheriting from SwiftExistentialType or SwiftClassExistentialType depending on whether the protocol requires a class constraint. The wrapper has a single _witnessTable member. Protocol requirement methods dispatch through _loadWitness, passing the projected value pointer, type metadata, and witness table to the witness function. Methods inherited from base protocols use two-level dispatch: load the base protocol's witness table from a known offset in the derived table, then dispatch through that. Conversion methods (asDrawable()) copy the existential container into a smaller base-protocol wrapper.
Protocols with primary associated types emit as C++ class templates. any Container<Int> maps to Container<swift::Int>. The template parameter is a compile-time type tag only; it does not affect container layout. Nested PATs (any Container<any Container<any Drawable>>) map to Container<Container<Drawable>> via recursive type visiting.
Marker protocols emit as final subclasses of swift::Any with no members.
Boxing and function signatures
For each same-module conformance the compiler emits a boxing constructor on the wrapper. Drawable(const Circle&) packs the concrete value into the existential container using the conformance's witness table. Class-bound boxing retains the class pointer directly. These constructors are implicit conversions, so std::convertible_to<T, Drawable> provides a generic boxing constraint for free.
Functions taking or returning existential types are emitted with thunk bodies that pack and unpack the containers. Parameters extract the opaque pointer from the wrapper; returns wrap the thunk result into a new wrapper instance. Class-bound parameters use swift_interop_passDirect_ for loadable direct-pass encoding.
Stdlib conformance records
Equatable, Hashable, and Comparable get existential wrappers in the scaffolding header. Since cross-module protocol witness tables are lazily instantiated at runtime (not emitted as standalone globals), conformance records use swift_getWitnessTable backed by conformance descriptors with a static const cache. Free operator templates (operator==, operator<) and std::sort/std::find compatibility follow from this.
Existential round-trip import
The ClangImporter recognizes existential wrapper types by walking the base class hierarchy to SwiftExistentialType or SwiftClassExistentialType. It uses getSwiftSourceSymbolAttr() to look through ClassTemplateSpecializationDecl to the primary template for the SWIFT_SYMBOL attribute. buildExistentialTypeForProtocol() extracts PAT template arguments, detects default args (swift::Any), and recursively imports nested existential wrapper args. Wrapper class templates and their specializations are skipped in ImportDecl to prevent duplicate import.
Current limitations
Ad-hoc protocol compositions (any Drawable & Resizable) are deferred; the workaround is a combined protocol. Stdlib protocol existentials (any Hashable, any Equatable) are not yet usable in function signatures. Throwing, mutating, and static protocol requirements are excluded from method emission. Protocol requirements with associated type parameters (as opposed to primary associated type constraints on the existential itself) are also excluded.
Implementation
The existential wrappers work is split into 10 commits on the swift-existential-type branch and the first two commits are up for review at #90449. I'd love feedback on the design and implementation.