// File: /workspace/github_c_makefile_star2_O1/hsjoihs[P]c-compiler/typecheck_expression.c
// Function: typecheck_expression
struct Expr typecheck_expression(struct AnalyzerState *ptr_ps,
                                 const struct UntypedExpr *ref_uexpr)
{
	const struct UntypedExpr uexpr = *ref_uexpr;
	switch (uexpr.category) {
	case AMPERSAND_DOT: {
		struct Expr struct_or_union_expr =
		    typecheck_expression(ptr_ps, uexpr.ptr1);
		const char *ident_after_dot = uexpr.ident_after_dot;
		if (!is_struct_or_union(&struct_or_union_expr.details.type)) {
			fprintf(stderr, "member is requested but the left operand is "
			                "neither a struct nor a union\n");
			exit(EXIT_FAILURE);
		}

		const char *tag =
		    struct_or_union_expr.details.type.s.struct_or_union_tag;
		const struct StructOrUnionInternalCompleteInfo *ptr_info =
		    lookup(ptr_ps->global_struct_or_union_tag_map, tag);
		if (!ptr_info) {
			fprintf(stderr,
			        "tried to use a member of incomplete type `struct %s` / "
			        "`union %s`\n",
			        tag, tag);
			exit(EXIT_FAILURE);
		}

		struct Vector /* <TypeAndIdent> */ vec =
		    *ptr_info->info.ptr_types_and_idents;
		int nth_member = -1;

		struct Expr expr;
		for (int i = 0; i < vec.length; i++) {
			const struct TypeAndIdent *ptr_vec_i = vec.vector[i];
			if (strcmp(ptr_vec_i->ident_str, ident_after_dot) == 0) {
				nth_member = i;

				struct Type t2 = ptr_vec_i->type;
				struct Type t3 = ptr_to_type(&t2);
				expr.details.type = t3;
				expr.details.true_type = t3;
				break;
			}
		}

		if (nth_member == -1) {
			fprintf(stderr, "member `%s` does not belong to struct `%s`\n",
			        ident_after_dot, tag);
			exit(EXIT_FAILURE);
		}

		int offset = ptr_info->offset_vec[nth_member];

		struct Expr *ptr_struct_or_union_expr = calloc(1, sizeof(struct Expr));
		*ptr_struct_or_union_expr = struct_or_union_expr;

		expr.category = PTR_STRUCT_AND_OFFSET;
		expr.struct_offset = offset;
		expr.ptr1 = ptr_struct_or_union_expr;
		expr.ptr2 = 0;
		expr.ptr3 = 0;
		return expr;
	}
	case SIZEOF_TYPE: {
		struct Expr expr;
		expr.details.type = INT_TYPE();
		expr.int_value = size_of(ptr_ps, &uexpr.operand_of_sizeof_or_alignof);
		expr.category = INT_VALUE;
		return expr;
	}
	case ALIGNOF_TYPE: {
		struct Expr expr;
		expr.details.type = INT_TYPE();
		expr.int_value = align_of(ptr_ps, &uexpr.operand_of_sizeof_or_alignof);
		expr.category = INT_VALUE;
		return expr;
	}
	case UNARY_EXPR: {
		enum TokenKind kind = uexpr.operator_;
		const struct Expr expr = typecheck_expression(ptr_ps, uexpr.ptr1);
		return typecheck_unary_expression(ptr_ps, &expr, kind);
	}
	case FUNC_PTR_CALL: {
		struct Expr fp_expr = typecheck_expression(ptr_ps, uexpr.ptr1);

		struct Type fn_type;
		if (fp_expr.details.type.type_category == FN) {
			fn_type = fp_expr.details.type;
		} else if (fp_expr.details.type.type_category == PTR_ &&
		           fp_expr.details.type.derived_from->type_category == FN) {
			fn_type = *(fp_expr.details.type.derived_from);
		} else {
			fprintf(
			    stderr,
			    "function call operator was applied to something of type `");
			debug_print_type(&fp_expr.details.type);
			fprintf(stderr,
			        "`, which is neither a function nor a function pointer\n");
			exit(EXIT_FAILURE);
		}

		int param_infos_validity = fn_type.param_infos_validity;
		struct Vector /*<TypeAndIdent>*/ param_infos;

		if (param_infos_validity != INVALID) {
			param_infos = fn_type.param_infos;
		}

		struct Expr expr =
		    func_call_expr(ptr_ps, fn_type.derived_from,
		                   param_infos_validity != INVALID ? &param_infos : 0,
		                   &uexpr.arg_exprs_vec, 1 /* is_fp_call */);

		struct Expr *ptr_fp_expr = calloc(1, sizeof(struct Expr));
		*ptr_fp_expr = fp_expr;
		expr.ptr1 = ptr_fp_expr;

		return expr;
	}
	case BUILTIN_FUNCCALL: {
		const char *ident_str = uexpr.var_name;
		struct Expr expr = builtin_func_call_expr(ptr_ps, &uexpr.arg_exprs_vec);
		expr.global_var_name = ident_str;
		return expr;
	}
	case FUNCCALL: {
		const char *ident_str = uexpr.var_name;

		struct Type ret_type;

		int param_infos_validity;
		struct Vector /*<TypeAndIdent>*/ param_infos;
		if (!isElem(ptr_ps->func_info_map, ident_str)) {
			if (from_name_to_expr(ptr_ps, ident_str)) {
				struct UntypedExpr *p_fp_u =
				    calloc(1, sizeof(struct UntypedExpr));
				p_fp_u->category = VAR;
				p_fp_u->var_name = ident_str;

				struct UntypedExpr u = uexpr;
				u.category = FUNC_PTR_CALL;
				u.ptr1 = p_fp_u;

				return typecheck_expression(ptr_ps, &u);
			}
			fprintf(stderr, "Undeclared function `%s()` detected.\n",
			        ident_str);
			fprintf(stderr, "Assumes that `%s()` returns `int`\n", ident_str);
			ret_type = INT_TYPE();
			param_infos_validity = INVALID;
		} else {
			struct Type *ptr_func_info =
			    lookup(ptr_ps->func_info_map, ident_str);
			ret_type = *(ptr_func_info->derived_from);
			param_infos_validity = ptr_func_info->param_infos_validity;
			if (param_infos_validity != INVALID) {
				param_infos = ptr_func_info->param_infos;
			}
		}

		struct Expr expr =
		    func_call_expr(ptr_ps, &ret_type,
		                   param_infos_validity != INVALID ? &param_infos : 0,
		                   &uexpr.arg_exprs_vec, 0 /* is_fp_call */);
		expr.global_var_name = ident_str;
		return expr;
	}
	case POSTFIX_EXPR: {
		struct Expr expr = typecheck_expression(ptr_ps, uexpr.ptr1);
		struct Expr *ptr_expr1 = calloc(1, sizeof(struct Expr));
		*ptr_expr1 = expr;

		enum TokenKind opkind = uexpr.operator_;

		struct Expr new_expr;
		new_expr.details.type = expr.details.type;
		new_expr.details.true_type = expr.details.type;
		new_expr.category =
		    opkind == OP_PLUS_PLUS ? POSTFIX_INCREMENT : POSTFIX_DECREMENT;
		new_expr.ptr1 = ptr_expr1;
		new_expr.ptr2 = 0;
		new_expr.ptr3 = 0;
		if (expr.details.type.type_category == PTR_) {
			const struct Type deref = deref_type(&expr.details.type);
			new_expr.size_info_for_pointer_arith = size_of(ptr_ps, &deref);
		}

		return new_expr;
	}
	case INT_LITERAL_: {
		struct Expr expr;
		expr.details.type = INT_TYPE();
		expr.int_value = uexpr.int_value;
		expr.category = INT_VALUE;
		return expr;
	}
	case VAR: {
		struct Expr *p = from_name_to_expr(ptr_ps, uexpr.var_name);
		if (!p) {
			fprintf(stderr, "%s is not declared either globally or locally\n",
			        uexpr.var_name);
			exit(EXIT_FAILURE);
		}
		return *p;
	}
	case STRING_LITERAL_: {
		return string_literal(uexpr.literal_string);
	}
	case CONDITIONAL: {
		struct Expr expr = typecheck_expression(ptr_ps, uexpr.ptr1);
		struct Expr true_branch = typecheck_expression(ptr_ps, uexpr.ptr2);
		struct Expr false_branch = typecheck_expression(ptr_ps, uexpr.ptr3);

		if_function_cast_to_pointer(&true_branch);
		if_function_cast_to_pointer(&false_branch);

		if (false_branch.details.type.type_category == PTR_) {
			cast_to_null_pointer_if_possible(&true_branch,
			                                 &false_branch.details);
		} else if (true_branch.details.type.type_category == PTR_) {
			cast_to_null_pointer_if_possible(&false_branch,
			                                 &true_branch.details);
		}

		expect_type(ptr_ps, &false_branch.details.type,
		            &true_branch.details.type,
		            "mismatch of type between the false branch and the true "
		            "branch of a conditional operator");
		struct Expr *ptr_expr1 = calloc(1, sizeof(struct Expr));
		struct Expr *ptr_expr2 = calloc(1, sizeof(struct Expr));
		struct Expr *ptr_expr3 = calloc(1, sizeof(struct Expr));
		*ptr_expr1 = expr;
		*ptr_expr2 = true_branch;
		*ptr_expr3 = false_branch;

		struct Expr new_expr;
		new_expr.details = true_branch.details;
		new_expr.category = CONDITIONAL_EXPR;
		new_expr.ptr1 = ptr_expr1;
		new_expr.ptr2 = ptr_expr2;
		new_expr.ptr3 = ptr_expr3;
		return new_expr;
	}
	case BINARY_EXPR: {
		const struct Expr expr = typecheck_expression(ptr_ps, uexpr.ptr1);
		const struct Expr expr2 = typecheck_expression(ptr_ps, uexpr.ptr2);
		return typecheck_binary_expression(ptr_ps, &expr, &expr2,
		                                   uexpr.operator_);
	}
	}
	fprintf(stderr,
	        "****************************\n"
	        "* INTERNAL COMPILER ERROR @ %s\n"
	        "* Unexpected value in UntypedExprCategory: `uexpr.category` is `%d`\n"
	        "****************************\n",
	        __func__, uexpr.category);
	exit(EXIT_FAILURE);
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x3d8, %rsp            # imm = 0x3D8
movq %rdx, %rax
movq %rsi, %r14
movq %rdi, %r13
leaq -0xf8(%rbp), %rbx
movl $0xa0, %edx
movq %rbx, %rdi
movq %rax, %rsi
callq 0x1100
movl 0x4(%rbx), %edi
cmpq $0xc, %rdi
ja 0x6cd4
leaq 0x8efd(%rip), %rax      # 0xf4b8
movslq (%rax,%rdi,4), %rcx
addq %rax, %rcx
jmpq *%rcx
movq -0xf0(%rbp), %rdx
leaq -0x300(%rbp), %rbx
movq %rbx, %rdi
movq %r14, %rsi
callq 0x6573
movq -0xe8(%rbp), %rdx
leaq -0x200(%rbp), %r15
movq %r15, %rdi
movq %r14, %rsi
callq 0x6573
movl -0xf8(%rbp), %r8d
movq %r13, %rdi
movq %r14, %rsi
movq %rbx, %rdx
movq %r15, %rcx
callq 0x7297
jmp 0x6c8d
movq -0xf0(%rbp), %rdx
leaq -0x300(%rbp), %rbx
movq %rbx, %rdi
movq %r14, %rsi
callq 0x6573
movq -0x60(%rbp), %rax
movq %rax, -0x40(%rbp)
movq %rbx, %rdi
callq 0x9516
testl %eax, %eax
je 0x6ca2
movq -0x2d0(%rbp), %rbx
movq 0x78(%r14), %rdi
movq %rbx, %rsi
callq 0xa32d
testq %rax, %rax
je 0x6cbf
movq %rbx, -0x48(%rbp)
movq %rax, -0x50(%rbp)
movq (%rax), %rax
movslq (%rax), %r12
testq %r12, %r12
movq %r13, -0x38(%rbp)
jle 0x6be7
movq 0x8(%rax), %r14
leaq 0x50(%r13), %rax
movq %rax, -0x58(%rbp)
decq %r12
movl $0xffffffff, -0x2c(%rbp) # imm = 0xFFFFFFFF
xorl %r15d, %r15d
movq (%r14,%r15,8), %r13
movq 0x50(%r13), %rdi
movq -0x40(%rbp), %rsi
callq 0x10e0
movl %eax, %ebx
testl %eax, %eax
jne 0x66f4
movl $0xa, %ecx
leaq -0x200(%rbp), %rax
movq %rax, %rdi
movq %r13, %rsi
rep  movsq (%rsi), %es:(%rdi)
leaq -0x400(%rbp), %r13
movq %r13, %rdi
movq %rax, %rsi
callq 0x9639
movl $0xa, %ecx
movq -0x38(%rbp), %rdi
movq %r13, %rsi
rep  movsq (%rsi), %es:(%rdi)
movl $0xa, %ecx
movq -0x58(%rbp), %rdi
movq %r13, %rsi
rep  movsq (%rsi), %es:(%rdi)
movl %r15d, -0x2c(%rbp)
testl %ebx, %ebx
je 0x6bee
leaq 0x1(%r15), %rax
cmpq %r15, %r12
movq %rax, %r15
jne 0x6694
jmp 0x6bee
movq %r13, %rdi
callq 0x94f8
movl -0xd8(%rbp), %eax
jmp 0x6a00
leaq -0xc0(%rbp), %rdx
movq -0x10(%rdx), %rbx
movq %r13, %rdi
movq %r14, %rsi
callq 0x64aa
movq %rbx, 0xd0(%r13)
jmp 0x6c8d
movq %r13, %rdi
callq 0x94f8
leaq -0xb0(%rbp), %rsi
movq %r14, %rdi
callq 0x2fe0
jmp 0x6a00
movq -0xf0(%rbp), %rdx
leaq -0x300(%rbp), %rdi
movq %r14, %rsi
callq 0x6573
movq -0xe8(%rbp), %rdx
leaq -0x200(%rbp), %rbx
movq %rbx, %rdi
movq %r14, %rsi
callq 0x6573
movq -0xe0(%rbp), %rdx
leaq -0x400(%rbp), %r15
movq %r15, %rdi
movq %r14, %rsi
callq 0x6573
movq %rbx, %rdi
callq 0x5fed
movq %r15, %rdi
callq 0x5fed
cmpl $0x1, (%r15)
jne 0x6a2b
movq %r13, %rbx
cmpl $0x9, -0x160(%rbp)
jne 0x6a6b
cmpl $0x0, -0x138(%rbp)
jne 0x6a6b
leaq -0x200(%rbp), %rdi
movl $0x12, 0xa0(%rdi)
leaq -0x400(%rbp), %rsi
jmp 0x6a61
movq -0xd0(%rbp), %rsi
movq %r14, %rdi
callq 0x6fa2
testq %rax, %rax
je 0x6ca7
movl $0x100, %edx            # imm = 0x100
movq %r13, %rdi
movq %rax, %rsi
callq 0x1100
jmp 0x6c8d
movq -0xf0(%rbp), %rdx
leaq -0x300(%rbp), %rbx
movq %rbx, %rdi
movq %r14, %rsi
callq 0x6573
movl (%rbx), %eax
cmpl $0x3, %eax
je 0x6860
cmpl $0x1, %eax
jne 0x6cb3
movq -0x2f8(%rbp), %rbx
cmpl $0x3, (%rbx)
jne 0x6cb3
movq 0x8(%rbx), %rdx
movq 0x20(%rbx), %rax
movq %rax, -0x1f0(%rbp)
movups 0x10(%rbx), %xmm0
movaps %xmm0, -0x200(%rbp)
cmpl $0x0, 0x28(%rbx)
je 0x6b9b
movq -0x1f8(%rbp), %rax
movq -0x1f0(%rbp), %rsi
leaq -0x400(%rbp), %rcx
movq %rax, (%rcx)
movq %rsi, 0x8(%rcx)
jmp 0x6b9d
movl -0xf8(%rbp), %ebx
movq -0xf0(%rbp), %rdx
leaq -0x300(%rbp), %r15
movq %r15, %rdi
movq %r14, %rsi
callq 0x6573
movq %r13, %rdi
movq %r14, %rsi
movq %r15, %rdx
movl %ebx, %ecx
callq 0x618a
jmp 0x6c8d
movq -0xf0(%rbp), %rdx
leaq -0x300(%rbp), %r15
movq %r15, %rdi
movq %r14, %rsi
callq 0x6573
movl $0x1, %edi
movl $0x100, %esi            # imm = 0x100
callq 0x10d0
movq %rax, %rbx
movl $0x100, %edx            # imm = 0x100
movq %rax, %rdi
movq %r15, %rsi
callq 0x1100
movl -0xf8(%rbp), %eax
movl $0xa, %ecx
movq %r13, %rdi
movq %r15, %rsi
rep  movsq (%rsi), %es:(%rdi)
leaq 0x50(%r13), %rdi
movl $0xa, %ecx
movq %r15, %rsi
rep  movsq (%rsi), %es:(%rdi)
xorl %ecx, %ecx
cmpl $0x32, %eax
sete %cl
xorl $0xb, %ecx
movl %ecx, 0xa0(%r13)
movq %rbx, 0xb0(%r13)
xorps %xmm0, %xmm0
movups %xmm0, 0xb8(%r13)
cmpl $0x1, (%r15)
jne 0x6c8d
leaq -0x200(%rbp), %rbx
leaq -0x300(%rbp), %rsi
movq %rbx, %rdi
callq 0x9607
movq %r14, %rdi
movq %rbx, %rsi
callq 0x2fe0
movl %eax, 0xf4(%r13)
jmp 0x6c8d
movq -0xd0(%rbp), %r15
movq 0x18(%r14), %rdi
movq %r15, %rsi
callq 0xa389
testl %eax, %eax
je 0x6b30
movq 0x18(%r14), %rdi
movq %r15, %rsi
callq 0xa32d
movq 0x8(%rax), %rsi
leaq -0x200(%rbp), %rdi
movl $0xa, %ecx
rep  movsq (%rsi), %es:(%rdi)
cmpl $0x0, 0x28(%rax)
je 0x6c68
movups 0x18(%rax), %xmm0
leaq -0x400(%rbp), %rcx
movaps %xmm0, (%rcx)
jmp 0x6c6a
movq %r13, %rdi
callq 0x94f8
leaq -0xb0(%rbp), %rsi
movq %r14, %rdi
callq 0x3070
movl %eax, 0xc8(%r13)
movl $0x9, 0xa0(%r13)
jmp 0x6c8d
movq -0xc8(%rbp), %rsi
movq %r13, %rdi
callq 0x7210
jmp 0x6c8d
movq %r13, %rbx
cmpl $0x1, -0x200(%rbp)
jne 0x6a6b
cmpl $0x9, -0x360(%rbp)
jne 0x6a6b
cmpl $0x0, -0x338(%rbp)
jne 0x6a6b
leaq -0x400(%rbp), %rdi
movl $0x12, 0xa0(%rdi)
leaq -0x200(%rbp), %rsi
movl $0xa0, %edx
callq 0x1100
leaq 0x8fb1(%rip), %rcx      # 0xfa23
leaq -0x400(%rbp), %rsi
leaq -0x200(%rbp), %r15
movq %r14, %rdi
movq %r15, %rdx
callq 0x5d65
movl $0x1, %edi
movl $0x100, %esi            # imm = 0x100
callq 0x10d0
movq %rax, %r14
movl $0x1, %edi
movl $0x100, %esi            # imm = 0x100
callq 0x10d0
movq %rax, %r13
movl $0x1, %edi
movl $0x100, %esi            # imm = 0x100
callq 0x10d0
movq %rax, %r12
leaq -0x300(%rbp), %rsi
movl $0x100, %edx            # imm = 0x100
movq %r14, %rdi
callq 0x1100
movl $0x100, %edx            # imm = 0x100
movq %r13, %rdi
movq %r15, %rsi
callq 0x1100
movl $0x100, %edx            # imm = 0x100
movq %r12, %rdi
leaq -0x400(%rbp), %rsi
callq 0x1100
movl $0xa0, %edx
movq %rbx, %rdi
movq %r15, %rsi
callq 0x1100
movl $0x5, 0xa0(%rbx)
movq %r14, 0xb0(%rbx)
movq %r13, 0xb8(%rbx)
movq %rbx, %r13
movq %r12, 0xc0(%rbx)
jmp 0x6c8d
movq %r14, %rdi
movq %r15, %rsi
callq 0x6fa2
testq %rax, %rax
je 0x6c59
movl $0x1, %edi
movl $0xa0, %esi
callq 0x10d0
movq %rax, %rbx
movl $0x3, 0x4(%rax)
movq %r15, 0x28(%rax)
leaq -0x300(%rbp), %r15
leaq -0xf8(%rbp), %rsi
movl $0xa0, %edx
movq %r15, %rdi
callq 0x1100
movl $0x7, 0x4(%r15)
movq %rbx, 0x8(%r15)
movq %r13, %rdi
movq %r14, %rsi
movq %r15, %rdx
callq 0x6573
jmp 0x6c8d
xorl %ecx, %ecx
leaq -0xc0(%rbp), %r8
movq %r13, %rdi
movq %r14, %rsi
movl $0x1, %r9d
callq 0x6cd9
movl $0x1, %edi
movl $0x100, %esi            # imm = 0x100
callq 0x10d0
movq %rax, %rbx
leaq -0x300(%rbp), %rsi
movl $0x100, %edx            # imm = 0x100
movq %rax, %rdi
callq 0x1100
movq %rbx, 0xb0(%r13)
jmp 0x6c8d
movl $0xffffffff, -0x2c(%rbp) # imm = 0xFFFFFFFF
movl -0x2c(%rbp), %ecx
cmpl $-0x1, %ecx
je 0x6cc7
movq -0x50(%rbp), %rax
movq 0x8(%rax), %rax
movslq %ecx, %rcx
movl (%rax,%rcx,4), %r14d
movl $0x1, %edi
movl $0x100, %esi            # imm = 0x100
callq 0x10d0
movq %rax, %rbx
leaq -0x300(%rbp), %rsi
movl $0x100, %edx            # imm = 0x100
movq %rax, %rdi
callq 0x1100
movq -0x38(%rbp), %r13
movl $0x1a, 0xa0(%r13)
movl %r14d, 0xfc(%r13)
movq %rbx, 0xb0(%r13)
xorps %xmm0, %xmm0
movups %xmm0, 0xb8(%r13)
jmp 0x6c8d
leaq -0x200(%rbp), %rsi
movq %r15, %rdi
callq 0x186d
xorl %ecx, %ecx
leaq -0xc0(%rbp), %r8
leaq -0x200(%rbp), %rdx
movq %r13, %rdi
movq %r14, %rsi
xorl %r9d, %r9d
callq 0x6cd9
movq %r15, 0xd0(%r13)
movq %r13, %rax
addq $0x3d8, %rsp            # imm = 0x3D8
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
callq 0x1965
leaq -0xd0(%rbp), %rdi
callq 0x1843
leaq -0x300(%rbp), %rdi
callq 0x18bf
movq %rbx, %rdi
callq 0x193b
movq -0x40(%rbp), %rdi
movq -0x48(%rbp), %rsi
callq 0x1911
callq 0x198d