ilusm.dev

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 bcvar searches from innermost to outermost - inner variables shadow outer ones.
  • bcana with an AST requires the host runtime to provide __borrow_check_ast and __parse_ast.
  • Requires trl and txt.