Skip to main content

cairo_lang_sierra_generator/
db.rs

1use std::sync::Arc;
2
3use cairo_lang_diagnostics::Maybe;
4use cairo_lang_filesystem::flag::flag_unsafe_panic;
5use cairo_lang_filesystem::ids::CrateId;
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 cairo_lang_utils::{LookupIntern, Upcast};
12use lowering::ids::ConcreteFunctionWithBodyId;
13use {cairo_lang_lowering as lowering, cairo_lang_semantic as semantic};
14
15use crate::program_generator::{self, SierraProgramWithDebug};
16use crate::replace_ids::SierraIdReplacer;
17use crate::specialization_context::SierraSignatureSpecializationContext;
18use crate::types::cycle_breaker_info;
19use crate::{ap_change, function_generator, pre_sierra, replace_ids};
20
21/// Helper type for Sierra long ids, which can be either a type long id or a cycle breaker.
22/// This is required for cases where the type long id is self referential.
23#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24pub enum SierraGeneratorTypeLongId {
25    /// A normal type long id.
26    Regular(Arc<cairo_lang_sierra::program::ConcreteTypeLongId>),
27    /// The long id for cycle breakers, such as `Box` and `Nullable`.
28    CycleBreaker(semantic::TypeId),
29    /// This is a long id of a phantom type.
30    /// Phantom types have a one to one mapping from the semantic type to the sierra type.
31    Phantom(semantic::TypeId),
32}
33
34#[salsa::query_group(SierraGenDatabase)]
35pub trait SierraGenGroup: LoweringGroup + Upcast<dyn LoweringGroup> {
36    #[salsa::interned]
37    fn intern_label_id(&self, id: pre_sierra::LabelLongId) -> pre_sierra::LabelId;
38
39    #[salsa::interned]
40    fn intern_concrete_lib_func(
41        &self,
42        id: cairo_lang_sierra::program::ConcreteLibfuncLongId,
43    ) -> cairo_lang_sierra::ids::ConcreteLibfuncId;
44
45    #[salsa::interned]
46    fn intern_concrete_type(
47        &self,
48        id: SierraGeneratorTypeLongId,
49    ) -> cairo_lang_sierra::ids::ConcreteTypeId;
50
51    /// Creates a Sierra function id for a lowering function id.
52    // TODO(lior): Can we have the short and long ids in the same place? Currently, the short
53    //   id is defined in sierra and the long id is defined in lowering.
54    #[salsa::interned]
55    fn intern_sierra_function(
56        &self,
57        id: lowering::ids::FunctionId,
58    ) -> cairo_lang_sierra::ids::FunctionId;
59
60    /// Returns the matching sierra concrete type id for a given semantic type id.
61    #[salsa::invoke(crate::types::get_concrete_type_id)]
62    fn get_concrete_type_id(
63        &self,
64        type_id: semantic::TypeId,
65    ) -> Maybe<cairo_lang_sierra::ids::ConcreteTypeId>;
66
67    /// Returns the ConcreteTypeId of the index enum type with the given index count.
68    #[salsa::invoke(crate::types::get_index_enum_type_id)]
69    fn get_index_enum_type_id(
70        &self,
71        index_count: usize,
72    ) -> Maybe<cairo_lang_sierra::ids::ConcreteTypeId>;
73
74    /// Returns the matching sierra concrete type long id for a given semantic type id.
75    #[salsa::invoke(crate::types::get_concrete_long_type_id)]
76    fn get_concrete_long_type_id(
77        &self,
78        type_id: semantic::TypeId,
79    ) -> Maybe<Arc<cairo_lang_sierra::program::ConcreteTypeLongId>>;
80
81    /// Returns if the semantic id has a circular definition.
82    #[salsa::invoke(crate::types::is_self_referential)]
83    fn is_self_referential(&self, type_id: semantic::TypeId) -> Maybe<bool>;
84
85    /// Returns the semantic type ids the type is directly dependent on.
86    ///
87    /// A type depends on another type if it contains or may contain it, as a field or by holding a
88    /// reference to it.
89    #[salsa::invoke(crate::types::type_dependencies)]
90    fn type_dependencies(&self, type_id: semantic::TypeId) -> Maybe<Arc<[semantic::TypeId]>>;
91
92    #[salsa::invoke(crate::types::has_in_deps)]
93    #[salsa::cycle(crate::types::has_in_deps_cycle)]
94    fn has_in_deps(&self, type_id: semantic::TypeId, needle: semantic::TypeId) -> Maybe<bool>;
95
96    /// Returns the [cairo_lang_sierra::program::FunctionSignature] object for the given function
97    /// id.
98    fn get_function_signature(
99        &self,
100        function_id: cairo_lang_sierra::ids::FunctionId,
101    ) -> Maybe<Arc<cairo_lang_sierra::program::FunctionSignature>>;
102
103    /// Returns the [cairo_lang_sierra::extensions::types::TypeInfo] object for the given type id.
104    fn get_type_info(
105        &self,
106        concrete_type_id: cairo_lang_sierra::ids::ConcreteTypeId,
107    ) -> Maybe<Arc<cairo_lang_sierra::extensions::types::TypeInfo>>;
108
109    /// Private query to compute Sierra data about a function with body.
110    #[salsa::invoke(function_generator::priv_function_with_body_sierra_data)]
111    fn priv_function_with_body_sierra_data(
112        &self,
113        function_id: ConcreteFunctionWithBodyId,
114    ) -> function_generator::SierraFunctionWithBodyData;
115    /// Returns the Sierra code (as [pre_sierra::Function]) for a given function with body.
116    #[salsa::invoke(function_generator::function_with_body_sierra)]
117    fn function_with_body_sierra(
118        &self,
119        function_id: ConcreteFunctionWithBodyId,
120    ) -> Maybe<Arc<pre_sierra::Function>>;
121
122    /// Private query to generate a dummy function for a given function with body.
123    #[salsa::invoke(function_generator::priv_get_dummy_function)]
124    fn priv_get_dummy_function(
125        &self,
126        function_id: ConcreteFunctionWithBodyId,
127    ) -> Maybe<Arc<pre_sierra::Function>>;
128
129    /// Returns the ap change of a given function if it is known at compile time or
130    /// [SierraApChange::Unknown] otherwise.
131    #[salsa::invoke(ap_change::get_ap_change)]
132    fn get_ap_change(&self, function_id: ConcreteFunctionWithBodyId) -> Maybe<SierraApChange>;
133
134    /// Returns the [SierraProgramWithDebug] object of the requested functions.
135    #[salsa::invoke(program_generator::get_sierra_program_for_functions)]
136    fn get_sierra_program_for_functions(
137        &self,
138        requested_function_ids: Vec<ConcreteFunctionWithBodyId>,
139    ) -> Maybe<Arc<SierraProgramWithDebug>>;
140
141    /// Returns the [SierraProgramWithDebug] object of the requested crates.
142    #[salsa::invoke(program_generator::get_sierra_program)]
143    fn get_sierra_program(
144        &self,
145        requested_crate_ids: Vec<CrateId>,
146    ) -> Maybe<Arc<SierraProgramWithDebug>>;
147}
148
149fn get_function_signature(
150    db: &dyn SierraGenGroup,
151    function_id: cairo_lang_sierra::ids::FunctionId,
152) -> Maybe<Arc<cairo_lang_sierra::program::FunctionSignature>> {
153    // TODO(yuval): add another version of this function that directly received semantic FunctionId.
154    // Call it from function_generators::get_function_code. Take ret_types from the result instead
155    // of only the explicit ret_type. Also use it for params instead of the current logic. Then use
156    // it in the end of program_generator::get_sierra_program instead of calling this function from
157    // there.
158    let lowered_function_id = function_id.lookup_intern(db);
159    let signature = lowered_function_id.signature(db)?;
160
161    let implicits = db
162        .function_implicits(lowered_function_id)?
163        .iter()
164        .map(|ty| db.get_concrete_type_id(*ty))
165        .collect::<Maybe<Vec<ConcreteTypeId>>>()?;
166
167    // TODO(spapini): Handle ret_types in lowering.
168    let mut all_params = implicits.clone();
169    let mut extra_rets = vec![];
170    for param in &signature.params {
171        let concrete_type_id = db.get_concrete_type_id(param.ty())?;
172        all_params.push(concrete_type_id.clone());
173    }
174    for var in &signature.extra_rets {
175        let concrete_type_id = db.get_concrete_type_id(var.ty())?;
176        extra_rets.push(concrete_type_id);
177    }
178
179    let mut ret_types = implicits;
180
181    let may_panic = !flag_unsafe_panic(db) && db.function_may_panic(lowered_function_id)?;
182    if may_panic {
183        let panic_info = PanicSignatureInfo::new(db, &signature);
184        ret_types.push(db.get_concrete_type_id(panic_info.actual_return_ty)?);
185    } else {
186        ret_types.extend(extra_rets);
187        // Functions that return the unit type don't have a return type in the signature.
188        if !signature.return_type.is_unit(db) {
189            ret_types.push(db.get_concrete_type_id(signature.return_type)?);
190        }
191    }
192
193    Ok(Arc::new(cairo_lang_sierra::program::FunctionSignature {
194        param_types: all_params,
195        ret_types,
196    }))
197}
198
199fn get_type_info(
200    db: &dyn SierraGenGroup,
201    concrete_type_id: cairo_lang_sierra::ids::ConcreteTypeId,
202) -> Maybe<Arc<cairo_lang_sierra::extensions::types::TypeInfo>> {
203    let long_id = match concrete_type_id.lookup_intern(db) {
204        SierraGeneratorTypeLongId::Regular(long_id) => long_id,
205        SierraGeneratorTypeLongId::CycleBreaker(ty) => {
206            let info = cycle_breaker_info(db, ty)?;
207            return Ok(Arc::new(cairo_lang_sierra::extensions::types::TypeInfo {
208                long_id: db.get_concrete_long_type_id(ty)?.as_ref().clone(),
209                storable: true,
210                droppable: info.droppable,
211                duplicatable: info.duplicatable,
212                zero_sized: false,
213            }));
214        }
215        SierraGeneratorTypeLongId::Phantom(ty) => {
216            let long_id = db.get_concrete_long_type_id(ty)?.as_ref().clone();
217            return Ok(Arc::new(cairo_lang_sierra::extensions::types::TypeInfo {
218                long_id,
219                storable: false,
220                droppable: false,
221                duplicatable: false,
222                zero_sized: true,
223            }));
224        }
225    };
226    let concrete_ty = cairo_lang_sierra::extensions::core::CoreType::specialize_by_id(
227        &SierraSignatureSpecializationContext(db),
228        &long_id.generic_id,
229        &long_id.generic_args,
230    )
231    .unwrap_or_else(|err| {
232        let mut long_id = long_id.as_ref().clone();
233        replace_ids::DebugReplacer { db }.replace_generic_args(&mut long_id.generic_args);
234        panic!("Got failure while specializing type `{long_id}`: {err}")
235    });
236    Ok(Arc::new(concrete_ty.info().clone()))
237}
238
239/// Returns the concrete Sierra long type id given the concrete id.
240pub fn sierra_concrete_long_id(
241    db: &dyn SierraGenGroup,
242    concrete_type_id: cairo_lang_sierra::ids::ConcreteTypeId,
243) -> Maybe<Arc<cairo_lang_sierra::program::ConcreteTypeLongId>> {
244    match concrete_type_id.lookup_intern(db) {
245        SierraGeneratorTypeLongId::Regular(long_id) => Ok(long_id),
246        SierraGeneratorTypeLongId::Phantom(type_id)
247        | SierraGeneratorTypeLongId::CycleBreaker(type_id) => db.get_concrete_long_type_id(type_id),
248    }
249}