introduce an actually memory safe (unlike Rust) compilation mode inspired by Fil-C
34 posts were split to a new topic: Social Discussion, Criticism and Bickering
Fil-c exists and can compile a lot of existing c projects. Has there been any meaningful adoption or anything else to suggest that people are willing to sacrifice performance for “actually memory safe” code?
I can’t think of any popular example, but I wouldn’t be surprised if people are just “silently” using it to build whatever pre-existing project they really want to ensure works and then they just don’t talk about it much.
At the very least this would be a neat feature to have when running tests and when you’re having trouble with heisenbugs and want to turn them into reproducible crashes.
For me the best way how this could work is to be able to switch to the safe memory mode (which would make the program slower), detect memory bugs, fix them and then be able to switch back to the normal (fast/performance) mode
This looks cool. I say why not.
Looks like a great middle road if you really want memory safety and don’t mind paying a cost. What’s the harm in offering -Doptimize=ReleaseMemSafe?
I’m not against that as long it’s opted out and it follows project’s philosophy (that’s why we are here right?), but I wonder how much this will postpone 1.0 release and features that I’m really looking forward to have, like being able to use a debugger without llvm
This proposal is to keep the existing optimization modes , while introducing an additional ABI (“fil”) alongside musl, gnu, msvc, etc.
I think zig is trying to juggle too many things here.
Can we finish the good stuff first guys?
I think getting more safety into zig is really interesting, besides the fil-c approach I’m also aware of Isaac Yonemoto’s “Checker for Lifetimes and other Refinement types”, GitHub - ityonemo/clr: Checker for Lifetimes and other Refinement types · GitHub
To understand the implications better I will list cons/pros for the different approaches that I am aware of.
fil-c approach:
- runtime checks
- race conditions is not prevented, but easier to analyse and investigate when they do occur
- Slower (1-6 times)
- Whole application gets the safety guarantee (since c libraries can be compiled with fil-c)
- prior art exist, low risk implementation
clr approach:
- compile time checks
- may have broader/different safety guarantees?
- no runtime cost as I understand it
- under development, no complete solution is ready yet to adapt for production.
To me the clr approach seems really good if it turns out well. Could it be meaningful to purchase the fil-c approach as a “short term” solution and still have the clr approach as a long term solution?
Is the fil-c approach good enough? For example I can imagine that fil-c will allow faster compilations compared with the clr.
Are there other ways that could be a good match for zig to get more safety?
If getting better tools for memory-safety becomes a stated goal of Zig’s then I do think other approaches more in line with Zig’s philosophy should be considered.
One I personally find quite compelling are linear types. They’re quite similar to Rust’s ownership & move semantics but unlike Rust where values can be “dropped” anywhere, allowing the compiler to insert an implicit destructor, linear types statically force you to manually consume/handle the value on all paths, it is not allowed to be silently forgotten.
Beyond being useful for a similar class of problems to Rust’s linear types are strictly more expressive and useful e.g. ensuring you don’t forget to invalidate a cache entry, or decrement an externally managed ref count.
As someone who writes a lot of Rust and dables in Zig I find myself often wishing for linear types in both languages. When designing data structures and APIs it’s really nice to have something which statically tells you “returning this value/token to the caller will guarantee they are forced to call one of x/y/z functions”.
The main problem I see where linear types really clash with Zig’s philosophy is that to make using such types & their values ergonomic Zig will likely need to adopt & implement at least some of Rust’s borrow-checking & lifetime system, which would as a result make the language and compiler more complex.
If you are into linear types you should check out Hylo.
Rust will eventually get linear types aswell, as they are necessary in order to get async/await out of the MVP state.
Still unsure how the “safer than Rust” claim can be made, yet Fil-C’s model does not meaningfully mitigate intra-object overflows and requires exercising bugs at runtime to… checks notes panic?
Not to poke particularly at Andrew too much here, but I found it interesting that in his “Software Should Be Perfect” talk “use-after-free” for example is never mentioned. There’s over-indexing on how allocation failures are actually one of biggest evils, and that allocation ergonomics should be the focal point of the language. If allocation failures are evil, surely crashing unexpectedly when non-deterministic issues occur is evil as well? But I suppose that’s less evil than silent memory corruption… which is barely mentioned in that talk as well.
I agree with the comments by alexrp that this seems at odds with Zig. I do appreciate trying something for memory safety, but this is certainly an interesting approach.
*I’ll add: the “safer than Rust” claim is directly a Filip and Fil-C claim, which is kind of bullshit. In practice yes capability pointers do provide more safety when doing wildly unsafe things, but it’s apples to oranges when you consider the runtime tradeoffs and other issues Rust does prevent or enforce like intra-object overflows, using non-defined values for enums, etc.
I’m by far no expert in low level programming and emhanced memory management. Thus, I can only reply based on my little knowledge.
Zig never claimed to be really memory safe (afaik), and beside that already is strong in detecring leaks using debug allocator (at least compared to C etc.). Therefore, I can see why some understand such a proposal as jumping on the Rust-Zig-rewrite bandwagon which seems to be a bigger thing since the Bun drama; even if that wasn’t the intention.
However, while kind of a memory safe mode seems interesting and maybe could attract some people, I really hope this doesnt have a hight priority. There are so many great features already in the making and on the road map to 1.0 that I think deserve to be handled first.
As an opt-in system? Sure. Migrating to a Rust style forced compilation mode presumably not.
As long as the language doesn’t have to compromise itself such as zero hidden allocations to do so I see nothing wrong.
I just don’t want a situation where zig compromises the amazing parts of itself just to settle up the score.
Can anyone explain this to me like I am a 5 year-old, especially for the following aspects?
Will it change and force us to use certain ways to write Zig codes and manage the memory?
Will it damage the CPU cache friendly and low syscall philosophy Andrew advertising before? (For example Zig allows me to let the memory alive longer so I can have less heap page allocating calls)
Will it require spamming Rc or functional style deep cloning everywhere (like the language I can’t say the name here)?
Can we change to ReleaseFast after a certain period of testing and debugging?
Sorry for my ignorance…
I am not intimately familiar with Fil-C, but tl;dr pointers carry information that at runtime allow for strong checks at pointer access time. The checks include information like the pointee’s size so accesses are bounded, the type of the pointee, etc. These are inserted automatically by the compiler. A violation of the checks results in a deterministic crash. The performance cost can be maybe 4x in the worst case.
You can read more here: InvisiCaps: The Fil-C Capability Model
I’m not really sure who it’s for. For C codebases which you cannot afford to modernize, translate, or fix, it makes sense since Fil-C generally allows for just compiling with a different compiler with minor code changes or none at all. For Zig codebases where everything is new code anyways it’s an odd choice. I’m not even sure it makes sense for fuzzing / CI testing since there’s overlap with ASAN which provides richer and more useful information.
(post deleted by author)