bor
Rust-style borrow checker and ownership analysis - track variable ownership across scopes, enforce single mutable borrow and no-move-while-borrowed rules, validate lifetimes, detect double-moves, and flag unsafe operations.
Load with: use bor
What this module does
bor implements a Rust-inspired static borrow checker as a library.
You build a borrow checker context (bcche), push and pop scopes,
declare variables with ownership and mutability flags, and simulate borrows and moves.
The checker accumulates errors and warnings as you go.
It enforces the core ownership rules:
- A value that has been moved cannot be used or borrowed.
- A mutably borrowed value cannot be immutably borrowed at the same time, and vice versa.
- Only one mutable borrow is allowed at a time.
- You cannot mutably borrow an immutable variable.
- Owned values that leave a scope without being moved or dropped produce an "unused owned" warning.
This module is primarily useful for building static analysis tools, linters, or educational demos of ownership semantics.
Quick example
use bor
# Create a checker context
bc = bcche()
# Enter a scope
bc = bcsco(bc) # push scope
# Declare an owned, mutable variable
bc = bcvar(bc, "x", tru, tru) # name, mutable=tru, owned=tru
# Immutable borrow
bc = bcvar(bc, "x", fls) # borrow x immutably
# Attempt a mutable borrow while immutably borrowed -> error
bc = bcvar(bc, "x", tru) # ERROR: cannot mutably borrow 'x' while borrowed
# Check results
prn(bcrep(bc))
prn(bcisv(bc)) # fls - has errors
Functions
Context
bcche()
Creates a new borrow checker context with one root scope, an empty errors list, and an empty warnings list.
Scopes
bcsco is overloaded.
bcsco(bc) (enter)
Pushes a new empty scope onto the scope stack.
bcsco(bc) (exit)
Pops the innermost scope. Before popping, checks for owned variables that were never moved - adds an "unused_owned" warning for each. Requires at least 2 scopes (won't pop the root).
Variable tracking
bcvar is overloaded on argument count and meaning.
bcvar(bc, name, mutable, owned)
Declares a variable in the current scope. mutable controls whether mutable borrows are allowed; owned controls whether the "unused owned" check applies. Starts with borrowed: fls, borrowed_mut: fls, moved: fls.
bcvar(bc, name) (move)
Simulates moving a variable. If the variable is undeclared, already moved, or currently borrowed - adds an error. Otherwise marks moved: tru.
bcvar(bc, name, mutable) (borrow)
Simulates borrowing. Checks for conflicts: moved values, mutable borrow of immutable variable, multiple mutable borrows, immutable borrow while mutably borrowed. Sets borrowed or borrowed_mut flags accordingly.
bcvar(bc, name, mutable) (release)
Releases a borrow - clears the borrowed or borrowed_mut flag on the variable.
Results
bcerr(bc)
Returns the list of error objects. Each error has {type, variable, message}.
bcwar(bc)
Returns the list of warning objects.
bcisv(bc)
Returns tru if there are no errors (valid). Does not check warnings.
bcrep(bc)
Returns a formatted text report listing all errors and warnings. If there are none, prints "✓ No borrow check errors".
Lifetime analysis
lifet(name)
Creates a lifetime descriptor with a name, start scope 0, no end scope, and empty borrows list.
lifet(bc, var_name, borrow_scope)
Checks that a borrowed variable's original outlives the borrow scope. Adds a "lifetime_mismatch" error if the original ends before the borrow scope.
bclif(bc, var_name)
Returns the inferred lifetime of a variable as {start: 0, end: scope_depth}, or nil if not found.
Ownership transfer
bcown(bc, from_var, to_var)
Transfers ownership from one variable to another. If from_var is owned and already moved, adds a "double_move" error. Otherwise marks it moved and declares to_var as the new owner. Non-owned (copy) types are just re-declared.
Function call, loops, pattern matching
bccal(bc, func, args)
Simulates passing arguments to a function - moves each string argument (treating string args as variable names).
bcloo(bc)
Enters a loop - pushes a new scope and increments loop_depth.
bcpat(bc, pattern, source)
Analyses a destructuring pattern - declares each pattern variable as a non-owned, immutable binding.
Unsafe blocks
bcuns(bc) (enter/exit)
Toggles bc.in_unsafe. Enter sets it to tru; exit sets it to fls.
bcuns(bc, operation)
Checks that an unsafe operation is within an unsafe block. Adds an "unsafe_outside_block" error if in_unsafe is fls.
AST-level analysis
bcana(ast)
Runs a full borrow check over an AST via the host native __borrow_check_ast.
bcana(path)
Reads a source file from path, parses it via __parse_ast, and runs the borrow checker.
Notes
- The scope stack search in
bcvarsearches from innermost to outermost - inner variables shadow outer ones. bcanawith an AST requires the host runtime to provide__borrow_check_astand__parse_ast.- Requires
trlandtxt.