A new way to think about borrow checking (in reverse) The Valen programming language's borrow checker runs its memory-safety check in reverse, walking a function from its last statement back to the top, according to a blog post by the Valen project. Valen compiles functions in four phases — static typing, static grouping, and a reverse safety check among them — plus a secret fifth phase that computes LLVM attributes describing which operations may mutate which groups. The author says Valen is the only borrow checker whose safety check goes backwards, a design chosen for convenience, loops and speed, and that it supports optional shared-xor-mutable checking and memory-safe calls into Rust functions. Have you ever had a weird idea, and then it worked, but you can't really believe it? That's what I'm feeling right now, about Valen's borrow checker. Because it travels backward through time. Or, to be less dramatic, it walks functions backwards, which is still very weird. Years ago, if you told me it would be good to iterate a function in reverse, I would have called you crazy. It turns out, there's actually some really good reasons to do this, and it's fairly common when you're doing e.g. optimization passes. 0 note0 But I don't think anyone's done this for checking memory safety. If you want a really deep look into the world of borrow checking, read on We're about to see some weird things. 1 note1 Or destructor insertion https://www.youtube.com/watch?v=IpuvQUVB8Cg , and now, checking memory safety 0% of this article was written by AI. More thoughts here https://verdagon.dev/blog/personal-ai-policy . My stance: if you want someone to take the time to read something, take the time to write it by hand. Thanks for reading = This isn't going to be your normal shared-xor-mutable borrow checking. This is Valen https://github.com/valen-lang/valen 's borrow checker, the one that makes shared-xor-mutable optional https://verdagon.dev/blog/valen-group-borrowing , and can call into Rust functions https://verdagon.dev/blog/golden-spike-reviving-vale-valen with memory safety across the boundary https://verdagon.dev/blog/boundary-memory-safety . It's kind of like Rust's borrow checker, but it also has memory-safe mutable aliasing. If you want to know more about that, check out this post https://verdagon.dev/blog/valen-group-borrowing , though you won't need to understand that for most of this post. Green dragons, specifically. Valen compiles a function in four phases: 3 note3 I'll explain all of this further below. The weirdest part is that 4 happens in reverse. It looks at the last statement in the function, and then walks backwards until it reaches the top of the function. This is weird because, as far as I know, it's the only borrow checker whose safety check goes backwards. 5 note5 Further below, I'll explain why we're going backwards hint: convenience, loops, and speed . But first, let's see how it works, and what it even means to go backwards. Let's skip phase 1, and start with phase 2. There's also a secret fifth phase, where we calculate what attributes to send to LLVM to describe what operations might change what groups. But that's out of scope for this article. It also figures out all the groups invalidated by any mut effects from any instructions. For example: The static typing pass will be familiar to some of you, it assigns types to variables. For example, it turns this program: js exported func main { let fuel = 5; let force = fuel; } into this program: js exported func main { let fuel i64 = 5; let force i64 = fuel; } This is pretty normal, lots of compilers do it. Compilers usually do this as a forward pass, walking from the top of the function to the bottom, figuring out types as it goes. We're going to do something similar but weirder in phase 3; phase 3 is going to do static grouping. This is another forward pass, which figures out where each reference points to. For example, in this program: js func print first fuel cell s &Ship { let first ref = &s.fuel cells 0 ; print int first ref ; } Static grouping figures out that first ref is pointing in s.fuel cells : 7 note7 func print first fuel cell s &Ship { // Static typing says: first ref &int // Static grouping says: first ref &int in s.fuel cells let first ref = &s.fuel cells 0 ; print int first ref ; } In other words, it's pointing to an int somewhere in the s.fuel cells array. This information is now part of the variable's type. We'll be using this information in the next phase, which actually checks for use-after-free. I'll probably start moving away from the term "group" soon, switching to "path" instead. Opinions welcome In V1's static grouping phase, it would also figure out when a reference was made. So for example, instead of &int in s.fuel cells , it would know that it was &int in s.fuel cells from line 7.2. This was pretty awesome, we were literally smuggling time and space information into a variable's type. This new V2 version doesn't need to do that because it does a backwards scan, as we'll see next. Now we can use everything Phase 3 calculated This is the phase that checks for use-after-free. It does it with a backwards scan, as was foretold. Let's see it happen in a slightly bigger example: struct Entity { hp int; buffs int; } func add buff self &Entity mut { ... } func foo e &Entity mut { let buff = &e.buffs 0 ; e.add buff ; print int buff ; } This program is doing a use after free, and we want the compiler to complain. First, I'll desugar it into a more explicit syntax: struct Entity { hp int; buffs int; } func add buff