inkwell icon indicating copy to clipboard operation
inkwell copied to clipboard

Can't get JitFunction for function that takes arguments involving lifetimes

Open harmic opened this issue 4 years ago • 5 comments

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

harmic avatar Mar 09 '22 05:03 harmic

Does this work if you do type FuncType<'a> = unsafe ... rather the for syntax?

TheDan64 avatar Apr 13 '22 15:04 TheDan64

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.

harmic avatar Apr 14 '22 02:04 harmic

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.

TheDan64 avatar Apr 26 '22 02:04 TheDan64

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

TheDan64 avatar Apr 26 '22 02:04 TheDan64

In any case, I suspect this is a limitation of Rust and not inkwell

TheDan64 avatar Apr 26 '22 02:04 TheDan64