While I think that having Fil-C as an ABI is useful (if not important) to have to link against existing (and maybe badly tested) C code, I don’t really get what pure Zig code would get out of it.
That’s actually a problem I have with the proposal on codeberg: It mixes ABI and compilation mode in a way that I’m not sure what the goal is: Is the goal to introduce an additional ABI (like e.g. glibc’s) or is it to add a different mode (like e.g. ReleaseSafe)?
Well, the comments under the proposal (including by Andrew himself) seem to mean ABI, not compilation mode.
As others pointed out with the intra-object example, this itself can be a memory safety issue which Fil-C isn’t able to detect (imagine if the struct has after the array a position field of the head of a CNC machine; or the flag in question is an “is_admin” boolean).
Btw, here an artificial example program with that which runs completely fine with Fil-C, even with -Wall -Wextra -Weverything (the latter creates some other warnings, but they have nothing to do with the problem here like stdout being a recursive macro or bool being not available pre C23 (which compiling in C23 mode)):
#include <stdio.h>
struct T {
char c[4];
bool b;
};
static void printT(const struct T* t) {
printf("T{ .c = .{ %c, %c, %c, %c }, .b = %s }\n", t->c[0], t->c[1], t->c[2], t->c[3], t->b ? "true" : "false");
}
int main() {
struct T t = {
.c = { '1', '1', '1', '1' },
.b = false,
};
printT(&t);
// simple off by one as an example
for (int i = 0; i <= 4; ++i) {
t.c[i] += 1;
}
printT(&t);
fflush(stdout);
}
Then there’s the problem with how to wrangle this up with allocators. After all one of Zig’s greatest pushes is to go away from the “one global allocator” thinking of the past, but Fil-C relies on that old kind of thinking.
If one only does it via mmap wrapping, then that leaves out (custom) allocators and Zig would need to extensively document on how to make an allocator implementation work (including how to set up the invisicaps etc.).
That means every allocator implementation needs to internally do an if (abi == .fil) { ... } if they don’t want things to break immediately as soon as that allocator gets combined with the fil ABI.
Ok, so, what would pure Zig code get? From what I can tell:
- finding illegal casts (which will get caught anyway by #2414)
- badly dealing with multi-item pointers instead of slices (using multi-item pointers directly is a code smell imo anyway, but could be worth it for the rare cases where you need them)
- figuring out when you mess up with direct slice manipulation (like
slice.len = 5; again, code smell imo, but you sometimes need it) - maybe more, would be nice if others could chime in here