use bimap::BiMap;
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
use num_bigint::BigInt;
use test_case::test_case;
use super::SpecializationError::{
self, IndexOutOfRange, MissingFunction, UnsupportedGenericArg, UnsupportedId,
WrongNumberOfGenericArgs,
};
use super::core::{CoreLibfunc, CoreType};
use super::lib_func::{SignatureSpecializationContext, SpecializationContext};
use super::types::TypeInfo;
use crate::extensions::type_specialization_context::TypeSpecializationContext;
use crate::extensions::{GenericLibfunc, GenericType};
use crate::ids::{ConcreteTypeId, FunctionId, GenericTypeId};
use crate::program::{ConcreteTypeLongId, Function, FunctionSignature, GenericArg, StatementIdx};
use crate::test_utils::build_bijective_mapping;
fn type_arg(name: &str) -> GenericArg {
GenericArg::Type(name.into())
}
fn user_type_arg(name: &str) -> GenericArg {
GenericArg::UserType(name.into())
}
fn value_arg(v: i64) -> GenericArg {
GenericArg::Value(BigInt::from(v))
}
struct MockSpecializationContext {
mapping: BiMap<ConcreteTypeId, ConcreteTypeLongId>,
type_infos: UnorderedHashMap<ConcreteTypeId, TypeInfo>,
}
impl MockSpecializationContext {
pub fn new() -> Self {
let mapping = build_bijective_mapping();
let mut type_infos = UnorderedHashMap::<ConcreteTypeId, TypeInfo>::default();
for name in [
"T",
"felt252",
"u128",
"bytes31",
"Option",
"NonZeroFelt252",
"Tuple<>",
"U128AndFelt252",
"StorageAddress",
"ContractAddress",
"BoundedInt0_3",
"BoundedInt0_-1",
"BoundedInt0_10",
"BoundedInt0_0",
"BoundedInt2_3",
] {
let key = name.into();
let long_id = mapping.get_by_left(&key).unwrap().clone();
type_infos.insert(
key,
TypeInfo {
long_id,
storable: true,
droppable: true,
duplicatable: true,
zero_sized: false,
},
);
}
for name in ["ArrayFelt252", "ArrayU128"] {
let key = name.into();
let long_id = mapping.get_by_left(&key).unwrap().clone();
type_infos.insert(
key,
TypeInfo {
long_id,
storable: true,
droppable: true,
duplicatable: false,
zero_sized: false,
},
);
}
{
let name = "UninitializedFelt252";
let key = name.into();
let long_id = mapping.get_by_left(&key).unwrap().clone();
type_infos.insert(
key,
TypeInfo {
long_id,
storable: false,
droppable: true,
duplicatable: false,
zero_sized: true,
},
);
}
for name in ["GasBuiltin", "System", "RangeCheck", "NonDupEnum", "NonDupStruct"] {
let key = name.into();
let long_id = mapping.get_by_left(&key).unwrap().clone();
type_infos.insert(
key,
TypeInfo {
long_id,
storable: true,
droppable: false,
duplicatable: false,
zero_sized: false,
},
);
}
for name in ["SnapshotRangeCheck", "SnapshotArrayU128"] {
let key = name.into();
let long_id = mapping.get_by_left(&key).unwrap().clone();
type_infos.insert(
key,
TypeInfo {
long_id,
storable: true,
droppable: true,
duplicatable: true,
zero_sized: false,
},
);
}
Self { mapping, type_infos }
}
}
impl TypeSpecializationContext for MockSpecializationContext {
fn try_get_type_info(&self, id: &ConcreteTypeId) -> Option<&TypeInfo> {
self.type_infos.get(id)
}
}
impl SignatureSpecializationContext for MockSpecializationContext {
fn try_get_concrete_type(
&self,
id: GenericTypeId,
generic_args: &[GenericArg],
) -> Option<ConcreteTypeId> {
self.mapping
.get_by_right(&ConcreteTypeLongId {
generic_id: id,
generic_args: generic_args.to_vec(),
})
.cloned()
}
fn try_get_function_signature(&self, function_id: &FunctionId) -> Option<FunctionSignature> {
self.try_get_function(function_id).map(|f| f.signature)
}
}
impl SpecializationContext for MockSpecializationContext {
fn try_get_function(&self, function_id: &FunctionId) -> Option<Function> {
match function_id {
id if id == &"RegisteredFunction".into() => {
Some(Function::new("RegisteredFunction".into(), vec![], vec![], StatementIdx(5)))
}
_ => None,
}
}
}
#[test_case("NoneExistent", vec![] => Err(UnsupportedId("NoneExistent".into())); "NoneExistent")]
#[test_case("GasBuiltin", vec![] => Ok(()); "GasBuiltin")]
#[test_case("GasBuiltin", vec![type_arg("T")] => Err(WrongNumberOfGenericArgs); "GasBuiltin<T>")]
#[test_case("RangeCheck", vec![] => Ok(()); "RangeCheck")]
#[test_case("RangeCheck", vec![type_arg("T")] => Err(WrongNumberOfGenericArgs); "RangeCheck<T>")]
#[test_case("felt252", vec![] => Ok(()); "felt252")]
#[test_case("felt252", vec![type_arg("T")] => Err(WrongNumberOfGenericArgs); "felt252<T>")]
#[test_case("u128", vec![] => Ok(()); "u128")]
#[test_case("u128", vec![type_arg("T")] => Err(WrongNumberOfGenericArgs); "u128<T>")]
#[test_case("Array", vec![type_arg("u128")] => Ok(()); "Array<u128>")]
#[test_case("Array", vec![] => Err(WrongNumberOfGenericArgs); "Array")]
#[test_case("Array", vec![value_arg(5)] => Err(UnsupportedGenericArg); "Array<5>")]
#[test_case("Array", vec![user_type_arg("Unit")] => Err(UnsupportedGenericArg); "Array<Unit>")]
#[test_case("Array", vec![type_arg("UninitializedFelt252")] => Err(UnsupportedGenericArg);
"Array<UninitializedFelt252>")]
#[test_case("NonZero", vec![type_arg("T")] => Ok(()); "NonZero<T>")]
#[test_case("NonZero", vec![] => Err(WrongNumberOfGenericArgs); "NonZero")]
#[test_case("NonZero", vec![value_arg(5)] => Err(UnsupportedGenericArg); "NonZero<5>")]
#[test_case("Box", vec![type_arg("T")] => Ok(()); "Box<T>")]
#[test_case("Box", vec![] => Err(WrongNumberOfGenericArgs); "Box<>")]
#[test_case("Box", vec![value_arg(5)] => Err(UnsupportedGenericArg); "Box<5>")]
#[test_case("Uninitialized", vec![type_arg("T")] => Ok(()); "Uninitialized<T>")]
#[test_case("Enum", vec![user_type_arg("name")] => Ok(()); "Enum<name>")]
#[test_case("Enum", vec![user_type_arg("name"), type_arg("u128")] => Ok(());
"Enum<name, u128>")]
#[test_case("Enum", vec![user_type_arg("name"), type_arg("u128"), type_arg("felt252")] => Ok(());
"Enum<name, u128, felt252>")]
#[test_case("Enum", vec![user_type_arg("name"), value_arg(5)] => Err(UnsupportedGenericArg);
"Enum<name, 5>")]
#[test_case("Enum", vec![user_type_arg("name"), type_arg("UninitializedFelt252")]
=> Err(UnsupportedGenericArg);
"Enum<name, UninitializedFelt252>")]
#[test_case("Enum", vec![type_arg("u128"), type_arg("felt252")] => Err(UnsupportedGenericArg);
"Enum<u128, felt252>")]
#[test_case("BoundedInt", vec![value_arg(3), value_arg(2)] => Err(UnsupportedGenericArg);
"BoundedInt<3, 2>")]
#[test_case("BoundedInt", vec![value_arg(0), value_arg(0)] => Ok(());
"BoundedInt<0, 0>")]
#[test_case("Struct", vec![user_type_arg("Unit")] => Ok(()); "Struct<Unit>")]
#[test_case("Struct", vec![user_type_arg("Wrap"), type_arg("u128")] => Ok(());
"Struct<Wrap, u128>")]
#[test_case("Struct", vec![user_type_arg("Pair"), type_arg("u128"), type_arg("felt252")] => Ok(());
"Struct<Pair, u128, felt252>")]
#[test_case("Struct", vec![user_type_arg("name"), value_arg(5)] => Err(UnsupportedGenericArg);
"Struct<name, 5>")]
#[test_case("Struct", vec![user_type_arg("name"), type_arg("UninitializedFelt252")]
=> Ok(());
"Struct<name, UninitializedFelt252>")]
#[test_case("Struct", vec![type_arg("u128"), type_arg("felt252")] => Err(UnsupportedGenericArg);
"Struct<u128, felt252>")]
#[test_case("System", vec![] => Ok(()); "System")]
#[test_case("StorageBaseAddress", vec![] => Ok(()); "StorageBaseAddress")]
#[test_case("Snapshot", vec![type_arg("RangeCheck")] => Ok(()); "Snapshot<RangeCheck>")]
#[test_case("Snapshot", vec![type_arg("felt252")] => Err(UnsupportedGenericArg); "Snapshot<felt252>")]
#[test_case("Snapshot", vec![type_arg("UninitializedFelt252")] => Err(UnsupportedGenericArg);
"Snapshot<UninitializedFelt252>")]
#[test_case("Snapshot", vec![type_arg("SnapshotRangeCheck")] => Err(UnsupportedGenericArg);
"Snapshot<SnapshotRangeCheck>")]
fn find_type_specialization(
id: &str,
generic_args: Vec<GenericArg>,
) -> Result<(), SpecializationError> {
CoreType::by_id(&id.into())
.ok_or(UnsupportedId(id.into()))?
.specialize(&MockSpecializationContext::new(), &generic_args)
.map(|_| ())
}
#[test_case("NoneExistent", vec![] => Err(UnsupportedId("NoneExistent".into())); "NoneExistent")]
#[test_case("function_call", vec![GenericArg::UserFunc("UnregisteredFunction".into())]
=> Err(MissingFunction("UnregisteredFunction".into()));
"function_call<&UnregisteredFunction>")]
#[test_case("function_call", vec![GenericArg::UserFunc("RegisteredFunction".into())]
=> Ok(()); "function_call<&RegisteredFunction>")]
#[test_case("function_call", vec![] => Err(WrongNumberOfGenericArgs); "function_call")]
#[test_case("array_new", vec![] => Err(WrongNumberOfGenericArgs); "array_new")]
#[test_case("array_new", vec![type_arg("u128")] => Ok(()); "array_new<u128>")]
#[test_case("array_append", vec![] => Err(WrongNumberOfGenericArgs); "array_append")]
#[test_case("array_append", vec![type_arg("u128")] => Ok(()); "array_append<u128>")]
#[test_case("array_get", vec![] => Err(WrongNumberOfGenericArgs); "array_get")]
#[test_case("array_get", vec![type_arg("u128")] => Ok(()); "array_get<u128>")]
#[test_case("array_len", vec![] => Err(WrongNumberOfGenericArgs); "array_len")]
#[test_case("array_len", vec![type_arg("u128")] => Ok(()); "array_len<u128>")]
#[test_case("withdraw_gas", vec![value_arg(0)] => Err(WrongNumberOfGenericArgs); "withdraw_gas<0>")]
#[test_case("withdraw_gas", vec![] => Ok(()); "withdraw_gas")]
#[test_case("redeposit_gas", vec![value_arg(0)] => Err(WrongNumberOfGenericArgs); "redeposit_gas<0>")]
#[test_case("redeposit_gas", vec![] => Ok(()); "redeposit_gas")]
#[test_case("felt252_add", vec![] => Ok(()); "felt252_add")]
#[test_case("felt252_add_const", vec![value_arg(0)] => Ok(()); "felt252_add_const<0>")]
#[test_case("felt252_mul", vec![] => Ok(()); "felt252_mul")]
#[test_case("felt252_mul_const", vec![value_arg(0)] => Ok(()); "felt252_mul_const<0>")]
#[test_case("felt252_is_zero", vec![] => Ok(()); "felt252_is_zero<>")]
#[test_case("felt252_is_zero", vec![type_arg("felt252")]
=> Err(WrongNumberOfGenericArgs); "felt252_is_zero<int>")]
#[test_case("u128_overflowing_add", vec![] => Ok(()); "u128_overflowing_add")]
#[test_case("u128_overflowing_sub", vec![] => Ok(()); "u128_overflowing_sub")]
#[test_case("u128_safe_divmod", vec![] => Ok(()); "u128_safe_divmod")]
#[test_case("u128_const", vec![value_arg(8)] => Ok(()); "u128_const<8>")]
#[test_case("u128_const", vec![] => Err(UnsupportedGenericArg); "u128_const")]
#[test_case("storage_base_address_const", vec![value_arg(8)] => Ok(()); "storage_base_address_const<8>")]
#[test_case("storage_base_address_const", vec![] => Err(WrongNumberOfGenericArgs);
"storage_base_address_const")]
#[test_case("contract_address_const", vec![value_arg(8)] => Ok(()); "contract_address_const<8>")]
#[test_case("contract_address_const", vec![] => Err(WrongNumberOfGenericArgs);
"contract_address_const")]
#[test_case("drop", vec![type_arg("u128")] => Ok(()); "drop<u128>")]
#[test_case("drop", vec![] => Err(WrongNumberOfGenericArgs); "drop<>")]
#[test_case("drop", vec![type_arg("GasBuiltin")] => Err(UnsupportedGenericArg);
"drop<GasBuiltin>")]
#[test_case("dup", vec![type_arg("u128")] => Ok(()); "dup<u128>")]
#[test_case("dup", vec![] => Err(WrongNumberOfGenericArgs); "dup<>")]
#[test_case("dup", vec![type_arg("GasBuiltin")] => Err(UnsupportedGenericArg);
"dup<GasBuiltin>")]
#[test_case("u128_is_zero", vec![] => Ok(()); "u128_is_zero<>")]
#[test_case("u128_is_zero", vec![type_arg("u128")]
=> Err(WrongNumberOfGenericArgs); "u128_is_zero<u128>")]
#[test_case("unwrap_non_zero", vec![type_arg("u128")] => Ok(()); "unwrap_non_zero<u128>")]
#[test_case("unwrap_non_zero", vec![] => Err(WrongNumberOfGenericArgs); "unwrap_non_zero")]
#[test_case("store_temp", vec![type_arg("u128")] => Ok(()); "store_temp<u128>")]
#[test_case("store_temp", vec![] => Err(WrongNumberOfGenericArgs); "store_temp")]
#[test_case("store_local", vec![type_arg("u128")] => Ok(()); "store_local<u128>")]
#[test_case("store_local", vec![] => Err(WrongNumberOfGenericArgs); "store_local")]
#[test_case("finalize_locals", vec![] => Ok(()); "finalize_locals")]
#[test_case("finalize_locals", vec![type_arg("u128")]
=> Err(WrongNumberOfGenericArgs); "finalize_locals<u128>")]
#[test_case("alloc_local", vec![type_arg("u128")] => Ok(()); "alloc_local<u128>")]
#[test_case("alloc_local", vec![] => Err(WrongNumberOfGenericArgs); "alloc_local<>")]
#[test_case("rename", vec![type_arg("u128")] => Ok(()); "rename<u128>")]
#[test_case("rename", vec![] => Err(WrongNumberOfGenericArgs); "rename")]
#[test_case("jump", vec![] => Ok(()); "jump")]
#[test_case("jump", vec![type_arg("T")] => Err(WrongNumberOfGenericArgs); "jump<T>")]
#[test_case("revoke_ap_tracking", vec![] => Ok(()); "revoke_ap_tracking")]
#[test_case("enum_init", vec![type_arg("Option"), value_arg(0)] => Ok(());
"enum_init<Option,0>")]
#[test_case("enum_init", vec![type_arg("Option"), value_arg(1)] =>
Ok(());"enum_init<Option,1>")]
#[test_case("enum_init", vec![type_arg("Option"), value_arg(2)]
=> Err(IndexOutOfRange{index: BigInt::from(2), range_size: 2});
"enum_init<Option,2>")]
#[test_case("enum_init", vec![type_arg("Option"), value_arg(-3)]
=> Err(IndexOutOfRange{index: BigInt::from(-3), range_size: 2});
"enum_init<Option,-3>")]
#[test_case("enum_init", vec![type_arg("Option")]
=> Err(WrongNumberOfGenericArgs); "enum_init<Option>")]
#[test_case("enum_init", vec![value_arg(0)] => Err(WrongNumberOfGenericArgs); "enum_init<0>")]
#[test_case("enum_init", vec![] => Err(WrongNumberOfGenericArgs); "enum_init")]
#[test_case("enum_init", vec![value_arg(0),type_arg("Option")]
=> Err(UnsupportedGenericArg); "enum_init<0,Option>")]
#[test_case("enum_init", vec![type_arg("Option"), type_arg("Option")]
=> Err(UnsupportedGenericArg); "enum_init<Option,Option>")]
#[test_case("enum_init", vec![value_arg(0), value_arg(0)]
=> Err(UnsupportedGenericArg); "enum_init<0,0>")]
#[test_case("enum_match", vec![type_arg("Option")] => Ok(()); "enum_match<Option>")]
#[test_case("enum_match", vec![value_arg(4)] => Err(UnsupportedGenericArg); "enum_match<4>")]
#[test_case("enum_match", vec![] => Err(WrongNumberOfGenericArgs); "enum_match")]
#[test_case("enum_snapshot_match", vec![type_arg("Option")] => Ok(());
"enum_snapshot_match<Option>")]
#[test_case("enum_snapshot_match", vec![type_arg("NonDupEnum")] => Ok(());
"enum_snapshot_match<NonDupEnum>")]
#[test_case("downcast", vec![type_arg("felt252"), type_arg("bytes31")]
=> Err(UnsupportedGenericArg); "downcast<felt252, bytes31>")]
#[test_case("downcast", vec![type_arg("bytes31"), type_arg("BoundedInt0_10")]
=> Err(UnsupportedGenericArg); "downcast<bytes31, BoundedInt<0, 10>>")]
#[test_case("struct_construct", vec![type_arg("U128AndFelt252")] => Ok(());
"struct_construct<U128AndFelt252>")]
#[test_case("struct_construct", vec![value_arg(4)] => Err(UnsupportedGenericArg);
"struct_construct<4>")]
#[test_case("struct_deconstruct", vec![type_arg("U128AndFelt252")] => Ok(());
"struct_deconstruct<U128AndFelt252>")]
#[test_case("struct_deconstruct", vec![value_arg(4)] => Err(UnsupportedGenericArg);
"struct_deconstruct<4>")]
#[test_case("struct_snapshot_deconstruct", vec![type_arg("U128AndFelt252")] => Ok(());
"struct_snapshot_deconstruct<U128AndFelt252>")]
#[test_case("struct_snapshot_deconstruct", vec![type_arg("NonDupStruct")] => Ok(());
"struct_snapshot_deconstruct<NonDupStruct>")]
#[test_case("storage_read_syscall", vec![] => Ok(()); "storage_read_syscall")]
#[test_case("storage_write_syscall", vec![] => Ok(()); "storage_write_syscall")]
#[test_case("snapshot_take", vec![type_arg("RangeCheck")] => Ok(()); "snapshot_take<RangeCheck>")]
#[test_case("snapshot_take", vec![type_arg("NonDupStruct")] => Ok(());
"snapshot_take<NonDupStruct>")]
#[test_case("snapshot_take", vec![type_arg("NonDupEnum")] => Ok(()); "snapshot_take<NonDupEnum>")]
#[test_case("snapshot_take", vec![type_arg("felt252")] => Ok(()); "snapshot_take<felt252>")]
#[test_case("snapshot_take", vec![type_arg("SnapshotRangeCheck")] => Ok(());
"snapshot_take<SnapshotRangeCheck>")]
#[test_case("coupon_buy", vec![type_arg("felt252")] => Err(UnsupportedGenericArg);
"coupon_buy<felt252>")]
fn find_libfunc_specialization(
id: &str,
generic_args: Vec<GenericArg>,
) -> Result<(), SpecializationError> {
CoreLibfunc::by_id(&id.into())
.ok_or(UnsupportedId(id.into()))?
.specialize(&MockSpecializationContext::new(), &generic_args)
.map(|_| ())
}