// File: cphinton42[P]hinton-language/src/check.cpp
// Function: typecheck_unary(Context*, unsigned int, Expr_AST*, Unary_Operator_AST*)
internal
bool typecheck_unary(Context *ctx, u32 stage, Expr_AST *type, Unary_Operator_AST *unop_ast)
{
    u32 new_stage = 0;
    u32 done_stage;
    
    switch(unop_ast->op)
    {
        case Unary_Operator::plus:
        case Unary_Operator::minus: {
            done_stage = 3;
            switch(stage)
            {
                case 0: {
                    if(type)
                    {
                        Status status = types_match(ctx, &numberlike_t_ast, type);
                        if(status != Status::good)
                        {
                            goto finish;
                        }
                    }
                } // fall through
                case 1: {
                    new_stage = 1;
                    unop_ast->flags |= EXPR_FLAG_NOT_LVALUE;
                    Expr_AST *number_type;
                    if(type)
                    {
                        number_type = type;
                    }
                    else
                    {
                        number_type = &numberlike_t_ast;
                    }
                    auto job = make_typecheck_job(number_type, unop_ast->operand);
                    do_typecheck_job(ctx, job);
                } // fall through
                case 2: {
                    new_stage = 2;
                    if(!type_resolved(unop_ast->operand))
                    {
                        goto finish;
                    }
                    // TODO: make sure this is a concrete type, rather than numberlike_t_ast
                    // TODO: make sure it isn't negative an unsigned type
                    set_resolved_type(unop_ast, unop_ast->operand->resolved_type);
                    new_stage = 3;
                } // fall through
            }
        } break;
        case Unary_Operator::deref: {
            done_stage = 2;
            switch(stage)
            {
                
                case 0: {
                    unop_ast->flags |= EXPR_FLAG_LVALUE;
                    Expr_AST *type_to_check = nullptr;
                    if(type)
                    {
                        // TODO: maybe make a canonical type table, so not every & makes a new type in memory
                        Unary_Operator_AST *type_to_deref = construct_ast(ctx->ast_pool, Unary_Operator_AST, 0, 0);
                        type_to_deref->flags |= AST_FLAG_SYNTHETIC;
                        set_resolved_type(type_to_deref, &type_t_ast);
                        type_to_deref->op = Unary_Operator::ref;
                        type_to_deref->operand = type;
                        type_to_check = type_to_deref;
                    }
                    auto job = make_typecheck_job(type_to_check, unop_ast->operand);
                    do_typecheck_job(ctx, job);
                } // fall through
                case 1: {
                    new_stage = 1;
                    if(!type_resolved(unop_ast->operand))
                    {
                        goto finish;
                    }
                    Expr_AST *reduced;
                    Status status = reduce_type(ctx, unop_ast->operand->resolved_type, &reduced);
                    if(status != Status::good)
                    {
                        goto finish;
                    }
                    if(reduced->type != AST_Type::unary_ast)
                    {
                        report_error("Expected expr of pointer type", unop_ast->operand);
                        ctx->success = false;
                        goto finish;
                    }
                    Unary_Operator_AST *operand_type = static_cast<Unary_Operator_AST*>(reduced);
                    assert(operand_type->resolved_type == &type_t_ast);
                    assert(operand_type->op == Unary_Operator::ref);
                    status = types_match(ctx, type, operand_type->operand);
                    if(status != Status::good)
                    {
                        goto finish;
                    }
                    set_resolved_type(unop_ast, operand_type->operand);
                    new_stage = 2;
                } // fall through
            }
        } break;
        case Unary_Operator::ref: {
            done_stage = 2;
            Expr_AST *inner_type;
            if(type)
            {
                Expr_AST *reduced;
                Status status = reduce_type(ctx, type, &reduced);
                if(status != Status::good)
                {
                    goto finish;
                }
                if(reduced->type == AST_Type::unary_ast)
                {
                    Unary_Operator_AST *type_to_match = static_cast<Unary_Operator_AST*>(reduced);
                    assert(type_to_match->op == Unary_Operator::ref);
                    inner_type = type_to_match->operand;
                }
                else if(reduced == &type_t_ast)
                {
                    // TODO: probably need to match Type more generally
                    // e.g. if user includes Type explicitly in the program
                    inner_type = &type_t_ast;
                }
                else
                {
                    report_error("Got expr of pointer type", unop_ast);
                    ctx->success = false;
                    goto finish;
                }
            }
            else
            {
                inner_type = nullptr;
            }
            
            
            // TODO: probably change so that &Type and &value are distinct
            // This way types can be values, and it may simplify some code
            switch(stage)
            {
                case 0: {
                    auto job = make_typecheck_job(inner_type, unop_ast->operand);
                    do_typecheck_job(ctx, job);
                } // fall through
                case 1: {
                    new_stage = 1;
                    if(!type_resolved(unop_ast->operand))
                    {
                        goto finish;
                    }
                    if(inner_type != &type_t_ast)
                    {
                        u16 lvalue_flags = unop_ast->operand->flags & EXPR_FLAG_LVALUE_MASK;
                        if(lvalue_flags == 0)
                        {
                            goto finish;
                        }
                        else if(lvalue_flags == EXPR_FLAG_NOT_LVALUE)
                        {
                            report_error("Can only take pointers to lvalues", unop_ast);
                            ctx->success = false;
                            goto finish;
                        }
                        else
                        {
                            assert(lvalue_flags == EXPR_FLAG_LVALUE);
                        }
                    }
                    if(unop_ast->operand->resolved_type == &type_t_ast)
                    {
                        set_resolved_type(unop_ast, &type_t_ast);
                    }
                    else if(type)
                    {
                        // TODO: check if type matches?
                        // inner_type should match unop_ast->operand->resolved_type
                        // type should be a pointer type
                        set_resolved_type(unop_ast, type);
                    }
                    else
                    {
                        // TODO: type table to reduce memory usage? (among other things)
                        Unary_Operator_AST *pointer_type = construct_ast(ctx->ast_pool, Unary_Operator_AST, 0, 0);
                        pointer_type->flags |= AST_FLAG_SYNTHETIC;
                        set_resolved_type(pointer_type, &type_t_ast);
                        pointer_type->op = Unary_Operator::ref;
                        pointer_type->operand = unop_ast->operand->resolved_type;
                        
                        set_resolved_type(unop_ast, pointer_type);
                    }
                    new_stage = 2;
                } // fall through
            }
        } break;
        case Unary_Operator::lnot: {
            done_stage = 2;
            switch(stage)
            {
                case 0: {
                    Expr_AST *bool_type;
                    if(type)
                    {
                        Status status = types_match(ctx, &boollike_t_ast, type);
                        if(status != Status::good)
                        {
                            goto finish;
                        }
                        bool_type = type;
                    }
                    else
                    {
                        bool_type = &bool8_t_ast;
                    }
                    auto job = make_typecheck_job(bool_type, unop_ast->operand);
                    do_typecheck_job(ctx, job);
                } // fall through
                case 1: {
                    new_stage = 1;
                    if(!type_resolved(unop_ast->operand))
                    {
                        goto finish;
                    }
                    // TODO: make sure this is a concrete type (instead of boollike)
                    set_resolved_type(unop_ast, unop_ast->operand->resolved_type);
                    new_stage = 2;
                } // fall through
            }
        } break;
    }
    finish:
    
    if(new_stage < done_stage)
    {
        auto job = make_typecheck_job(type, unop_ast, new_stage);
        array_add(ctx->waiting_jobs, job);
    }
    return new_stage != stage;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x1d0, %rsp            # imm = 0x1D0
movq %rdi, -0x8(%rbp)
movl %esi, -0xc(%rbp)
movq %rdx, -0x18(%rbp)
movq %rcx, -0x20(%rbp)
movl $0x0, -0x24(%rbp)
movq -0x20(%rbp), %rax
movq 0x20(%rax), %rax
movq %rax, -0x1a8(%rbp)
subq $0x4, %rax
ja 0x8c37
movq -0x1a8(%rbp), %rax
leaq 0x10cc4(%rip), %rcx     # 0x1914c
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movl $0x3, -0x28(%rbp)
movl -0xc(%rbp), %eax
movl %eax, -0x1ac(%rbp)
testl %eax, %eax
je 0x84c8
jmp 0x84a7
movl -0x1ac(%rbp), %eax
subl $0x1, %eax
je 0x84fa
jmp 0x84b4
movl -0x1ac(%rbp), %eax
subl $0x2, %eax
je 0x8581
jmp 0x85ba
cmpq $0x0, -0x18(%rbp)
je 0x84f8
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rdx
leaq 0x17a8a(%rip), %rsi     # 0x1ff68
movl $0x1, %ecx
callq 0x9240
movl %eax, -0x2c(%rbp)
cmpl $0x0, -0x2c(%rbp)
je 0x84f6
jmp 0x8c39
jmp 0x84f8
jmp 0x84fa
movl $0x1, -0x24(%rbp)
movq -0x20(%rbp), %rax
movzwl 0x2(%rax), %ecx
orl $0x20, %ecx
movw %cx, 0x2(%rax)
cmpq $0x0, -0x18(%rbp)
je 0x8521
movq -0x18(%rbp), %rax
movq %rax, -0x38(%rbp)
jmp 0x852c
leaq 0x17a40(%rip), %rax     # 0x1ff68
movq %rax, -0x38(%rbp)
movq -0x38(%rbp), %rsi
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdx
leaq -0x50(%rbp), %rdi
xorl %ecx, %ecx
callq 0x4a80
movq -0x8(%rbp), %rdi
movq -0x50(%rbp), %rax
movq %rax, -0x68(%rbp)
movq -0x48(%rbp), %rax
movq %rax, -0x60(%rbp)
movq -0x40(%rbp), %rax
movq %rax, -0x58(%rbp)
leaq -0x68(%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
movl $0x2, -0x24(%rbp)
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdi
callq 0x5600
testb $0x1, %al
jne 0x859e
jmp 0x8c39
movq -0x20(%rbp), %rdi
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rax
movq 0x18(%rax), %rsi
callq 0x3be0
movl $0x3, -0x24(%rbp)
jmp 0x8c37
movl $0x2, -0x28(%rbp)
movl -0xc(%rbp), %eax
movl %eax, -0x1b0(%rbp)
testl %eax, %eax
je 0x85e9
jmp 0x85d5
movl -0x1b0(%rbp), %eax
subl $0x1, %eax
je 0x86d9
jmp 0x8812
movq -0x20(%rbp), %rax
movzwl 0x2(%rax), %ecx
orl $0x10, %ecx
movw %cx, 0x2(%rax)
movq $0x0, -0x70(%rbp)
cmpq $0x0, -0x18(%rbp)
je 0x866f
movq -0x8(%rbp), %rax
movq (%rax), %rdi
movl $0x30, %esi
callq 0xcd80
movq %rax, %rdi
movl $0x10, %esi
xorl %eax, %eax
movl %eax, %ecx
movq %rcx, %rdx
callq 0x3c10
movq %rax, -0x78(%rbp)
movq -0x78(%rbp), %rax
movzwl 0x2(%rax), %ecx
orl $0x1, %ecx
movw %cx, 0x2(%rax)
movq -0x78(%rbp), %rdi
leaq 0x178a6(%rip), %rsi     # 0x1fef0
callq 0x3be0
movq -0x78(%rbp), %rax
movq $0x3, 0x20(%rax)
movq -0x18(%rbp), %rcx
movq -0x78(%rbp), %rax
movq %rcx, 0x28(%rax)
movq -0x78(%rbp), %rax
movq %rax, -0x70(%rbp)
movq -0x70(%rbp), %rsi
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdx
leaq -0x90(%rbp), %rdi
xorl %ecx, %ecx
callq 0x4a80
movq -0x8(%rbp), %rdi
movq -0x90(%rbp), %rax
movq %rax, -0xa8(%rbp)
movq -0x88(%rbp), %rax
movq %rax, -0xa0(%rbp)
movq -0x80(%rbp), %rax
movq %rax, -0x98(%rbp)
leaq -0xa8(%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
movl $0x1, -0x24(%rbp)
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdi
callq 0x5600
testb $0x1, %al
jne 0x86f6
jmp 0x8c39
movq -0x8(%rbp), %rdi
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rax
movq 0x18(%rax), %rsi
leaq -0xb0(%rbp), %rdx
callq 0x9040
movl %eax, -0xb4(%rbp)
cmpl $0x0, -0xb4(%rbp)
je 0x8726
jmp 0x8c39
movq -0xb0(%rbp), %rax
cmpw $0x10, (%rax)
je 0x8754
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rsi
leaq 0x122f5(%rip), %rdi     # 0x1aa37
callq 0x4950
movq -0x8(%rbp), %rax
movb $0x0, 0x10(%rax)
jmp 0x8c39
movq -0xb0(%rbp), %rax
movq %rax, -0xc0(%rbp)
movq -0xc0(%rbp), %rax
leaq 0x17780(%rip), %rcx     # 0x1fef0
cmpq %rcx, 0x18(%rax)
jne 0x8778
jmp 0x8797
leaq 0x12496(%rip), %rdi     # 0x1ac15
leaq 0x11766(%rip), %rsi     # 0x19eec
movl $0x522, %edx            # imm = 0x522
leaq 0x124ae(%rip), %rcx     # 0x1ac40
callq 0x3080
movq -0xc0(%rbp), %rax
cmpq $0x3, 0x20(%rax)
jne 0x87a7
jmp 0x87c6
leaq 0x124d9(%rip), %rdi     # 0x1ac87
leaq 0x11737(%rip), %rsi     # 0x19eec
movl $0x523, %edx            # imm = 0x523
leaq 0x1247f(%rip), %rcx     # 0x1ac40
callq 0x3080
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rsi
movq -0xc0(%rbp), %rax
movq 0x28(%rax), %rdx
movl $0x1, %ecx
callq 0x9240
movl %eax, -0xb4(%rbp)
cmpl $0x0, -0xb4(%rbp)
je 0x87f7
jmp 0x8c39
movq -0x20(%rbp), %rdi
movq -0xc0(%rbp), %rax
movq 0x28(%rax), %rsi
callq 0x3be0
movl $0x2, -0x24(%rbp)
jmp 0x8c37
movl $0x2, -0x28(%rbp)
cmpq $0x0, -0x18(%rbp)
je 0x88f0
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rsi
leaq -0xd0(%rbp), %rdx
callq 0x9040
movl %eax, -0xd4(%rbp)
cmpl $0x0, -0xd4(%rbp)
je 0x8851
jmp 0x8c39
movq -0xd0(%rbp), %rax
cmpw $0x10, (%rax)
jne 0x88af
movq -0xd0(%rbp), %rax
movq %rax, -0xe0(%rbp)
movq -0xe0(%rbp), %rax
cmpq $0x3, 0x20(%rax)
jne 0x887c
jmp 0x889b
leaq 0x1242c(%rip), %rdi     # 0x1acaf
leaq 0x11662(%rip), %rsi     # 0x19eec
movl $0x53c, %edx            # imm = 0x53C
leaq 0x123aa(%rip), %rcx     # 0x1ac40
callq 0x3080
movq -0xe0(%rbp), %rax
movq 0x28(%rax), %rax
movq %rax, -0xc8(%rbp)
jmp 0x88ee
leaq 0x1763a(%rip), %rax     # 0x1fef0
cmpq %rax, -0xd0(%rbp)
jne 0x88cf
leaq 0x1762a(%rip), %rax     # 0x1fef0
movq %rax, -0xc8(%rbp)
jmp 0x88ec
movq -0x20(%rbp), %rsi
leaq 0x123fe(%rip), %rdi     # 0x1acd8
callq 0x4950
movq -0x8(%rbp), %rax
movb $0x0, 0x10(%rax)
jmp 0x8c39
jmp 0x88ee
jmp 0x88fb
movq $0x0, -0xc8(%rbp)
movl -0xc(%rbp), %eax
movl %eax, -0x1b4(%rbp)
testl %eax, %eax
je 0x891a
jmp 0x890a
movl -0x1b4(%rbp), %eax
subl $0x1, %eax
je 0x898a
jmp 0x8b11
movq -0xc8(%rbp), %rsi
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdx
leaq -0xf8(%rbp), %rdi
xorl %ecx, %ecx
callq 0x4a80
movq -0x8(%rbp), %rdi
movq -0xf8(%rbp), %rax
movq %rax, -0x110(%rbp)
movq -0xf0(%rbp), %rax
movq %rax, -0x108(%rbp)
movq -0xe8(%rbp), %rax
movq %rax, -0x100(%rbp)
leaq -0x110(%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
movl $0x1, -0x24(%rbp)
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdi
callq 0x5600
testb $0x1, %al
jne 0x89a7
jmp 0x8c39
leaq 0x17542(%rip), %rax     # 0x1fef0
cmpq %rax, -0xc8(%rbp)
je 0x8a3e
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rax
movzwl 0x2(%rax), %eax
andl $0x30, %eax
movw %ax, -0x112(%rbp)
movzwl -0x112(%rbp), %eax
cmpl $0x0, %eax
jne 0x89e2
jmp 0x8c39
movzwl -0x112(%rbp), %eax
cmpl $0x20, %eax
jne 0x8a0b
movq -0x20(%rbp), %rsi
leaq 0x122f8(%rip), %rdi     # 0x1acf1
callq 0x4950
movq -0x8(%rbp), %rax
movb $0x0, 0x10(%rax)
jmp 0x8c39
movzwl -0x112(%rbp), %eax
cmpl $0x10, %eax
jne 0x8a19
jmp 0x8a38
leaq 0x11e49(%rip), %rdi     # 0x1a869
leaq 0x114c5(%rip), %rsi     # 0x19eec
movl $0x56f, %edx            # imm = 0x56F
leaq 0x1220d(%rip), %rcx     # 0x1ac40
callq 0x3080
jmp 0x8a3a
jmp 0x8a3c
jmp 0x8a3e
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rax
leaq 0x174a3(%rip), %rcx     # 0x1fef0
cmpq %rcx, 0x18(%rax)
jne 0x8a68
movq -0x20(%rbp), %rdi
leaq 0x17492(%rip), %rsi     # 0x1fef0
callq 0x3be0
jmp 0x8b0a
cmpq $0x0, -0x18(%rbp)
je 0x8a81
movq -0x20(%rbp), %rdi
movq -0x18(%rbp), %rsi
callq 0x3be0
jmp 0x8b08
movq -0x8(%rbp), %rax
movq (%rax), %rdi
movl $0x30, %esi
callq 0xcd80
movq %rax, %rdi
movl $0x10, %esi
xorl %eax, %eax
movl %eax, %ecx
movq %rcx, %rdx
callq 0x3c10
movq %rax, -0x120(%rbp)
movq -0x120(%rbp), %rax
movzwl 0x2(%rax), %ecx
orl $0x1, %ecx
movw %cx, 0x2(%rax)
movq -0x120(%rbp), %rdi
leaq 0x17423(%rip), %rsi     # 0x1fef0
callq 0x3be0
movq -0x120(%rbp), %rax
movq $0x3, 0x20(%rax)
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rax
movq 0x18(%rax), %rcx
movq -0x120(%rbp), %rax
movq %rcx, 0x28(%rax)
movq -0x20(%rbp), %rdi
movq -0x120(%rbp), %rsi
callq 0x3be0
jmp 0x8b0a
movl $0x2, -0x24(%rbp)
jmp 0x8c37
movl $0x2, -0x28(%rbp)
movl -0xc(%rbp), %eax
movl %eax, -0x1b8(%rbp)
testl %eax, %eax
je 0x8b40
jmp 0x8b2c
movl -0x1b8(%rbp), %eax
subl $0x1, %eax
je 0x8bff
jmp 0x8c35
cmpq $0x0, -0x18(%rbp)
je 0x8b81
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rdx
leaq 0x1743a(%rip), %rsi     # 0x1ff90
movl $0x1, %ecx
callq 0x9240
movl %eax, -0x12c(%rbp)
cmpl $0x0, -0x12c(%rbp)
je 0x8b74
jmp 0x8c39
movq -0x18(%rbp), %rax
movq %rax, -0x128(%rbp)
jmp 0x8b8f
leaq 0x17250(%rip), %rax     # 0x1fdd8
movq %rax, -0x128(%rbp)
movq -0x128(%rbp), %rsi
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdx
leaq -0x148(%rbp), %rdi
xorl %ecx, %ecx
callq 0x4a80
movq -0x8(%rbp), %rdi
movq -0x148(%rbp), %rax
movq %rax, -0x160(%rbp)
movq -0x140(%rbp), %rax
movq %rax, -0x158(%rbp)
movq -0x138(%rbp), %rax
movq %rax, -0x150(%rbp)
leaq -0x160(%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
movl $0x1, -0x24(%rbp)
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rdi
callq 0x5600
testb $0x1, %al
jne 0x8c19
jmp 0x8c39
movq -0x20(%rbp), %rdi
movq -0x20(%rbp), %rax
movq 0x28(%rax), %rax
movq 0x18(%rax), %rsi
callq 0x3be0
movl $0x2, -0x24(%rbp)
jmp 0x8c37
jmp 0x8c39
movl -0x24(%rbp), %eax
cmpl -0x28(%rbp), %eax
jae 0x8cdd
movq -0x18(%rbp), %rsi
movq -0x20(%rbp), %rdx
movl -0x24(%rbp), %ecx
leaq -0x178(%rbp), %rdi
callq 0x4a80
movq -0x8(%rbp), %rax
movq 0x8(%rax), %rdi
movq -0x178(%rbp), %rax
movq %rax, -0x190(%rbp)
movq -0x170(%rbp), %rax
movq %rax, -0x188(%rbp)
movq -0x168(%rbp), %rax
movq %rax, -0x180(%rbp)
movq 0x16feb(%rip), %rax     # 0x1fc80
movq %rax, -0x1a0(%rbp)
movq 0x16fe5(%rip), %rax     # 0x1fc88
movq %rax, -0x198(%rbp)
movq -0x1a0(%rbp), %rsi
movq -0x198(%rbp), %rdx
leaq -0x190(%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 $0x1d0, %rsp            # imm = 0x1D0
popq %rbp
retq
nopw %cs:(%rax,%rax)