// #target: macos-x64
module test;
import std::io;

alias FooFn = fn void(Foo* f, int x);

struct Foo
{
	FooFn x;
}

struct FooTest
{
	inline Foo a;
}

struct FooTest2
{
	inline Foo a;
	int z;
}

fn void Foo.test(Foo* f, int x)
{
	f.x(f, x);
}

fn void FooTest.init(FooTest* this)
{
	static FooFn foo = foo_fn(FooTest);
	this.x = foo;
}

fn void FooTest.high(FooTest* t, int x)
{
	io::printfn("High: %d", x);
}

fn void FooTest.low(FooTest* t, int x)
{
	io::printfn("Low: %d", x);
}

fn void FooTest2.init(FooTest2* this, int z)
{
	static FooFn foo = foo_fn(FooTest2);
	this.x = foo;
	this.z = z;
}

fn void FooTest2.high(FooTest2* t, int x)
{
	io::printfn("High2: %d", x * t.z);
}

fn void FooTest2.low(FooTest2* t, int x)
{
	io::printfn("Low2: %d", x * t.z);
}

macro FooFn foo_fn($FooType)
{
	return fn void(Foo* f, int x) {
		$FooType* z = ($FooType*)f;
		if (x > 0) return z.high(x);
		return z.low(x);
	};
}

fn int main()
{
	FooTest a;
	a.init();
	a.test(10);
	a.test(0);
	a.test(-1);
	FooTest2 b;
	b.init(100);
	b.test(10);
	b.test(0);
	b.test(-1);
	return 0;
}

/* #expect: test.ll

@init.foo = internal unnamed_addr global ptr @"test.foo_fn$lambda1", align 8
@init.foo.2 = internal unnamed_addr global ptr @"test.foo_fn$lambda2", align 8

define void @test.Foo.test(ptr %0, i32 %1) #0 {
entry:
  %2 = load ptr, ptr %0, align 8
  call void %2(ptr %0, i32 %1)
  ret void
}

define void @test.FooTest.init(ptr %0) #0 {
entry:
  %1 = load ptr, ptr @init.foo, align 8
  store ptr %1, ptr %0, align 8
  ret void
}

define internal void @"test.foo_fn$lambda1"(ptr %0, i32 %1) #0 {
entry:
  %z = alloca ptr, align 8
  store ptr %0, ptr %z, align 8
  %gt = icmp sgt i32 %1, 0
  br i1 %gt, label %if.then, label %if.exit

if.then:                                          ; preds = %entry
  %2 = load ptr, ptr %z, align 8
  call void @test.FooTest.high(ptr %2, i32 %1)
  ret void

if.exit:                                          ; preds = %entry
  %3 = load ptr, ptr %z, align 8
  call void @test.FooTest.low(ptr %3, i32 %1)
  ret void
}

define internal void @"test.foo_fn$lambda2"(ptr %0, i32 %1) #0 {
entry:
  %z = alloca ptr, align 8
  store ptr %0, ptr %z, align 8
  %gt = icmp sgt i32 %1, 0
  br i1 %gt, label %if.then, label %if.exit

if.then:                                          ; preds = %entry
  %2 = load ptr, ptr %z, align 8
  call void @test.FooTest2.high(ptr %2, i32 %1)
  ret void

if.exit:                                          ; preds = %entry
  %3 = load ptr, ptr %z, align 8
  call void @test.FooTest2.low(ptr %3, i32 %1)
  ret void
}