// File: cphinton42[P]hinton-language/src/check.cpp
// Function: typecheck_function(Context*, unsigned int, Expr_AST*, Function_AST*)
internal
bool typecheck_function(Context *ctx, u32 stage, Expr_AST *type, Function_AST *function_ast)
{
    u32 new_stage = 0;
    bool add_job = false;
    
    // TODO: test if you can use a function recursively in a default value
    // I think this will work, though it isn't self-evident
    
    switch(stage)
    {
        case 0: {
            function_ast->flags |= EXPR_FLAG_NOT_LVALUE;
            set_resolved_type(function_ast->prototype, &type_t_ast);
            set_resolved_type(function_ast, function_ast->prototype);
            auto parameter_types = function_ast->prototype->parameter_types;
            for(u64 i = 0; i < parameter_types.count; ++i)
            {
                if(parameter_types[i])
                {
                    auto job = make_typecheck_job(&type_t_ast, parameter_types[i]);
                    do_typecheck_job(ctx, job);
                }
            }
            auto return_types = function_ast->prototype->return_types;
            for(u64 i = 0; i < return_types.count; ++i)
            {
                assert(return_types[i]);
                auto job = make_typecheck_job(&type_t_ast, return_types[i]);
                do_typecheck_job(ctx, job);
            }
            add_job = true;
        } // fall through
        case 1: {
            new_stage = 1;
            auto parameter_types = function_ast->prototype->parameter_types;
            auto default_values = function_ast->default_values;
            for(u64 i = 0; i < parameter_types.count; ++i)
            {
                auto param_type = parameter_types[i];
                if(default_values[i] && param_type && !type_resolved(param_type))
                {
                    goto finish;
                }
            }
            for(u64 i = 0; i < default_values.count; ++i)
            {
                if(default_values[i])
                {
                    auto job = make_typecheck_job(parameter_types[i], default_values[i]);
                    do_typecheck_job(ctx, job);
                }
            }
            add_job = true;
        } // fall through
        case 2: {
            new_stage = 2;
            auto default_values = function_ast->default_values;
            auto parameter_types = function_ast->prototype->parameter_types;
            for(u64 i = 0; i < parameter_types.count; ++i)
            {
                if(!parameter_types[i])
                {
                    auto val = default_values[i];
                    assert(val);
                    if(type_resolved(val))
                    {
                        parameter_types[i] = val->resolved_type;
                    }
                    else
                    {
                        goto finish;
                    }
                }
            }
            auto param_names = function_ast->param_names;
            for(u64 i = 0; i < param_names.count; ++i)
            {
                assert(parameter_types[i]);
                if(param_names[i])
                {
                    set_resolved_type(param_names[i], parameter_types[i]);
                }
            }
            auto job = make_typecheck_job(nullptr, function_ast->block);
            do_typecheck_job(ctx, job);
            add_job = true;
        } // fall through
        case 3: {
            new_stage = 3;
            Status match = types_match(ctx, type, function_ast->resolved_type);
            if(match != Status::good)
            {
                break;
            }
            new_stage = 4;
        } // fall through
    }
    finish:
    
    if(new_stage < 4)
    {
        auto job = make_typecheck_job(type, function_ast, new_stage);
        array_add(ctx->waiting_jobs, job);
    }
    return new_stage != stage;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x220, %rsp            # imm = 0x220
movq %rdi, -0x8(%rbp)
movl %esi, -0xc(%rbp)
movq %rdx, -0x18(%rbp)
movq %rcx, -0x20(%rbp)
movl $0x0, -0x24(%rbp)
movb $0x0, -0x25(%rbp)
movl -0xc(%rbp), %eax
movq %rax, -0x1e8(%rbp)
subq $0x3, %rax
ja 0x6956
movq -0x1e8(%rbp), %rax
leaq 0x12dc9(%rip), %rcx     # 0x190c0
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq -0x20(%rbp), %rax
movzwl 0x2(%rax), %ecx
orl $0x20, %ecx
movw %cx, 0x2(%rax)
movq -0x20(%rbp), %rax
movq 0x20(%rax), %rdi
leaq 0x19bd2(%rip), %rsi     # 0x1fef0
callq 0x3be0
movq -0x20(%rbp), %rdi
movq -0x20(%rbp), %rax
movq 0x20(%rax), %rsi
callq 0x3be0
movq -0x20(%rbp), %rax
movq 0x20(%rax), %rax
movq 0x20(%rax), %rcx
movq %rcx, -0x38(%rbp)
movq 0x28(%rax), %rax
movq %rax, -0x30(%rbp)
movq $0x0, -0x40(%rbp)
movq -0x40(%rbp), %rax
cmpq -0x38(%rbp), %rax
jae 0x63e8
movq -0x40(%rbp), %rsi
leaq -0x38(%rbp), %rdi
callq 0x17050
cmpq $0x0, (%rax)
je 0x63d5
movq -0x40(%rbp), %rsi
leaq -0x38(%rbp), %rdi
callq 0x17050
movq (%rax), %rdx
leaq -0x58(%rbp), %rdi
leaq 0x19b60(%rip), %rsi     # 0x1fef0
xorl %ecx, %ecx
callq 0x4a80
movq -0x8(%rbp), %rdi
movq -0x58(%rbp), %rax
movq %rax, -0x70(%rbp)
movq -0x50(%rbp), %rax
movq %rax, -0x68(%rbp)
movq -0x48(%rbp), %rax
movq %rax, -0x60(%rbp)
leaq -0x70(%rbp), %rax
movq (%rax), %rcx
movq %rcx, (%rsp)
movq 0x8(%rax), %rcx
movq %rcx, 0x8(%rsp)
movq 0x10(%rax), %rax
movq %rax, 0x10(%rsp)
callq 0x4b40
jmp 0x63d7
movq -0x40(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x40(%rbp)
jmp 0x6354
movq -0x20(%rbp), %rax
movq 0x20(%rax), %rax
movq 0x30(%rax), %rcx
movq %rcx, -0x80(%rbp)
movq 0x38(%rax), %rax
movq %rax, -0x78(%rbp)
movq $0x0, -0x88(%rbp)
movq -0x88(%rbp), %rax
cmpq -0x80(%rbp), %rax
jae 0x64e5
movq -0x88(%rbp), %rsi
leaq -0x80(%rbp), %rdi
callq 0x17050
cmpq $0x0, (%rax)
je 0x6434
jmp 0x6453
leaq 0x144e2(%rip), %rdi     # 0x1a91d
leaq 0x13aaa(%rip), %rsi     # 0x19eec
movl $0x1ef, %edx            # imm = 0x1EF
leaq 0x144df(%rip), %rcx     # 0x1a92d
callq 0x3080
movq -0x88(%rbp), %rsi
leaq -0x80(%rbp), %rdi
callq 0x17050
movq (%rax), %rdx
leaq -0xa0(%rbp), %rdi
leaq 0x19a7c(%rip), %rsi     # 0x1fef0
xorl %ecx, %ecx
callq 0x4a80
movq -0x8(%rbp), %rdi
movq -0xa0(%rbp), %rax
movq %rax, -0xb8(%rbp)
movq -0x98(%rbp), %rax
movq %rax, -0xb0(%rbp)
movq -0x90(%rbp), %rax
movq %rax, -0xa8(%rbp)
leaq -0xb8(%rbp), %rax
movq (%rax), %rcx
movq %rcx, (%rsp)
movq 0x8(%rax), %rcx
movq %rcx, 0x8(%rsp)
movq 0x10(%rax), %rax
movq %rax, 0x10(%rsp)
callq 0x4b40
movq -0x88(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x88(%rbp)
jmp 0x640b
movb $0x1, -0x25(%rbp)
movl $0x1, -0x24(%rbp)
movq -0x20(%rbp), %rax
movq 0x20(%rax), %rax
movq 0x20(%rax), %rcx
movq %rcx, -0xc8(%rbp)
movq 0x28(%rax), %rax
movq %rax, -0xc0(%rbp)
movq -0x20(%rbp), %rax
movq 0x38(%rax), %rcx
movq %rcx, -0xd8(%rbp)
movq 0x40(%rax), %rax
movq %rax, -0xd0(%rbp)
movq $0x0, -0xe0(%rbp)
movq -0xe0(%rbp), %rax
cmpq -0xc8(%rbp), %rax
jae 0x65ae
movq -0xe0(%rbp), %rsi
leaq -0xc8(%rbp), %rdi
callq 0x17050
movq (%rax), %rax
movq %rax, -0xe8(%rbp)
movq -0xe0(%rbp), %rsi
leaq -0xd8(%rbp), %rdi
callq 0x17050
cmpq $0x0, (%rax)
je 0x6598
cmpq $0x0, -0xe8(%rbp)
je 0x6598
movq -0xe8(%rbp), %rdi
callq 0x5600
testb $0x1, %al
jne 0x6598
jmp 0x6958
jmp 0x659a
movq -0xe0(%rbp), %rax
addq $0x1, %rax
movq %rax, -0xe0(%rbp)
jmp 0x6533
movq $0x0, -0xf0(%rbp)
movq -0xf0(%rbp), %rax
cmpq -0xd8(%rbp), %rax
jae 0x669e
movq -0xf0(%rbp), %rsi
leaq -0xd8(%rbp), %rdi
callq 0x17050
cmpq $0x0, (%rax)
je 0x6685
movq -0xf0(%rbp), %rsi
leaq -0xc8(%rbp), %rdi
callq 0x17050
movq (%rax), %rax
movq %rax, -0x1f0(%rbp)
movq -0xf0(%rbp), %rsi
leaq -0xd8(%rbp), %rdi
callq 0x17050
movq -0x1f0(%rbp), %rsi
movq (%rax), %rdx
leaq -0x108(%rbp), %rdi
xorl %ecx, %ecx
callq 0x4a80
movq -0x8(%rbp), %rdi
movq -0x108(%rbp), %rax
movq %rax, -0x120(%rbp)
movq -0x100(%rbp), %rax
movq %rax, -0x118(%rbp)
movq -0xf8(%rbp), %rax
movq %rax, -0x110(%rbp)
leaq -0x120(%rbp), %rax
movq (%rax), %rcx
movq %rcx, (%rsp)
movq 0x8(%rax), %rcx
movq %rcx, 0x8(%rsp)
movq 0x10(%rax), %rax
movq %rax, 0x10(%rsp)
callq 0x4b40
jmp 0x6687
movq -0xf0(%rbp), %rax
addq $0x1, %rax
movq %rax, -0xf0(%rbp)
jmp 0x65b9
movb $0x1, -0x25(%rbp)
movl $0x2, -0x24(%rbp)
movq -0x20(%rbp), %rax
movq 0x38(%rax), %rcx
movq %rcx, -0x130(%rbp)
movq 0x40(%rax), %rax
movq %rax, -0x128(%rbp)
movq -0x20(%rbp), %rax
movq 0x20(%rax), %rax
movq 0x20(%rax), %rcx
movq %rcx, -0x140(%rbp)
movq 0x28(%rax), %rax
movq %rax, -0x138(%rbp)
movq $0x0, -0x148(%rbp)
movq -0x148(%rbp), %rax
cmpq -0x140(%rbp), %rax
jae 0x67c8
movq -0x148(%rbp), %rsi
leaq -0x140(%rbp), %rdi
callq 0x17050
cmpq $0x0, (%rax)
jne 0x67af
movq -0x148(%rbp), %rsi
leaq -0x130(%rbp), %rdi
callq 0x17050
movq (%rax), %rax
movq %rax, -0x150(%rbp)
cmpq $0x0, -0x150(%rbp)
je 0x6746
jmp 0x6765
leaq 0x14224(%rip), %rdi     # 0x1a971
leaq 0x13798(%rip), %rsi     # 0x19eec
movl $0x214, %edx            # imm = 0x214
leaq 0x141cd(%rip), %rcx     # 0x1a92d
callq 0x3080
movq -0x150(%rbp), %rdi
callq 0x5600
testb $0x1, %al
jne 0x6777
jmp 0x67a8
movq -0x150(%rbp), %rax
movq 0x18(%rax), %rax
movq %rax, -0x1f8(%rbp)
movq -0x148(%rbp), %rsi
leaq -0x140(%rbp), %rdi
callq 0x17050
movq -0x1f8(%rbp), %rcx
movq %rcx, (%rax)
jmp 0x67ad
jmp 0x6958
jmp 0x67af
jmp 0x67b1
movq -0x148(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x148(%rbp)
jmp 0x66ec
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rcx
movq %rcx, -0x160(%rbp)
movq 0x30(%rax), %rax
movq %rax, -0x158(%rbp)
movq $0x0, -0x168(%rbp)
movq -0x168(%rbp), %rax
cmpq -0x160(%rbp), %rax
jae 0x68ac
movq -0x168(%rbp), %rsi
leaq -0x140(%rbp), %rdi
callq 0x17050
cmpq $0x0, (%rax)
je 0x681c
jmp 0x683b
leaq 0x14152(%rip), %rdi     # 0x1a975
leaq 0x136c2(%rip), %rsi     # 0x19eec
movl $0x222, %edx            # imm = 0x222
leaq 0x140f7(%rip), %rcx     # 0x1a92d
callq 0x3080
movq -0x168(%rbp), %rsi
leaq -0x160(%rbp), %rdi
callq 0x172d0
cmpq $0x0, (%rax)
je 0x6893
movq -0x168(%rbp), %rsi
leaq -0x160(%rbp), %rdi
callq 0x172d0
movq (%rax), %rax
movq %rax, -0x200(%rbp)
movq -0x168(%rbp), %rsi
leaq -0x140(%rbp), %rdi
callq 0x17050
movq -0x200(%rbp), %rdi
movq (%rax), %rsi
callq 0x3be0
jmp 0x6895
movq -0x168(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x168(%rbp)
jmp 0x67ed
movq -0x20(%rbp), %rax
movq 0x48(%rax), %rdx
leaq -0x180(%rbp), %rdi
xorl %eax, %eax
movl %eax, %esi
xorl %ecx, %ecx
callq 0x4a80
movq -0x8(%rbp), %rdi
movq -0x180(%rbp), %rax
movq %rax, -0x198(%rbp)
movq -0x178(%rbp), %rax
movq %rax, -0x190(%rbp)
movq -0x170(%rbp), %rax
movq %rax, -0x188(%rbp)
leaq -0x198(%rbp), %rax
movq (%rax), %rcx
movq %rcx, (%rsp)
movq 0x8(%rax), %rcx
movq %rcx, 0x8(%rsp)
movq 0x10(%rax), %rax
movq %rax, 0x10(%rsp)
callq 0x4b40
movb $0x1, -0x25(%rbp)
movl $0x3, -0x24(%rbp)
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rsi
movq -0x20(%rbp), %rax
movq 0x18(%rax), %rdx
movl $0x1, %ecx
callq 0x9240
movl %eax, -0x19c(%rbp)
cmpl $0x0, -0x19c(%rbp)
je 0x694f
jmp 0x6956
movl $0x4, -0x24(%rbp)
jmp 0x6958
cmpl $0x4, -0x24(%rbp)
jae 0x69fa
movq -0x18(%rbp), %rsi
movq -0x20(%rbp), %rdx
movl -0x24(%rbp), %ecx
leaq -0x1b8(%rbp), %rdi
callq 0x4a80
movq -0x8(%rbp), %rax
movq 0x8(%rax), %rdi
movq -0x1b8(%rbp), %rax
movq %rax, -0x1d0(%rbp)
movq -0x1b0(%rbp), %rax
movq %rax, -0x1c8(%rbp)
movq -0x1a8(%rbp), %rax
movq %rax, -0x1c0(%rbp)
movq 0x192ce(%rip), %rax     # 0x1fc80
movq %rax, -0x1e0(%rbp)
movq 0x192c8(%rip), %rax     # 0x1fc88
movq %rax, -0x1d8(%rbp)
movq -0x1e0(%rbp), %rsi
movq -0x1d8(%rbp), %rdx
leaq -0x1d0(%rbp), %rax
movq (%rax), %rcx
movq %rcx, (%rsp)
movq 0x8(%rax), %rcx
movq %rcx, 0x8(%rsp)
movq 0x10(%rax), %rax
movq %rax, 0x10(%rsp)
callq 0x17c30
movl -0x24(%rbp), %eax
cmpl -0xc(%rbp), %eax
setne %al
andb $0x1, %al
addq $0x220, %rsp            # imm = 0x220
popq %rbp
retq
nop