use super::Builder;
use crate::{
ast::{self},
checker::types::TypeId,
ir::{self, IrBasicValue},
report::throw_ir_build_error,
};
type CalleeResolvedType = (String, Option<IrBasicValue>);
impl Builder<'_> {
pub fn build_call_expr(&mut self, expr: &mut ast::CallExpr) -> IrBasicValue {
let ret_type = self.build_type(expr.get_ret_type_id(), expr.get_range());
let dest = self.ctx.new_register(ret_type);
let (callee, self_value) = self.resolve_callee(expr);
let mut args = self.build_function_args(&mut expr.args, &expr.args_type);
if let Some(self_value) = self_value {
args.insert(0, self_value);
}
if !ret_type.is_nothing() {
if let Some(size) = self.is_need_heap_allocation(ret_type) {
self.ctx.add_free_value(dest.clone());
let unary_instr = ir::UnInstr::new(dest.clone(), size.into());
self.ctx.block.add_instr(ir::Instr::Halloc(unary_instr));
} else {
let salloc = ir::SallocInstr::new(dest.clone(), ret_type);
self.ctx.block.add_instr(salloc.into());
}
}
let call_instr = ir::CallInstr::new(dest.clone(), callee, ret_type, args);
self.ctx.block.add_instr(call_instr.into());
dest
}
#[inline(always)]
fn resolve_callee(&mut self, expr: &mut ast::CallExpr) -> CalleeResolvedType {
match &mut *expr.callee {
ast::Expr::Ident(ident) => (ident.lexeme().to_string(), None),
ast::Expr::Associate(associate_expr) => self.resolve_associate_expr(associate_expr),
ast::Expr::Member(member) => self.resolve_member_expr(member),
_ => todo!("unrecognized callee: {:?}", expr.callee),
}
}
#[inline(always)]
fn resolve_associate_expr(&mut self, expr: &mut ast::AssociateExpr) -> CalleeResolvedType {
let self_name = expr.self_name.lexeme();
let method_name = expr.method.lexeme();
(self.create_bind_method_with_selfname(self_name, method_name), None)
}
#[inline(always)]
fn resolve_member_expr(&mut self, member: &mut ast::MemberExpr) -> CalleeResolvedType {
self.ctx.push_member_scope();
let self_value = self.build_expr(&mut member.left);
let self_register = self_value.value.as_str();
self.ctx.pop_scope();
let self_name = match self.type_store.get_struct_name(self_value.get_type()) {
Some(name) => name,
None => {
throw_ir_build_error(format!("cannot resolve struct name for type {}", self_register))
}
};
let method_name = member.method.lexeme();
let method = self.create_bind_method_with_selfname(self_name, method_name);
(method, Some(self_value))
}
#[rustfmt::skip]
fn build_function_args(&mut self, args_expr: &mut [ast::Expr], args_type: &[TypeId]) -> Vec<IrBasicValue> {
let mut basic_values = Vec::with_capacity(args_type.len());
for (position, expr) in args_expr.iter_mut().enumerate() {
let mut basic_value = self.build_expr(expr);
if let Some(expr_type) = args_type.get(position) {
basic_value = basic_value.with_new_type(*expr_type)
}
basic_values.push(basic_value);
}
basic_values
}
}