From f698b0c1faacfea7cd1398b00e0e69f521ccc7d3 Mon Sep 17 00:00:00 2001 From: lyken Date: Sun, 25 Aug 2024 16:55:56 +0800 Subject: [PATCH] core/model: refactor CSlice and Exception with models --- nac3artiq/src/codegen.rs | 36 ++-- nac3artiq/src/symbol_resolver.rs | 4 +- nac3core/src/codegen/expr.rs | 124 ++++++------- nac3core/src/codegen/mod.rs | 42 ++--- nac3core/src/codegen/object/cslice.rs | 24 +++ nac3core/src/codegen/object/exception.rs | 41 +++++ nac3core/src/codegen/object/mod.rs | 3 + .../src/codegen/object/ndarray/factory.rs | 4 +- nac3core/src/codegen/object/ndarray/str.rs | 3 + nac3core/src/codegen/object/str.rs | 11 ++ nac3core/src/codegen/stmt.rs | 170 ++++++++---------- 11 files changed, 247 insertions(+), 215 deletions(-) create mode 100644 nac3core/src/codegen/object/cslice.rs create mode 100644 nac3core/src/codegen/object/exception.rs create mode 100644 nac3core/src/codegen/object/ndarray/str.rs create mode 100644 nac3core/src/codegen/object/str.rs diff --git a/nac3artiq/src/codegen.rs b/nac3artiq/src/codegen.rs index cca41bbf..4b157840 100644 --- a/nac3artiq/src/codegen.rs +++ b/nac3artiq/src/codegen.rs @@ -4,7 +4,7 @@ use nac3core::{ expr::gen_call, llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave}, model::*, - object::{any::AnyObject, ndarray::NDArrayObject, range::RangeObject}, + object::{any::AnyObject, ndarray::NDArrayObject, range::RangeObject, str::str_model}, stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with}, CodeGenContext, CodeGenerator, }, @@ -877,12 +877,12 @@ fn polymorphic_print<'ctx>( }); let fmt = ctx.gen_string(generator, fmt); - let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value(); + let fmt = fmt.get_field(generator, ctx.ctx, |f| f.base); ctx.builder .build_call( print_fn, - &once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(), + &once(fmt.value.into()).chain(args).map(BasicValueEnum::into).collect_vec(), "", ) .unwrap(); @@ -957,20 +957,23 @@ fn polymorphic_print<'ctx>( fmt.push_str("%.*s"); let true_str = ctx.gen_string(generator, "True"); - let true_data = - unsafe { true_str.get_field_at_index_unchecked(0) }.into_pointer_value(); - let true_len = unsafe { true_str.get_field_at_index_unchecked(1) }.into_int_value(); let false_str = ctx.gen_string(generator, "False"); - let false_data = - unsafe { false_str.get_field_at_index_unchecked(0) }.into_pointer_value(); - let false_len = - unsafe { false_str.get_field_at_index_unchecked(1) }.into_int_value(); + + let true_data = true_str.get_field(generator, ctx.ctx, |f| f.base); + let true_len = true_str.get_field(generator, ctx.ctx, |f| f.len); + + let false_data = false_str.get_field(generator, ctx.ctx, |f| f.base); + let false_len = false_str.get_field(generator, ctx.ctx, |f| f.len); let bool_val = generator.bool_to_i1(ctx, value.into_int_value()); args.extend([ - ctx.builder.build_select(bool_val, true_len, false_len, "").unwrap(), - ctx.builder.build_select(bool_val, true_data, false_data, "").unwrap(), + ctx.builder + .build_select(bool_val, true_len.value, false_len.value, "") + .unwrap(), + ctx.builder + .build_select(bool_val, true_data.value, false_data.value, "") + .unwrap(), ]); } @@ -1008,11 +1011,12 @@ fn polymorphic_print<'ctx>( fmt.push_str("%.*s"); } - let str = value.into_struct_value(); - let str_data = unsafe { str.get_field_at_index_unchecked(0) }.into_pointer_value(); - let str_len = unsafe { str.get_field_at_index_unchecked(1) }.into_int_value(); + let str = str_model().check_value(generator, ctx.ctx, value).unwrap(); - args.extend(&[str_len.into(), str_data.into()]); + let str_data = str.get_field(generator, ctx.ctx, |f| f.base); + let str_len = str.get_field(generator, ctx.ctx, |f| f.len); + + args.extend(&[str_len.value.into(), str_data.value.into()]); } TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => { diff --git a/nac3artiq/src/symbol_resolver.rs b/nac3artiq/src/symbol_resolver.rs index 21b1ac10..cb82654e 100644 --- a/nac3artiq/src/symbol_resolver.rs +++ b/nac3artiq/src/symbol_resolver.rs @@ -166,7 +166,7 @@ impl StaticValue for PythonValue { PrimitiveValue::Bool(val) => { ctx.ctx.i8_type().const_int(u64::from(*val), false).into() } - PrimitiveValue::Str(val) => ctx.gen_string(generator, val).into(), + PrimitiveValue::Str(val) => ctx.gen_string(generator, val).value.into(), }); } if let Some(global) = ctx.module.get_global(&self.id.to_string()) { @@ -980,7 +980,7 @@ impl InnerResolver { } else if ty_id == self.primitive_ids.string || ty_id == self.primitive_ids.np_str_ { let val: String = obj.extract().unwrap(); self.id_to_primitive.write().insert(id, PrimitiveValue::Str(val.clone())); - Ok(Some(ctx.gen_string(generator, val).into())) + Ok(Some(ctx.gen_string(generator, val).value.into())) } else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 { let val: f64 = obj.extract().unwrap(); self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val)); diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 47437688..a7eb85e3 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -12,7 +12,11 @@ use crate::{ call_int_umin, call_memcpy_generic, }, need_sret, - object::ndarray::{NDArrayOut, ScalarOrNDArray}, + object::{ + exception::Exception, + ndarray::{NDArrayOut, ScalarOrNDArray}, + str::str_model, + }, stmt::{ gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise, gen_var, @@ -29,10 +33,7 @@ use crate::{ use inkwell::{ attributes::{Attribute, AttributeLoc}, types::{AnyType, BasicType, BasicTypeEnum}, - values::{ - BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue, - StructValue, - }, + values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue}, AddressSpace, IntPredicate, OptimizationLevel, }; use itertools::{chain, izip, Either, Itertools}; @@ -50,6 +51,7 @@ use super::{ any::AnyObject, ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject}, range::RangeObject, + str::Str, }, }; @@ -160,14 +162,8 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { SymbolValue::Bool(v) => self.ctx.i8_type().const_int(u64::from(*v), true).into(), SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(), SymbolValue::Str(v) => { - let str_ptr = self - .builder - .build_global_string_ptr(v, "const") - .map(|v| v.as_pointer_value().into()) - .unwrap(); - let size = generator.get_size_type(self.ctx).const_int(v.len() as u64, false); - let ty = self.get_llvm_type(generator, self.primitives.str).into_struct_type(); - ty.const_named_struct(&[str_ptr, size.into()]).into() + let string = self.gen_string(generator, v); + string.value.into() } SymbolValue::Tuple(ls) => { let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec(); @@ -310,21 +306,8 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { } Constant::Str(v) => { assert!(self.unifier.unioned(ty, self.primitives.str)); - if let Some(v) = self.const_strings.get(v) { - Some(*v) - } else { - let str_ptr = self - .builder - .build_global_string_ptr(v, "const") - .map(|v| v.as_pointer_value().into()) - .unwrap(); - let size = generator.get_size_type(self.ctx).const_int(v.len() as u64, false); - let ty = self.get_llvm_type(generator, self.primitives.str); - let val = - ty.into_struct_type().const_named_struct(&[str_ptr, size.into()]).into(); - self.const_strings.insert(v.to_string(), val); - Some(val) - } + let string = self.gen_string(generator, v); + Some(string.value.into()) } Constant::Ellipsis => { let msg = self.gen_string(generator, "NotImplementedError"); @@ -332,7 +315,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { self.raise_exn( generator, "0:NotImplementedError", - msg.into(), + msg, [None, None, None], self.current_loc, ); @@ -592,60 +575,59 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { } /// Helper function for generating a LLVM variable storing a [String]. - pub fn gen_string(&mut self, generator: &mut G, s: S) -> StructValue<'ctx> + pub fn gen_string(&mut self, generator: &mut G, string: S) -> Instance<'ctx, Str> where G: CodeGenerator + ?Sized, S: Into, { - self.gen_const(generator, &Constant::Str(s.into()), self.primitives.str) - .map(BasicValueEnum::into_struct_value) - .unwrap() + let string = string.into(); + self.const_strings.get(&string).copied().unwrap_or_else(|| { + let str_ptr = self.builder.build_global_string_ptr(&string, "const").unwrap(); + let str_ptr = str_ptr.as_basic_value_enum(); + + let str_len = Int(SizeT).const_int(generator, self.ctx, string.len() as u64); + let str_len = str_len.value.as_basic_value_enum(); + + let str = str_model().const_struct(generator, self.ctx, &[str_ptr, str_len]); + self.const_strings.insert(string, str); + str + }) } pub fn raise_exn( &mut self, generator: &mut G, name: &str, - msg: BasicValueEnum<'ctx>, - params: [Option>; 3], + msg: Instance<'ctx, Str>, + params: [Option>; 3], // Can have any bit-width. loc: Location, ) { - let zelf = if let Some(exception_val) = self.exception_val { - exception_val + let exn = if let Some(exn) = self.exception_val { + exn } else { - let ty = self.get_llvm_type(generator, self.primitives.exception).into_pointer_type(); - let zelf_ty: BasicTypeEnum = ty.get_element_type().into_struct_type().into(); - let zelf = generator.gen_var_alloc(self, zelf_ty, Some("exn")).unwrap(); - *self.exception_val.insert(zelf) + let exn = Struct(Exception).var_alloca(generator, self, Some("exn")).unwrap(); + *self.exception_val.insert(exn) }; - let int32 = self.ctx.i32_type(); - let zero = int32.const_zero(); - unsafe { - let id_ptr = self.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id").unwrap(); - let id = self.resolver.get_string_id(name); - self.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap(); - let ptr = self - .builder - .build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg") - .unwrap(); - self.builder.build_store(ptr, msg).unwrap(); - let i64_zero = self.ctx.i64_type().const_zero(); - for (i, attr_ind) in [6, 7, 8].iter().enumerate() { - let ptr = self - .builder - .build_in_bounds_gep( - zelf, - &[zero, int32.const_int(*attr_ind, false)], - "exn.param", - ) - .unwrap(); - let val = params[i].map_or(i64_zero, |v| { - self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").unwrap() - }); - self.builder.build_store(ptr, val).unwrap(); - } + + // Set exception ID + let id = self.resolver.get_string_id(name); + let id = Int(Int32).const_int(generator, self.ctx, id as u64); + exn.set(self, |f| f.id, id); + + // Set message + exn.set(self, |f| f.msg, msg); + + // Set parameters. + let num_0 = Int(Int64).const_0(generator, self.ctx); + for (i, param) in params.iter().enumerate() { + // Param can be of any bit-width. We need to cast them. + // Defaults to 0 if not provided + let param = param + .map_or(num_0, |param| Int(Int64).s_extend_or_bit_cast(generator, self, param)); + + exn.set(self, |f| f.params[i], param); } - gen_raise(generator, self, Some(&zelf.into()), loc); + gen_raise(generator, self, Some(exn), loc); } pub fn make_assert( @@ -658,7 +640,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { loc: Location, ) { let err_msg = self.gen_string(generator, err_msg); - self.make_assert_impl(generator, cond, err_name, err_msg.into(), params, loc); + self.make_assert_impl(generator, cond, err_name, err_msg, params, loc); } pub fn make_assert_impl( @@ -666,7 +648,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { generator: &mut G, cond: IntValue<'ctx>, err_name: &str, - err_msg: BasicValueEnum<'ctx>, + err_msg: Instance<'ctx, Str>, params: [Option>; 3], loc: Location, ) { @@ -2899,7 +2881,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>( ctx.raise_exn( generator, "0:UnwrapNoneError", - err_msg.into(), + err_msg, [None, None, None], ctx.current_loc, ); diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index d50a1bf7..284f1fdf 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -26,7 +26,12 @@ use inkwell::{ use itertools::Itertools; use model::*; use nac3parser::ast::{Location, Stmt, StrRef}; -use object::{ndarray::NDArray, range::range_model}; +use object::{ + exception::Exception, + ndarray::NDArray, + range::range_model, + str::{str_model, Str}, +}; use parking_lot::{Condvar, Mutex}; use std::collections::{HashMap, HashSet}; use std::sync::{ @@ -172,11 +177,11 @@ pub struct CodeGenContext<'ctx, 'a> { pub registry: &'a WorkerRegistry, /// Cache for constant strings. - pub const_strings: HashMap>, + pub const_strings: HashMap>, /// [`BasicBlock`] containing all `alloca` statements for the current function. pub init_bb: BasicBlock<'ctx>, - pub exception_val: Option>, + pub exception_val: Option>>>, /// The header and exit basic blocks of a loop in this context. See /// for explanation of these terminology. @@ -706,36 +711,9 @@ pub fn gen_func_impl< (primitives.uint64, context.i64_type().into()), (primitives.float, context.f64_type().into()), (primitives.bool, context.i8_type().into()), - (primitives.str, { - let name = "str"; - match module.get_struct_type(name) { - None => { - let str_type = context.opaque_struct_type("str"); - let fields = [ - context.i8_type().ptr_type(AddressSpace::default()).into(), - generator.get_size_type(context).into(), - ]; - str_type.set_body(&fields, false); - str_type.into() - } - Some(t) => t.as_basic_type_enum(), - } - }), + (primitives.str, str_model().get_type(generator, context).into()), (primitives.range, Ptr(range_model()).get_type(generator, context).into()), - (primitives.exception, { - let name = "Exception"; - if let Some(t) = module.get_struct_type(name) { - t.ptr_type(AddressSpace::default()).as_basic_type_enum() - } else { - let exception = context.opaque_struct_type("Exception"); - let int32 = context.i32_type().into(); - let int64 = context.i64_type().into(); - let str_ty = module.get_struct_type("str").unwrap().as_basic_type_enum(); - let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64]; - exception.set_body(&fields, false); - exception.ptr_type(AddressSpace::default()).as_basic_type_enum() - } - }), + (primitives.exception, { Ptr(Struct(Exception)).get_type(generator, context).into() }), ] .iter() .copied() diff --git a/nac3core/src/codegen/object/cslice.rs b/nac3core/src/codegen/object/cslice.rs new file mode 100644 index 00000000..311bd6c3 --- /dev/null +++ b/nac3core/src/codegen/object/cslice.rs @@ -0,0 +1,24 @@ +use crate::codegen::model::*; + +/// Fields of [`CSlice`] +pub struct CSliceFields<'ctx, F: FieldTraversal<'ctx>, Item: Model<'ctx>> { + /// Pointer to items + pub base: F::Out>, + /// Number of items (not bytes) + pub len: F::Out>, +} + +/// See . +/// +/// Additionally, see ) +/// for ARTIQ-specific notes. +#[derive(Debug, Clone, Copy, Default)] +pub struct CSlice(pub Item); + +impl<'ctx, Item: Model<'ctx>> StructKind<'ctx> for CSlice { + type Fields> = CSliceFields<'ctx, F, Item>; + + fn traverse_fields>(&self, traversal: &mut F) -> Self::Fields { + CSliceFields { base: traversal.add("base", Ptr(self.0)), len: traversal.add_auto("len") } + } +} diff --git a/nac3core/src/codegen/object/exception.rs b/nac3core/src/codegen/object/exception.rs new file mode 100644 index 00000000..72674072 --- /dev/null +++ b/nac3core/src/codegen/object/exception.rs @@ -0,0 +1,41 @@ +use crate::codegen::model::*; + +use super::str::Str; + +/// Fields of [`Exception<'ctx>`] +/// +/// The definition came from `pub struct Exception<'a>` in +/// . +pub struct ExceptionFields<'ctx, F: FieldTraversal<'ctx>> { + pub id: F::Out>, + pub filename: F::Out, + pub line: F::Out>, + pub column: F::Out>, + pub function: F::Out, + pub msg: F::Out, + pub params: [F::Out>; 3], +} + +/// nac3core & ARTIQ's Exception +#[derive(Debug, Clone, Copy, Default)] +pub struct Exception; + +impl<'ctx> StructKind<'ctx> for Exception { + type Fields> = ExceptionFields<'ctx, F>; + + fn traverse_fields>(&self, traversal: &mut F) -> Self::Fields { + Self::Fields { + id: traversal.add_auto("id"), + filename: traversal.add_auto("filename"), + line: traversal.add_auto("line"), + column: traversal.add_auto("column"), + function: traversal.add_auto("function"), + msg: traversal.add_auto("msg"), + params: [ + traversal.add_auto("params[0]"), + traversal.add_auto("params[1]"), + traversal.add_auto("params[2]"), + ], + } + } +} diff --git a/nac3core/src/codegen/object/mod.rs b/nac3core/src/codegen/object/mod.rs index c3bade4b..98574036 100644 --- a/nac3core/src/codegen/object/mod.rs +++ b/nac3core/src/codegen/object/mod.rs @@ -1,5 +1,8 @@ pub mod any; +pub mod cslice; +pub mod exception; pub mod list; pub mod ndarray; pub mod range; +pub mod str; pub mod tuple; diff --git a/nac3core/src/codegen/object/ndarray/factory.rs b/nac3core/src/codegen/object/ndarray/factory.rs index 04ee79ae..c1a74c0a 100644 --- a/nac3core/src/codegen/object/ndarray/factory.rs +++ b/nac3core/src/codegen/object/ndarray/factory.rs @@ -31,7 +31,7 @@ fn ndarray_zero_value<'ctx, G: CodeGenerator + ?Sized>( } else if ctx.unifier.unioned(dtype, ctx.primitives.bool) { ctx.ctx.bool_type().const_zero().into() } else if ctx.unifier.unioned(dtype, ctx.primitives.str) { - ctx.gen_string(generator, "").into() + ctx.gen_string(generator, "").value.into() } else { panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype)); } @@ -60,7 +60,7 @@ fn ndarray_one_value<'ctx, G: CodeGenerator + ?Sized>( } else if ctx.unifier.unioned(dtype, ctx.primitives.bool) { ctx.ctx.bool_type().const_int(1, false).into() } else if ctx.unifier.unioned(dtype, ctx.primitives.str) { - ctx.gen_string(generator, "1").into() + ctx.gen_string(generator, "1").value.into() } else { panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype)); } diff --git a/nac3core/src/codegen/object/ndarray/str.rs b/nac3core/src/codegen/object/ndarray/str.rs new file mode 100644 index 00000000..9924b74d --- /dev/null +++ b/nac3core/src/codegen/object/ndarray/str.rs @@ -0,0 +1,3 @@ + +pub fn str_type() { +} \ No newline at end of file diff --git a/nac3core/src/codegen/object/str.rs b/nac3core/src/codegen/object/str.rs new file mode 100644 index 00000000..5d67e1ec --- /dev/null +++ b/nac3core/src/codegen/object/str.rs @@ -0,0 +1,11 @@ +use super::cslice::CSlice; +use crate::codegen::model::*; + +/// A string in NAC3. +pub type Str = Struct>>; + +/// An alias for `Str::default()` +#[must_use] +pub fn str_model() -> Str { + Str::default() +} diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 115ca636..b2f51256 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -1,12 +1,15 @@ use super::{ super::symbol_resolver::ValueEnum, irrt::{handle_slice_indices, list_slice_assignment}, + model::*, object::{ any::AnyObject, + exception::Exception, ndarray::{ indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray, }, range::RangeObject, + str::str_model, }, CodeGenContext, CodeGenerator, }; @@ -30,9 +33,7 @@ use inkwell::{ IntPredicate, }; use itertools::{izip, Itertools}; -use nac3parser::ast::{ - Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef, -}; +use nac3parser::ast::{ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef}; /// See [`CodeGenerator::gen_var_alloc`]. pub fn gen_var<'ctx>( @@ -1247,65 +1248,58 @@ pub fn exn_constructor<'ctx>( mut args: Vec<(Option, ValueEnum<'ctx>)>, generator: &mut dyn CodeGenerator, ) -> Result>, 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) { + let (exn_ty, exn) = obj.unwrap(); + let exn = exn.to_basic_value_enum(ctx, generator, exn_ty)?; + let exn = Ptr(Struct(Exception)).check_value(generator, ctx.ctx, exn).unwrap(); + + // Get the Exception name `exn_name` of this Exception object. + let exn_def_id = if let TypeEnum::TObj { obj_id, .. } = &*ctx.unifier.get_ty(exn_ty) { obj_id.0 } else { unreachable!() }; let defs = ctx.top_level.definitions.read(); - let def = defs[zelf_id].read(); - let TopLevelDef::Class { name: zelf_name, .. } = &*def 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").unwrap(); - let id = ctx.resolver.get_string_id(&exception_name); - ctx.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap(); - let empty_string = - ctx.gen_const(generator, &Constant::Str(String::new()), ctx.primitives.str); - let ptr = ctx - .builder - .build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg") - .unwrap(); - let msg = if args.is_empty() { - empty_string.unwrap() - } else { - args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)? - }; - ctx.builder.build_store(ptr, msg).unwrap(); - for i in &[6, 7, 8] { - let value = if args.is_empty() { - ctx.ctx.i64_type().const_zero().into() - } else { - args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.int64)? - }; - let ptr = ctx - .builder - .build_in_bounds_gep(zelf, &[zero, int32.const_int(*i, false)], "exn.param") - .unwrap(); - ctx.builder.build_store(ptr, value).unwrap(); - } - // set file, func to empty string - for i in &[1, 4] { - let ptr = ctx - .builder - .build_in_bounds_gep(zelf, &[zero, int32.const_int(*i, false)], "exn.str") - .unwrap(); - ctx.builder.build_store(ptr, empty_string.unwrap()).unwrap(); - } - // set ints to zero - for i in &[2, 3] { - let ptr = ctx - .builder - .build_in_bounds_gep(zelf, &[zero, int32.const_int(*i, false)], "exn.ints") - .unwrap(); - ctx.builder.build_store(ptr, zero).unwrap(); - } + let exn_def = defs[exn_def_id].read(); + let TopLevelDef::Class { name: exn_name, .. } = &*exn_def else { unreachable!() }; + let exn_name = format!("{}:{}", ctx.resolver.get_exception_id(exn_def_id), exn_name); + + // Initialize the fields of the Exception object. + + let empty_str = ctx.gen_string(generator, ""); + let num_0 = Int(Int32).const_0(generator, ctx.ctx); + + // Initialize `self.id`. + let id = ctx.resolver.get_string_id(&exn_name); + let id = Int(Int32).const_int(generator, ctx.ctx, id as u64); + exn.set(ctx, |f| f.id, id); + + // Initialize `self.msg`. + let msg = if args.is_empty() { + // Default to `msg` to "" if the user didn't pass anything. + empty_str + } else { + let msg = args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?; + str_model().check_value(generator, ctx.ctx, msg).unwrap() + }; + exn.set(ctx, |f| f.msg, msg); + + // Initialize `self.params`, the arguments after `msg` are the params. + for (i, (_, param)) in args.into_iter().enumerate() { + assert!(i <= 3, "There should only be at most 3 exception parameters"); + + let param = param.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?; + let param = Int(Int64).check_value(generator, ctx.ctx, param).unwrap(); + + exn.set(ctx, |f| f.params[i], param); } - Ok(Some(zelf.into())) + + // Initialize everything else to 0 or "". + exn.set(ctx, |f| f.line, num_0); + exn.set(ctx, |f| f.column, num_0); + exn.set(ctx, |f| f.function, empty_str); + exn.set(ctx, |f| f.filename, empty_str); + + Ok(Some(exn.value.into())) } /// Generates IR for a `raise` statement. @@ -1315,43 +1309,27 @@ pub fn exn_constructor<'ctx>( pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>( generator: &mut G, ctx: &mut CodeGenContext<'ctx, '_>, - exception: Option<&BasicValueEnum<'ctx>>, + exception: Option>>>, 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") - .unwrap(); - let filename = ctx.gen_string(generator, loc.file.0); - ctx.builder.build_store(file_ptr, filename).unwrap(); - let row_ptr = ctx - .builder - .build_in_bounds_gep(exception, &[zero, int32.const_int(2, false)], "row_ptr") - .unwrap(); - ctx.builder.build_store(row_ptr, int32.const_int(loc.row as u64, false)).unwrap(); - let col_ptr = ctx - .builder - .build_in_bounds_gep(exception, &[zero, int32.const_int(3, false)], "col_ptr") - .unwrap(); - ctx.builder.build_store(col_ptr, int32.const_int(loc.column as u64, false)).unwrap(); + if let Some(exn) = exception { + let filename = loc.file.0; + let filename = ctx.gen_string(generator, filename); + exn.set(ctx, |f| f.filename, filename); - 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") - .unwrap(); - ctx.builder.build_store(name_ptr, fun_name).unwrap(); - } + let row = Int(Int32).const_int(generator, ctx.ctx, loc.row as u64); + exn.set(ctx, |f| f.line, row); + + let column = Int(Int32).const_int(generator, ctx.ctx, loc.column as u64); + exn.set(ctx, |f| f.column, column); + + let current_fn = ctx.builder.get_insert_block().unwrap().get_parent().unwrap(); + let current_fn_name = current_fn.get_name().to_str().unwrap(); + let current_fn_name = ctx.gen_string(generator, current_fn_name); + exn.set(ctx, |f| f.function, current_fn_name); let raise = get_builtins(generator, ctx, "__nac3_raise"); - let exception = *exception; - ctx.build_call_or_invoke(raise, &[exception], "raise"); + ctx.build_call_or_invoke(raise, &[exn.value.into()], "raise"); } else { let resume = get_builtins(generator, ctx, "__nac3_resume"); ctx.build_call_or_invoke(resume, &[], "resume"); @@ -1817,7 +1795,9 @@ pub fn gen_stmt( } else { return Ok(()); }; - gen_raise(generator, ctx, Some(&exc), stmt.location); + + let exc = Ptr(Struct(Exception)).check_value(generator, ctx.ctx, exc).unwrap(); + gen_raise(generator, ctx, Some(exc), stmt.location); } else { gen_raise(generator, ctx, None, stmt.location); } @@ -1830,13 +1810,19 @@ pub fn gen_stmt( }; let err_msg = match msg { Some(msg) => { - if let Some(v) = generator.gen_expr(ctx, msg)? { - v.to_basic_value_enum(ctx, generator, msg.custom.unwrap())? + let msg_ty = msg.custom.unwrap(); + if let Some(msg) = generator.gen_expr(ctx, msg)? { + let msg = msg.to_basic_value_enum(ctx, generator, msg_ty)?; + let msg = str_model().check_value(generator, ctx.ctx, msg).unwrap(); + msg } else { return Ok(()); } } - None => ctx.gen_string(generator, "").into(), + None => { + // Return an empty string. + ctx.gen_string(generator, "") + } }; ctx.make_assert_impl( generator,