vx-lang / vx-lang/Vx

The use-scan drops an unsafe block's trailing expression, and panics on any variant it does not list

Open
#490 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug core-lang
Dominant language
Rust
Stars
14
Forks
2
Avg merge
12h 42m
Merged PRs (30d)
61

Description

TypeChecker::extract_uses_expr (src/hir/env.rs) walks an expression collecting the names it reads. It serves two purposes: liveness and residency analysis, and — in the flat driver — discovering which imported functions a body calls, so their portable bodies can be linked from a .vxlib.

The user-visible bug. The UnsafeBlock arm walks the block's statements and never visits its trailing expression:

Expr::UnsafeBlock(u) => {
    for s in &u.stmts {
        Self::extract_uses_stmt(s, uses);
    }
    // u.ret is not visited
}

So let v = unsafe { imported(21) }; — where the call is the trailing expression — records no use of imported. Its body is never appended from the artifact, the module declares the symbol private, and the program fails at the linker:

ld: symbol(s) not found for architecture arm64

with no diagnostic naming the cause. It predates the unsafe fn work; the older binary reproduces it.

Why the obvious fix does not work, measured. Visiting u.ret is two lines, and it does fix the link failure. It also breaks the build, because the match ends in

_ => panic!("Unsupported expr for extract_uses_expr: {:?}", expr),

Every variant the walk has never reached is a compiler panic, not a silent miss. Widening the walk by one arm made previously unreachable variants reachable and crashed on them: first Number (13 unit tests in codegen::flat::tests), then, after adding the leaf arms, Grad/Jvp/Vjp from the autodiff test plus test_middle_end, test_frontend_pass, test_backend and the object-emission suite. Each fix uncovered the next variant.

So the walk is total only over the subset it happens to be called with today, and that subset is enforced by a panic rather than by the type system.

What the fix actually needs.

  1. Make the match exhaustive over Expr — remove _ => entirely, so the compiler names every variant that must be considered. Unvisited today, ignoring pure leaves: ComptimeBlock, IndirectCall, EnumVariant, VecMacro, MacroCall, Grad, Jvp, Vjp, InlineMlir, TransferPredicate.
  2. Decide for each whether it carries a use, and recurse where it does. IndirectCall and ComptimeBlock are the two most likely to hide one.
  3. Re-run the residency and memory-capacity suites: seeing more uses changes which tiles count as live, and those diagnostics depend on it. That is the reason this is not a drive-by.

Until then the linking failure stands. driver_link_interface_refuses_an_unguarded_unsafe_import deliberately avoids it: it checks the refusal against an unsafe library and the absence of a refusal against a safe one, both with --action print-ast, so it discriminates without needing a body to link.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/hir/env.rs at TypeChecker::extract_uses_expr and inspect the Expr variants and recursive helpers it currently handles. Use the listed codegen, autodiff, middle-end, frontend, backend, object-emission, residency, and memory-capacity tests to identify missing cases; done means the walk handles every Expr variant without compiler panics and the affected suites pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.