cairo_lang_sierra/
program.rs

1use std::fmt;
2
3use anyhow::Result;
4use num_bigint::BigInt;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use crate::debug_info::DebugInfo;
9use crate::extensions::NamedLibfunc;
10use crate::extensions::gas::{
11    BuiltinCostWithdrawGasLibfunc, RedepositGasLibfunc, WithdrawGasLibfunc,
12};
13use crate::ids::{
14    ConcreteLibfuncId, ConcreteTypeId, FunctionId, GenericLibfuncId, GenericTypeId, UserTypeId,
15    VarId,
16};
17
18/// Version-tagged representation of Sierra program.
19///
20/// Always prefer using this struct as saved artifacts instead of inner ones.
21#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
22#[serde(untagged)]
23pub enum VersionedProgram {
24    V1 {
25        version: Version<1>,
26        #[serde(flatten)]
27        program: ProgramArtifact,
28    },
29}
30
31impl VersionedProgram {
32    pub fn v1(program: ProgramArtifact) -> Self {
33        Self::V1 { program, version: Version::<1> }
34    }
35}
36
37#[derive(Clone, Debug, Eq, PartialEq)]
38pub struct Version<const V: u8>;
39
40#[derive(Debug, Error)]
41#[error("Unsupported Sierra program version")]
42struct VersionError;
43
44impl<const V: u8> Serialize for Version<V> {
45    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
46    where
47        S: serde::Serializer,
48    {
49        serializer.serialize_u8(V)
50    }
51}
52
53impl<'de, const V: u8> Deserialize<'de> for Version<V> {
54    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55    where
56        D: serde::Deserializer<'de>,
57    {
58        let value = u8::deserialize(deserializer)?;
59        if value == V { Ok(Version::<V>) } else { Err(serde::de::Error::custom(VersionError)) }
60    }
61}
62
63impl From<ProgramArtifact> for VersionedProgram {
64    fn from(value: ProgramArtifact) -> Self {
65        VersionedProgram::v1(value)
66    }
67}
68
69impl VersionedProgram {
70    pub fn into_v1(self) -> Result<ProgramArtifact> {
71        match self {
72            VersionedProgram::V1 { program, .. } => Ok(program),
73        }
74    }
75}
76
77impl fmt::Display for VersionedProgram {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            VersionedProgram::V1 { program, .. } => fmt::Display::fmt(program, f),
81        }
82    }
83}
84
85/// Sierra program in a form for storage on the filesystem and sharing externally.
86///
87/// Do not serialize this struct directly, use [`VersionedProgram`] instead.
88#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
89pub struct ProgramArtifact {
90    /// Sierra program itself.
91    #[serde(flatten)]
92    pub program: Program,
93    /// Debug information for a Sierra program.
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub debug_info: Option<DebugInfo>,
96}
97
98impl ProgramArtifact {
99    /// Create a new [`ProgramArtifact`] without any extra information.
100    pub fn stripped(program: Program) -> Self {
101        Self { program, debug_info: None }
102    }
103
104    /// Add [`DebugInfo`] to the [`ProgramArtifact`], replacing existing one.
105    pub fn with_debug_info(self, debug_info: DebugInfo) -> Self {
106        Self { program: self.program, debug_info: Some(debug_info) }
107    }
108}
109
110impl fmt::Display for ProgramArtifact {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        fmt::Display::fmt(&self.program, f)
113    }
114}
115
116/// A full Sierra program.
117#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
118pub struct Program {
119    /// Declarations for all the used types.
120    pub type_declarations: Vec<TypeDeclaration>,
121    /// Declarations for all the used library functions.
122    pub libfunc_declarations: Vec<LibfuncDeclaration>,
123    /// The code of the program.
124    pub statements: Vec<Statement>,
125    /// Descriptions of the functions - signatures and entry points.
126    pub funcs: Vec<Function>,
127}
128impl Program {
129    pub fn get_statement(&self, id: &StatementIdx) -> Option<&Statement> {
130        self.statements.get(id.0)
131    }
132
133    /// Create a new [`ProgramArtifact`] out of this [`Program`].
134    pub fn into_artifact(self) -> VersionedProgram {
135        ProgramArtifact::stripped(self).into()
136    }
137}
138
139/// Declaration of a concrete type.
140#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
141pub struct TypeDeclaration {
142    /// The id of the declared concrete type.
143    pub id: ConcreteTypeId,
144    pub long_id: ConcreteTypeLongId,
145    pub declared_type_info: Option<DeclaredTypeInfo>,
146}
147
148/// Declaration of a concrete type info.
149#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
150pub struct DeclaredTypeInfo {
151    /// Can the type be stored by any of the store commands.
152    pub storable: bool,
153    /// Can the type be (trivially) dropped.
154    pub droppable: bool,
155    /// Can the type be (trivially) duplicated.
156    pub duplicatable: bool,
157    /// The size of an element of this type.
158    pub zero_sized: bool,
159}
160
161/// A concrete type (the generic parent type and the generic arguments).
162#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
163pub struct ConcreteTypeLongId {
164    /// The id of the used generic type.
165    pub generic_id: GenericTypeId,
166    /// The arguments for the generic type.
167    pub generic_args: Vec<GenericArg>,
168}
169
170/// Declaration of a concrete library function.
171#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
172pub struct LibfuncDeclaration {
173    /// The id of the declared concrete libfunc.
174    pub id: ConcreteLibfuncId,
175    pub long_id: ConcreteLibfuncLongId,
176}
177
178/// A concrete library function (the generic parent function and the generic arguments).
179#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
180pub struct ConcreteLibfuncLongId {
181    /// The id of the used generic libfunc.
182    pub generic_id: GenericLibfuncId,
183    /// The arguments for the specialization.
184    pub generic_args: Vec<GenericArg>,
185}
186
187/// Represents the signature of a function.
188#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
189pub struct FunctionSignature {
190    /// The types of the parameters of the function.
191    pub param_types: Vec<ConcreteTypeId>,
192    /// The return types.
193    pub ret_types: Vec<ConcreteTypeId>,
194}
195
196/// Represents a function (its name, signature and entry point).
197#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
198pub struct GenFunction<StatementId> {
199    /// The name of the function.
200    pub id: FunctionId,
201    /// The parameter types and return types.
202    pub signature: FunctionSignature,
203    /// The parameters of the function.
204    // TODO(lior): Consider keeping here only the var ids, instead of the full Param (the types
205    //   are stored in `signature`).
206    pub params: Vec<Param>,
207    /// The statement id where the function starts.
208    pub entry_point: StatementId,
209}
210
211impl<StatementId> GenFunction<StatementId> {
212    pub fn new(
213        id: FunctionId,
214        params: Vec<Param>,
215        ret_types: Vec<ConcreteTypeId>,
216        entry_point: StatementId,
217    ) -> Self {
218        let param_types: Vec<_> = params.iter().map(|Param { id: _, ty }| ty.clone()).collect();
219        GenFunction {
220            id,
221            signature: FunctionSignature { param_types, ret_types },
222            params,
223            entry_point,
224        }
225    }
226}
227
228/// Descriptor of a variable.
229#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
230pub struct Param {
231    pub id: VarId,
232    pub ty: ConcreteTypeId,
233}
234
235/// Represents the index of a Sierra statement in the Program::statements vector.
236#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, PartialOrd, Ord)]
237pub struct StatementIdx(pub usize);
238impl StatementIdx {
239    pub fn next(&self, target: &BranchTarget) -> StatementIdx {
240        match target {
241            BranchTarget::Fallthrough => StatementIdx(self.0 + 1),
242            BranchTarget::Statement(id) => *id,
243        }
244    }
245}
246
247/// Possible arguments for generic type.
248#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
249pub enum GenericArg {
250    UserType(UserTypeId),
251    Type(ConcreteTypeId),
252    Value(BigInt),
253    UserFunc(FunctionId),
254    Libfunc(ConcreteLibfuncId),
255}
256
257/// A possible statement.
258#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
259pub enum GenStatement<StatementId> {
260    Invocation(GenInvocation<StatementId>),
261    Return(Vec<VarId>),
262}
263impl<StatementId> GenStatement<StatementId> {
264    pub fn map<T>(self, f: impl Fn(StatementId) -> T) -> GenStatement<T> {
265        match self {
266            GenStatement::Invocation(invocation) => GenStatement::Invocation(GenInvocation {
267                libfunc_id: invocation.libfunc_id,
268                args: invocation.args.clone(),
269                branches: invocation
270                    .branches
271                    .into_iter()
272                    .map(|branch| GenBranchInfo {
273                        target: branch.target.map(&f),
274                        results: branch.results.clone(),
275                    })
276                    .collect(),
277            }),
278            GenStatement::Return(results) => GenStatement::Return(results.clone()),
279        }
280    }
281}
282
283/// An invocation statement.
284#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
285pub struct GenInvocation<StatementId> {
286    /// The called libfunc.
287    pub libfunc_id: ConcreteLibfuncId,
288    /// The arguments consumed by the libfunc's invocation.
289    pub args: Vec<VarId>,
290    /// The possible branches to continue to after the invocation.
291    /// The program would continue to exactly one of the branches.
292    pub branches: Vec<GenBranchInfo<StatementId>>,
293}
294
295/// Describes the flow of a chosen libfunc's branch.
296#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
297pub struct GenBranchInfo<StatementId> {
298    /// The target the branch continues the run through.
299    pub target: GenBranchTarget<StatementId>,
300    /// The resulting identifiers from the libfunc call.
301    pub results: Vec<VarId>,
302}
303
304#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
305pub enum GenBranchTarget<StatementId> {
306    /// Continues a run to the next statement.
307    Fallthrough,
308    /// Continues the run to provided statement.
309    Statement(StatementId),
310}
311impl<StatementId> GenBranchTarget<StatementId> {
312    pub fn map<T>(self, f: impl Fn(StatementId) -> T) -> GenBranchTarget<T> {
313        match self {
314            GenBranchTarget::Fallthrough => GenBranchTarget::Fallthrough,
315            GenBranchTarget::Statement(id) => GenBranchTarget::Statement(f(id)),
316        }
317    }
318}
319
320pub type Function = GenFunction<StatementIdx>;
321pub type Statement = GenStatement<StatementIdx>;
322pub type Invocation = GenInvocation<StatementIdx>;
323pub type BranchInfo = GenBranchInfo<StatementIdx>;
324pub type BranchTarget = GenBranchTarget<StatementIdx>;
325
326impl Program {
327    /// Checks if this Sierra program needs a gas counter set up in order to be executed.
328    ///
329    /// This is determined by checking if the program uses any of gas-related libfuncs.
330    pub fn requires_gas_counter(&self) -> bool {
331        self.libfunc_declarations.iter().any(|decl| {
332            matches!(
333                decl.long_id.generic_id.0.as_str(),
334                WithdrawGasLibfunc::STR_ID
335                    | BuiltinCostWithdrawGasLibfunc::STR_ID
336                    | RedepositGasLibfunc::STR_ID
337            )
338        })
339    }
340}