diff --git a/nac3core/irrt/irrt.cpp b/nac3core/irrt/irrt.cpp index 1093e8e0..5eafe013 100644 --- a/nac3core/irrt/irrt.cpp +++ b/nac3core/irrt/irrt.cpp @@ -2,6 +2,7 @@ #include "irrt/list.hpp" #include "irrt/math.hpp" #include "irrt/ndarray.hpp" +#include "irrt/range.hpp" #include "irrt/slice.hpp" #include "irrt/ndarray/basic.hpp" #include "irrt/ndarray/def.hpp" diff --git a/nac3core/irrt/irrt/range.hpp b/nac3core/irrt/irrt/range.hpp new file mode 100644 index 00000000..e9d4e612 --- /dev/null +++ b/nac3core/irrt/irrt/range.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "irrt/debug.hpp" +#include "irrt/int_types.hpp" + +namespace { +namespace range { +template +T len(T start, T stop, T step) { + // Reference: + // https://github.com/python/cpython/blob/9dbd12375561a393eaec4b21ee4ac568a407cdb0/Objects/rangeobject.c#L933 + if (step > 0 && start < stop) + return 1 + (stop - 1 - start) / step; + else if (step < 0 && start > stop) + return 1 + (start - 1 - stop) / (-step); + else + return 0; +} +} // namespace range + +/** + * @brief A Python range. + */ +template +struct Range { + T start; + T stop; + T step; + + /** + * @brief Calculate the `len()` of this range. + */ + template + T len() { + debug_assert(SizeT, step != 0); + return range::len(start, stop, step); + } +}; +} // namespace + +extern "C" { +using namespace range; + +SliceIndex __nac3_range_slice_len(const SliceIndex start, const SliceIndex end, const SliceIndex step) { + return len(start, end, step); +} +} \ No newline at end of file diff --git a/nac3core/irrt/irrt/slice.hpp b/nac3core/irrt/irrt/slice.hpp index 3f6d83a3..4cf13e05 100644 --- a/nac3core/irrt/irrt/slice.hpp +++ b/nac3core/irrt/irrt/slice.hpp @@ -1,6 +1,145 @@ #pragma once +#include "irrt/debug.hpp" +#include "irrt/exception.hpp" #include "irrt/int_types.hpp" +#include "irrt/math_util.hpp" +#include "irrt/range.hpp" + +namespace { +namespace slice { +/** + * @brief Resolve a possibly negative index in a list of a known length. + * + * Returns -1 if the resolved index is out of the list's bounds. + */ +template +T resolve_index_in_length(T length, T index) { + T resolved = index < 0 ? length + index : index; + if (0 <= resolved && resolved < length) { + return resolved; + } else { + return -1; + } +} + +/** + * @brief Resolve a slice as a range. + * + * This is equivalent to `range(*slice(start, stop, step).indices(length))` in Python. + */ +template +void indices(bool start_defined, + T start, + bool stop_defined, + T stop, + bool step_defined, + T step, + T length, + T* range_start, + T* range_stop, + T* range_step) { + // Reference: https://github.com/python/cpython/blob/main/Objects/sliceobject.c#L388 + *range_step = step_defined ? step : 1; + bool step_is_negative = *range_step < 0; + + T lower, upper; + if (step_is_negative) { + lower = -1; + upper = length - 1; + } else { + lower = 0; + upper = length; + } + + if (start_defined) { + *range_start = start < 0 ? max(lower, start + length) : min(upper, start); + } else { + *range_start = step_is_negative ? upper : lower; + } + + if (stop_defined) { + *range_stop = stop < 0 ? max(lower, stop + length) : min(upper, stop); + } else { + *range_stop = step_is_negative ? lower : upper; + } +} +} // namespace slice + +/** + * @brief A Python-like slice with **unresolved** indices. + */ +template +struct Slice { + bool start_defined; + T start; + + bool stop_defined; + T stop; + + bool step_defined; + T step; + + Slice() { this->reset(); } + + void reset() { + this->start_defined = false; + this->stop_defined = false; + this->step_defined = false; + } + + void set_start(T start) { + this->start_defined = true; + this->start = start; + } + + void set_stop(T stop) { + this->stop_defined = true; + this->stop = stop; + } + + void set_step(T step) { + this->step_defined = true; + this->step = step; + } + + /** + * @brief Resolve this slice as a range. + * + * In Python, this would be `range(*slice(start, stop, step).indices(length))`. + */ + template + Range indices(T length) { + // Reference: + // https://github.com/python/cpython/blob/main/Objects/sliceobject.c#L388 + debug_assert(SizeT, length >= 0); + + Range result; + slice::indices(start_defined, start, stop_defined, stop, step_defined, step, length, &result.start, + &result.stop, &result.step); + return result; + } + + /** + * @brief Like `.indices()` but with assertions. + */ + template + Range indices_checked(T length) { + // TODO: Switch to `SizeT length` + + if (length < 0) { + raise_exception(SizeT, EXN_VALUE_ERROR, "length should not be negative, got {0}", length, NO_PARAM, + NO_PARAM); + } + + if (this->step_defined && this->step == 0) { + raise_exception(SizeT, EXN_VALUE_ERROR, "slice step cannot be zero", NO_PARAM, NO_PARAM, NO_PARAM); + } + + return this->indices(length); + } +}; +} // namespace extern "C" { SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) { @@ -14,15 +153,4 @@ SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) { } return i; } - -SliceIndex __nac3_range_slice_len(const SliceIndex start, const SliceIndex end, const SliceIndex step) { - SliceIndex diff = end - start; - if (diff > 0 && step > 0) { - return ((diff - 1) / step) + 1; - } else if (diff < 0 && step < 0) { - return ((diff + 1) / step) + 1; - } else { - return 0; - } } -} // namespace \ No newline at end of file diff --git a/nac3core/src/codegen/irrt/mod.rs b/nac3core/src/codegen/irrt/mod.rs index ed922db5..824921cd 100644 --- a/nac3core/src/codegen/irrt/mod.rs +++ b/nac3core/src/codegen/irrt/mod.rs @@ -13,11 +13,13 @@ use super::{CodeGenContext, CodeGenerator}; use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type}; pub use list::*; pub use math::*; +pub use range::*; pub use slice::*; mod list; mod math; pub mod ndarray; +mod range; mod slice; #[must_use] diff --git a/nac3core/src/codegen/irrt/range.rs b/nac3core/src/codegen/irrt/range.rs new file mode 100644 index 00000000..47c63c4f --- /dev/null +++ b/nac3core/src/codegen/irrt/range.rs @@ -0,0 +1,42 @@ +use inkwell::{ + values::{BasicValueEnum, CallSiteValue, IntValue}, + IntPredicate, +}; +use itertools::Either; + +use crate::codegen::{CodeGenContext, CodeGenerator}; + +pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + start: IntValue<'ctx>, + end: IntValue<'ctx>, + step: IntValue<'ctx>, +) -> IntValue<'ctx> { + const SYMBOL: &str = "__nac3_range_slice_len"; + let len_func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| { + let i32_t = ctx.ctx.i32_type(); + let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into(), i32_t.into()], false); + ctx.module.add_function(SYMBOL, fn_t, None) + }); + + // assert step != 0, throw exception if not + let not_zero = ctx + .builder + .build_int_compare(IntPredicate::NE, step, step.get_type().const_zero(), "range_step_ne") + .unwrap(); + ctx.make_assert( + generator, + not_zero, + "0:ValueError", + "step must not be zero", + [None, None, None], + ctx.current_loc, + ); + ctx.builder + .build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len") + .map(CallSiteValue::try_as_basic_value) + .map(|v| v.map_left(BasicValueEnum::into_int_value)) + .map(Either::unwrap_left) + .unwrap() +} diff --git a/nac3core/src/codegen/irrt/slice.rs b/nac3core/src/codegen/irrt/slice.rs index eb7037ac..35e21513 100644 --- a/nac3core/src/codegen/irrt/slice.rs +++ b/nac3core/src/codegen/irrt/slice.rs @@ -1,8 +1,6 @@ -use inkwell::{ - values::{BasicValueEnum, CallSiteValue, IntValue}, - IntPredicate, -}; +use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue}; use itertools::Either; + use nac3parser::ast::Expr; use crate::{ @@ -39,38 +37,3 @@ pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>( .unwrap(), )) } - -pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>( - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - start: IntValue<'ctx>, - end: IntValue<'ctx>, - step: IntValue<'ctx>, -) -> IntValue<'ctx> { - const SYMBOL: &str = "__nac3_range_slice_len"; - let len_func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| { - let i32_t = ctx.ctx.i32_type(); - let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into(), i32_t.into()], false); - ctx.module.add_function(SYMBOL, fn_t, None) - }); - - // assert step != 0, throw exception if not - let not_zero = ctx - .builder - .build_int_compare(IntPredicate::NE, step, step.get_type().const_zero(), "range_step_ne") - .unwrap(); - ctx.make_assert( - generator, - not_zero, - "0:ValueError", - "step must not be zero", - [None, None, None], - ctx.current_loc, - ); - ctx.builder - .build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len") - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_int_value)) - .map(Either::unwrap_left) - .unwrap() -} diff --git a/nac3core/src/codegen/types/mod.rs b/nac3core/src/codegen/types/mod.rs index 06baaa7b..022f897b 100644 --- a/nac3core/src/codegen/types/mod.rs +++ b/nac3core/src/codegen/types/mod.rs @@ -29,6 +29,7 @@ mod list; pub mod ndarray; mod range; pub mod structure; +pub mod utils; /// A LLVM type that is used to represent a corresponding type in NAC3. pub trait ProxyType<'ctx>: Into { diff --git a/nac3core/src/codegen/types/utils/mod.rs b/nac3core/src/codegen/types/utils/mod.rs new file mode 100644 index 00000000..d4137768 --- /dev/null +++ b/nac3core/src/codegen/types/utils/mod.rs @@ -0,0 +1,3 @@ +pub use slice::*; + +mod slice; diff --git a/nac3core/src/codegen/types/utils/slice.rs b/nac3core/src/codegen/types/utils/slice.rs new file mode 100644 index 00000000..aba0efb2 --- /dev/null +++ b/nac3core/src/codegen/types/utils/slice.rs @@ -0,0 +1,254 @@ +use inkwell::{ + context::{AsContextRef, Context, ContextRef}, + types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType}, + values::IntValue, + AddressSpace, +}; +use itertools::Itertools; + +use nac3core_derive::StructFields; + +use crate::codegen::{ + types::{ + structure::{ + check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields, + }, + ProxyType, + }, + values::{utils::SliceValue, ArraySliceValue, ProxyValue}, + CodeGenContext, CodeGenerator, +}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct SliceType<'ctx> { + ty: PointerType<'ctx>, + int_ty: IntType<'ctx>, + llvm_usize: IntType<'ctx>, +} + +#[derive(PartialEq, Eq, Clone, Copy, StructFields)] +pub struct SliceFields<'ctx> { + #[value_type(bool_type())] + pub start_defined: StructField<'ctx, IntValue<'ctx>>, + #[value_type(usize)] + pub start: StructField<'ctx, IntValue<'ctx>>, + #[value_type(bool_type())] + pub stop_defined: StructField<'ctx, IntValue<'ctx>>, + #[value_type(usize)] + pub stop: StructField<'ctx, IntValue<'ctx>>, + #[value_type(bool_type())] + pub step_defined: StructField<'ctx, IntValue<'ctx>>, + #[value_type(usize)] + pub step: StructField<'ctx, IntValue<'ctx>>, +} + +impl<'ctx> SliceFields<'ctx> { + /// Creates a new instance of [`SliceFields`] with a custom integer type for its range values. + #[must_use] + pub fn new_sized(ctx: &impl AsContextRef<'ctx>, int_ty: IntType<'ctx>) -> Self { + let ctx = unsafe { ContextRef::new(ctx.as_ctx_ref()) }; + let mut counter = FieldIndexCounter::default(); + + SliceFields { + start_defined: StructField::create(&mut counter, "start_defined", ctx.bool_type()), + start: StructField::create(&mut counter, "start", int_ty), + stop_defined: StructField::create(&mut counter, "stop_defined", ctx.bool_type()), + stop: StructField::create(&mut counter, "stop", int_ty), + step_defined: StructField::create(&mut counter, "step_defined", ctx.bool_type()), + step: StructField::create(&mut counter, "step", int_ty), + } + } +} + +impl<'ctx> SliceType<'ctx> { + /// Checks whether `llvm_ty` represents a `slice` type, returning [Err] if it does not. + pub fn is_representable( + llvm_ty: PointerType<'ctx>, + llvm_usize: IntType<'ctx>, + ) -> Result<(), String> { + let ctx = llvm_ty.get_context(); + + let fields = SliceFields::new(ctx, llvm_usize); + + let llvm_ty = llvm_ty.get_element_type(); + let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else { + return Err(format!("Expected struct type for `Slice` type, got {llvm_ty}")); + }; + + check_struct_type_matches_fields( + fields, + llvm_ty, + "Slice", + &[ + (fields.start.name(), &|ty| { + if ty.is_int_type() { + Ok(()) + } else { + Err(format!("Expected int type for `Slice.start`, got {ty}")) + } + }), + (fields.stop.name(), &|ty| { + if ty.is_int_type() { + Ok(()) + } else { + Err(format!("Expected int type for `Slice.stop`, got {ty}")) + } + }), + (fields.step.name(), &|ty| { + if ty.is_int_type() { + Ok(()) + } else { + Err(format!("Expected int type for `Slice.step`, got {ty}")) + } + }), + ], + ) + } + + // TODO: Move this into e.g. StructProxyType + #[must_use] + pub fn get_fields(&self) -> SliceFields<'ctx> { + SliceFields::new_sized(&self.int_ty.get_context(), self.int_ty) + } + + /// Creates an LLVM type corresponding to the expected structure of a `Slice`. + #[must_use] + fn llvm_type(ctx: &'ctx Context, int_ty: IntType<'ctx>) -> PointerType<'ctx> { + let field_tys = SliceFields::new_sized(&int_ty.get_context(), int_ty) + .into_iter() + .map(|field| field.1) + .collect_vec(); + + ctx.struct_type(&field_tys, false).ptr_type(AddressSpace::default()) + } + + /// Creates an instance of [`SliceType`] with `int_ty` as its backing integer type. + #[must_use] + pub fn new(ctx: &'ctx Context, int_ty: IntType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { + let llvm_ty = Self::llvm_type(ctx, int_ty); + + Self { ty: llvm_ty, int_ty, llvm_usize } + } + + /// Creates an instance of [`SliceType`] with `usize` as its backing integer type. + #[must_use] + pub fn new_usize(generator: &G, ctx: &'ctx Context) -> Self { + let llvm_usize = generator.get_size_type(ctx); + Self::new(ctx, llvm_usize, llvm_usize) + } + + /// Creates an [`SliceType`] from a [`PointerType`] representing a `slice`. + #[must_use] + pub fn from_type( + ptr_ty: PointerType<'ctx>, + int_ty: IntType<'ctx>, + llvm_usize: IntType<'ctx>, + ) -> Self { + debug_assert!(Self::is_representable(ptr_ty, int_ty).is_ok()); + + Self { ty: ptr_ty, int_ty, llvm_usize } + } + + #[must_use] + pub fn element_type(&self) -> IntType<'ctx> { + self.int_ty + } + + /// Allocates an instance of [`ContiguousNDArrayValue`] as if by calling `alloca` on the base type. + #[must_use] + pub fn alloca( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> >::Value { + >::Value::from_pointer_value( + self.raw_alloca(generator, ctx, name), + self.int_ty, + self.llvm_usize, + name, + ) + } + + /// Converts an existing value into a [`ContiguousNDArrayValue`]. + #[must_use] + pub fn map_value( + &self, + value: <>::Value as ProxyValue<'ctx>>::Base, + name: Option<&'ctx str>, + ) -> >::Value { + >::Value::from_pointer_value( + value, + self.int_ty, + self.llvm_usize, + name, + ) + } +} + +impl<'ctx> ProxyType<'ctx> for SliceType<'ctx> { + type Base = PointerType<'ctx>; + type Value = SliceValue<'ctx>; + + fn is_type( + generator: &G, + ctx: &'ctx Context, + llvm_ty: impl BasicType<'ctx>, + ) -> Result<(), String> { + if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { + >::is_representable(generator, ctx, ty) + } else { + Err(format!("Expected pointer type, got {llvm_ty:?}")) + } + } + + fn is_representable( + generator: &G, + ctx: &'ctx Context, + llvm_ty: Self::Base, + ) -> Result<(), String> { + Self::is_representable(llvm_ty, generator.get_size_type(ctx)) + } + + fn raw_alloca( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> >::Base { + generator + .gen_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + name, + ) + .unwrap() + } + + fn array_alloca( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + size: IntValue<'ctx>, + name: Option<&'ctx str>, + ) -> ArraySliceValue<'ctx> { + generator + .gen_array_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + size, + name, + ) + .unwrap() + } + + fn as_base_type(&self) -> Self::Base { + self.ty + } +} + +impl<'ctx> From> for PointerType<'ctx> { + fn from(value: SliceType<'ctx>) -> Self { + value.as_base_type() + } +} diff --git a/nac3core/src/codegen/values/mod.rs b/nac3core/src/codegen/values/mod.rs index b8bba4e6..032f0417 100644 --- a/nac3core/src/codegen/values/mod.rs +++ b/nac3core/src/codegen/values/mod.rs @@ -10,6 +10,7 @@ mod array; mod list; pub mod ndarray; mod range; +pub mod utils; /// A LLVM type that is used to represent a non-primitive value in NAC3. pub trait ProxyValue<'ctx>: Into { diff --git a/nac3core/src/codegen/values/utils/mod.rs b/nac3core/src/codegen/values/utils/mod.rs new file mode 100644 index 00000000..d4137768 --- /dev/null +++ b/nac3core/src/codegen/values/utils/mod.rs @@ -0,0 +1,3 @@ +pub use slice::*; + +mod slice; diff --git a/nac3core/src/codegen/values/utils/slice.rs b/nac3core/src/codegen/values/utils/slice.rs new file mode 100644 index 00000000..dffe6cef --- /dev/null +++ b/nac3core/src/codegen/values/utils/slice.rs @@ -0,0 +1,231 @@ +use inkwell::{ + types::IntType, + values::{IntValue, PointerValue}, +}; + +use nac3parser::ast::Expr; + +use crate::{ + codegen::{ + types::{structure::StructField, utils::SliceType}, + values::ProxyValue, + CodeGenContext, CodeGenerator, + }, + typecheck::typedef::Type, +}; + +/// An IRRT representation of an (unresolved) slice. +#[derive(Copy, Clone)] +pub struct SliceValue<'ctx> { + value: PointerValue<'ctx>, + int_ty: IntType<'ctx>, + llvm_usize: IntType<'ctx>, + name: Option<&'ctx str>, +} + +impl<'ctx> SliceValue<'ctx> { + /// Checks whether `value` is an instance of `ContiguousNDArray`, returning [Err] if `value` is + /// not an instance. + pub fn is_representable( + value: PointerValue<'ctx>, + llvm_usize: IntType<'ctx>, + ) -> Result<(), String> { + >::Type::is_representable(value.get_type(), llvm_usize) + } + + /// Creates an [`SliceValue`] from a [`PointerValue`]. + #[must_use] + pub fn from_pointer_value( + ptr: PointerValue<'ctx>, + int_ty: IntType<'ctx>, + llvm_usize: IntType<'ctx>, + name: Option<&'ctx str>, + ) -> Self { + debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok()); + + Self { value: ptr, int_ty, llvm_usize, name } + } + + fn start_defined_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().start_defined + } + + pub fn load_start_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.start_defined_field().get(ctx, self.value, self.name) + } + + fn start_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().start + } + + pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.start_field().get(ctx, self.value, self.name) + } + + pub fn store_start(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option>) { + match value { + Some(start) => { + self.start_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_all_ones(), + self.name, + ); + self.start_field().set(ctx, self.value, start, self.name); + } + + None => self.start_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_zero(), + self.name, + ), + } + } + + fn stop_defined_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().stop_defined + } + + pub fn load_stop_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.stop_defined_field().get(ctx, self.value, self.name) + } + + fn stop_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().stop + } + + pub fn load_stop(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.stop_field().get(ctx, self.value, self.name) + } + + pub fn store_stop(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option>) { + match value { + Some(stop) => { + self.stop_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_all_ones(), + self.name, + ); + self.stop_field().set(ctx, self.value, stop, self.name); + } + + None => self.stop_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_zero(), + self.name, + ), + } + } + + fn step_defined_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().step_defined + } + + pub fn load_step_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.step_defined_field().get(ctx, self.value, self.name) + } + + fn step_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().step + } + + pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.step_field().get(ctx, self.value, self.name) + } + + pub fn store_step(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option>) { + match value { + Some(step) => { + self.step_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_all_ones(), + self.name, + ); + self.step_field().set(ctx, self.value, step, self.name); + } + + None => self.step_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_zero(), + self.name, + ), + } + } +} + +impl<'ctx> ProxyValue<'ctx> for SliceValue<'ctx> { + type Base = PointerValue<'ctx>; + type Type = SliceType<'ctx>; + + fn get_type(&self) -> Self::Type { + Self::Type::from_type(self.value.get_type(), self.int_ty, self.llvm_usize) + } + + fn as_base_value(&self) -> Self::Base { + self.value + } +} + +impl<'ctx> From> for PointerValue<'ctx> { + fn from(value: SliceValue<'ctx>) -> Self { + value.as_base_value() + } +} + +/// A slice represented in compile-time by `start`, `stop` and `step`, all held as LLVM values. +// TODO: Rename this to CTConstSlice +#[derive(Debug, Copy, Clone)] +pub struct RustSlice<'ctx> { + int_ty: IntType<'ctx>, + start: Option>, + stop: Option>, + step: Option>, +} + +impl<'ctx> RustSlice<'ctx> { + /// Generate LLVM IR for an [`ExprKind::Slice`] and convert it into a [`RustSlice`]. + #[allow(clippy::type_complexity)] + pub fn from_slice_expr( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + lower: &Option>>>, + upper: &Option>>>, + step: &Option>>>, + ) -> Result, String> { + let mut value_mapper = |value_expr: &Option>>>| -> Result<_, String> { + Ok(match value_expr { + None => None, + Some(value_expr) => { + let value_expr = generator + .gen_expr(ctx, value_expr)? + .map(|value| { + value.to_basic_value_enum(ctx, generator, ctx.primitives.int32) + }) + .unwrap()?; + + Some(value_expr.into_int_value()) + } + }) + }; + + let start = value_mapper(lower)?; + let stop = value_mapper(upper)?; + let step = value_mapper(step)?; + + Ok(RustSlice { int_ty: ctx.ctx.i32_type(), start, stop, step }) + } + + /// Write the contents to an LLVM [`SliceValue`]. + pub fn write_to_slice(&self, ctx: &CodeGenContext<'ctx, '_>, dst_slice_ptr: SliceValue<'ctx>) { + assert_eq!(self.int_ty, dst_slice_ptr.int_ty); + + dst_slice_ptr.store_start(ctx, self.start); + dst_slice_ptr.store_stop(ctx, self.stop); + dst_slice_ptr.store_step(ctx, self.step); + } +}