kyren / piccolo

An experimental stackless Lua VM implemented in pure Rust
Creative Commons Zero v1.0 Universal
1.62k stars 59 forks source link

Unexpected panic: `assertion failed: self.stack.is_empty()` #44

Closed jrobsonchase closed 4 months ago

jrobsonchase commented 4 months ago

From what I can tell, this occurs when directly returning the result of an attempted call to a nonexistent function. Using an intermediate variable produces a Result::Err as expected.

Full example to reproduce:

use std::string::String as StdString;

use piccolo::*;

const SOURCE: &str = r#"
local function format(who)
    return "Hello, "..who.."!"
end

-- Intentional typo here, causes a failed assertion
return fomat("world")

-- Splitting it across a variable binding causes a runtime error
-- local out = fomat("world")
-- return out
"#;

fn main() {
    let mut lua = Lua::full();

    let exec = lua.enter(|ctx| ctx.stash(Executor::new(ctx)));

    lua.try_enter(|ctx| {
        let closure = Closure::load(ctx, None, SOURCE.as_bytes())?;

        ctx.fetch(&exec).restart(ctx, closure.into(), ());

        Ok(())
    })
    .expect("load closure");

    let res = lua.execute::<StdString>(&exec);

    match res {
        Ok(s) => println!("{s}"),
        Err(e) => println!("execution returned error: {e}"),
    }
}

Full backtrace: backtrace.txt

kyren commented 4 months ago

Very nice catch!

The logic for Thread::tail_call_function was incorrect in the case where meta_ops::call triggered a type error. This only affected tail calls because the first thing that Thread::tail_call_function did was pop the current Lua frame.

This should be fixed now, thank you for finding it!