forked from M-Labs/nac3
David Mak
e0de82993f
Previously, the final value of the target expression would be one after the last element of the loop, which does not match Python's behavior. This commit fixes this problem while also preserving the last assigned value of the loop beyond the loop, matching Python's behavior.
1151 lines
47 KiB
Rust
1151 lines
47 KiB
Rust
use super::{
|
|
super::symbol_resolver::ValueEnum,
|
|
expr::destructure_range,
|
|
irrt::{handle_slice_indices, list_slice_assignment},
|
|
CodeGenContext, CodeGenerator,
|
|
};
|
|
use crate::{
|
|
codegen::expr::gen_binop_expr,
|
|
toplevel::{DefinitionId, TopLevelDef},
|
|
typecheck::typedef::{FunSignature, Type, TypeEnum},
|
|
};
|
|
use inkwell::{
|
|
attributes::{Attribute, AttributeLoc},
|
|
basic_block::BasicBlock,
|
|
types::BasicTypeEnum,
|
|
values::{BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue},
|
|
IntPredicate,
|
|
};
|
|
use nac3parser::ast::{
|
|
Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef,
|
|
};
|
|
use std::convert::TryFrom;
|
|
|
|
pub fn gen_var<'ctx, 'a>(
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
ty: BasicTypeEnum<'ctx>,
|
|
name: Option<&str>,
|
|
) -> Result<PointerValue<'ctx>, String> {
|
|
// put the alloca in init block
|
|
let current = ctx.builder.get_insert_block().unwrap();
|
|
// position before the last branching instruction...
|
|
ctx.builder.position_before(&ctx.init_bb.get_last_instruction().unwrap());
|
|
let ptr = ctx.builder.build_alloca(ty, name.unwrap_or(""));
|
|
ctx.builder.position_at_end(current);
|
|
Ok(ptr)
|
|
}
|
|
|
|
pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
pattern: &Expr<Option<Type>>,
|
|
name: Option<&str>,
|
|
) -> Result<PointerValue<'ctx>, String> {
|
|
// very similar to gen_expr, but we don't do an extra load at the end
|
|
// and we flatten nested tuples
|
|
Ok(match &pattern.node {
|
|
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
|
|
None => {
|
|
let ptr_ty = ctx.get_llvm_type(generator, pattern.custom.unwrap());
|
|
let ptr = generator.gen_var_alloc(ctx, ptr_ty, name)?;
|
|
ctx.var_assignment.insert(*id, (ptr, None, 0));
|
|
ptr
|
|
}
|
|
Some(v) => {
|
|
let (ptr, counter) = (v.0, v.2);
|
|
ctx.var_assignment.insert(*id, (ptr, None, counter));
|
|
ptr
|
|
}
|
|
}
|
|
ExprKind::Attribute { value, attr, .. } => {
|
|
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
|
|
let val = generator.gen_expr(ctx, value)?.unwrap().to_basic_value_enum(
|
|
ctx,
|
|
generator,
|
|
value.custom.unwrap(),
|
|
)?;
|
|
let ptr = if let BasicValueEnum::PointerValue(v) = val {
|
|
v
|
|
} else {
|
|
unreachable!();
|
|
};
|
|
unsafe {
|
|
ctx.builder.build_in_bounds_gep(
|
|
ptr,
|
|
&[
|
|
ctx.ctx.i32_type().const_zero(),
|
|
ctx.ctx.i32_type().const_int(index as u64, false),
|
|
],
|
|
name.unwrap_or(""),
|
|
)
|
|
}
|
|
}
|
|
ExprKind::Subscript { value, slice, .. } => {
|
|
assert!(matches!(
|
|
ctx.unifier.get_ty_immutable(value.custom.unwrap()).as_ref(),
|
|
TypeEnum::TList { .. },
|
|
));
|
|
let i32_type = ctx.ctx.i32_type();
|
|
let zero = i32_type.const_zero();
|
|
let v = generator
|
|
.gen_expr(ctx, value)?
|
|
.unwrap()
|
|
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
|
.into_pointer_value();
|
|
let len = ctx
|
|
.build_gep_and_load(v, &[zero, i32_type.const_int(1, false)])
|
|
.into_int_value();
|
|
let raw_index = generator
|
|
.gen_expr(ctx, slice)?
|
|
.unwrap()
|
|
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
|
|
.into_int_value();
|
|
let raw_index = ctx.builder.build_int_s_extend(
|
|
raw_index,
|
|
generator.get_size_type(ctx.ctx),
|
|
"sext",
|
|
);
|
|
// handle negative index
|
|
let is_negative = ctx.builder.build_int_compare(
|
|
IntPredicate::SLT,
|
|
raw_index,
|
|
generator.get_size_type(ctx.ctx).const_zero(),
|
|
"is_neg",
|
|
);
|
|
let adjusted = ctx.builder.build_int_add(raw_index, len, "adjusted");
|
|
let index = ctx
|
|
.builder
|
|
.build_select(is_negative, adjusted, raw_index, "index")
|
|
.into_int_value();
|
|
// unsigned less than is enough, because negative index after adjustment is
|
|
// bigger than the length (for unsigned cmp)
|
|
let bound_check = ctx.builder.build_int_compare(
|
|
IntPredicate::ULT,
|
|
index,
|
|
len,
|
|
"inbound",
|
|
);
|
|
ctx.make_assert(
|
|
generator,
|
|
bound_check,
|
|
"0:IndexError",
|
|
"index {0} out of bounds 0:{1}",
|
|
[Some(raw_index), Some(len), None],
|
|
slice.location,
|
|
);
|
|
unsafe {
|
|
let arr_ptr = ctx
|
|
.build_gep_and_load(v, &[i32_type.const_zero(), i32_type.const_zero()])
|
|
.into_pointer_value();
|
|
ctx.builder.build_gep(arr_ptr, &[index], name.unwrap_or(""))
|
|
}
|
|
}
|
|
_ => unreachable!(),
|
|
})
|
|
}
|
|
|
|
pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
target: &Expr<Option<Type>>,
|
|
value: ValueEnum<'ctx>,
|
|
) -> Result<(), String> {
|
|
match &target.node {
|
|
ExprKind::Tuple { elts, .. } => {
|
|
if let BasicValueEnum::StructValue(v) =
|
|
value.to_basic_value_enum(ctx, generator, target.custom.unwrap())?
|
|
{
|
|
for (i, elt) in elts.iter().enumerate() {
|
|
let v = ctx
|
|
.builder
|
|
.build_extract_value(v, u32::try_from(i).unwrap(), "struct_elem")
|
|
.unwrap();
|
|
generator.gen_assign(ctx, elt, v.into())?;
|
|
}
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
ExprKind::Subscript { value: ls, slice, .. }
|
|
if matches!(&slice.node, ExprKind::Slice { .. }) =>
|
|
{
|
|
if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
|
let ls = generator
|
|
.gen_expr(ctx, ls)?
|
|
.unwrap()
|
|
.to_basic_value_enum(ctx, generator, ls.custom.unwrap())?
|
|
.into_pointer_value();
|
|
let (start, end, step) =
|
|
handle_slice_indices(lower, upper, step, ctx, generator, ls)?;
|
|
let value = value
|
|
.to_basic_value_enum(ctx, generator, target.custom.unwrap())?
|
|
.into_pointer_value();
|
|
let ty =
|
|
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(target.custom.unwrap()) {
|
|
ctx.get_llvm_type(generator, *ty)
|
|
} else {
|
|
unreachable!()
|
|
};
|
|
let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value)?;
|
|
list_slice_assignment(generator, ctx, ty, ls, (start, end, step), value, src_ind)
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
_ => {
|
|
let name = if let ExprKind::Name { id, .. } = &target.node {
|
|
format!("{}.addr", id.to_string())
|
|
} else {
|
|
String::from("target.addr")
|
|
};
|
|
let ptr = generator.gen_store_target(ctx, target, Some(name.as_str()))?;
|
|
|
|
if let ExprKind::Name { id, .. } = &target.node {
|
|
let (_, static_value, counter) = ctx.var_assignment.get_mut(id).unwrap();
|
|
*counter += 1;
|
|
if let ValueEnum::Static(s) = &value {
|
|
*static_value = Some(s.clone());
|
|
}
|
|
}
|
|
let val = value.to_basic_value_enum(ctx, generator, target.custom.unwrap())?;
|
|
ctx.builder.build_store(ptr, val);
|
|
}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
/// Generates a sequence of IR which checks whether [value] does not exceed the upper bound of the
|
|
/// range as defined by [stop] and [step].
|
|
///
|
|
/// Note that the generated IR will **not** check whether value is part of the range or whether
|
|
/// value exceeds the lower bound of the range (as evident by the missing `start` argument).
|
|
///
|
|
/// Returns an [IntValue] representing the result of whether the [value] is in the range.
|
|
fn gen_in_range_check<'ctx, 'a>(
|
|
ctx: &CodeGenContext<'ctx, 'a>,
|
|
value: IntValue<'ctx>,
|
|
stop: IntValue<'ctx>,
|
|
step: IntValue<'ctx>,
|
|
) -> IntValue<'ctx> {
|
|
let sign = ctx.builder.build_int_compare(IntPredicate::SGT, step, ctx.ctx.i32_type().const_zero(), "");
|
|
let lo = ctx.builder.build_select(sign, value, stop, "").into_int_value();
|
|
let hi = ctx.builder.build_select(sign, stop, value, "").into_int_value();
|
|
|
|
ctx.builder.build_int_compare(IntPredicate::SLT, lo, hi, "cmp")
|
|
}
|
|
|
|
pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String> {
|
|
if let StmtKind::For { iter, target, body, orelse, .. } = &stmt.node {
|
|
// var_assignment static values may be changed in another branch
|
|
// if so, remove the static value as it may not be correct in this branch
|
|
let var_assignment = ctx.var_assignment.clone();
|
|
|
|
let int32 = ctx.ctx.i32_type();
|
|
let size_t = generator.get_size_type(ctx.ctx);
|
|
let zero = int32.const_zero();
|
|
let current = ctx.builder.get_insert_block().and_then(|bb| bb.get_parent()).unwrap();
|
|
let test_bb = ctx.ctx.append_basic_block(current, "for.cond");
|
|
let body_bb = ctx.ctx.append_basic_block(current, "for.body");
|
|
let cont_bb = ctx.ctx.append_basic_block(current, "for.end");
|
|
// if there is no orelse, we just go to cont_bb
|
|
let orelse_bb =
|
|
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "for.orelse") };
|
|
|
|
// Whether the iterable is a range() expression
|
|
let is_iterable_range_expr = ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range);
|
|
|
|
// store loop bb information and restore it later
|
|
let loop_bb = if is_iterable_range_expr {
|
|
ctx.loop_target.replace((body_bb, cont_bb))
|
|
} else {
|
|
ctx.loop_target.replace((test_bb, cont_bb))
|
|
};
|
|
|
|
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(
|
|
ctx,
|
|
generator,
|
|
iter.custom.unwrap(),
|
|
)?;
|
|
if is_iterable_range_expr {
|
|
let iter_val = iter_val.into_pointer_value();
|
|
// Internal variable for loop; Cannot be assigned
|
|
let i = generator.gen_var_alloc(ctx, int32.into(), Some("for.i.addr"))?;
|
|
// Variable declared in "target" expression of the loop; Can be reassigned *or* shadowed
|
|
let target_i = generator.gen_store_target(ctx, target, Some("for.target.addr"))?;
|
|
let (start, stop, step) = destructure_range(ctx, iter_val);
|
|
|
|
ctx.builder.build_store(i, start);
|
|
|
|
// Pre-Loop Checks:
|
|
// - step == 0 -> ValueError
|
|
// - start < stop for step > 0 || start > stop for step < 0
|
|
// TODO: Generate step == 0 -> raise ValueError
|
|
|
|
ctx.builder.build_conditional_branch(
|
|
gen_in_range_check(ctx, start, stop, step),
|
|
body_bb,
|
|
orelse_bb,
|
|
);
|
|
|
|
ctx.builder.position_at_end(body_bb);
|
|
ctx.builder.build_store(target_i, ctx.builder.build_load(i, "").into_int_value());
|
|
gen_block(generator, ctx, body.iter())?;
|
|
|
|
// Test if next element is still in range
|
|
let next_i = ctx.builder.build_int_add(
|
|
ctx.builder.build_load(i, "").into_int_value(),
|
|
step,
|
|
"next_i",
|
|
);
|
|
let cond_cont_bb = ctx.ctx.append_basic_block(current, "for.cond.cont");
|
|
ctx.builder.build_conditional_branch(
|
|
gen_in_range_check(ctx, next_i, stop, step),
|
|
cond_cont_bb,
|
|
orelse_bb,
|
|
);
|
|
|
|
ctx.builder.position_at_end(cond_cont_bb);
|
|
ctx.builder.build_store(i, next_i);
|
|
} else {
|
|
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("for.counter.addr"))?;
|
|
// counter = -1
|
|
ctx.builder.build_store(counter, size_t.const_int(u64::max_value(), true));
|
|
let len = ctx
|
|
.build_gep_and_load(
|
|
iter_val.into_pointer_value(),
|
|
&[zero, int32.const_int(1, false)],
|
|
)
|
|
.into_int_value();
|
|
ctx.builder.build_unconditional_branch(test_bb);
|
|
|
|
ctx.builder.position_at_end(test_bb);
|
|
let tmp = ctx.builder.build_load(counter, "i").into_int_value();
|
|
let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc");
|
|
ctx.builder.build_store(counter, tmp);
|
|
let cmp = ctx.builder.build_int_compare(IntPredicate::SLT, tmp, len, "cmp");
|
|
ctx.builder.build_conditional_branch(cmp, body_bb, orelse_bb);
|
|
|
|
ctx.builder.position_at_end(body_bb);
|
|
let arr_ptr = ctx
|
|
.build_gep_and_load(iter_val.into_pointer_value(), &[zero, zero])
|
|
.into_pointer_value();
|
|
let val = ctx.build_gep_and_load(arr_ptr, &[tmp]);
|
|
generator.gen_assign(ctx, target, val.into())?;
|
|
|
|
gen_block(generator, ctx, body.iter())?;
|
|
}
|
|
|
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
|
if counter != counter2 {
|
|
*static_val = None;
|
|
}
|
|
}
|
|
|
|
if !ctx.is_terminated() {
|
|
if is_iterable_range_expr {
|
|
ctx.builder.build_unconditional_branch(body_bb);
|
|
} else {
|
|
ctx.builder.build_unconditional_branch(test_bb);
|
|
}
|
|
}
|
|
|
|
if !orelse.is_empty() {
|
|
ctx.builder.position_at_end(orelse_bb);
|
|
gen_block(generator, ctx, orelse.iter())?;
|
|
if !ctx.is_terminated() {
|
|
ctx.builder.build_unconditional_branch(cont_bb);
|
|
}
|
|
}
|
|
|
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
|
if counter != counter2 {
|
|
*static_val = None;
|
|
}
|
|
}
|
|
|
|
// Clear test_bb if unused
|
|
if is_iterable_range_expr {
|
|
ctx.builder.position_at_end(test_bb);
|
|
ctx.builder.build_unreachable();
|
|
}
|
|
|
|
ctx.builder.position_at_end(cont_bb);
|
|
ctx.loop_target = loop_bb;
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String> {
|
|
if let StmtKind::While { test, body, orelse, .. } = &stmt.node {
|
|
// var_assignment static values may be changed in another branch
|
|
// if so, remove the static value as it may not be correct in this branch
|
|
let var_assignment = ctx.var_assignment.clone();
|
|
|
|
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
|
let test_bb = ctx.ctx.append_basic_block(current, "while.test");
|
|
let body_bb = ctx.ctx.append_basic_block(current, "while.body");
|
|
let cont_bb = ctx.ctx.append_basic_block(current, "while.cont");
|
|
// if there is no orelse, we just go to cont_bb
|
|
let orelse_bb =
|
|
if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "while.orelse") };
|
|
// store loop bb information and restore it later
|
|
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
|
|
ctx.builder.build_unconditional_branch(test_bb);
|
|
ctx.builder.position_at_end(test_bb);
|
|
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
|
|
ctx,
|
|
generator,
|
|
test.custom.unwrap(),
|
|
)?;
|
|
if let BasicValueEnum::IntValue(test) = test {
|
|
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
|
|
} else {
|
|
unreachable!()
|
|
};
|
|
ctx.builder.position_at_end(body_bb);
|
|
gen_block(generator, ctx, body.iter())?;
|
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
|
if counter != counter2 {
|
|
*static_val = None;
|
|
}
|
|
}
|
|
if !ctx.is_terminated() {
|
|
ctx.builder.build_unconditional_branch(test_bb);
|
|
}
|
|
if !orelse.is_empty() {
|
|
ctx.builder.position_at_end(orelse_bb);
|
|
gen_block(generator, ctx, orelse.iter())?;
|
|
if !ctx.is_terminated() {
|
|
ctx.builder.build_unconditional_branch(cont_bb);
|
|
}
|
|
}
|
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
|
if counter != counter2 {
|
|
*static_val = None;
|
|
}
|
|
}
|
|
ctx.builder.position_at_end(cont_bb);
|
|
ctx.loop_target = loop_bb;
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String> {
|
|
if let StmtKind::If { test, body, orelse, .. } = &stmt.node {
|
|
// var_assignment static values may be changed in another branch
|
|
// if so, remove the static value as it may not be correct in this branch
|
|
let var_assignment = ctx.var_assignment.clone();
|
|
|
|
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
|
let test_bb = ctx.ctx.append_basic_block(current, "if.test");
|
|
let body_bb = ctx.ctx.append_basic_block(current, "if.body");
|
|
let mut cont_bb = None;
|
|
// if there is no orelse, we just go to cont_bb
|
|
let orelse_bb = if orelse.is_empty() {
|
|
cont_bb = Some(ctx.ctx.append_basic_block(current, "if.cont"));
|
|
cont_bb.unwrap()
|
|
} else {
|
|
ctx.ctx.append_basic_block(current, "if.orelse")
|
|
};
|
|
ctx.builder.build_unconditional_branch(test_bb);
|
|
ctx.builder.position_at_end(test_bb);
|
|
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
|
|
ctx,
|
|
generator,
|
|
test.custom.unwrap(),
|
|
)?;
|
|
if let BasicValueEnum::IntValue(test) = test {
|
|
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
|
|
} else {
|
|
unreachable!()
|
|
};
|
|
ctx.builder.position_at_end(body_bb);
|
|
gen_block(generator, ctx, body.iter())?;
|
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
|
if counter != counter2 {
|
|
*static_val = None;
|
|
}
|
|
}
|
|
|
|
if !ctx.is_terminated() {
|
|
if cont_bb.is_none() {
|
|
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
|
}
|
|
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
|
|
}
|
|
if !orelse.is_empty() {
|
|
ctx.builder.position_at_end(orelse_bb);
|
|
gen_block(generator, ctx, orelse.iter())?;
|
|
if !ctx.is_terminated() {
|
|
if cont_bb.is_none() {
|
|
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
|
}
|
|
ctx.builder.build_unconditional_branch(cont_bb.unwrap());
|
|
}
|
|
}
|
|
if let Some(cont_bb) = cont_bb {
|
|
ctx.builder.position_at_end(cont_bb);
|
|
}
|
|
for (k, (_, _, counter)) in var_assignment.iter() {
|
|
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
|
if counter != counter2 {
|
|
*static_val = None;
|
|
}
|
|
}
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn final_proxy<'ctx, 'a>(
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
target: BasicBlock<'ctx>,
|
|
block: BasicBlock<'ctx>,
|
|
final_data: &mut (PointerValue, Vec<BasicBlock<'ctx>>, Vec<BasicBlock<'ctx>>),
|
|
) {
|
|
let (final_state, final_targets, final_paths) = final_data;
|
|
let prev = ctx.builder.get_insert_block().unwrap();
|
|
ctx.builder.position_at_end(block);
|
|
unsafe {
|
|
ctx.builder.build_store(*final_state, target.get_address().unwrap());
|
|
}
|
|
ctx.builder.position_at_end(prev);
|
|
final_targets.push(target);
|
|
final_paths.push(block);
|
|
}
|
|
|
|
pub fn get_builtins<'ctx, 'a>(
|
|
generator: &mut dyn CodeGenerator,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
symbol: &str,
|
|
) -> FunctionValue<'ctx> {
|
|
ctx.module.get_function(symbol).unwrap_or_else(|| {
|
|
let ty = match symbol {
|
|
"__nac3_raise" => ctx
|
|
.ctx
|
|
.void_type()
|
|
.fn_type(&[ctx.get_llvm_type(generator, ctx.primitives.exception).into()], false),
|
|
"__nac3_resume" => ctx.ctx.void_type().fn_type(&[], false),
|
|
"__nac3_end_catch" => ctx.ctx.void_type().fn_type(&[], false),
|
|
_ => unimplemented!(),
|
|
};
|
|
let fun = ctx.module.add_function(symbol, ty, None);
|
|
if symbol == "__nac3_raise" || symbol == "__nac3_resume" {
|
|
fun.add_attribute(
|
|
AttributeLoc::Function,
|
|
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 0),
|
|
);
|
|
}
|
|
fun
|
|
})
|
|
}
|
|
|
|
pub fn exn_constructor<'ctx, 'a>(
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
obj: Option<(Type, ValueEnum<'ctx>)>,
|
|
_fun: (&FunSignature, DefinitionId),
|
|
mut args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
|
generator: &mut dyn CodeGenerator,
|
|
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
|
let (zelf_ty, zelf) = obj.unwrap();
|
|
let zelf = zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value();
|
|
let int32 = ctx.ctx.i32_type();
|
|
let zero = int32.const_zero();
|
|
let zelf_id = {
|
|
if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(zelf_ty) {
|
|
obj_id.0
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
};
|
|
let defs = ctx.top_level.definitions.read();
|
|
let def = defs[zelf_id].read();
|
|
let zelf_name =
|
|
if let TopLevelDef::Class { name, .. } = &*def { *name } else { unreachable!() };
|
|
let exception_name = format!("{}:{}", ctx.resolver.get_exception_id(zelf_id), zelf_name);
|
|
unsafe {
|
|
let id_ptr = ctx.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id");
|
|
let id = ctx.resolver.get_string_id(&exception_name);
|
|
ctx.builder.build_store(id_ptr, int32.const_int(id as u64, false));
|
|
let empty_string = ctx.gen_const(generator, &Constant::Str("".into()), ctx.primitives.str);
|
|
let ptr =
|
|
ctx.builder.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg");
|
|
let msg = if !args.is_empty() {
|
|
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?
|
|
} else {
|
|
empty_string
|
|
};
|
|
ctx.builder.build_store(ptr, msg);
|
|
for i in [6, 7, 8].iter() {
|
|
let value = if !args.is_empty() {
|
|
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?
|
|
} else {
|
|
ctx.ctx.i64_type().const_zero().into()
|
|
};
|
|
let ptr = ctx.builder.build_in_bounds_gep(
|
|
zelf,
|
|
&[zero, int32.const_int(*i, false)],
|
|
"exn.param",
|
|
);
|
|
ctx.builder.build_store(ptr, value);
|
|
}
|
|
// set file, func to empty string
|
|
for i in [1, 4].iter() {
|
|
let ptr = ctx.builder.build_in_bounds_gep(
|
|
zelf,
|
|
&[zero, int32.const_int(*i, false)],
|
|
"exn.str",
|
|
);
|
|
ctx.builder.build_store(ptr, empty_string);
|
|
}
|
|
// set ints to zero
|
|
for i in [2, 3].iter() {
|
|
let ptr = ctx.builder.build_in_bounds_gep(
|
|
zelf,
|
|
&[zero, int32.const_int(*i, false)],
|
|
"exn.ints",
|
|
);
|
|
ctx.builder.build_store(ptr, zero);
|
|
}
|
|
}
|
|
Ok(Some(zelf.into()))
|
|
}
|
|
|
|
pub fn gen_raise<'ctx, 'a>(
|
|
generator: &mut dyn CodeGenerator,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
exception: Option<&BasicValueEnum<'ctx>>,
|
|
loc: Location,
|
|
) {
|
|
if let Some(exception) = exception {
|
|
unsafe {
|
|
let int32 = ctx.ctx.i32_type();
|
|
let zero = int32.const_zero();
|
|
let exception = exception.into_pointer_value();
|
|
let file_ptr = ctx.builder.build_in_bounds_gep(
|
|
exception,
|
|
&[zero, int32.const_int(1, false)],
|
|
"file_ptr",
|
|
);
|
|
let filename = ctx.gen_string(generator, loc.file.0);
|
|
ctx.builder.build_store(file_ptr, filename);
|
|
let row_ptr = ctx.builder.build_in_bounds_gep(
|
|
exception,
|
|
&[zero, int32.const_int(2, false)],
|
|
"row_ptr",
|
|
);
|
|
ctx.builder.build_store(row_ptr, int32.const_int(loc.row as u64, false));
|
|
let col_ptr = ctx.builder.build_in_bounds_gep(
|
|
exception,
|
|
&[zero, int32.const_int(3, false)],
|
|
"col_ptr",
|
|
);
|
|
ctx.builder.build_store(col_ptr, int32.const_int(loc.column as u64, false));
|
|
|
|
let current_fun = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
|
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
|
|
let name_ptr = ctx.builder.build_in_bounds_gep(
|
|
exception,
|
|
&[zero, int32.const_int(4, false)],
|
|
"name_ptr",
|
|
);
|
|
ctx.builder.build_store(name_ptr, fun_name);
|
|
}
|
|
|
|
let raise = get_builtins(generator, ctx, "__nac3_raise");
|
|
let exception = *exception;
|
|
ctx.build_call_or_invoke(raise, &[exception], "raise");
|
|
} else {
|
|
let resume = get_builtins(generator, ctx, "__nac3_resume");
|
|
ctx.build_call_or_invoke(resume, &[], "resume");
|
|
}
|
|
ctx.builder.build_unreachable();
|
|
}
|
|
|
|
pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
target: &Stmt<Option<Type>>,
|
|
) -> Result<(), String> {
|
|
if let StmtKind::Try { body, handlers, orelse, finalbody, .. } = &target.node {
|
|
// if we need to generate anything related to exception, we must have personality defined
|
|
let personality_symbol = ctx.top_level.personality_symbol.as_ref().unwrap();
|
|
let personality = ctx.module.get_function(personality_symbol).unwrap_or_else(|| {
|
|
let ty = ctx.ctx.i32_type().fn_type(&[], true);
|
|
ctx.module.add_function(personality_symbol, ty, None)
|
|
});
|
|
let exception_type = ctx.get_llvm_type(generator, ctx.primitives.exception);
|
|
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::default());
|
|
let current_block = ctx.builder.get_insert_block().unwrap();
|
|
let current_fun = current_block.get_parent().unwrap();
|
|
let landingpad = ctx.ctx.append_basic_block(current_fun, "try.landingpad");
|
|
let dispatcher = ctx.ctx.append_basic_block(current_fun, "try.dispatch");
|
|
let mut dispatcher_end = dispatcher;
|
|
ctx.builder.position_at_end(dispatcher);
|
|
let exn = ctx.builder.build_phi(exception_type, "exn");
|
|
ctx.builder.position_at_end(current_block);
|
|
|
|
let mut cleanup = None;
|
|
let mut old_loop_target = None;
|
|
let mut old_return = None;
|
|
let mut final_data = None;
|
|
let has_cleanup = !finalbody.is_empty();
|
|
if has_cleanup {
|
|
let final_state = generator.gen_var_alloc(ctx, ptr_type.into(), Some("try.final_state.addr"))?;
|
|
final_data = Some((final_state, Vec::new(), Vec::new()));
|
|
if let Some((continue_target, break_target)) = ctx.loop_target {
|
|
let break_proxy = ctx.ctx.append_basic_block(current_fun, "try.break");
|
|
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "try.continue");
|
|
final_proxy(ctx, break_target, break_proxy, final_data.as_mut().unwrap());
|
|
final_proxy(ctx, continue_target, continue_proxy, final_data.as_mut().unwrap());
|
|
old_loop_target = ctx.loop_target.replace((continue_proxy, break_proxy));
|
|
}
|
|
let return_proxy = ctx.ctx.append_basic_block(current_fun, "try.return");
|
|
if let Some(return_target) = ctx.return_target {
|
|
final_proxy(ctx, return_target, return_proxy, final_data.as_mut().unwrap());
|
|
} else {
|
|
let return_target = ctx.ctx.append_basic_block(current_fun, "try.return_target");
|
|
ctx.builder.position_at_end(return_target);
|
|
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret"));
|
|
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue));
|
|
ctx.builder.position_at_end(current_block);
|
|
final_proxy(ctx, return_target, return_proxy, final_data.as_mut().unwrap());
|
|
}
|
|
old_return = ctx.return_target.replace(return_proxy);
|
|
cleanup = Some(ctx.ctx.append_basic_block(current_fun, "try.cleanup"));
|
|
}
|
|
|
|
let mut clauses = Vec::new();
|
|
let mut found_catch_all = false;
|
|
for handler_node in handlers.iter() {
|
|
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler_node.node;
|
|
// none or Exception
|
|
if type_.is_none()
|
|
|| ctx
|
|
.unifier
|
|
.unioned(type_.as_ref().unwrap().custom.unwrap(), ctx.primitives.exception)
|
|
{
|
|
clauses.push(None);
|
|
found_catch_all = true;
|
|
break;
|
|
} else {
|
|
let type_ = type_.as_ref().unwrap();
|
|
let exn_name = ctx.resolver.get_type_name(
|
|
&ctx.top_level.definitions.read(),
|
|
&mut ctx.unifier,
|
|
type_.custom.unwrap(),
|
|
);
|
|
let obj_id = if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(type_.custom.unwrap()) {
|
|
*obj_id
|
|
} else {
|
|
unreachable!()
|
|
};
|
|
let exception_name = format!("{}:{}", ctx.resolver.get_exception_id(obj_id.0), exn_name);
|
|
let exn_id = ctx.resolver.get_string_id(&exception_name);
|
|
let exn_id_global =
|
|
ctx.module.add_global(ctx.ctx.i32_type(), None, &format!("exn.{}", exn_id));
|
|
exn_id_global.set_initializer(&ctx.ctx.i32_type().const_int(exn_id as u64, false));
|
|
clauses.push(Some(exn_id_global.as_pointer_value().as_basic_value_enum()));
|
|
}
|
|
}
|
|
let mut all_clauses = clauses.clone();
|
|
if let Some(old_clauses) = &ctx.outer_catch_clauses {
|
|
if !found_catch_all {
|
|
all_clauses.extend_from_slice(&old_clauses.0)
|
|
}
|
|
}
|
|
let old_clauses = ctx.outer_catch_clauses.replace((all_clauses, dispatcher, exn));
|
|
let old_unwind = ctx.unwind_target.replace(landingpad);
|
|
gen_block(generator, ctx, body.iter())?;
|
|
if ctx.builder.get_insert_block().unwrap().get_terminator().is_none() {
|
|
gen_block(generator, ctx, orelse.iter())?;
|
|
}
|
|
let body = ctx.builder.get_insert_block().unwrap();
|
|
// reset old_clauses and old_unwind
|
|
let (all_clauses, _, _) = ctx.outer_catch_clauses.take().unwrap();
|
|
ctx.outer_catch_clauses = old_clauses;
|
|
ctx.unwind_target = old_unwind;
|
|
ctx.return_target = old_return;
|
|
ctx.loop_target = old_loop_target;
|
|
old_loop_target = None;
|
|
|
|
let old_unwind = if !finalbody.is_empty() {
|
|
let final_landingpad = ctx.ctx.append_basic_block(current_fun, "try.catch.final");
|
|
ctx.builder.position_at_end(final_landingpad);
|
|
ctx.builder.build_landing_pad(
|
|
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
|
|
personality,
|
|
&[],
|
|
true,
|
|
"try.catch.final",
|
|
);
|
|
ctx.builder.build_unconditional_branch(cleanup.unwrap());
|
|
ctx.builder.position_at_end(body);
|
|
ctx.unwind_target.replace(final_landingpad)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// run end_catch before continue/break/return
|
|
let mut final_proxy_lambda =
|
|
|ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
target: BasicBlock<'ctx>,
|
|
block: BasicBlock<'ctx>| final_proxy(ctx, target, block, final_data.as_mut().unwrap());
|
|
let mut redirect_lambda = |ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
target: BasicBlock<'ctx>,
|
|
block: BasicBlock<'ctx>| {
|
|
ctx.builder.position_at_end(block);
|
|
ctx.builder.build_unconditional_branch(target);
|
|
ctx.builder.position_at_end(body);
|
|
};
|
|
let redirect = if has_cleanup {
|
|
&mut final_proxy_lambda
|
|
as &mut dyn FnMut(&mut CodeGenContext<'ctx, 'a>, BasicBlock<'ctx>, BasicBlock<'ctx>)
|
|
} else {
|
|
&mut redirect_lambda
|
|
as &mut dyn FnMut(&mut CodeGenContext<'ctx, 'a>, BasicBlock<'ctx>, BasicBlock<'ctx>)
|
|
};
|
|
let resume = get_builtins(generator, ctx, "__nac3_resume");
|
|
let end_catch = get_builtins(generator, ctx, "__nac3_end_catch");
|
|
if let Some((continue_target, break_target)) = ctx.loop_target.take() {
|
|
let break_proxy = ctx.ctx.append_basic_block(current_fun, "try.break");
|
|
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "try.continue");
|
|
ctx.builder.position_at_end(break_proxy);
|
|
ctx.builder.build_call(end_catch, &[], "end_catch");
|
|
ctx.builder.position_at_end(continue_proxy);
|
|
ctx.builder.build_call(end_catch, &[], "end_catch");
|
|
ctx.builder.position_at_end(body);
|
|
redirect(ctx, break_target, break_proxy);
|
|
redirect(ctx, continue_target, continue_proxy);
|
|
ctx.loop_target = Some((continue_proxy, break_proxy));
|
|
old_loop_target = Some((continue_target, break_target));
|
|
}
|
|
let return_proxy = ctx.ctx.append_basic_block(current_fun, "try.return");
|
|
ctx.builder.position_at_end(return_proxy);
|
|
ctx.builder.build_call(end_catch, &[], "end_catch");
|
|
let return_target = ctx.return_target.take().unwrap_or_else(|| {
|
|
let doreturn = ctx.ctx.append_basic_block(current_fun, "try.doreturn");
|
|
ctx.builder.position_at_end(doreturn);
|
|
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret"));
|
|
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue));
|
|
doreturn
|
|
});
|
|
redirect(ctx, return_target, return_proxy);
|
|
ctx.return_target = Some(return_proxy);
|
|
old_return = Some(return_target);
|
|
|
|
let mut post_handlers = Vec::new();
|
|
|
|
let exnid = if !handlers.is_empty() {
|
|
ctx.builder.position_at_end(dispatcher);
|
|
unsafe {
|
|
let zero = ctx.ctx.i32_type().const_zero();
|
|
let exnid_ptr = ctx.builder.build_gep(
|
|
exn.as_basic_value().into_pointer_value(),
|
|
&[zero, zero],
|
|
"exnidptr",
|
|
);
|
|
Some(ctx.builder.build_load(exnid_ptr, "exnid"))
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
for (handler_node, exn_type) in handlers.iter().zip(clauses.iter()) {
|
|
let ExcepthandlerKind::ExceptHandler { type_, name, body } = &handler_node.node;
|
|
let handler_bb = ctx.ctx.append_basic_block(current_fun, "try.handler");
|
|
ctx.builder.position_at_end(handler_bb);
|
|
if let Some(name) = name {
|
|
let exn_ty = ctx.get_llvm_type(generator, type_.as_ref().unwrap().custom.unwrap());
|
|
let exn_store = generator.gen_var_alloc(ctx, exn_ty, Some("try.exn_store.addr"))?;
|
|
ctx.var_assignment.insert(*name, (exn_store, None, 0));
|
|
ctx.builder.build_store(exn_store, exn.as_basic_value());
|
|
}
|
|
gen_block(generator, ctx, body.iter())?;
|
|
let current = ctx.builder.get_insert_block().unwrap();
|
|
// only need to call end catch if not terminated
|
|
// otherwise, we already handled in return/break/continue/raise
|
|
if current.get_terminator().is_none() {
|
|
ctx.builder.build_call(end_catch, &[], "end_catch");
|
|
}
|
|
post_handlers.push(current);
|
|
ctx.builder.position_at_end(dispatcher_end);
|
|
if let Some(exn_type) = exn_type {
|
|
let dispatcher_cont =
|
|
ctx.ctx.append_basic_block(current_fun, "try.dispatcher_cont");
|
|
let actual_id = exnid.unwrap().into_int_value();
|
|
let expected_id = ctx
|
|
.builder
|
|
.build_load(exn_type.into_pointer_value(), "expected_id")
|
|
.into_int_value();
|
|
let result = ctx.builder.build_int_compare(IntPredicate::EQ, actual_id, expected_id, "exncheck");
|
|
ctx.builder.build_conditional_branch(result, handler_bb, dispatcher_cont);
|
|
dispatcher_end = dispatcher_cont;
|
|
} else {
|
|
ctx.builder.build_unconditional_branch(handler_bb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ctx.unwind_target = old_unwind;
|
|
ctx.loop_target = old_loop_target;
|
|
ctx.return_target = old_return;
|
|
|
|
ctx.builder.position_at_end(landingpad);
|
|
let clauses: Vec<_> = if finalbody.is_empty() { &all_clauses } else { &clauses }
|
|
.iter()
|
|
.map(|v| v.unwrap_or(ptr_type.const_zero().into()))
|
|
.collect();
|
|
let landingpad_value = ctx
|
|
.builder
|
|
.build_landing_pad(
|
|
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
|
|
personality,
|
|
&clauses,
|
|
has_cleanup,
|
|
"try.landingpad",
|
|
)
|
|
.into_struct_value();
|
|
let exn_val = ctx.builder.build_extract_value(landingpad_value, 1, "exn").unwrap();
|
|
ctx.builder.build_unconditional_branch(dispatcher);
|
|
exn.add_incoming(&[(&exn_val, landingpad)]);
|
|
|
|
if dispatcher_end.get_terminator().is_none() {
|
|
ctx.builder.position_at_end(dispatcher_end);
|
|
if let Some(cleanup) = cleanup {
|
|
ctx.builder.build_unconditional_branch(cleanup);
|
|
} else if let Some((_, outer_dispatcher, phi)) = ctx.outer_catch_clauses {
|
|
phi.add_incoming(&[(&exn_val, dispatcher_end)]);
|
|
ctx.builder.build_unconditional_branch(outer_dispatcher);
|
|
} else {
|
|
ctx.build_call_or_invoke(resume, &[], "resume");
|
|
ctx.builder.build_unreachable();
|
|
}
|
|
}
|
|
|
|
if finalbody.is_empty() {
|
|
let tail = ctx.ctx.append_basic_block(current_fun, "try.tail");
|
|
if body.get_terminator().is_none() {
|
|
ctx.builder.position_at_end(body);
|
|
ctx.builder.build_unconditional_branch(tail);
|
|
}
|
|
if matches!(cleanup, Some(cleanup) if cleanup.get_terminator().is_none()) {
|
|
ctx.builder.position_at_end(cleanup.unwrap());
|
|
ctx.builder.build_unconditional_branch(tail);
|
|
}
|
|
for post_handler in post_handlers {
|
|
if post_handler.get_terminator().is_none() {
|
|
ctx.builder.position_at_end(post_handler);
|
|
ctx.builder.build_unconditional_branch(tail);
|
|
}
|
|
}
|
|
ctx.builder.position_at_end(tail);
|
|
} else {
|
|
// exception path
|
|
let cleanup = cleanup.unwrap();
|
|
ctx.builder.position_at_end(cleanup);
|
|
gen_block(generator, ctx, finalbody.iter())?;
|
|
if !ctx.is_terminated() {
|
|
ctx.build_call_or_invoke(resume, &[], "resume");
|
|
ctx.builder.build_unreachable();
|
|
}
|
|
|
|
// normal path
|
|
let (final_state, mut final_targets, final_paths) = final_data.unwrap();
|
|
let tail = ctx.ctx.append_basic_block(current_fun, "try.tail");
|
|
final_targets.push(tail);
|
|
let finalizer = ctx.ctx.append_basic_block(current_fun, "try.finally");
|
|
ctx.builder.position_at_end(finalizer);
|
|
gen_block(generator, ctx, finalbody.iter())?;
|
|
if !ctx.is_terminated() {
|
|
let dest = ctx.builder.build_load(final_state, "final_dest");
|
|
ctx.builder.build_indirect_branch(dest, &final_targets);
|
|
}
|
|
for block in final_paths.iter() {
|
|
if block.get_terminator().is_none() {
|
|
ctx.builder.position_at_end(*block);
|
|
ctx.builder.build_unconditional_branch(finalizer);
|
|
}
|
|
}
|
|
for block in [body].iter().chain(post_handlers.iter()) {
|
|
if block.get_terminator().is_none() {
|
|
ctx.builder.position_at_end(*block);
|
|
unsafe {
|
|
ctx.builder.build_store(final_state, tail.get_address().unwrap());
|
|
}
|
|
ctx.builder.build_unconditional_branch(finalizer);
|
|
}
|
|
}
|
|
ctx.builder.position_at_end(tail);
|
|
}
|
|
Ok(())
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
|
|
pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
|
|
_: &mut G,
|
|
_: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String> {
|
|
// TODO: Implement with statement after finishing exceptions
|
|
Err(format!("With statement with custom types is not yet supported (at {})", stmt.location))
|
|
}
|
|
|
|
pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
value: &Option<Box<Expr<Option<Type>>>>,
|
|
) -> Result<(), String> {
|
|
let value = value
|
|
.as_ref()
|
|
.map(|v_expr| {
|
|
generator.gen_expr(ctx, v_expr).and_then(|v| {
|
|
v.unwrap().to_basic_value_enum(ctx, generator, v_expr.custom.unwrap())
|
|
})
|
|
})
|
|
.transpose()?;
|
|
if let Some(return_target) = ctx.return_target {
|
|
if let Some(value) = value {
|
|
ctx.builder.build_store(ctx.return_buffer.unwrap(), value);
|
|
}
|
|
ctx.builder.build_unconditional_branch(return_target);
|
|
} else if ctx.need_sret {
|
|
// sret
|
|
ctx.builder.build_store(ctx.return_buffer.unwrap(), value.unwrap());
|
|
ctx.builder.build_return(None);
|
|
} else {
|
|
let value = value.as_ref().map(|v| v as &dyn BasicValue);
|
|
ctx.builder.build_return(value);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String> {
|
|
ctx.current_loc = stmt.location;
|
|
|
|
let loc = ctx.debug_info.0.create_debug_location(
|
|
ctx.ctx,
|
|
ctx.current_loc.row as u32,
|
|
ctx.current_loc.column as u32,
|
|
ctx.debug_info.2,
|
|
None,
|
|
);
|
|
ctx.builder.set_current_debug_location(loc);
|
|
|
|
match &stmt.node {
|
|
StmtKind::Pass { .. } => {}
|
|
StmtKind::Expr { value, .. } => {
|
|
generator.gen_expr(ctx, value)?;
|
|
}
|
|
StmtKind::Return { value, .. } => {
|
|
gen_return(generator, ctx, value)?;
|
|
}
|
|
StmtKind::AnnAssign { target, value, .. } => {
|
|
if let Some(value) = value {
|
|
let value = generator.gen_expr(ctx, value)?.unwrap();
|
|
generator.gen_assign(ctx, target, value)?;
|
|
}
|
|
}
|
|
StmtKind::Assign { targets, value, .. } => {
|
|
let value = generator.gen_expr(ctx, value)?.unwrap();
|
|
for target in targets.iter() {
|
|
generator.gen_assign(ctx, target, value.clone())?;
|
|
}
|
|
}
|
|
StmtKind::Continue { .. } => {
|
|
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().0);
|
|
}
|
|
StmtKind::Break { .. } => {
|
|
ctx.builder.build_unconditional_branch(ctx.loop_target.unwrap().1);
|
|
}
|
|
StmtKind::If { .. } => generator.gen_if(ctx, stmt)?,
|
|
StmtKind::While { .. } => generator.gen_while(ctx, stmt)?,
|
|
StmtKind::For { .. } => generator.gen_for(ctx, stmt)?,
|
|
StmtKind::With { .. } => generator.gen_with(ctx, stmt)?,
|
|
StmtKind::AugAssign { target, op, value, .. } => {
|
|
let value = gen_binop_expr(generator, ctx, target, op, value, stmt.location, true)?;
|
|
generator.gen_assign(ctx, target, value.unwrap())?;
|
|
}
|
|
StmtKind::Try { .. } => gen_try(generator, ctx, stmt)?,
|
|
StmtKind::Raise { exc, .. } => {
|
|
if let Some(exc) = exc {
|
|
let exc = generator.gen_expr(ctx, exc)?.unwrap().to_basic_value_enum(
|
|
ctx,
|
|
generator,
|
|
exc.custom.unwrap(),
|
|
)?;
|
|
gen_raise(generator, ctx, Some(&exc), stmt.location);
|
|
} else {
|
|
gen_raise(generator, ctx, None, stmt.location);
|
|
}
|
|
}
|
|
StmtKind::Assert { test, msg, .. } => {
|
|
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
|
|
ctx,
|
|
generator,
|
|
test.custom.unwrap(),
|
|
)?;
|
|
let err_msg = match msg {
|
|
Some(msg) => generator.gen_expr(ctx, msg)?.unwrap().to_basic_value_enum(
|
|
ctx,
|
|
generator,
|
|
msg.custom.unwrap(),
|
|
)?,
|
|
None => ctx.gen_string(generator, ""),
|
|
};
|
|
ctx.make_assert_impl(
|
|
generator,
|
|
test.into_int_value(),
|
|
"0:AssertionError",
|
|
err_msg,
|
|
[None, None, None],
|
|
stmt.location,
|
|
)
|
|
}
|
|
_ => unimplemented!()
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
pub fn gen_block<'ctx, 'a, 'b, G: CodeGenerator, I: Iterator<Item = &'b Stmt<Option<Type>>>>(
|
|
generator: &mut G,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmts: I,
|
|
) -> Result<(), String> {
|
|
for stmt in stmts {
|
|
generator.gen_stmt(ctx, stmt)?;
|
|
if ctx.is_terminated() {
|
|
break;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|