cairo_lang_sierra/extensions/modules/
casts.rs1use num_traits::Zero;
2use starknet_types_core::felt::Felt as Felt252;
3
4use super::range_check::RangeCheckType;
5use super::utils::{Range, reinterpret_cast_signature};
6use crate::define_libfunc_hierarchy;
7use crate::extensions::lib_func::{
8 BranchSignature, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange,
9 SignatureOnlyGenericLibfunc, SignatureSpecializationContext, SpecializationContext,
10};
11use crate::extensions::{
12 NamedLibfunc, NamedType, OutputVarReferenceInfo, SignatureBasedConcreteLibfunc,
13 SpecializationError, args_as_two_types,
14};
15use crate::ids::ConcreteTypeId;
16use crate::program::GenericArg;
17
18define_libfunc_hierarchy! {
19 pub enum CastLibfunc {
20 Downcast(DowncastLibfunc),
21 Upcast(UpcastLibfunc),
22 }, CastConcreteLibfunc
23}
24
25#[derive(PartialEq, Eq)]
27pub struct CastType {
28 pub overflow_above: bool,
30 pub overflow_below: bool,
32}
33
34#[derive(Default)]
37pub struct UpcastLibfunc {}
38impl SignatureOnlyGenericLibfunc for UpcastLibfunc {
39 const STR_ID: &'static str = "upcast";
40
41 fn specialize_signature(
42 &self,
43 context: &dyn SignatureSpecializationContext,
44 args: &[GenericArg],
45 ) -> Result<LibfuncSignature, SpecializationError> {
46 let (from_ty, to_ty) = args_as_two_types(args)?;
47 let from_range = Range::from_type(context, from_ty.clone())?;
48 let to_range: Range = Range::from_type(context, to_ty.clone())?;
49 let is_upcast = to_range.lower <= from_range.lower && from_range.upper <= to_range.upper;
50 if !is_upcast {
51 return Err(SpecializationError::UnsupportedGenericArg);
52 }
53 Ok(reinterpret_cast_signature(from_ty, to_ty))
54 }
55}
56
57pub struct DowncastConcreteLibfunc {
59 pub signature: LibfuncSignature,
60 pub from_ty: ConcreteTypeId,
61 pub from_range: Range,
62 pub to_ty: ConcreteTypeId,
63 pub to_range: Range,
64}
65impl DowncastConcreteLibfunc {
66 pub fn cast_type(&self) -> CastType {
68 if self.from_ty == self.to_ty && self.from_range.lower.is_zero() {
69 CastType { overflow_above: true, overflow_below: false }
71 } else {
72 CastType {
73 overflow_above: self.to_range.upper < self.from_range.upper,
74 overflow_below: self.to_range.lower > self.from_range.lower,
75 }
76 }
77 }
78}
79
80impl SignatureBasedConcreteLibfunc for DowncastConcreteLibfunc {
81 fn signature(&self) -> &LibfuncSignature {
82 &self.signature
83 }
84}
85
86#[derive(Default)]
89pub struct DowncastLibfunc {}
90impl NamedLibfunc for DowncastLibfunc {
91 type Concrete = DowncastConcreteLibfunc;
92 const STR_ID: &'static str = "downcast";
93
94 fn specialize_signature(
95 &self,
96 context: &dyn SignatureSpecializationContext,
97 args: &[GenericArg],
98 ) -> Result<LibfuncSignature, SpecializationError> {
99 let (from_ty, to_ty) = args_as_two_types(args)?;
100 let to_range = Range::from_type(context, to_ty.clone())?;
101 let from_range = Range::from_type(context, from_ty.clone())?;
102 let to_range =
111 to_range.intersection(&from_range).ok_or(SpecializationError::UnsupportedGenericArg)?;
112
113 if !to_range.is_small_range() {
115 return Err(SpecializationError::UnsupportedGenericArg);
116 }
117 let is_small_values_downcast = from_range.is_small_range();
118 let is_felt252_valid_downcast = from_range.is_full_felt252_range()
121 && to_range.size() < (Felt252::prime() % u128::MAX).into();
122 if !(is_small_values_downcast || is_felt252_valid_downcast) {
123 return Err(SpecializationError::UnsupportedGenericArg);
124 }
125
126 let range_check_type = context.get_concrete_type(RangeCheckType::id(), &[])?;
127 let rc_output_info = OutputVarInfo::new_builtin(range_check_type.clone(), 0);
128 Ok(LibfuncSignature {
129 param_signatures: vec![
130 ParamSignature::new(range_check_type).with_allow_add_const(),
131 ParamSignature::new(from_ty),
132 ],
133 branch_signatures: vec![
134 BranchSignature {
136 vars: vec![rc_output_info.clone(), OutputVarInfo {
137 ty: to_ty,
138 ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 1 },
139 }],
140 ap_change: SierraApChange::Known { new_vars_only: false },
141 },
142 BranchSignature {
144 vars: vec![rc_output_info],
145 ap_change: SierraApChange::Known { new_vars_only: false },
146 },
147 ],
148 fallthrough: Some(0),
149 })
150 }
151
152 fn specialize(
153 &self,
154 context: &dyn SpecializationContext,
155 args: &[GenericArg],
156 ) -> Result<Self::Concrete, SpecializationError> {
157 let (from_ty, to_ty) = args_as_two_types(args)?;
158 let from_range = Range::from_type(context.upcast(), from_ty.clone())?;
159 let to_range: Range = Range::from_type(context.upcast(), to_ty.clone())?
161 .intersection(&from_range)
162 .ok_or(SpecializationError::UnsupportedGenericArg)?;
163 Ok(DowncastConcreteLibfunc {
164 signature: self.specialize_signature(context.upcast(), args)?,
165 from_range,
166 from_ty,
167 to_range,
168 to_ty,
169 })
170 }
171}