cairo_lang_sierra_generator/
db.rs

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