diff --git a/Cargo.toml b/Cargo.toml index f33dcdc..336c062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ description = "High level bindings to Lua 5.1 / LuaJIT / Lua 5.3" repository = "https://github.com/khvzak/mlua" documentation = "https://docs.rs/mlua" readme = "README.md" -keywords = ["lua"] +keywords = ["lua", "luajit"] license = "MIT" # [badges] @@ -34,6 +34,7 @@ pkg-config = { version = "0.3.11" } [dev-dependencies] rustyline = "5.0" criterion = "0.2.0" +trybuild = "1.0" [[bench]] name = "benchmark" diff --git a/tests/compile_fail.rs b/tests/compile_fail.rs new file mode 100644 index 0000000..20b0587 --- /dev/null +++ b/tests/compile_fail.rs @@ -0,0 +1,5 @@ +#[test] +fn test_compile_fail() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compile_fail/*.rs"); +} diff --git a/tests/compile_fail/function_borrow.rs b/tests/compile_fail/function_borrow.rs new file mode 100644 index 0000000..f64f3b8 --- /dev/null +++ b/tests/compile_fail/function_borrow.rs @@ -0,0 +1,12 @@ +use mlua::{Lua, Result}; + +struct Test(i32); + +fn main() { + let test = Test(0); + + let lua = Lua::new(); + let _ = lua.create_function(|_, ()| -> Result { + Ok(test.0) + }); +} diff --git a/tests/compile_fail/function_borrow.stderr b/tests/compile_fail/function_borrow.stderr new file mode 100644 index 0000000..c1c4d1f --- /dev/null +++ b/tests/compile_fail/function_borrow.stderr @@ -0,0 +1,20 @@ +error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function + --> $DIR/function_borrow.rs:9:33 + | +9 | let _ = lua.create_function(|_, ()| -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test` +10 | Ok(test.0) + | ---- `test` is borrowed here + | +note: function requires argument type to outlive `'static` + --> $DIR/function_borrow.rs:9:13 + | +9 | let _ = lua.create_function(|_, ()| -> Result { + | _____________^ +10 | | Ok(test.0) +11 | | }); + | |______^ +help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword + | +9 | let _ = lua.create_function(move |_, ()| -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/compile_fail/lua_norefunwindsafe.rs b/tests/compile_fail/lua_norefunwindsafe.rs new file mode 100644 index 0000000..805f61f --- /dev/null +++ b/tests/compile_fail/lua_norefunwindsafe.rs @@ -0,0 +1,8 @@ +use std::panic::catch_unwind; + +use mlua::Lua; + +fn main() { + let lua = Lua::new(); + catch_unwind(|| lua.create_table().unwrap()); +} diff --git a/tests/compile_fail/lua_norefunwindsafe.stderr b/tests/compile_fail/lua_norefunwindsafe.stderr new file mode 100644 index 0000000..5bdd274 --- /dev/null +++ b/tests/compile_fail/lua_norefunwindsafe.stderr @@ -0,0 +1,43 @@ +error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + --> $DIR/lua_norefunwindsafe.rs:7:5 + | +7 | catch_unwind(|| lua.create_table().unwrap()); + | ^^^^^^^^^^^^ `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | + = help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<()>` + = note: required because it appears within the type `std::marker::PhantomData>` + = note: required because it appears within the type `mlua::lua::Lua` + = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua` + = note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]` + = note: required by `std::panic::catch_unwind` + +error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + --> $DIR/lua_norefunwindsafe.rs:7:5 + | +7 | catch_unwind(|| lua.create_table().unwrap()); + | ^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | + = help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` + = note: required because it appears within the type `std::cell::RefCell` + = note: required because it appears within the type `std::marker::PhantomData>` + = note: required because it appears within the type `std::sync::Arc>` + = note: required because it appears within the type `mlua::lua::Lua` + = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua` + = note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]` + = note: required by `std::panic::catch_unwind` + +error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + --> $DIR/lua_norefunwindsafe.rs:7:5 + | +7 | catch_unwind(|| lua.create_table().unwrap()); + | ^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | + = help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` + = note: required because it appears within the type `std::cell::Cell` + = note: required because it appears within the type `std::cell::RefCell` + = note: required because it appears within the type `std::marker::PhantomData>` + = note: required because it appears within the type `std::sync::Arc>` + = note: required because it appears within the type `mlua::lua::Lua` + = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua` + = note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]` + = note: required by `std::panic::catch_unwind` diff --git a/tests/compile_fail/ref_nounwindsafe.rs b/tests/compile_fail/ref_nounwindsafe.rs new file mode 100644 index 0000000..8518c40 --- /dev/null +++ b/tests/compile_fail/ref_nounwindsafe.rs @@ -0,0 +1,9 @@ +use std::panic::catch_unwind; + +use mlua::Lua; + +fn main() { + let lua = Lua::new(); + let table = lua.create_table().unwrap(); + catch_unwind(move || table.set("a", "b").unwrap()); +} diff --git a/tests/compile_fail/ref_nounwindsafe.stderr b/tests/compile_fail/ref_nounwindsafe.stderr new file mode 100644 index 0000000..4047f3d --- /dev/null +++ b/tests/compile_fail/ref_nounwindsafe.stderr @@ -0,0 +1,49 @@ +error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + --> $DIR/ref_nounwindsafe.rs:8:5 + | +8 | catch_unwind(move || table.set("a", "b").unwrap()); + | ^^^^^^^^^^^^ `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | + = help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<()>` + = note: required because it appears within the type `std::marker::PhantomData>` + = note: required because it appears within the type `mlua::lua::Lua` + = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua` + = note: required because it appears within the type `mlua::types::LuaRef<'_>` + = note: required because it appears within the type `mlua::table::Table<'_>` + = note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]` + = note: required by `std::panic::catch_unwind` + +error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + --> $DIR/ref_nounwindsafe.rs:8:5 + | +8 | catch_unwind(move || table.set("a", "b").unwrap()); + | ^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | + = help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` + = note: required because it appears within the type `std::cell::RefCell` + = note: required because it appears within the type `std::marker::PhantomData>` + = note: required because it appears within the type `std::sync::Arc>` + = note: required because it appears within the type `mlua::lua::Lua` + = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua` + = note: required because it appears within the type `mlua::types::LuaRef<'_>` + = note: required because it appears within the type `mlua::table::Table<'_>` + = note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]` + = note: required by `std::panic::catch_unwind` + +error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + --> $DIR/ref_nounwindsafe.rs:8:5 + | +8 | catch_unwind(move || table.set("a", "b").unwrap()); + | ^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | + = help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` + = note: required because it appears within the type `std::cell::Cell` + = note: required because it appears within the type `std::cell::RefCell` + = note: required because it appears within the type `std::marker::PhantomData>` + = note: required because it appears within the type `std::sync::Arc>` + = note: required because it appears within the type `mlua::lua::Lua` + = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua` + = note: required because it appears within the type `mlua::types::LuaRef<'_>` + = note: required because it appears within the type `mlua::table::Table<'_>` + = note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]` + = note: required by `std::panic::catch_unwind` diff --git a/tests/compile_fail/scope_callback_capture.rs b/tests/compile_fail/scope_callback_capture.rs new file mode 100644 index 0000000..93667f0 --- /dev/null +++ b/tests/compile_fail/scope_callback_capture.rs @@ -0,0 +1,22 @@ +use mlua::{Lua, Table, Result}; + +struct Test { + field: i32, +} + +fn main() { + let lua = Lua::new(); + lua.scope(|scope| -> Result<()> { + let mut inner: Option = None; + let f = scope + .create_function_mut(move |lua, t: Table| { + if let Some(old) = inner.take() { + // Access old callback `Lua`. + } + inner = Some(t); + Ok(()) + })?; + f.call::<_, ()>(lua.create_table()?)?; + Ok(()) + }); +} diff --git a/tests/compile_fail/scope_callback_capture.stderr b/tests/compile_fail/scope_callback_capture.stderr new file mode 100644 index 0000000..b23ea95 --- /dev/null +++ b/tests/compile_fail/scope_callback_capture.stderr @@ -0,0 +1,45 @@ +error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements + --> $DIR/scope_callback_capture.rs:12:14 + | +12 | .create_function_mut(move |lua, t: Table| { + | ^^^^^^^^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 9:15... + --> $DIR/scope_callback_capture.rs:9:15 + | +9 | lua.scope(|scope| -> Result<()> { + | _______________^ +10 | | let mut inner: Option
= None; +11 | | let f = scope +12 | | .create_function_mut(move |lua, t: Table| { +... | +20 | | Ok(()) +21 | | }); + | |_____^ +note: ...so that reference does not outlive borrowed content + --> $DIR/scope_callback_capture.rs:11:17 + | +11 | let f = scope + | ^^^^^ +note: but, the lifetime must be valid for the method call at 9:5... + --> $DIR/scope_callback_capture.rs:9:5 + | +9 | / lua.scope(|scope| -> Result<()> { +10 | | let mut inner: Option
= None; +11 | | let f = scope +12 | | .create_function_mut(move |lua, t: Table| { +... | +20 | | Ok(()) +21 | | }); + | |______^ +note: ...so that a type/lifetime parameter is in scope here + --> $DIR/scope_callback_capture.rs:9:5 + | +9 | / lua.scope(|scope| -> Result<()> { +10 | | let mut inner: Option
= None; +11 | | let f = scope +12 | | .create_function_mut(move |lua, t: Table| { +... | +20 | | Ok(()) +21 | | }); + | |______^ diff --git a/tests/compile_fail/scope_callback_inner.rs b/tests/compile_fail/scope_callback_inner.rs new file mode 100644 index 0000000..56b56ba --- /dev/null +++ b/tests/compile_fail/scope_callback_inner.rs @@ -0,0 +1,19 @@ +use mlua::{Lua, Table, Result}; + +struct Test { + field: i32, +} + +fn main() { + let lua = Lua::new(); + lua.scope(|scope| -> Result<()> { + let mut inner: Option
= None; + let f = scope + .create_function_mut(|_, t: Table| { + inner = Some(t); + Ok(()) + })?; + f.call::<_, ()>(lua.create_table()?)?; + Ok(()) + }); +} diff --git a/tests/compile_fail/scope_callback_inner.stderr b/tests/compile_fail/scope_callback_inner.stderr new file mode 100644 index 0000000..077fba4 --- /dev/null +++ b/tests/compile_fail/scope_callback_inner.stderr @@ -0,0 +1,45 @@ +error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements + --> $DIR/scope_callback_inner.rs:12:14 + | +12 | .create_function_mut(|_, t: Table| { + | ^^^^^^^^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 9:15... + --> $DIR/scope_callback_inner.rs:9:15 + | +9 | lua.scope(|scope| -> Result<()> { + | _______________^ +10 | | let mut inner: Option
= None; +11 | | let f = scope +12 | | .create_function_mut(|_, t: Table| { +... | +17 | | Ok(()) +18 | | }); + | |_____^ +note: ...so that reference does not outlive borrowed content + --> $DIR/scope_callback_inner.rs:11:17 + | +11 | let f = scope + | ^^^^^ +note: but, the lifetime must be valid for the method call at 9:5... + --> $DIR/scope_callback_inner.rs:9:5 + | +9 | / lua.scope(|scope| -> Result<()> { +10 | | let mut inner: Option
= None; +11 | | let f = scope +12 | | .create_function_mut(|_, t: Table| { +... | +17 | | Ok(()) +18 | | }); + | |______^ +note: ...so that a type/lifetime parameter is in scope here + --> $DIR/scope_callback_inner.rs:9:5 + | +9 | / lua.scope(|scope| -> Result<()> { +10 | | let mut inner: Option
= None; +11 | | let f = scope +12 | | .create_function_mut(|_, t: Table| { +... | +17 | | Ok(()) +18 | | }); + | |______^ diff --git a/tests/compile_fail/scope_callback_outer.rs b/tests/compile_fail/scope_callback_outer.rs new file mode 100644 index 0000000..57437c9 --- /dev/null +++ b/tests/compile_fail/scope_callback_outer.rs @@ -0,0 +1,19 @@ +use mlua::{Lua, Table, Result}; + +struct Test { + field: i32, +} + +fn main() { + let lua = Lua::new(); + let mut outer: Option
= None; + lua.scope(|scope| -> Result<()> { + let f = scope + .create_function_mut(|_, t: Table| { + outer = Some(t); + Ok(()) + })?; + f.call::<_, ()>(lua.create_table()?)?; + Ok(()) + }); +} diff --git a/tests/compile_fail/scope_callback_outer.stderr b/tests/compile_fail/scope_callback_outer.stderr new file mode 100644 index 0000000..c68bb4f --- /dev/null +++ b/tests/compile_fail/scope_callback_outer.stderr @@ -0,0 +1,11 @@ +error: borrowed data cannot be stored outside of its closure + --> $DIR/scope_callback_outer.rs:11:17 + | +9 | let mut outer: Option
= None; + | --------- ...so that variable is valid at time of its declaration +10 | lua.scope(|scope| -> Result<()> { + | --------------------- borrowed data cannot outlive this closure +11 | let f = scope + | ^^^^^ cannot be stored outside of its closure +12 | .create_function_mut(|_, t: Table| { + | ------------------- cannot infer an appropriate lifetime... diff --git a/tests/compile_fail/scope_invariance.rs b/tests/compile_fail/scope_invariance.rs new file mode 100644 index 0000000..569ada1 --- /dev/null +++ b/tests/compile_fail/scope_invariance.rs @@ -0,0 +1,23 @@ +use mlua::{Lua, Result}; + +struct Test { + field: i32, +} + +fn main() { + let lua = Lua::new(); + lua.scope(|scope| -> Result<()> { + let f = { + let mut test = Test { field: 0 }; + + scope + .create_function_mut(|_, ()| { + test.field = 42; + //~^ error: `test` does not live long enough + Ok(()) + })? + }; + + f.call::<_, ()>(()) + }); +} diff --git a/tests/compile_fail/scope_invariance.stderr b/tests/compile_fail/scope_invariance.stderr new file mode 100644 index 0000000..434eb78 --- /dev/null +++ b/tests/compile_fail/scope_invariance.stderr @@ -0,0 +1,25 @@ +error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function + --> $DIR/scope_invariance.rs:14:38 + | +9 | lua.scope(|scope| -> Result<()> { + | ----- has type `&mlua::scope::Scope<'_, '1>` +... +14 | .create_function_mut(|_, ()| { + | ^^^^^^^ may outlive borrowed value `test` +15 | test.field = 42; + | ---- `test` is borrowed here + | +note: function requires argument type to outlive `'1` + --> $DIR/scope_invariance.rs:13:13 + | +13 | / scope +14 | | .create_function_mut(|_, ()| { +15 | | test.field = 42; +16 | | //~^ error: `test` does not live long enough +17 | | Ok(()) +18 | | })? + | |__________________^ +help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword + | +14 | .create_function_mut(move |_, ()| { + | ^^^^^^^^^^^^ diff --git a/tests/compile_fail/scope_mutable_aliasing.rs b/tests/compile_fail/scope_mutable_aliasing.rs new file mode 100644 index 0000000..4d8d2f0 --- /dev/null +++ b/tests/compile_fail/scope_mutable_aliasing.rs @@ -0,0 +1,15 @@ +use mlua::{Lua, UserData, Result}; + +struct MyUserData<'a>(&'a mut i32); +impl<'a> UserData for MyUserData<'a> {} + +fn main() { + let mut i = 1; + + let lua = Lua::new(); + lua.scope(|scope| -> Result<()> { + let _a = scope.create_nonstatic_userdata(MyUserData(&mut i))?; + let _b = scope.create_nonstatic_userdata(MyUserData(&mut i))?; + Ok(()) + }); +} diff --git a/tests/compile_fail/scope_mutable_aliasing.stderr b/tests/compile_fail/scope_mutable_aliasing.stderr new file mode 100644 index 0000000..d78f983 --- /dev/null +++ b/tests/compile_fail/scope_mutable_aliasing.stderr @@ -0,0 +1,9 @@ +error[E0499]: cannot borrow `i` as mutable more than once at a time + --> $DIR/scope_mutable_aliasing.rs:12:61 + | +11 | let _a = scope.create_nonstatic_userdata(MyUserData(&mut i))?; + | ------ first mutable borrow occurs here +12 | let _b = scope.create_nonstatic_userdata(MyUserData(&mut i))?; + | ------------------------- ^^^^^^ second mutable borrow occurs here + | | + | first borrow later used by call diff --git a/tests/compile_fail/scope_userdata_borrow.rs b/tests/compile_fail/scope_userdata_borrow.rs new file mode 100644 index 0000000..b88d1d6 --- /dev/null +++ b/tests/compile_fail/scope_userdata_borrow.rs @@ -0,0 +1,20 @@ +use mlua::{Lua, UserData, Result}; + +struct MyUserData<'a>(&'a i32); +impl<'a> UserData for MyUserData<'a> {} + +fn main() { + // Should not allow userdata borrow to outlive lifetime of AnyUserData handle + + let igood = 1; + + let lua = Lua::new(); + lua.scope(|scope| -> Result<()> { + let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood))?; + let _ubad = { + let ibad = 42; + scope.create_nonstatic_userdata(MyUserData(&ibad))?; + }; + Ok(()) + }); +} diff --git a/tests/compile_fail/scope_userdata_borrow.stderr b/tests/compile_fail/scope_userdata_borrow.stderr new file mode 100644 index 0000000..c143c9a --- /dev/null +++ b/tests/compile_fail/scope_userdata_borrow.stderr @@ -0,0 +1,13 @@ +error[E0597]: `ibad` does not live long enough + --> $DIR/scope_userdata_borrow.rs:16:56 + | +12 | lua.scope(|scope| -> Result<()> { + | ----- has type `&mlua::scope::Scope<'_, '1>` +... +16 | scope.create_nonstatic_userdata(MyUserData(&ibad))?; + | -------------------------------------------^^^^^-- + | | | + | | borrowed value does not live long enough + | argument requires that `ibad` is borrowed for `'1` +17 | }; + | - `ibad` dropped here while still borrowed diff --git a/tests/compile_fail/userdata_borrow.rs b/tests/compile_fail/userdata_borrow.rs new file mode 100644 index 0000000..26eb3c7 --- /dev/null +++ b/tests/compile_fail/userdata_borrow.rs @@ -0,0 +1,19 @@ +use mlua::{AnyUserData, Lua, Table, UserData, Result}; + +fn main() -> Result<()> { + let lua = Lua::new(); + let globals = lua.globals(); + + // Should not allow userdata borrow to outlive lifetime of AnyUserData handle + struct MyUserData; + impl UserData for MyUserData {}; + let _userdata_ref; + { + let touter = globals.get::<_, Table>("touter")?; + touter.set("userdata", lua.create_userdata(MyUserData)?)?; + let userdata = touter.get::<_, AnyUserData>("userdata")?; + _userdata_ref = userdata.borrow::(); + //~^ error: `userdata` does not live long enough + } + Ok(()) +} diff --git a/tests/compile_fail/userdata_borrow.stderr b/tests/compile_fail/userdata_borrow.stderr new file mode 100644 index 0000000..7ac9670 --- /dev/null +++ b/tests/compile_fail/userdata_borrow.stderr @@ -0,0 +1,13 @@ +error[E0597]: `userdata` does not live long enough + --> $DIR/userdata_borrow.rs:15:25 + | +15 | _userdata_ref = userdata.borrow::(); + | ^^^^^^^^ borrowed value does not live long enough +16 | //~^ error: `userdata` does not live long enough +17 | } + | - `userdata` dropped here while still borrowed +18 | Ok(()) +19 | } + | - borrow might be used here, when `_userdata_ref` is dropped and runs the destructor for type `std::result::Result, mlua::error::Error>` + | + = note: values in a scope are dropped in the opposite order they are defined