use super::int::unsigned::{Uint16Type, Uint32Type, Uint64Type, Uint8Type};
use super::int::unsigned128::Uint128Type;
use super::range_check::RangeCheckType;
use super::utils::reinterpret_cast_signature;
use crate::define_libfunc_hierarchy;
use crate::extensions::lib_func::{
BranchSignature, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange,
SignatureOnlyGenericLibfunc, SignatureSpecializationContext, SpecializationContext,
};
use crate::extensions::{
args_as_two_types, NamedLibfunc, NamedType, OutputVarReferenceInfo,
SignatureBasedConcreteLibfunc, SpecializationError,
};
use crate::ids::ConcreteTypeId;
use crate::program::GenericArg;
define_libfunc_hierarchy! {
pub enum CastLibfunc {
Downcast(DowncastLibfunc),
Upcast(UpcastLibfunc),
}, CastConcreteLibfunc
}
fn get_nbits(
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<usize, SpecializationError> {
match context.get_type_info(ty)?.long_id.generic_id {
id if id == Uint8Type::ID => Ok(8),
id if id == Uint16Type::ID => Ok(16),
id if id == Uint32Type::ID => Ok(32),
id if id == Uint64Type::ID => Ok(64),
id if id == Uint128Type::ID => Ok(128),
_ => Err(SpecializationError::UnsupportedGenericArg),
}
}
#[derive(Default)]
pub struct UpcastLibfunc {}
impl SignatureOnlyGenericLibfunc for UpcastLibfunc {
const STR_ID: &'static str = "upcast";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let (from_ty, to_ty) = args_as_two_types(args)?;
let is_valid = get_nbits(context, from_ty.clone())? <= get_nbits(context, to_ty.clone())?;
if !is_valid {
return Err(SpecializationError::UnsupportedGenericArg);
}
Ok(reinterpret_cast_signature(from_ty, to_ty))
}
}
pub struct DowncastConcreteLibfunc {
pub signature: LibfuncSignature,
pub from_ty: ConcreteTypeId,
pub from_nbits: usize,
pub to_ty: ConcreteTypeId,
pub to_nbits: usize,
}
impl SignatureBasedConcreteLibfunc for DowncastConcreteLibfunc {
fn signature(&self) -> &LibfuncSignature {
&self.signature
}
}
#[derive(Default)]
pub struct DowncastLibfunc {}
impl NamedLibfunc for DowncastLibfunc {
type Concrete = DowncastConcreteLibfunc;
const STR_ID: &'static str = "downcast";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let (from_ty, to_ty) = args_as_two_types(args)?;
let is_valid = get_nbits(context, from_ty.clone())? >= get_nbits(context, to_ty.clone())?;
if !is_valid {
return Err(SpecializationError::UnsupportedGenericArg);
}
let range_check_type = context.get_concrete_type(RangeCheckType::id(), &[])?;
let rc_output_info = OutputVarInfo::new_builtin(range_check_type.clone(), 0);
Ok(LibfuncSignature {
param_signatures: vec![
ParamSignature::new(range_check_type).with_allow_add_const(),
ParamSignature::new(from_ty),
],
branch_signatures: vec![
BranchSignature {
vars: vec![
rc_output_info.clone(),
OutputVarInfo {
ty: to_ty,
ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 1 },
},
],
ap_change: SierraApChange::Known { new_vars_only: false },
},
BranchSignature {
vars: vec![rc_output_info],
ap_change: SierraApChange::Known { new_vars_only: false },
},
],
fallthrough: Some(0),
})
}
fn specialize(
&self,
context: &dyn SpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let (from_ty, to_ty) = args_as_two_types(args)?;
Ok(DowncastConcreteLibfunc {
signature: self.specialize_signature(context.upcast(), args)?,
from_nbits: get_nbits(context.upcast(), from_ty.clone())?,
from_ty,
to_nbits: get_nbits(context.upcast(), to_ty.clone())?,
to_ty,
})
}
}