cairo_lang_sierra_generator/
db.rs

1use std::sync::Arc;
2
3use cairo_lang_diagnostics::{Maybe, MaybeAsRef};
4use cairo_lang_filesystem::flag::flag_unsafe_panic;
5use cairo_lang_filesystem::ids::{CrateId, Tracked};
6use cairo_lang_lowering::db::LoweringGroup;
7use cairo_lang_lowering::panic::PanicSignatureInfo;
8use cairo_lang_sierra::extensions::lib_func::SierraApChange;
9use cairo_lang_sierra::extensions::{ConcreteType, GenericTypeEx};
10use cairo_lang_sierra::ids::ConcreteTypeId;
11use lowering::ids::ConcreteFunctionWithBodyId;
12use salsa::plumbing::FromId;
13use salsa::{Database, Id};
14use {cairo_lang_lowering as lowering, cairo_lang_semantic as semantic};
15
16use crate::program_generator::{self, SierraProgramWithDebug};
17use crate::replace_ids::SierraIdReplacer;
18use crate::specialization_context::SierraSignatureSpecializationContext;
19use crate::types::cycle_breaker_info;
20use crate::{ap_change, function_generator, pre_sierra, replace_ids};
21
22/// Helper type for Sierra long IDs, which can be either a type long ID or a cycle breaker.
23/// This is required for cases where the type long id is self referential.
24#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)]
25pub enum SierraGeneratorTypeLongId<'db> {
26    /// A normal type long id.
27    Regular(Arc<cairo_lang_sierra::program::ConcreteTypeLongId>),
28    /// The long id for cycle breakers, such as `Box` and `Nullable`.
29    CycleBreaker(semantic::TypeId<'db>),
30    /// This is a long id of a phantom type.
31    /// Phantom types have a one to one mapping from the semantic type to the Sierra type.
32    Phantom(semantic::TypeId<'db>),
33}
34
35/// Wrapper for the concrete libfunc long id, providing a unique id for each libfunc.
36#[salsa::interned(revisions = usize::MAX)]
37struct ConcreteLibfuncIdLongWrapper {
38    id: cairo_lang_sierra::program::ConcreteLibfuncLongId,
39}
40
41/// Handle for the concrete libfunc long id, used to lookup the concrete libfunc long id.
42struct ConcreteLibfuncHandle(u64);
43
44/// Wrapper for Sierra type long ID, providing a unique ID for each type.
45#[salsa::interned(revisions = usize::MAX)]
46struct SierraGeneratorTypeLongIdWrapper<'db> {
47    id: SierraGeneratorTypeLongId<'db>,
48}
49
50/// Handle for the concrete type long id, used to lookup the concrete type long id.
51#[derive(Copy, Clone, PartialEq, Eq, Hash)]
52struct ConcreteTypeHandle(u64);
53
54/// Handle for the Sierra function ID, used to lookup the Sierra function ID.
55struct FunctionHandle(u64);
56
57fn intern_concrete_lib_func(
58    db: &dyn Database,
59    id: cairo_lang_sierra::program::ConcreteLibfuncLongId,
60) -> cairo_lang_sierra::ids::ConcreteLibfuncId {
61    let interned = ConcreteLibfuncIdLongWrapper::new(db, id);
62    cairo_lang_sierra::ids::ConcreteLibfuncId::from(interned.0.as_bits())
63}
64
65fn lookup_concrete_lib_func(
66    db: &dyn Database,
67    id: ConcreteLibfuncHandle,
68) -> cairo_lang_sierra::program::ConcreteLibfuncLongId {
69    let interned = ConcreteLibfuncIdLongWrapper::from_id(Id::from_bits(id.0));
70    interned.id(db)
71}
72
73fn intern_concrete_type<'db>(
74    db: &'db dyn Database,
75    id: SierraGeneratorTypeLongId<'db>,
76) -> cairo_lang_sierra::ids::ConcreteTypeId {
77    let interned = SierraGeneratorTypeLongIdWrapper::new(db, id);
78    cairo_lang_sierra::ids::ConcreteTypeId::from(interned.0.as_bits())
79}
80
81fn lookup_concrete_type<'db>(
82    db: &'db dyn Database,
83    id: ConcreteTypeHandle,
84) -> SierraGeneratorTypeLongId<'db> {
85    let interned = SierraGeneratorTypeLongIdWrapper::from_id(Id::from_bits(id.0));
86    interned.id(db)
87}
88
89fn intern_sierra_function<'db>(
90    id: lowering::ids::FunctionId<'db>,
91) -> cairo_lang_sierra::ids::FunctionId {
92    cairo_lang_sierra::ids::FunctionId::from(id.as_intern_id().as_bits())
93}
94
95fn lookup_sierra_function<'db>(id: FunctionHandle) -> lowering::ids::FunctionId<'db> {
96    lowering::ids::FunctionId::from_id(Id::from_bits(id.0))
97}
98
99pub trait SierraGenGroup: Database {
100    fn intern_concrete_lib_func(
101        &self,
102        id: cairo_lang_sierra::program::ConcreteLibfuncLongId,
103    ) -> cairo_lang_sierra::ids::ConcreteLibfuncId {
104        intern_concrete_lib_func(self.as_dyn_database(), id)
105    }
106
107    fn lookup_concrete_lib_func(
108        &self,
109        id: &cairo_lang_sierra::ids::ConcreteLibfuncId,
110    ) -> cairo_lang_sierra::program::ConcreteLibfuncLongId {
111        lookup_concrete_lib_func(self.as_dyn_database(), ConcreteLibfuncHandle(id.id))
112    }
113
114    fn intern_concrete_type<'db>(
115        &'db self,
116        id: SierraGeneratorTypeLongId<'db>,
117    ) -> cairo_lang_sierra::ids::ConcreteTypeId {
118        intern_concrete_type(self.as_dyn_database(), id)
119    }
120
121    fn lookup_concrete_type<'db>(
122        &'db self,
123        id: &cairo_lang_sierra::ids::ConcreteTypeId,
124    ) -> SierraGeneratorTypeLongId<'db> {
125        lookup_concrete_type(self.as_dyn_database(), ConcreteTypeHandle(id.id))
126    }
127
128    /// Creates a Sierra function id for a lowering function id.
129    // TODO(lior): Can we have the short and long ids in the same place? Currently, the short
130    //   id is defined in sierra and the long id is defined in lowering.
131    fn intern_sierra_function<'db>(
132        &'db self,
133        id: lowering::ids::FunctionId<'db>,
134    ) -> cairo_lang_sierra::ids::FunctionId {
135        intern_sierra_function(id)
136    }
137
138    fn lookup_sierra_function<'db>(
139        &'db self,
140        id: &cairo_lang_sierra::ids::FunctionId,
141    ) -> lowering::ids::FunctionId<'db> {
142        lookup_sierra_function(FunctionHandle(id.id))
143    }
144
145    /// Returns the matching Sierra concrete type ID for a given semantic type ID.
146    fn get_concrete_type_id<'db>(
147        &'db self,
148        type_id: semantic::TypeId<'db>,
149    ) -> Maybe<&'db cairo_lang_sierra::ids::ConcreteTypeId> {
150        crate::types::get_concrete_type_id(self.as_dyn_database(), type_id).maybe_as_ref()
151    }
152
153    /// Returns the ConcreteTypeId of the index enum type with the given index count.
154    fn get_index_enum_type_id(
155        &self,
156        index_count: usize,
157    ) -> Maybe<&cairo_lang_sierra::ids::ConcreteTypeId> {
158        crate::types::get_index_enum_type_id(self.as_dyn_database(), (), index_count).maybe_as_ref()
159    }
160
161    /// Returns the matching Sierra concrete type long ID for a given semantic type ID.
162    fn get_concrete_long_type_id<'db>(
163        &'db self,
164        type_id: semantic::TypeId<'db>,
165    ) -> Maybe<&'db Arc<cairo_lang_sierra::program::ConcreteTypeLongId>> {
166        crate::types::get_concrete_long_type_id(self.as_dyn_database(), type_id).maybe_as_ref()
167    }
168
169    /// Returns if the semantic id has a circular definition.
170    fn is_self_referential<'db>(&self, type_id: semantic::TypeId<'db>) -> Maybe<bool> {
171        crate::types::is_self_referential(self.as_dyn_database(), type_id)
172    }
173
174    /// Returns the semantic type IDs the type is directly dependent on.
175    ///
176    /// A type depends on another type if it contains or may contain it, as a field or by holding a
177    /// reference to it.
178    fn type_dependencies<'db>(
179        &'db self,
180        type_id: semantic::TypeId<'db>,
181    ) -> Maybe<&'db [semantic::TypeId<'db>]> {
182        Ok(crate::types::type_dependencies(self.as_dyn_database(), type_id).maybe_as_ref()?)
183    }
184
185    fn has_in_deps<'db>(
186        &self,
187        type_id: semantic::TypeId<'db>,
188        needle: semantic::TypeId<'db>,
189    ) -> Maybe<bool> {
190        crate::types::has_in_deps(self.as_dyn_database(), type_id, needle)
191    }
192
193    /// Returns the [cairo_lang_sierra::program::FunctionSignature] object for the given function
194    /// id.
195    fn get_function_signature(
196        &self,
197        function_id: cairo_lang_sierra::ids::FunctionId,
198    ) -> Maybe<&cairo_lang_sierra::program::FunctionSignature> {
199        get_function_signature(self.as_dyn_database(), (), function_id).maybe_as_ref()
200    }
201
202    /// Returns the [cairo_lang_sierra::extensions::types::TypeInfo] object for the given type id.
203    fn get_type_info(
204        &self,
205        concrete_type_id: cairo_lang_sierra::ids::ConcreteTypeId,
206    ) -> Maybe<&cairo_lang_sierra::extensions::types::TypeInfo> {
207        get_type_info(self.as_dyn_database(), (), ConcreteTypeHandle(concrete_type_id.id))
208            .maybe_as_ref()
209    }
210
211    /// Private query to compute Sierra data about a function with body.
212    fn priv_function_with_body_sierra_data<'db>(
213        &'db self,
214        function_id: ConcreteFunctionWithBodyId<'db>,
215    ) -> Maybe<&'db function_generator::SierraFunctionWithBodyData<'db>> {
216        function_generator::priv_function_with_body_sierra_data(self.as_dyn_database(), function_id)
217            .maybe_as_ref()
218    }
219    /// Returns the Sierra code (as [pre_sierra::Function]) for a given function with body.
220    fn function_with_body_sierra<'db>(
221        &'db self,
222        function_id: ConcreteFunctionWithBodyId<'db>,
223    ) -> Maybe<&'db pre_sierra::Function<'db>> {
224        self.priv_function_with_body_sierra_data(function_id)?.function.maybe_as_ref()
225    }
226
227    /// Private query to generate a dummy function for a given function with body.
228    fn priv_get_dummy_function<'db>(
229        &'db self,
230        function_id: ConcreteFunctionWithBodyId<'db>,
231    ) -> Maybe<&'db pre_sierra::Function<'db>> {
232        function_generator::priv_get_dummy_function(self.as_dyn_database(), function_id)
233            .maybe_as_ref()
234    }
235
236    /// Returns the ap change of a given function if it is known at compile time or
237    /// [SierraApChange::Unknown] otherwise.
238    fn get_ap_change<'db>(
239        &self,
240        function_id: ConcreteFunctionWithBodyId<'db>,
241    ) -> Maybe<SierraApChange> {
242        ap_change::get_ap_change(self.as_dyn_database(), function_id)
243    }
244
245    /// Private query to returns the type dependencies of a given libfunc.
246    fn priv_libfunc_dependencies(
247        &self,
248        libfunc_id: cairo_lang_sierra::ids::ConcreteLibfuncId,
249    ) -> &[ConcreteTypeId] {
250        program_generator::priv_libfunc_dependencies(self.as_dyn_database(), (), libfunc_id)
251    }
252
253    /// Returns the [SierraProgramWithDebug] object of the requested functions.
254    fn get_sierra_program_for_functions<'db>(
255        &'db self,
256        requested_function_ids: Vec<ConcreteFunctionWithBodyId<'db>>,
257    ) -> Maybe<&'db SierraProgramWithDebug<'db>> {
258        program_generator::get_sierra_program_for_functions(
259            self.as_dyn_database(),
260            (),
261            requested_function_ids,
262        )
263        .maybe_as_ref()
264    }
265
266    /// Returns the [SierraProgramWithDebug] object of the requested crates.
267    fn get_sierra_program<'db>(
268        &'db self,
269        requested_crate_ids: Vec<CrateId<'db>>,
270    ) -> Maybe<&'db SierraProgramWithDebug<'db>> {
271        program_generator::get_sierra_program(self.as_dyn_database(), (), requested_crate_ids)
272            .maybe_as_ref()
273    }
274}
275impl<T: Database + ?Sized> SierraGenGroup for T {}
276
277#[salsa::tracked(returns(ref))]
278fn get_function_signature(
279    db: &dyn Database,
280    _tracked: Tracked,
281    function_id: cairo_lang_sierra::ids::FunctionId,
282) -> Maybe<cairo_lang_sierra::program::FunctionSignature> {
283    // TODO(yuval): add another version of this function that directly received semantic FunctionId.
284    // Call it from function_generators::get_function_code. Take ret_types from the result instead
285    // of only the explicit ret_type. Also use it for params instead of the current logic. Then use
286    // it in the end of program_generator::get_sierra_program instead of calling this function from
287    // there.
288    let lowered_function_id = db.lookup_sierra_function(&function_id);
289    let signature = lowered_function_id.signature(db)?;
290
291    let implicits = db
292        .function_implicits(lowered_function_id)?
293        .iter()
294        .map(|ty| db.get_concrete_type_id(*ty).cloned())
295        .collect::<Maybe<Vec<ConcreteTypeId>>>()?;
296
297    // TODO(spapini): Handle ret_types in lowering.
298    let mut all_params = implicits.clone();
299    let mut extra_rets = vec![];
300    for param in &signature.params {
301        let concrete_type_id = db.get_concrete_type_id(param.ty())?;
302        all_params.push(concrete_type_id.clone());
303    }
304    for var in &signature.extra_rets {
305        let concrete_type_id = db.get_concrete_type_id(var.ty())?;
306        extra_rets.push(concrete_type_id.clone());
307    }
308
309    let mut ret_types = implicits;
310
311    let may_panic = !flag_unsafe_panic(db) && db.function_may_panic(lowered_function_id)?;
312    if may_panic {
313        let panic_info = PanicSignatureInfo::new(db, &signature);
314        ret_types.push(db.get_concrete_type_id(panic_info.actual_return_ty)?.clone());
315    } else {
316        ret_types.extend(extra_rets);
317        // Functions that return the unit type don't have a return type in the signature.
318        if !signature.return_type.is_unit(db) {
319            ret_types.push(db.get_concrete_type_id(signature.return_type)?.clone());
320        }
321    }
322
323    Ok(cairo_lang_sierra::program::FunctionSignature { param_types: all_params, ret_types })
324}
325
326/// Initializes the [`Database`] database to a proper state.
327/// Currently does nothing, but is required to initialize the downcaster.
328pub fn init_sierra_gen_group(db: &mut dyn Database) {
329    Database::zalsa_register_downcaster(db);
330}
331
332#[salsa::tracked(returns(ref))]
333fn get_type_info(
334    db: &dyn Database,
335    _tracked: Tracked,
336    id: ConcreteTypeHandle,
337) -> Maybe<cairo_lang_sierra::extensions::types::TypeInfo> {
338    let long_id = match lookup_concrete_type(db, id) {
339        SierraGeneratorTypeLongId::Regular(long_id) => long_id,
340        SierraGeneratorTypeLongId::CycleBreaker(ty) => {
341            let info = cycle_breaker_info(db, ty)?;
342            return Ok(cairo_lang_sierra::extensions::types::TypeInfo {
343                long_id: db.get_concrete_long_type_id(ty)?.as_ref().clone(),
344                storable: true,
345                droppable: info.droppable,
346                duplicatable: info.duplicatable,
347                zero_sized: false,
348            });
349        }
350        SierraGeneratorTypeLongId::Phantom(ty) => {
351            let long_id = db.get_concrete_long_type_id(ty)?.as_ref().clone();
352            return Ok(cairo_lang_sierra::extensions::types::TypeInfo {
353                long_id,
354                storable: false,
355                droppable: false,
356                duplicatable: false,
357                zero_sized: true,
358            });
359        }
360    };
361    let concrete_ty = cairo_lang_sierra::extensions::core::CoreType::specialize_by_id(
362        &SierraSignatureSpecializationContext(db),
363        &long_id.generic_id,
364        &long_id.generic_args,
365    )
366    .unwrap_or_else(|err| {
367        let mut long_id = long_id.as_ref().clone();
368        replace_ids::DebugReplacer { db }.replace_generic_args(&mut long_id.generic_args);
369        panic!("Got failure while specializing type `{long_id}`: {err}")
370    });
371    Ok(concrete_ty.info().clone())
372}
373
374/// Returns the concrete Sierra long type id given the concrete id.
375pub fn sierra_concrete_long_id(
376    db: &dyn Database,
377    concrete_type_id: cairo_lang_sierra::ids::ConcreteTypeId,
378) -> Maybe<Arc<cairo_lang_sierra::program::ConcreteTypeLongId>> {
379    match db.lookup_concrete_type(&concrete_type_id) {
380        SierraGeneratorTypeLongId::Regular(long_id) => Ok(long_id),
381        SierraGeneratorTypeLongId::Phantom(type_id)
382        | SierraGeneratorTypeLongId::CycleBreaker(type_id) => {
383            db.get_concrete_long_type_id(type_id).cloned()
384        }
385    }
386}