use crate::register_intrinsic;
use crate::types::{TypeId, TypeManager};
use crate::value::ValueId;
use crate::value::insn::{Intrinsic, IntrinsicId, Mnemonic, Simplified};
use crate::value::{BodyView, QCodeView};
struct Len;
impl Intrinsic for Len {
fn name(&self) -> &'static str {
"len"
}
fn arity(&self) -> usize {
1
}
fn result_type(&self, types: &TypeManager, _args: &[TypeId]) -> TypeId {
types.get_int(8)
}
fn eval(&self, _args: &[(u128, usize)], _out_size: usize) -> Option<u128> {
None
}
fn simplify(
&self,
view: BodyView<'_, '_>,
_id: IntrinsicId,
out_size: usize,
args: &[ValueId],
) -> Option<Simplified> {
let &[seq] = args else {
return None;
};
let ty = view.type_of(seq);
if let Some((_, n)) = view.shared().types.array_of(ty) {
let lit = view.shared().get_const(n as u64, out_size);
return Some(Simplified::Value(lit));
}
if let ValueId::Instruction(iid) = seq
&& let Mnemonic::Intrinsic(app) = view.instruction(iid).mnemonic()
&& app.id.name() == "iota"
{
return Some(Simplified::Value(app.args[0].qualify(iid.func)));
}
None
}
}
register_intrinsic!(Len);
#[cfg(test)]
mod tests {
use super::*;
use crate::context::Context;
use crate::value::insn::IntrinsicId;
use crate::value::{BasicBlock, ValueId};
#[test]
fn len_registered_and_resolves() {
let id = IntrinsicId::from_name("len").expect("len registered");
assert_eq!(id.name(), "len");
assert_eq!(id.desc().arity(), 1);
}
#[test]
fn result_type_is_a_machine_word() {
let types = TypeManager::default();
let i8 = types.get_or_make_int(1);
let arr = types.get_or_make_array(i8, 4);
let i64 = types.get_or_make_int(8);
let id = IntrinsicId::from_name("len").unwrap();
assert_eq!(id.desc().result_type(&types, &[arr]), i64);
}
#[test]
fn len_of_array_folds_list_does_not() {
let mut ctx = Context::new();
let i8 = ctx.shared.types.get_or_make_int(1);
let arr = ctx.shared.types.get_or_make_array(i8, 6);
let list = ctx.shared.types.get_or_make_list(i8, 6);
let blk = {
let __f = ctx.anon_function();
ctx.get_or_make_block(0x1000, __f)
};
let ap = BasicBlock::from_id_mut(&mut ctx, blk).push_param(6).id;
ctx.block_param_mut(ap).type_id = arr;
let lp = BasicBlock::from_id_mut(&mut ctx, blk).push_param(6).id;
ctx.block_param_mut(lp).type_id = list;
let id = IntrinsicId::from_name("len").unwrap();
match id.desc().simplify(
BodyView::new(&ctx.bodies[blk.func], &ctx.shared, &ctx.interfaces),
id,
8,
&[ValueId::BlockParam(ap)],
) {
Some(Simplified::Value(ValueId::Literal(lid))) => {
assert_eq!(ctx.shared.values.literals[lid].value, 6);
}
other => panic!("len of an array must fold to the literal 6, got {other:?}"),
}
assert!(
id.desc()
.simplify(
BodyView::new(&ctx.bodies[blk.func], &ctx.shared, &ctx.interfaces),
id,
8,
&[ValueId::BlockParam(lp)],
)
.is_none(),
"len of a list must not fold (data-dependent length)"
);
}
}