[core] codegen: Add Option{Type,Value}
This commit is contained in:
parent
35e9c5b38e
commit
57552fb2f6
@ -32,7 +32,7 @@ use super::{
|
||||
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
|
||||
gen_var,
|
||||
},
|
||||
types::{ndarray::NDArrayType, ListType, RangeType, StringType, TupleType},
|
||||
types::{ndarray::NDArrayType, ListType, OptionType, RangeType, StringType, TupleType},
|
||||
values::{
|
||||
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
|
||||
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
|
||||
@ -179,34 +179,16 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
|
||||
.into()
|
||||
}
|
||||
SymbolValue::OptionSome(v) => {
|
||||
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == self.primitives.option.obj_id(&self.unifier).unwrap() =>
|
||||
{
|
||||
*params.iter().next().unwrap().1
|
||||
}
|
||||
_ => codegen_unreachable!(self, "must be option type"),
|
||||
};
|
||||
let val = self.gen_symbol_val(generator, v, ty);
|
||||
let ptr = generator
|
||||
.gen_var_alloc(self, val.get_type(), Some("default_opt_some"))
|
||||
.unwrap();
|
||||
self.builder.build_store(ptr, val).unwrap();
|
||||
ptr.into()
|
||||
}
|
||||
SymbolValue::OptionNone => {
|
||||
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == self.primitives.option.obj_id(&self.unifier).unwrap() =>
|
||||
{
|
||||
*params.iter().next().unwrap().1
|
||||
}
|
||||
_ => codegen_unreachable!(self, "must be option type"),
|
||||
};
|
||||
let actual_ptr_type =
|
||||
self.get_llvm_type(generator, ty).ptr_type(AddressSpace::default());
|
||||
actual_ptr_type.const_null().into()
|
||||
OptionType::from_unifier_type(generator, self, ty)
|
||||
.construct_some_value(generator, self, &val, None)
|
||||
.as_abi_value(self)
|
||||
.into()
|
||||
}
|
||||
SymbolValue::OptionNone => OptionType::from_unifier_type(generator, self, ty)
|
||||
.construct_empty(generator, self, None)
|
||||
.as_abi_value(self)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2333,16 +2315,13 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
const_val.into()
|
||||
}
|
||||
ExprKind::Name { id, .. } if id == &"none".into() => {
|
||||
match (
|
||||
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
|
||||
ctx.unifier.get_ty(ctx.primitives.option).as_ref(),
|
||||
) {
|
||||
(TypeEnum::TObj { obj_id, params, .. }, TypeEnum::TObj { obj_id: opt_id, .. })
|
||||
if *obj_id == *opt_id =>
|
||||
match &*ctx.unifier.get_ty(expr.custom.unwrap()) {
|
||||
TypeEnum::TObj { obj_id, .. }
|
||||
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
ctx.get_llvm_type(generator, *params.iter().next().unwrap().1)
|
||||
.ptr_type(AddressSpace::default())
|
||||
.const_null()
|
||||
OptionType::from_unifier_type(generator, ctx, expr.custom.unwrap())
|
||||
.construct_empty(generator, ctx, None)
|
||||
.as_abi_value(ctx)
|
||||
.into()
|
||||
}
|
||||
_ => codegen_unreachable!(ctx, "must be option type"),
|
||||
@ -2827,8 +2806,12 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
};
|
||||
}
|
||||
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
|
||||
let not_null =
|
||||
ctx.builder.build_is_not_null(ptr, "unwrap_not_null").unwrap();
|
||||
let option = OptionType::from_pointer_type(
|
||||
ptr.get_type(),
|
||||
ctx.get_size_type(),
|
||||
)
|
||||
.map_pointer_value(ptr, None);
|
||||
let not_null = option.is_some(ctx);
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
not_null,
|
||||
@ -2837,12 +2820,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
[None, None, None],
|
||||
expr.location,
|
||||
);
|
||||
return Ok(Some(
|
||||
ctx.builder
|
||||
.build_load(ptr, "unwrap_some_load")
|
||||
.map(Into::into)
|
||||
.unwrap(),
|
||||
));
|
||||
return Ok(Some(unsafe { option.load(ctx).into() }));
|
||||
}
|
||||
ValueEnum::Dynamic(_) => {
|
||||
codegen_unreachable!(ctx, "option must be static or ptr")
|
||||
|
@ -43,7 +43,9 @@ use crate::{
|
||||
};
|
||||
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
|
||||
pub use generator::{CodeGenerator, DefaultCodeGenerator};
|
||||
use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType, StringType, TupleType};
|
||||
use types::{
|
||||
ndarray::NDArrayType, ListType, OptionType, ProxyType, RangeType, StringType, TupleType,
|
||||
};
|
||||
|
||||
pub mod builtin_fns;
|
||||
pub mod concrete_type;
|
||||
@ -538,7 +540,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if PrimDef::contains_id(*obj_id) {
|
||||
return match &*unifier.get_ty_immutable(ty) {
|
||||
TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => {
|
||||
get_llvm_type(
|
||||
let element_type = get_llvm_type(
|
||||
ctx,
|
||||
module,
|
||||
generator,
|
||||
@ -546,9 +548,9 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
top_level,
|
||||
type_cache,
|
||||
*params.iter().next().unwrap().1,
|
||||
)
|
||||
.ptr_type(AddressSpace::default())
|
||||
.into()
|
||||
);
|
||||
|
||||
OptionType::new_with_generator(generator, ctx, &element_type).as_abi_type().into()
|
||||
}
|
||||
|
||||
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
|
@ -26,12 +26,14 @@ use super::{
|
||||
{CodeGenContext, CodeGenerator},
|
||||
};
|
||||
pub use list::*;
|
||||
pub use option::*;
|
||||
pub use range::*;
|
||||
pub use string::*;
|
||||
pub use tuple::*;
|
||||
|
||||
mod list;
|
||||
pub mod ndarray;
|
||||
mod option;
|
||||
mod range;
|
||||
mod string;
|
||||
pub mod structure;
|
||||
|
188
nac3core/src/codegen/types/option.rs
Normal file
188
nac3core/src/codegen/types/option.rs
Normal file
@ -0,0 +1,188 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{BasicValue, BasicValueEnum, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
|
||||
use super::ProxyType;
|
||||
use crate::{
|
||||
codegen::{values::OptionValue, CodeGenContext, CodeGenerator},
|
||||
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
|
||||
};
|
||||
|
||||
/// Proxy type for an `Option` type in LLVM.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct OptionType<'ctx> {
|
||||
ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> OptionType<'ctx> {
|
||||
/// Creates an LLVM type corresponding to the expected structure of an `Option`.
|
||||
#[must_use]
|
||||
fn llvm_type(element_type: &impl BasicType<'ctx>) -> PointerType<'ctx> {
|
||||
element_type.ptr_type(AddressSpace::default())
|
||||
}
|
||||
|
||||
fn new_impl(element_type: &impl BasicType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
let llvm_option = Self::llvm_type(element_type);
|
||||
|
||||
Self { ty: llvm_option, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an instance of [`OptionType`].
|
||||
#[must_use]
|
||||
pub fn new(ctx: &CodeGenContext<'ctx, '_>, element_type: &impl BasicType<'ctx>) -> Self {
|
||||
Self::new_impl(element_type, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an instance of [`OptionType`].
|
||||
#[must_use]
|
||||
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
element_type: &impl BasicType<'ctx>,
|
||||
) -> Self {
|
||||
Self::new_impl(element_type, generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates an [`OptionType`] from a [unifier type][Type].
|
||||
#[must_use]
|
||||
pub fn from_unifier_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ty: Type,
|
||||
) -> Self {
|
||||
// Check unifier type and extract `element_type`
|
||||
let elem_type = match &*ctx.unifier.get_ty_immutable(ty) {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
iter_type_vars(params).next().unwrap().ty
|
||||
}
|
||||
|
||||
_ => panic!("Expected `option` type, but got {}", ctx.unifier.stringify(ty)),
|
||||
};
|
||||
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_elem_type = ctx.get_llvm_type(generator, elem_type);
|
||||
|
||||
Self::new_impl(&llvm_elem_type, llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`OptionType`] from a [`PointerType`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
Self { ty: ptr_ty, llvm_usize }
|
||||
}
|
||||
|
||||
/// Returns the element type of this `Option` type.
|
||||
#[must_use]
|
||||
pub fn element_type(&self) -> BasicTypeEnum<'ctx> {
|
||||
BasicTypeEnum::try_from(self.ty.get_element_type()).unwrap()
|
||||
}
|
||||
|
||||
/// Allocates an [`OptionValue`] on the stack.
|
||||
///
|
||||
/// The returned value will be `Some(v)` if [`value` contains a value][Option::is_some],
|
||||
/// otherwise `none` will be returned.
|
||||
#[must_use]
|
||||
pub fn construct<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: Option<BasicValueEnum<'ctx>>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
let ptr = if let Some(v) = value {
|
||||
let pvar = self.raw_alloca_var(generator, ctx, name);
|
||||
ctx.builder.build_store(pvar, v).unwrap();
|
||||
pvar
|
||||
} else {
|
||||
self.ty.const_null()
|
||||
};
|
||||
|
||||
self.map_pointer_value(ptr, name)
|
||||
}
|
||||
/// Allocates an [`OptionValue`] on the stack.
|
||||
///
|
||||
/// The returned value will always be `none`.
|
||||
#[must_use]
|
||||
pub fn construct_empty<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
self.construct(generator, ctx, None, name)
|
||||
}
|
||||
|
||||
/// Allocates an [`OptionValue`] on the stack.
|
||||
///
|
||||
/// The returned value will be set to `Some(value)`.
|
||||
#[must_use]
|
||||
pub fn construct_some_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: &impl BasicValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
self.construct(generator, ctx, Some(value.as_basic_value_enum()), name)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`OptionValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for OptionType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = OptionValue<'ctx>;
|
||||
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn has_same_repr(ty: Self::Base, _: IntType<'ctx>) -> Result<(), String> {
|
||||
BasicTypeEnum::try_from(ty.get_element_type())
|
||||
.map_err(|()| format!("Expected `ty` to be a BasicTypeEnum, got {ty}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.element_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<OptionType<'ctx>> for PointerType<'ctx> {
|
||||
fn from(value: OptionType<'ctx>) -> Self {
|
||||
value.as_base_type()
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ use inkwell::{types::IntType, values::BasicValue};
|
||||
use super::{types::ProxyType, CodeGenContext};
|
||||
pub use array::*;
|
||||
pub use list::*;
|
||||
pub use option::*;
|
||||
pub use range::*;
|
||||
pub use string::*;
|
||||
pub use tuple::*;
|
||||
@ -10,6 +11,7 @@ pub use tuple::*;
|
||||
mod array;
|
||||
mod list;
|
||||
pub mod ndarray;
|
||||
mod option;
|
||||
mod range;
|
||||
mod string;
|
||||
pub mod structure;
|
||||
|
75
nac3core/src/codegen/values/option.rs
Normal file
75
nac3core/src/codegen/values/option.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
};
|
||||
|
||||
use super::ProxyValue;
|
||||
use crate::codegen::{types::OptionType, CodeGenContext};
|
||||
|
||||
/// Proxy type for accessing a `Option` value in LLVM.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct OptionValue<'ctx> {
|
||||
value: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
}
|
||||
|
||||
impl<'ctx> OptionValue<'ctx> {
|
||||
/// Creates an [`OptionValue`] from a [`PointerValue`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_value(
|
||||
ptr: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
Self { value: ptr, llvm_usize, name }
|
||||
}
|
||||
|
||||
/// Returns an `i1` indicating if this `Option` instance does not hold a value.
|
||||
#[must_use]
|
||||
pub fn is_none(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
ctx.builder.build_is_null(self.value, "").unwrap()
|
||||
}
|
||||
|
||||
/// Returns an `i1` indicating if this `Option` instance contains a value.
|
||||
#[must_use]
|
||||
pub fn is_some(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
ctx.builder.build_is_not_null(self.value, "").unwrap()
|
||||
}
|
||||
|
||||
/// Loads the value present in this `Option` instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that this `option` value [contains a value][Self::is_some].
|
||||
#[must_use]
|
||||
pub unsafe fn load(&self, ctx: &CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
|
||||
ctx.builder.build_load(self.value, "").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for OptionValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = OptionType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<OptionValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: OptionValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user