Can't get JitFunction for function that takes arguments involving lifetimes
Describe the Bug
I am using inkwell to JIT compile a function. This function takes a pointer to a struct as an argument, and this struct contains references so it has a lifetime associated with it.
Calls to ExecutionEngine::get_function fail to compile, with the following error:
error: implementation of `execution_engine::private::SealedUnsafeFunctionPointer` is not general enough
--> src/main.rs:39:43
|
39 | let test_fn: JitFunction<FuncType> = ee.get_function("test_fn").unwrap();
| ^^^^^^^^^^^^ implementation of `execution_engine::private::SealedUnsafeFunctionPointer` is not general enough
|
= note: `execution_engine::private::SealedUnsafeFunctionPointer` would have to be implemented for the type `for<'a> unsafe extern "C" fn(*const Test<'a>) -> i32`
= note: ...but `execution_engine::private::SealedUnsafeFunctionPointer` is actually implemented for the type `unsafe extern "C" fn(*const Test<'0>) -> i32`, for some specific lifetime `'0`
I believe this may be due to the generated implementations of private::SealedUnsafeFunctionPointer, which don't mention lifetimes in the function signatures.
To Reproduce
use inkwell::{OptimizationLevel, context::Context, execution_engine::JitFunction};
#[repr(C)]
struct Test<'a> {
a: &'a i32,
b: i32,
}
type FuncType = for<'a> unsafe extern "C" fn(*const Test<'a>) -> i32;
fn main() {
let a: i32 = 42;
let test = Test { a: &a, b: 0 };
let context = Context::create();
let module = context.create_module("test");
let builder = context.create_builder();
// Set up the function signature
let arg_type = context.i32_type().ptr_type(inkwell::AddressSpace::Global);
let inttype = context.i32_type();
let sig = inttype.fn_type(&[arg_type.into()], false);
// Add the function to our module
let f = module.add_function("test_fn", sig, None);
let b = context.append_basic_block(f, "entry");
builder.position_at_end(b);
// Insert a return statement
let ret = inttype.const_int(42, false);
builder.build_return(Some(&ret));
// create the JIT engine
let ee = module.create_jit_execution_engine(OptimizationLevel::None).unwrap();
// fetch our JIT'd function and execute it
unsafe {
let test_fn: JitFunction<FuncType> = ee.get_function("test_fn").unwrap();
let return_value = test_fn.call(&test);
assert_eq!(return_value, 42);
}
}
with Cargo.toml:
[dependencies]
inkwell = { git = "https://github.com/TheDan64/inkwell", rev="aa4de1d", features = ["llvm11-0"] }
Expected Behavior
Compilation to succeed.
LLVM Version (please complete the following information):
- LLVM: 11.1.0
- Inkwell: llvm11-0
Desktop (please complete the following information):
- OS: Centos 7
Additional Context N/A
Does this work if you do type FuncType<'a> = unsafe ... rather the for syntax?
The sample above does compile if I make that change, but it is not very ergonomic: in my real program I am using the equivalent of FuncType in a lot of places, and I would need to introduce lifetime parameters all over the place if FuncType took one.
I'm also not sure it means the same thing though?
If I understand the usage of higher ranked trait bounds as applied to functions, it allows the lifetime 'a to be determined by the caller rather than the callee, which is what I need. I got that understanding from here.
You're correct that they're not equivalent, but I actually think the non HRTB might be the correct approach here - or at least I don't think there is any way for us to support the HRTB.
Imagine trying to call the HRTB function, I think you'd want to be able to specify the lifetime in the struct? Making it work for all possible lifetime values doesn't seem to make sense to me. Not sure
In any case, I suspect this is a limitation of Rust and not inkwell