use super::WithSelf;
use crate::{
error::Result,
metadata::{
drop_overrides::DropOverridesMeta, dup_overrides::DupOverridesMeta, MetadataStorage,
},
utils::ProgramRegistryExt,
};
use cairo_lang_sierra::{
extensions::{
core::{CoreLibfunc, CoreType},
structure::StructConcreteType,
},
program_registry::ProgramRegistry,
};
use melior::{
dialect::{func, llvm},
helpers::{BuiltinBlockExt, LlvmBlockExt},
ir::{Block, BlockLike, Location, Module, Region, Type},
Context,
};
pub fn build<'ctx>(
context: &'ctx Context,
module: &Module<'ctx>,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
metadata: &mut MetadataStorage,
info: WithSelf<StructConcreteType>,
) -> Result<Type<'ctx>> {
DupOverridesMeta::register_with(
context,
module,
registry,
metadata,
info.self_ty(),
|metadata| {
let mut needs_override = false;
for member in &info.members {
registry.build_type(context, module, metadata, member)?;
if DupOverridesMeta::is_overriden(metadata, member) {
needs_override = true;
break;
}
}
needs_override
.then(|| build_dup(context, module, registry, metadata, &info))
.transpose()
},
)?;
DropOverridesMeta::register_with(
context,
module,
registry,
metadata,
info.self_ty(),
|metadata| {
let mut needs_override = false;
for member in &info.members {
registry.build_type(context, module, metadata, member)?;
if DropOverridesMeta::is_overriden(metadata, member) {
needs_override = true;
break;
}
}
needs_override
.then(|| build_drop(context, module, registry, metadata, &info))
.transpose()
},
)?;
let members = info
.members
.iter()
.map(|member| registry.build_type(context, module, metadata, member))
.collect::<Result<Vec<_>>>()?;
Ok(llvm::r#type::r#struct(context, &members, false))
}
fn build_dup<'ctx>(
context: &'ctx Context,
module: &Module<'ctx>,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
metadata: &mut MetadataStorage,
info: &WithSelf<StructConcreteType>,
) -> Result<Region<'ctx>> {
let location = Location::unknown(context);
let self_ty = registry.build_type(context, module, metadata, info.self_ty())?;
let region = Region::new();
let entry = region.append_block(Block::new(&[(self_ty, location)]));
let mut src_value = entry.arg(0)?;
let mut dst_value = entry.append_op_result(llvm::undef(self_ty, location))?;
for (idx, member_id) in info.members.iter().enumerate() {
let member_ty = registry.build_type(context, module, metadata, member_id)?;
let member_val = entry.extract_value(context, location, src_value, member_ty, idx)?;
let values = DupOverridesMeta::invoke_override(
context, registry, module, &entry, &entry, location, metadata, member_id, member_val,
)?;
src_value = entry.insert_value(context, location, src_value, values.0, idx)?;
dst_value = entry.insert_value(context, location, dst_value, values.1, idx)?;
}
entry.append_operation(func::r#return(&[src_value, dst_value], location));
Ok(region)
}
fn build_drop<'ctx>(
context: &'ctx Context,
module: &Module<'ctx>,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
metadata: &mut MetadataStorage,
info: &WithSelf<StructConcreteType>,
) -> Result<Region<'ctx>> {
let location = Location::unknown(context);
let self_ty = registry.build_type(context, module, metadata, info.self_ty())?;
let region = Region::new();
let entry = region.append_block(Block::new(&[(self_ty, location)]));
let value = entry.arg(0)?;
for (idx, member_id) in info.members.iter().enumerate() {
let member_ty = registry.build_type(context, module, metadata, member_id)?;
let member_val = entry.extract_value(context, location, value, member_ty, idx)?;
DropOverridesMeta::invoke_override(
context, registry, module, &entry, &entry, location, metadata, member_id, member_val,
)?;
}
entry.append_operation(func::r#return(&[], location));
Ok(region)
}