tertsdiepraam opened issue #9280:
Currently, the
free_memorymethod onJITModuletakesself. This makes some sense, because the whole thing is then invalidated. However, it is preventing me from (easily) making a wrapper type that frees the memory on drop:struct MyJIT(JITModule); impl Drop for MyJit { fn drop(&mut self) { unsafe { self.0.free_memory() } // error! Can't move out of &mut self } }The above is simplified from what I'm actually trying to do because the above is obviously unsafe. I was trying to do this:
struct MyJIT(Arc<JITModule>); impl Drop for MyJit { fn drop(&mut self) { if let Some(module) = self.0.get_mut() { unsafe { module.free_memory() } } } }where all functions that I give out would have a
MyJIT, so that the memory automatically gets dropped once the last function that uses it would drop the memory.I can work around this by swapping in a new
JITModule, but that doesn't seem ideal. Sincefree_memoryis alreadyunsafe, I think it's alright to let it take&mut selfand allow people to build safe abstractions over it.
tertsdiepraam commented on issue #9280:
To illustrate why the workaround is annoying: it requires creating a
JITModule, which requires creating aJITBuilder, which requires a function for libcalls and can fail. So the final function becomes something like this:#[derive(Clone)] pub struct ModuleData(Arc<JITModule>); impl Drop for ModuleData { fn drop(&mut self) { // get_mut returns None if we are not the last Arc, so the JITModule // shouldn't be dropped yet. let Some(module) = Arc::get_mut(&mut self.0) else { return; }; // If this fails we'd rather just return and leak some memory than // panic. let Ok(builder) = JITBuilder::new(cranelift_module::default_libcall_names()) else { return; }; // Swap in an empty JITModule so that we can take ownership // so that we can call `free_memory` on the old one. // The new one hasn't allocated anything so it will just get dropped // normally. let mut other = JITModule::new(builder); std::mem::swap(&mut other, module); // SAFETY: We only give out functions that hold a ModuleData and // therefore an Arc to this module. By `get_mut`, we know that we are // the last Arc to this memory and hence it is safe to free its // memory. New Arcs cannot have been created in the meantime because // that requires access to the last Arc, which we know that we have. unsafe { other.free_memory() }; } }
bjorn3 commented on issue #9280:
The canonical solution for this is wrapping
JITModuleinManuallyDropand then doing something likelet Some(module) = Arc::get_mut(&mut self.0) else { return; }; unsafe { let module = ManuallyDrop::take(module); module.free_memory(); }In this case given that
free_memoryis unsafe anyway and it is already possible for pointers to outlive the jit module, I think accepting&mut selfinfree_memoryis fine.
tertsdiepraam commented on issue #9280:
Oh that sounds reasonable! Thanks!
tertsdiepraam commented on issue #9280:
I've got it working. Feel free to close this, though it might still be useful. I'll leave that decision up to you. If you want this I'd be happy to attempt a PR.
tertsdiepraam closed issue #9280:
Currently, the
free_memorymethod onJITModuletakesself. This makes some sense, because the whole thing is then invalidated. However, it is preventing me from (easily) making a wrapper type that frees the memory on drop:struct MyJIT(JITModule); impl Drop for MyJit { fn drop(&mut self) { unsafe { self.0.free_memory() } // error! Can't move out of &mut self } }The above is simplified from what I'm actually trying to do because the above is obviously unsafe. I was trying to do this:
struct MyJIT(Arc<JITModule>); impl Drop for MyJit { fn drop(&mut self) { if let Some(module) = self.0.get_mut() { unsafe { module.free_memory() } } } }where all functions that I give out would have a
MyJIT, so that the memory automatically gets dropped once the last function that uses it would drop the memory.I can work around this by swapping in a new
JITModule, but that doesn't seem ideal. Sincefree_memoryis alreadyunsafe, I think it's alright to let it take&mut selfand allow people to build safe abstractions over it.
tertsdiepraam commented on issue #9280:
Found this issue again, and I now agree that
ManuallyDropis the right way to do this. Closing!
Last updated: Dec 13 2025 at 19:03 UTC