A research language for borrow-checked memory safety.

A statically typed language exploring compile-time memory safety without lifetime annotations — designed and implemented from scratch in C++20, compiling down to yvm, a custom bytecode virtual machine.

Nwogbo Eric — B.Sc. Thesis, Computer Science, Nile University of Nigeria, 2026
Built on and around Git-i/yoyo-lang, an open-source research compiler.
01 — the problem

Overview

Most memory-safe languages force a choice: limiting static checking (Rust), or a runtime garbage collector (Go, Java, Swift). This compiler explores a different approach to static checking.

The borrow checker runs a dataflow analysis directly over each function's control-flow graph, tracking every variable and field access as it flows through branches and loops, to catch use-after-free and aliasing bugs at compile time.

The syntax itself borrows deliberately from Herb Sutter's cpp2/cppfront proposal: every declaration reads as name: kind = value, which keeps the grammar small and mostly unambiguous to parse.

Implemented inC++20
Build systemMeson
Test frameworkCatch2
Backendyvm (custom VM)
Memory modelcompile-time borrow checking
02 — how it compiles

Architecture

The language compiles in five linear stages. Source is scanned, parsed, type-checked, and borrow-checked, then lowered to bytecode for yvm — a virtual machine built specifically for the language.

scannertokens
parserPratt-style
type checker+ generics
borrow checkerCFG dataflow
yvmbytecode VM
03 — seeing it work

Visualizer

Yoyo Viz ↗ is a companion Qt/QML application built alongside the compiler. It pairs a live code editor with panes onto the compiler's own internal state — the parsed AST, the type checker's constraints and substitutions, and the borrow checker's CFG-based IR and points-to graph — recompiling as you type.

AST tree inspect the parsed tree for any snippet, node by node
Type checker watch constraints get generated and solved, and substitutions applied
Borrow checker IR the CFG-based instructions the borrow checker actually reasons over
Points-to graph which values a reference could be pointing to at a given program point
04 — what it looks like

Syntax

Generic structs with explicit lifetime parameters on borrowed methods, plus operator overloading used to define an implicit reference conversion between related types.

  example.yoyo
Point: struct::<T> = { 
    x: T,
    y: T,

    get_x: fn(&'a this)(a) -> &T = return &this.x;
    get_y: fn(&'a this)(a) -> &T = return &this.y;
}
Point3: struct::<T> = {
    xy: Point::<T>, z: T,
    get_z: fn(&'a this)(a) -> &T = return &this.z;
}
operator: &::<T>(inp: &'a Point3::<T>)(a) -> &Point::<T> = return &inp.xy;

main: fn = {
    x: f32 = 100.0;
    y := 20.0;

    value := Point{ .x, .y };
    x_ref: mut = value.get_x();
    y_ref: mut = value.get_y();

    test::print("${*x_ref}, ${*y_ref}");
    other := Point3 {
        .xy = Point{ .x, .y },
        .z = 40.0
    };
    x_ref = other.get_x();
    y_ref = other.get_y();
    z_ref := other.get_z();

    test::print("${*x_ref}, ${*y_ref}, ${*z_ref}");
}
fn(&'a this)(a) -> &T a method borrowing this under lifetime 'a, returning a reference tied to it
operator: &::<T>(...) overloads the borrow operator to define an implicit reference conversion
other.get_x() works on a Point3 because the operator above converts &Point3 to &Point
value.get_x() calling a method through &T to get a field reference
.x, .y shorthand struct-literal fields, filled from same-named locals
${expr} string interpolation inside a string literal
05 — under the hood

Features

Borrow checking

SSA-based dataflow over each function's control-flow graph catches aliasing and use-after-free.

Custom VM runtime

Compiles to bytecode for yvm, a virtual machine built specifically for the language, after the original LLVM backend was retired.

Generics & traits

Generic structs and functions, plus interface-style impl blocks, resolved at compile time.

Optional pattern matching

Optional values are unwrapped with if |val| (expr), binding the contained value only on the non-null path.

cpp2-inspired grammar

A cppfront-inspired name: kind = value declaration form used consistently throughout the language.

Build & test tooling

Meson build system, with Catch2 unit tests covering the parser and IR generator.