# What's stopping zig for adopting c abi for function calls

**URL:** <https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980>\
**Category:** Help\
**Tags:** language\
**Created:** [October 15, 2026, 8:00pm UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980 "2026-10-15T20:00:21Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![eddineimad0](https://ziggit.dev/user_avatar/ziggit.dev/eddineimad0/32/6879_2.png) [@eddineimad0](https://ziggit.dev/u/eddineimad0)\
**Post date:** [October 15, 2026, 8:00pm UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/1 "2026-10-15T20:00:22Z")

</div>

For structs i understand why the abi is unstable but for function i don’t see why the abi is unstable since zig doesn’t pass any hidden context or this pointer ?

---

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [October 15, 2026, 8:55pm UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/2 "2026-10-15T20:55:33Z")

</div>

Internal functions without a stable ABI can bring huge optimization benefits, which LLVM can already achieve.  
Unused function parameters can be removed directly.  
Struct pointer parameters can be optimized to pass by value, especially when a struct pointer is passed in but only part of its fields are used.  
Passing large structs by value can be optimized to passing by reference, which is what Zig used to do with PRO. Even though it’s been removed, there are still lots of safe opportunities to apply this optimization.  
When passing parameters, you can also largely ignore the platform ABI rules and make heavy use of registers.

---

<div class="post-metadata">

**Author:** ![floooh](https://ziggit.dev/user_avatar/ziggit.dev/floooh/32/2989_2.png) [@floooh](https://ziggit.dev/u/floooh)\
**Post date:** [October 15, 2026, 8:59pm UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/3 "2026-10-15T20:59:15Z")

</div>

Nitpicking, but technically there is no such thing as a “C ABI”, ABIs are defined by CPU and OS vendors, C (and all other) compilers can only follow those ABI conventions, otherwise they couldn’t talk to operating system functions.

And since Zig definitely can talk to operating systems, it must support those “target ABIs” too (and it does via `extern` and `export`).

For regular Zig functions I guess the main problem is that Zig has builtin types that are not easily expressable in C (like wide integers, AFAIK even in C there are all sorts of ABI problems with `int128_t`, and I guess also with C23’s `_BitInt(N)` - e.g. even what’s commonly called “C ABI” covers only a small subset of the C type system… and then Zig has optionals, error unions and tagged unions which all don’t easily map to C-style ABIs unless you define the underlying memory layout (which isn’t fixed either, e.g. optional pointers don’t need an extra ‘valid’ boolean because the special null-pointer value can be used for that). Oh, and then there are slices too…

Just speculating here of course, but C is the rare high level language without builtin ‘compound types’ (with the notable exception of `_Complex`, which is so completely alien to the rest of the language that I have no idea how it ever ended up in the C standard) - while languages like Zig or Rust have all sorts of ‘opaque struct types’ which would need to define their interior layout, which then in turn would introduce a rigidness that’s simply counterproductive for “internal” function calls.

---

<div class="post-metadata">

**Author:** ![alanza](https://ziggit.dev/user_avatar/ziggit.dev/alanza/32/6006_2.png) [@alanza](https://ziggit.dev/u/alanza)\
**Post date:** [October 15, 2026, 9:39pm UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/4 "2026-10-15T21:39:05Z")

</div>

`.inline` as a calling convention is one thing that would be (technically) prevented by adopting the C ABI for function calls. Of course, having a function that leaves symbol present in your binary that could be called from C and inlining some or all of the calls of a function are not mutually exclusive…

that’s also another thing: in order to allow comptime monomorphization to play nicely with debug symbols and so forth, Zig function names are “mangled” (i.e. currently this means “deduplicated across comptime instantiations by appending stuff”). you can’t `export` a generic function, but you can `export` an instantiation of it.

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [October 16, 2026, 1:24am UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/5 "2026-10-16T01:24:41Z")

</div>

- users don’t need every function to have C ABI
- it places unnecessary restrictions on machine code generation, optimizations, and other tooling

One example: [error return traces](https://ziglang.org/documentation/master/#Error-Return-Traces) rely on passing an invisible parameter around.

Also very related to [stackless coroutines](https://github.com/ziglang/zig/issues/23446).

---

<div class="post-metadata">

**Author:** ![joshua](https://ziggit.dev/letter_avatar_proxy/v4/letter/j/48db29/32.png) [@joshua](https://ziggit.dev/u/joshua)\
**Post date:** [October 16, 2026, 5:25am UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/6 "2026-10-16T05:25:02Z")

</div>

From the other angle, I both dream of and dread the day a plucky upstart language says:  
“If you see a slice in an `extern fn`, that will always be `{count: usize, data: *T}`!”  
(Of course, the order of the fields will be debated till the crops die)

On one hand, the C ABI is sort of a miracle we all still use, and benefit from.  
On the other hand I want my damn slices without wrapper functions 🙂

In principle there’s nothing stopping Zig from defining the memory layout and calling convention for specifically `extern` functions and structs (doing whatever it wants for non-`extern`), but I reckon the confusion, debate and possibly hidden runtime cost of having to swap struct fields up isn’t worth it.

---

<div class="post-metadata">

**Author:** ![lalinsky](https://ziggit.dev/user_avatar/ziggit.dev/lalinsky/32/3720_2.png) [@lalinsky](https://ziggit.dev/u/lalinsky)\
**Post date:** [October 16, 2026, 5:59am UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/7 "2026-10-16T05:59:28Z")

</div>

I’ll just show one more case, stackful coroutines in Zig are extremely fast, partially thanks to the fact that it doesn’t have the C ABI boundary between function calls. The compiler can optimize context switches away to pretty much nothing.

---

<div class="post-metadata">

**Author:** ![gwenzek](https://ziggit.dev/user_avatar/ziggit.dev/gwenzek/32/8137_2.png) [@gwenzek](https://ziggit.dev/u/gwenzek)\
**Post date:** [October 16, 2026, 7:18am UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/8 "2026-10-16T07:18:14Z")

</div>

Slices already have a well defined layout in Posix, LLVM and Zig implementation (not Zig spec),  
I don’t think it would be that big of a problem if that layout was part of the spec.

Often when talking about “undefined” layout, I hear mention of optimizations. But when you look at your stack everything is layed out very standardly like it would be in C, with each struct field being adressable including hidden fields like union tag and optional tag. And each sub struct are gently layed out in their parent struct with padding between them.

I would love to explore more options like having a unadressable “soup” field allowing to explode a sub struct inside its parent.

---

<div class="post-metadata">

**Author:** ![rdmsr](https://ziggit.dev/user_avatar/ziggit.dev/rdmsr/32/8660_2.png) [@rdmsr](https://ziggit.dev/u/rdmsr)\
**Post date:** [October 16, 2026, 11:47pm UTC](https://ziggit.dev/t/whats-stopping-zig-for-adopting-c-abi-for-function-calls/17980/9 "2026-10-16T23:47:12Z")

</div>

Maybe I’m missing something, but how do you implement context switching without a defined ABI? Typically you need to save/restore callee-saved registers
