1. 20

[Making a quick post about this and I can write more in response to comments, but just wanted to share something from an experiment I did over the past couple weeks. I’ll do a writeup or video discussing more of this soon along with my C workflow generally.]

I wrote a borrow checker for C that uses libclang. You can see an example test case with the resulting checker output in the linked gist (this post’s title is the link).

Below the checker output in the gist is the code for the borrow checker itself. The main function code quality is ¯_(ツ)_/¯ because it has some copypasta to read ‘compile_commands.json’ with libclang, and you should probably ignore that, I’ll be cleaning that up. The ‘Visit’ section is where the core of the checker lives. There’s a lot of statically-sized arrays and linear searches and repeat function calls but … it runs pretty fast and is far from the bottleneck in the build pipeline of project(s) I’m testing it on (checking takes 0.04 sec while the actual compile+link takes about 1 sec). The code is definitely still in a proof-of-concept state.

It’s meant to pair with a codegen system I have that also generates TArray types and functions like TDealloc and TClone etc. that recurse through struct fields, and the codegen also generates other useful things like json serialization – that I use in my own projects. The codegen and checker currently work on a little game project I wrote as a pragmatic test for all this with Raylib. You can see what the codegen logic looks like here – https://gist.github.com/nikki93/5752dff93fa4cb78c4750548cc588b64#file-00-generate-c-L318 (big dump of my game’s code, not about the details as much as the structure) – there’s a simple ‘quasiquotation’ going on there, and you can see the game code in ‘game.c’ with ‘generated.{h,c}’ showing the generated API given the types in ‘game.c’. The codegen is from before I started using libclang so it uses a … homemade and incomplete … ‘parser’.

[Video of the game – https://youtu.be/EK_zuTqsKL4 – the first wave and first boss you see have their part layouts and firing patterns and emitters procedurally generated – I show some more generations and then also some hand-written bosses and opponents]

The target use case is on app / game ‘business logic’ code written in a style that uses a bunch of arrays or other data structures and accesses things through ids or iteration, but has short-lived local pointers from those accesses and benefits from checking for invalidation of those pointers, and also of accidental copies or missed deallocs etc. of the data structures themselves. There can be a lot of this code that grows and churns with demands on project feature set and having automatic checking helps. I also always have asan+lsan running in development mode on my projects in addition to this. It’s not as catered right now towards, for example, the internal code of an allocator or something like that where you probably want some other system of checking correctness (lots of testing, fuzzing, proofs, thinking, …).

[For people familiar with Rust borrow check semantics / ‘linear types’ stuff generally: It avoids a lot of the complexities the Rust borrow checker deals with by not allowing structs to have pointers in them with (multiple) generic lifetime parameters, not needing to do much inference if at all, not dealing with escaping closures, etc. In the future it could have structs that just have one inferred lifetime parameter by analyzing them the same way as pointers currently. It also assumes the first parameter of a function is the one a returning pointer borrows from (eg. in FooArrayPush in the example), so you don’t need to annotate that.]

[This might miss some entire set of cases I didn’t think of yet, so there’s that…]

[Edit: Oh – it also doesn’t have immutable references yet, all references are mutable and exclusive (it does allow ‘reborrowing’) – const T * as an immutable borrow is something I will likely add]