Is it safe to call `_typeName()` in production?

Based on this thread where folks are discussing a version of String(describing:) which would call it it seems like the answer is "yes," but I wanted to ask here in case there's something I'm missing.

I have a unit test where 80% of the runtime (500ms total) is spent in the runtime, and removing String(describing:) calls would go a long way to mitigating this. But I'd also like to be able to call this in production.

Is this part of the Swift ABI? Is it merely very unlikely to change? Or should I rethink this?

1 Like

A leading underscore denotes private API, so best to avoid using it.

1 Like

It’s a gray area, it might change, and the compiler does not generate calls to this function, but in practice it’s part of a set of entry points which are used by the compiler to instantiate generic metadata, so it’s unlikely to go away on existing platforms.

Of course it doesn’t exist in Embedded Swift, and it’s also possible that a future port of Swift to some platform might not have it.

If this is just for testing purposes, it’s probably safe, but I would keep some kind of fallback working just in case.

2 Likes

Thanks. If I want to use this in production, something like this should be safe, right?

extension String {

    init(
        descriptionOfType type: Any.Type
    ) {
        #if DEBUG
        self = _typeName(type, qualified: false)
        #else
        // Keep incrementing this every time a new iOS version that still supports this comes out.
        if #unavailable(iOS 27, macOS 27) {
            // assuming that this was available on all OS versions since my minimum deployment target
            self = _typeName(type, qualified: false)
        } else {
            self = String(describing: type)
        }
        #endif
    }

}

and the compiler does not generate calls to this function

So, I looked into this a little more, and it looks to me like there's another way for a Swift program to wind up referencing _typeName directly: String interpolations like Swift String and OSLog.

A swift program as short as let s = "\(Int.self) outputs it in the assembly:

Swift.DefaultStringInterpolation.appendInterpolation(Any.Type) -> ():
00000001000009b0	sub	sp, sp, #0x20
00000001000009b4	stp	x29, x30, [sp, #0x10]
00000001000009b8	add	x29, sp, #0x10
00000001000009bc	mov	w8, #0x0
00000001000009c0	and	w1, w8, #0x1
00000001000009c4	bl	0x100000a48 ; symbol stub for: Swift._typeName(_: Any.Type, qualified: Swift.Bool) -> Swift.String
00000001000009c8	str	x1, [sp, #0x8]
00000001000009cc	bl	0x100000a24 ; symbol stub for: Swift.String.append(Swift.String) -> ()
00000001000009d0	ldr	x0, [sp, #0x8]
00000001000009d4	bl	0x100000a54 ; symbol stub for: _swift_bridgeObjectRelease
00000001000009d8	ldp	x29, x30, [sp, #0x10]
00000001000009dc	add	sp, sp, #0x20
00000001000009e0	ret

So it seems like that effectively makes it part of the ABI, perhaps unintentionally? Ironically, it might also solve my problem since a plain string interpolation might be faster than String(describing:).

3 Likes