// File: /workspace/github_cpp_makefile_star2_O2/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
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x18, %rsp
movl %ecx, %ebp
movq %rdx, %r14
testq %rsi, %rsi
sete %cl
testq %rdx, %rdx
sete %dl
xorl %eax, %eax
orb %cl, %dl
jne 0x6706
movq %rdi, %rbx
leaq 0x10(%rsp), %rdx
callq 0x636c
testl %eax, %eax
jne 0x6706
leaq 0x8(%rsp), %rdx
movq %rbx, %rdi
movq %r14, %rsi
callq 0x636c
testl %eax, %eax
jne 0x6706
movq 0x10(%rsp), %r14
movzwl (%r14), %eax
pushq $0x2
popq %r12
cmpq $0x13, %rax
ja 0x6703
leaq 0x7cfa(%rip), %rcx      # 0xe1e4
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq 0x8(%rsp), %r15
cmpw $0x8, (%r15)
jne 0x6651
movq 0x20(%r14), %rax
cmpq 0x20(%r15), %rax
jne 0x6693
movq 0x30(%r14), %rax
cmpq 0x30(%r15), %rax
jne 0x66e8
xorl %r13d, %r13d
movzbl %bpl, %ebp
cmpq 0x20(%r14), %r13
jae 0x6715
movq 0x28(%r14), %rax
movq (%rax,%r13,8), %rsi
movq 0x28(%r15), %rax
movq (%rax,%r13,8), %rdx
movq %rbx, %rdi
movl %ebp, %ecx
callq 0x6476
incq %r13
testl %eax, %eax
je 0x6526
jmp 0x6706
movq 0x8(%rsp), %rdx
cmpq %rdx, %r14
sete %al
xorb $0x1, %bpl
orb %al, %bpl
jne 0x6703
leaq 0x89d0(%rip), %rdi      # 0xef45
jmp 0x6649
movq 0x8(%rsp), %rdx
cmpw $0x10, (%rdx)
jne 0x6627
cmpq $0x3, 0x20(%r14)
jne 0x6786
cmpq $0x3, 0x20(%rdx)
jne 0x67a5
movq 0x28(%r14), %rsi
movq 0x28(%rdx), %rdx
movzbl %bpl, %ecx
movq %rbx, %rdi
callq 0x6476
jmp 0x6706
movq 0x8(%rsp), %rdx
cmpq %rdx, %r14
sete %al
xorb $0x1, %bpl
orb %al, %bpl
jne 0x6703
leaq 0x895c(%rip), %rdi      # 0xef33
jmp 0x6649
movq 0x8(%rsp), %rdx
cmpw $0x11, (%rdx)
jne 0x6639
movq 0x20(%r14), %rcx
movq 0x20(%rdx), %rsi
movl %ecx, %eax
andl $0x7, %eax
movl %esi, %edi
andl $0x7, %edi
testb $0x40, %cl
je 0x6666
movl %esi, %r8d
andl $0x40, %r8d
je 0x6666
testq %rax, %rax
setne %cl
cmpl %eax, %edi
seta %al
andb %cl, %al
andb %al, %bpl
cmpb $0x1, %bpl
jne 0x66a1
leaq 0x89f6(%rip), %rdi      # 0xf01b
jmp 0x6649
testb %bpl, %bpl
je 0x6703
leaq 0x8922(%rip), %rdi      # 0xef59
jmp 0x6649
testb %bpl, %bpl
je 0x6703
leaq 0x89b2(%rip), %rdi      # 0xeffb
movq %r14, %rsi
jmp 0x66fa
testb %bpl, %bpl
je 0x6703
leaq 0x8842(%rip), %rdi      # 0xeea3
jmp 0x66f4
testb $0x20, %cl
je 0x66a8
movl %esi, %r8d
andl $0x20, %r8d
je 0x66a8
testq %rax, %rax
setne %cl
cmpl %eax, %edi
seta %al
andb %cl, %al
andb %al, %bpl
cmpb $0x1, %bpl
jne 0x66a1
leaq 0x899e(%rip), %rdi      # 0xf02f
jmp 0x6649
testb %bpl, %bpl
je 0x6703
leaq 0x8827(%rip), %rdi      # 0xeec6
jmp 0x66f4
movzbl %al, %eax
addl %eax, %eax
jmp 0x6706
xorl %eax, %eax
testb %cl, %cl
jns 0x66b8
movl %esi, %edi
andl $0x80, %edi
jne 0x6706
movq %rcx, %rdi
orq %rsi, %rdi
je 0x6706
movl $0x100, %eax            # imm = 0x100
xorq %rax, %rcx
xorq %rax, %rsi
xorl %eax, %eax
orq %rcx, %rsi
setne %cl
andb %cl, %bpl
cmpb $0x1, %bpl
jne 0x6741
leaq 0x8962(%rip), %rdi      # 0xf045
jmp 0x6649
testb %bpl, %bpl
je 0x6703
leaq 0x8807(%rip), %rdi      # 0xeefb
movq %r14, %rsi
movq %r15, %rdx
callq 0x3f5b
movb $0x0, 0x10(%rbx)
movl %r12d, %eax
addq $0x18, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
xorl %r13d, %r13d
cmpq 0x30(%r14), %r13
jae 0x6703
movq 0x38(%r14), %rax
movq (%rax,%r13,8), %rsi
movq 0x38(%r15), %rax
movq (%rax,%r13,8), %rdx
movq %rbx, %rdi
movl %ebp, %ecx
callq 0x6476
incq %r13
testl %eax, %eax
je 0x6718
jmp 0x6706
movb %cl, %al
jmp 0x66a4
leaq 0x8551(%rip), %rdi      # 0xeca0
leaq 0x8614(%rip), %rsi      # 0xed6a
leaq 0x883d(%rip), %rcx      # 0xef9a
movl $0x7df, %edx            # imm = 0x7DF
callq 0x3080
leaq 0x8532(%rip), %rdi      # 0xeca0
leaq 0x85f5(%rip), %rsi      # 0xed6a
leaq 0x881e(%rip), %rcx      # 0xef9a
movl $0x7e2, %edx            # imm = 0x7E2
callq 0x3080
leaq 0x87e8(%rip), %rdi      # 0xef75
leaq 0x85d6(%rip), %rsi      # 0xed6a
leaq 0x87ff(%rip), %rcx      # 0xef9a
movl $0x779, %edx            # imm = 0x779
callq 0x3080
leaq 0x882a(%rip), %rdi      # 0xefd6
leaq 0x85b7(%rip), %rsi      # 0xed6a
leaq 0x87e0(%rip), %rcx      # 0xef9a
movl $0x77a, %edx            # imm = 0x77A
callq 0x3080