// File: cphinton42[P]hinton-language/src/check.cpp
// Function: types_match(Context*, Expr_AST*, Expr_AST*, bool)
Status types_match(Context *ctx, Expr_AST *t1, Expr_AST *t2, bool report)
{
    if(!t1 || !t2)
    {
        return Status::good;
    }
    
    Expr_AST *lhs, *rhs;
    Status status = reduce_type(ctx, t1, &lhs);
    if(status != Status::good)
    {
        return status;
    }
    status = reduce_type(ctx, t2, &rhs);
    if(status != Status::good)
    {
        return status;
    }
    
    switch(lhs->type)
    {
        case AST_Type::function_type_ast: {
            if(rhs->type != AST_Type::function_type_ast)
            {
                if(report)
                {
                    report_error("Function type vs non-function type", lhs, rhs);
                    ctx->success = false;
                }
                return Status::bad;
            }
            Function_Type_AST *lhs_func = static_cast<Function_Type_AST*>(lhs);
            Function_Type_AST *rhs_func = static_cast<Function_Type_AST*>(rhs);
            
            if(lhs_func->parameter_types.count != rhs_func->parameter_types.count)
            {
                if(report)
                {
                    report_error("Function types do not have same number of parameters", lhs, rhs);
                    ctx->success = false;
                }
                return Status::bad;
            }
            if(lhs_func->return_types.count != rhs_func->return_types.count)
            {
                if(report)
                {
                    report_error("Function types do not have same number of return values", lhs, rhs);
                    ctx->success = false;
                }
                return Status::bad;
            }
            
            for(u64 i = 0; i < lhs_func->parameter_types.count; ++i)
            {
                status = types_match(ctx, lhs_func->parameter_types[i], rhs_func->parameter_types[i], report);
                if(status != Status::good)
                {
                    return status;
                }
            }
            for(u64 i = 0; i < lhs_func->return_types.count; ++i)
            {
                status = types_match(ctx, lhs_func->return_types[i], rhs_func->return_types[i], report);
                if(status != Status::good)
                {
                    return status;
                }
            }
        } break;
        case AST_Type::enum_ast: {
            if(lhs != rhs)
            {
                if(report)
                {
                    report_error("Mismatching enums", lhs, rhs);
                    ctx->success = false;
                }
                return Status::bad;
            }
        } break;
        case AST_Type::struct_ast: {
            if(lhs != rhs)
            {
                if(report)
                {
                    report_error("Mismatching structs", lhs, rhs);
                    ctx->success = false;
                }
                return Status::bad;
            }
        } break;
        case AST_Type::unary_ast: {
            if(rhs->type != AST_Type::unary_ast)
            {
                if(report)
                {
                    report_error("Pointer vs non-pointer type", lhs, rhs);
                    ctx->success = false;
                }
                return Status::bad;
            }
            Unary_Operator_AST *lhs_unary = static_cast<Unary_Operator_AST*>(lhs);
            Unary_Operator_AST *rhs_unary = static_cast<Unary_Operator_AST*>(rhs);
            
            // Assert since resolved_type is type_t_ast
            assert(lhs_unary->op == Unary_Operator::ref);
            assert(rhs_unary->op == Unary_Operator::ref);
            
            return types_match(ctx, lhs_unary->operand, rhs_unary->operand, report);
        } break;
        case AST_Type::primitive_ast: {
            if(rhs->type != AST_Type::primitive_ast)
            {
                if(report)
                {
                    report_error("Primitive vs non-primitive type", lhs, rhs);
                    ctx->success = false;
                }
                return Status::bad;
            }
            Primitive_AST *lhs_prim_ast = static_cast<Primitive_AST*>(lhs);
            Primitive_AST *rhs_prim_ast = static_cast<Primitive_AST*>(rhs);
            
            u64 lhs_prim = lhs_prim_ast->primitive;
            u64 rhs_prim = rhs_prim_ast->primitive;
            u64 lhs_size = lhs_prim & PRIM_SIZE_MASK;
            u64 rhs_size = rhs_prim & PRIM_SIZE_MASK;
            
            if((lhs_prim & PRIM_FLOATLIKE) && (rhs_prim & PRIM_FLOATLIKE))
            {
                if(lhs_size != rhs_size && lhs_size != 0 && rhs_size != 0)
                {
                    if(rhs_size > lhs_size)
                    {
                        if(report)
                        {
                            report_error("Float size mismatch", lhs, rhs);
                            ctx->success = false;
                        }
                        return Status::bad;
                    }
                    else
                    {
                        return Status::good;
                    }
                }
                else
                {
                    return Status::good;
                }
            }
            else if((lhs_prim & PRIM_INTLIKE) && (rhs_prim & PRIM_INTLIKE))
            {
                if(lhs_size != rhs_size && lhs_size != 0 && rhs_size != 0)
                {
                    if(rhs_size > lhs_size)
                    {
                        if(report)
                        {
                            report_error("Integer size mismatch", lhs, rhs);
                            ctx->success = false;
                        }
                        return Status::bad;
                    }
                    else
                    {
                        return Status::good;
                    }
                }
                else
                {
                    return Status::good;
                }
            }
            else if((lhs_prim & PRIM_BOOLLIKE) &&
                    (rhs_prim & PRIM_BOOLLIKE))
            {
                return Status::good;
            }
            else if(lhs_prim == PRIM_VOID && rhs_prim == PRIM_VOID)
            {
                return Status::good;
            }
            else if(lhs_prim == PRIM_TYPE && rhs_prim == PRIM_TYPE)
            {
                return Status::good;
            }
            else
            {
                if(report)
                {
                    report_error("Primitive type mismatch", lhs, rhs);
                    ctx->success = false;
                }
                return Status::bad;
            }
        } break;
        
        case AST_Type::ident_ast: // TODO: this could be a type in the future
        case AST_Type::function_ast:
        case AST_Type::function_call_ast:
        case AST_Type::access_ast: // Note: would have been reduced
        case AST_Type::binary_operator_ast:
        case AST_Type::number_ast:
        case AST_Type::string_ast:
        case AST_Type::bool_ast: {
            // Not type
            assert(false);
        } break;
        case_non_exprs: {
            assert(false);
        } break;
    }
    
    return Status::bad;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xd0, %rsp
movb %cl, %al
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movq %rdx, -0x20(%rbp)
andb $0x1, %al
movb %al, -0x21(%rbp)
cmpq $0x0, -0x18(%rbp)
je 0x926c
cmpq $0x0, -0x20(%rbp)
jne 0x9278
movl $0x0, -0x4(%rbp)
jmp 0x98d4
movq -0x10(%rbp), %rdi
movq -0x18(%rbp), %rsi
leaq -0x30(%rbp), %rdx
callq 0x9040
movl %eax, -0x3c(%rbp)
cmpl $0x0, -0x3c(%rbp)
je 0x929d
movl -0x3c(%rbp), %eax
movl %eax, -0x4(%rbp)
jmp 0x98d4
movq -0x10(%rbp), %rdi
movq -0x20(%rbp), %rsi
leaq -0x38(%rbp), %rdx
callq 0x9040
movl %eax, -0x3c(%rbp)
cmpl $0x0, -0x3c(%rbp)
je 0x92c2
movl -0x3c(%rbp), %eax
movl %eax, -0x4(%rbp)
jmp 0x98d4
movq -0x30(%rbp), %rax
movzwl (%rax), %eax
movq %rax, -0xa8(%rbp)
subq $0x13, %rax
ja 0x98cd
movq -0xa8(%rbp), %rax
leaq 0xfedc(%rip), %rcx      # 0x191c4
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq -0x38(%rbp), %rax
cmpw $0x8, (%rax)
je 0x9329
testb $0x1, -0x21(%rbp)
je 0x931d
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x10d15(%rip), %rdi     # 0x1a025
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
movq -0x30(%rbp), %rax
movq %rax, -0x48(%rbp)
movq -0x38(%rbp), %rax
movq %rax, -0x50(%rbp)
movq -0x48(%rbp), %rax
movq 0x20(%rax), %rax
movq -0x50(%rbp), %rcx
cmpq 0x20(%rcx), %rax
je 0x9379
testb $0x1, -0x21(%rbp)
je 0x936d
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x10ce8(%rip), %rdi     # 0x1a048
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
movq -0x48(%rbp), %rax
movq 0x30(%rax), %rax
movq -0x50(%rbp), %rcx
cmpq 0x30(%rcx), %rax
je 0x93b9
testb $0x1, -0x21(%rbp)
je 0x93ad
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x10cdd(%rip), %rdi     # 0x1a07d
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
movq $0x0, -0x58(%rbp)
movq -0x58(%rbp), %rax
movq -0x48(%rbp), %rcx
cmpq 0x20(%rcx), %rax
jae 0x944b
movq -0x10(%rbp), %rax
movq %rax, -0xb8(%rbp)
movq -0x48(%rbp), %rdi
addq $0x20, %rdi
movq -0x58(%rbp), %rsi
callq 0x17050
movq (%rax), %rax
movq %rax, -0xb0(%rbp)
movq -0x50(%rbp), %rdi
addq $0x20, %rdi
movq -0x58(%rbp), %rsi
callq 0x17050
movq -0xb8(%rbp), %rdi
movq -0xb0(%rbp), %rsi
movq (%rax), %rdx
movb -0x21(%rbp), %al
andb $0x1, %al
movzbl %al, %ecx
callq 0x9240
movl %eax, -0x3c(%rbp)
cmpl $0x0, -0x3c(%rbp)
je 0x9438
movl -0x3c(%rbp), %eax
movl %eax, -0x4(%rbp)
jmp 0x98d4
jmp 0x943a
movq -0x58(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x58(%rbp)
jmp 0x93c1
movq $0x0, -0x60(%rbp)
movq -0x60(%rbp), %rax
movq -0x48(%rbp), %rcx
cmpq 0x30(%rcx), %rax
jae 0x94dd
movq -0x10(%rbp), %rax
movq %rax, -0xc8(%rbp)
movq -0x48(%rbp), %rdi
addq $0x30, %rdi
movq -0x60(%rbp), %rsi
callq 0x17050
movq (%rax), %rax
movq %rax, -0xc0(%rbp)
movq -0x50(%rbp), %rdi
addq $0x30, %rdi
movq -0x60(%rbp), %rsi
callq 0x17050
movq -0xc8(%rbp), %rdi
movq -0xc0(%rbp), %rsi
movq (%rax), %rdx
movb -0x21(%rbp), %al
andb $0x1, %al
movzbl %al, %ecx
callq 0x9240
movl %eax, -0x3c(%rbp)
cmpl $0x0, -0x3c(%rbp)
je 0x94ca
movl -0x3c(%rbp), %eax
movl %eax, -0x4(%rbp)
jmp 0x98d4
jmp 0x94cc
movq -0x60(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x60(%rbp)
jmp 0x9453
jmp 0x98cd
movq -0x30(%rbp), %rax
cmpq -0x38(%rbp), %rax
je 0x951a
testb $0x1, -0x21(%rbp)
je 0x950e
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x10bb4(%rip), %rdi     # 0x1a0b5
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
jmp 0x98cd
movq -0x30(%rbp), %rax
cmpq -0x38(%rbp), %rax
je 0x9557
testb $0x1, -0x21(%rbp)
je 0x954b
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x10b89(%rip), %rdi     # 0x1a0c7
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
jmp 0x98cd
movq -0x38(%rbp), %rax
cmpw $0x10, (%rax)
je 0x9594
testb $0x1, -0x21(%rbp)
je 0x9588
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x10b60(%rip), %rdi     # 0x1a0db
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
movq -0x30(%rbp), %rax
movq %rax, -0x68(%rbp)
movq -0x38(%rbp), %rax
movq %rax, -0x70(%rbp)
movq -0x68(%rbp), %rax
cmpq $0x3, 0x20(%rax)
jne 0x95b1
jmp 0x95d0
leaq 0x10b3f(%rip), %rdi     # 0x1a0f7
leaq 0x1092d(%rip), %rsi     # 0x19eec
movl $0x779, %edx            # imm = 0x779
leaq 0x10b51(%rip), %rcx     # 0x1a11c
callq 0x3080
movq -0x70(%rbp), %rax
cmpq $0x3, 0x20(%rax)
jne 0x95dd
jmp 0x95fc
leaq 0x10b74(%rip), %rdi     # 0x1a158
leaq 0x10901(%rip), %rsi     # 0x19eec
movl $0x77a, %edx            # imm = 0x77A
leaq 0x10b25(%rip), %rcx     # 0x1a11c
callq 0x3080
movq -0x10(%rbp), %rdi
movq -0x68(%rbp), %rax
movq 0x28(%rax), %rsi
movq -0x70(%rbp), %rax
movq 0x28(%rax), %rdx
movb -0x21(%rbp), %al
andb $0x1, %al
movzbl %al, %ecx
callq 0x9240
movl %eax, -0x4(%rbp)
jmp 0x98d4
movq -0x38(%rbp), %rax
cmpw $0x11, (%rax)
je 0x965d
testb $0x1, -0x21(%rbp)
je 0x9651
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x10b39(%rip), %rdi     # 0x1a17d
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
movq -0x30(%rbp), %rax
movq %rax, -0x78(%rbp)
movq -0x38(%rbp), %rax
movq %rax, -0x80(%rbp)
movq -0x78(%rbp), %rax
movq 0x20(%rax), %rax
movq %rax, -0x88(%rbp)
movq -0x80(%rbp), %rax
movq 0x20(%rax), %rax
movq %rax, -0x90(%rbp)
movq -0x88(%rbp), %rax
andq $0x7, %rax
movq %rax, -0x98(%rbp)
movq -0x90(%rbp), %rax
andq $0x7, %rax
movq %rax, -0xa0(%rbp)
movq -0x88(%rbp), %rax
andq $0x40, %rax
cmpq $0x0, %rax
je 0x974f
movq -0x90(%rbp), %rax
andq $0x40, %rax
cmpq $0x0, %rax
je 0x974f
movq -0x98(%rbp), %rax
cmpq -0xa0(%rbp), %rax
je 0x9743
cmpq $0x0, -0x98(%rbp)
je 0x9743
cmpq $0x0, -0xa0(%rbp)
je 0x9743
movq -0xa0(%rbp), %rax
cmpq -0x98(%rbp), %rax
jbe 0x9737
testb $0x1, -0x21(%rbp)
je 0x972b
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x10a7f(%rip), %rdi     # 0x1a19d
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
movl $0x0, -0x4(%rbp)
jmp 0x98d4
movl $0x0, -0x4(%rbp)
jmp 0x98d4
movq -0x88(%rbp), %rax
andq $0x20, %rax
cmpq $0x0, %rax
je 0x97ef
movq -0x90(%rbp), %rax
andq $0x20, %rax
cmpq $0x0, %rax
je 0x97ef
movq -0x98(%rbp), %rax
cmpq -0xa0(%rbp), %rax
je 0x97e3
cmpq $0x0, -0x98(%rbp)
je 0x97e3
cmpq $0x0, -0xa0(%rbp)
je 0x97e3
movq -0xa0(%rbp), %rax
cmpq -0x98(%rbp), %rax
jbe 0x97d7
testb $0x1, -0x21(%rbp)
je 0x97cb
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x109f3(%rip), %rdi     # 0x1a1b1
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
movl $0x0, -0x4(%rbp)
jmp 0x98d4
movl $0x0, -0x4(%rbp)
jmp 0x98d4
movq -0x88(%rbp), %rax
andq $0x80, %rax
cmpq $0x0, %rax
je 0x9821
movq -0x90(%rbp), %rax
andq $0x80, %rax
cmpq $0x0, %rax
je 0x9821
movl $0x0, -0x4(%rbp)
jmp 0x98d4
cmpq $0x0, -0x88(%rbp)
jne 0x9841
cmpq $0x0, -0x90(%rbp)
jne 0x9841
movl $0x0, -0x4(%rbp)
jmp 0x98d4
cmpq $0x100, -0x88(%rbp) # imm = 0x100
jne 0x9864
cmpq $0x100, -0x90(%rbp) # imm = 0x100
jne 0x9864
movl $0x0, -0x4(%rbp)
jmp 0x98d4
testb $0x1, -0x21(%rbp)
je 0x9886
movq -0x30(%rbp), %rsi
movq -0x38(%rbp), %rdx
leaq 0x1094e(%rip), %rdi     # 0x1a1c7
callq 0x4a30
movq -0x10(%rbp), %rax
movb $0x0, 0x10(%rax)
movl $0x2, -0x4(%rbp)
jmp 0x98d4
leaq 0x1058c(%rip), %rdi     # 0x19e22
leaq 0x1064f(%rip), %rsi     # 0x19eec
movl $0x7df, %edx            # imm = 0x7DF
leaq 0x10873(%rip), %rcx     # 0x1a11c
callq 0x3080
leaq 0x1056d(%rip), %rdi     # 0x19e22
leaq 0x10630(%rip), %rsi     # 0x19eec
movl $0x7e2, %edx            # imm = 0x7E2
leaq 0x10854(%rip), %rcx     # 0x1a11c
callq 0x3080
movl $0x2, -0x4(%rbp)
movl -0x4(%rbp), %eax
addq $0xd0, %rsp
popq %rbp
retq