Skip to main content

cairo_lang_sierra_generator/
db.rs

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