[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_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
|
||||||
gen_var,
|
gen_var,
|
||||||
},
|
},
|
||||||
types::{ndarray::NDArrayType, ListType, RangeType, StringType, TupleType},
|
types::{ndarray::NDArrayType, ListType, OptionType, RangeType, StringType, TupleType},
|
||||||
values::{
|
values::{
|
||||||
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
|
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
|
||||||
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
|
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
|
||||||
@ -179,34 +179,16 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
SymbolValue::OptionSome(v) => {
|
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 val = self.gen_symbol_val(generator, v, ty);
|
||||||
let ptr = generator
|
OptionType::from_unifier_type(generator, self, ty)
|
||||||
.gen_var_alloc(self, val.get_type(), Some("default_opt_some"))
|
.construct_some_value(generator, self, &val, None)
|
||||||
.unwrap();
|
.as_abi_value(self)
|
||||||
self.builder.build_store(ptr, val).unwrap();
|
.into()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
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()
|
const_val.into()
|
||||||
}
|
}
|
||||||
ExprKind::Name { id, .. } if id == &"none".into() => {
|
ExprKind::Name { id, .. } if id == &"none".into() => {
|
||||||
match (
|
match &*ctx.unifier.get_ty(expr.custom.unwrap()) {
|
||||||
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
|
TypeEnum::TObj { obj_id, .. }
|
||||||
ctx.unifier.get_ty(ctx.primitives.option).as_ref(),
|
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
|
||||||
) {
|
|
||||||
(TypeEnum::TObj { obj_id, params, .. }, TypeEnum::TObj { obj_id: opt_id, .. })
|
|
||||||
if *obj_id == *opt_id =>
|
|
||||||
{
|
{
|
||||||
ctx.get_llvm_type(generator, *params.iter().next().unwrap().1)
|
OptionType::from_unifier_type(generator, ctx, expr.custom.unwrap())
|
||||||
.ptr_type(AddressSpace::default())
|
.construct_empty(generator, ctx, None)
|
||||||
.const_null()
|
.as_abi_value(ctx)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
_ => codegen_unreachable!(ctx, "must be option type"),
|
_ => codegen_unreachable!(ctx, "must be option type"),
|
||||||
@ -2827,8 +2806,12 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
|
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
|
||||||
let not_null =
|
let option = OptionType::from_pointer_type(
|
||||||
ctx.builder.build_is_not_null(ptr, "unwrap_not_null").unwrap();
|
ptr.get_type(),
|
||||||
|
ctx.get_size_type(),
|
||||||
|
)
|
||||||
|
.map_pointer_value(ptr, None);
|
||||||
|
let not_null = option.is_some(ctx);
|
||||||
ctx.make_assert(
|
ctx.make_assert(
|
||||||
generator,
|
generator,
|
||||||
not_null,
|
not_null,
|
||||||
@ -2837,12 +2820,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
|||||||
[None, None, None],
|
[None, None, None],
|
||||||
expr.location,
|
expr.location,
|
||||||
);
|
);
|
||||||
return Ok(Some(
|
return Ok(Some(unsafe { option.load(ctx).into() }));
|
||||||
ctx.builder
|
|
||||||
.build_load(ptr, "unwrap_some_load")
|
|
||||||
.map(Into::into)
|
|
||||||
.unwrap(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
ValueEnum::Dynamic(_) => {
|
ValueEnum::Dynamic(_) => {
|
||||||
codegen_unreachable!(ctx, "option must be static or ptr")
|
codegen_unreachable!(ctx, "option must be static or ptr")
|
||||||
|
@ -43,7 +43,9 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
|
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
|
||||||
pub use generator::{CodeGenerator, DefaultCodeGenerator};
|
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 builtin_fns;
|
||||||
pub mod concrete_type;
|
pub mod concrete_type;
|
||||||
@ -538,7 +540,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
if PrimDef::contains_id(*obj_id) {
|
if PrimDef::contains_id(*obj_id) {
|
||||||
return match &*unifier.get_ty_immutable(ty) {
|
return match &*unifier.get_ty_immutable(ty) {
|
||||||
TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => {
|
TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => {
|
||||||
get_llvm_type(
|
let element_type = get_llvm_type(
|
||||||
ctx,
|
ctx,
|
||||||
module,
|
module,
|
||||||
generator,
|
generator,
|
||||||
@ -546,9 +548,9 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
|||||||
top_level,
|
top_level,
|
||||||
type_cache,
|
type_cache,
|
||||||
*params.iter().next().unwrap().1,
|
*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() => {
|
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||||
|
@ -26,12 +26,14 @@ use super::{
|
|||||||
{CodeGenContext, CodeGenerator},
|
{CodeGenContext, CodeGenerator},
|
||||||
};
|
};
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
|
pub use option::*;
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
pub use string::*;
|
pub use string::*;
|
||||||
pub use tuple::*;
|
pub use tuple::*;
|
||||||
|
|
||||||
mod list;
|
mod list;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
|
mod option;
|
||||||
mod range;
|
mod range;
|
||||||
mod string;
|
mod string;
|
||||||
pub mod structure;
|
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};
|
use super::{types::ProxyType, CodeGenContext};
|
||||||
pub use array::*;
|
pub use array::*;
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
|
pub use option::*;
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
pub use string::*;
|
pub use string::*;
|
||||||
pub use tuple::*;
|
pub use tuple::*;
|
||||||
@ -10,6 +11,7 @@ pub use tuple::*;
|
|||||||
mod array;
|
mod array;
|
||||||
mod list;
|
mod list;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
|
mod option;
|
||||||
mod range;
|
mod range;
|
||||||
mod string;
|
mod string;
|
||||||
pub mod structure;
|
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