Skip to main content

cairo_lang_semantic/items/
free_function.rs

1use std::sync::Arc;
2
3use cairo_lang_defs::db::DefsGroup;
4use cairo_lang_defs::ids::{
5    FreeFunctionId, FunctionTitleId, LanguageElementId, LookupItemId, ModuleItemId,
6};
7use cairo_lang_diagnostics::{Diagnostics, Maybe, MaybeAsRef, skip_diagnostic};
8use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize};
9use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
10use cairo_lang_utils::Intern;
11use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
12use salsa::Database;
13
14use super::function_with_body::{FunctionBody, FunctionBodyData, get_inline_config};
15use super::functions::{
16    FunctionDeclarationData, GenericFunctionId, InlineConfiguration,
17    forbid_inline_always_with_impl_generic_param,
18};
19use super::generics::{GenericParamsData, semantic_generic_params};
20use crate::diagnostic::SemanticDiagnostics;
21use crate::expr::compute::{ComputationContext, ContextFunction, Environment, compute_root_expr};
22use crate::expr::inference::InferenceId;
23use crate::expr::inference::canonic::ResultNoErrEx;
24use crate::items::function_with_body::get_implicit_precedence;
25use crate::items::functions::ImplicitPrecedence;
26use crate::resolve::{Resolver, ResolverData};
27use crate::substitution::SemanticRewriter;
28use crate::{FunctionLongId, GenericParam, SemanticDiagnostic, semantic};
29
30#[cfg(test)]
31#[path = "free_function_test.rs"]
32mod test;
33
34/// Returns the generic params data of a free function.
35#[salsa::tracked(returns(ref))]
36fn free_function_generic_params_data<'db>(
37    db: &'db dyn Database,
38    free_function_id: FreeFunctionId<'db>,
39) -> Maybe<GenericParamsData<'db>> {
40    let module_id = free_function_id.parent_module(db);
41    let mut diagnostics = SemanticDiagnostics::new(module_id);
42    let free_function_syntax = db.module_free_function_by_id(free_function_id)?;
43    let declaration = free_function_syntax.declaration(db);
44
45    // Generic params.
46    let inference_id = InferenceId::LookupItemGenerics(LookupItemId::ModuleItem(
47        ModuleItemId::FreeFunction(free_function_id),
48    ));
49    let mut resolver = Resolver::new(db, module_id, inference_id);
50    resolver.set_feature_config(&free_function_id, &free_function_syntax, &mut diagnostics);
51    let generic_params = semantic_generic_params(
52        db,
53        &mut diagnostics,
54        &mut resolver,
55        module_id,
56        &declaration.generic_params(db),
57    );
58
59    let inference = &mut resolver.inference();
60    inference.finalize(&mut diagnostics, free_function_syntax.stable_ptr(db).untyped());
61
62    let generic_params = inference.rewrite(generic_params).no_err();
63    let resolver_data = Arc::new(resolver.data);
64    Ok(GenericParamsData { diagnostics: diagnostics.build(), generic_params, resolver_data })
65}
66
67/// Returns data about a free function declaration - its signature excluding its body.
68#[salsa::tracked(returns(ref))]
69fn free_function_declaration_data<'db>(
70    db: &'db dyn Database,
71    free_function_id: FreeFunctionId<'db>,
72) -> Maybe<FunctionDeclarationData<'db>> {
73    let mut diagnostics = SemanticDiagnostics::new(free_function_id.parent_module(db));
74    let free_function_syntax = db.module_free_function_by_id(free_function_id)?;
75    let declaration = free_function_syntax.declaration(db);
76
77    // Generic params.
78    let generic_params_data =
79        free_function_generic_params_data(db, free_function_id).maybe_as_ref()?;
80    let generic_params = &generic_params_data.generic_params;
81    let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::FreeFunction(free_function_id));
82    let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
83    let mut resolver = Resolver::with_data(
84        db,
85        (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
86    );
87    diagnostics.extend(generic_params_data.diagnostics.clone());
88
89    let mut environment = Environment::empty();
90
91    let signature = semantic::Signature::from_ast(
92        &mut diagnostics,
93        db,
94        &mut resolver,
95        &declaration,
96        FunctionTitleId::Free(free_function_id),
97        &mut environment,
98    );
99
100    let attributes = free_function_syntax.attributes(db).structurize(db);
101
102    let inline_config = get_inline_config(db, &mut diagnostics, &attributes)?;
103
104    forbid_inline_always_with_impl_generic_param(&mut diagnostics, generic_params, &inline_config);
105
106    let (implicit_precedence, _) =
107        get_implicit_precedence(db, &mut diagnostics, &mut resolver, &attributes);
108
109    // Check fully resolved.
110    let inference = &mut resolver.inference();
111
112    inference.finalize(&mut diagnostics, declaration.stable_ptr(db).untyped());
113    let signature = inference.rewrite(signature).no_err();
114
115    Ok(FunctionDeclarationData {
116        diagnostics: diagnostics.build(),
117        signature,
118        environment,
119        attributes,
120        resolver_data: Arc::new(resolver.data),
121        inline_config,
122        implicit_precedence,
123    })
124}
125
126/// Query implementation of [FreeFunctionSemantic::priv_free_function_body_data].
127#[salsa::tracked(returns(ref), cycle_fn=priv_free_function_body_data_cycle, cycle_initial=priv_free_function_body_data_initial)]
128fn priv_free_function_body_data<'db>(
129    db: &'db dyn Database,
130    free_function_id: FreeFunctionId<'db>,
131) -> Maybe<FunctionBodyData<'db>> {
132    let mut diagnostics = SemanticDiagnostics::new(free_function_id.parent_module(db));
133    let free_function_syntax = db.module_free_function_by_id(free_function_id)?;
134    // Compute declaration semantic.
135    let declaration = free_function_declaration_data(db, free_function_id).maybe_as_ref()?;
136
137    // Generic params.
138    let parent_resolver_data = db.free_function_declaration_resolver_data(free_function_id)?;
139    let inference_id = InferenceId::LookupItemDefinition(LookupItemId::ModuleItem(
140        ModuleItemId::FreeFunction(free_function_id),
141    ));
142    let mut resolver =
143        Resolver::with_data(db, (*parent_resolver_data).clone_with_inference_id(db, inference_id));
144
145    let environment = declaration.environment.clone();
146    let function_id = (|| {
147        let generic_function = GenericFunctionId::Free(free_function_id);
148
149        Ok(FunctionLongId::from_generic(db, generic_function)?.intern(db))
150    })();
151    // Compute body semantic expr.
152    let mut ctx = ComputationContext::new(
153        db,
154        &mut diagnostics,
155        &mut resolver,
156        Some(&declaration.signature),
157        environment,
158        ContextFunction::Function(function_id),
159    );
160    let function_body = free_function_syntax.body(db);
161    let return_type = declaration.signature.return_type;
162    let body_expr = compute_root_expr(&mut ctx, &function_body, return_type)?;
163    let ComputationContext { arenas, .. } = ctx;
164
165    let expr_lookup: UnorderedHashMap<_, _> =
166        arenas.exprs.iter().map(|(id, expr)| (expr.stable_ptr(), id)).collect();
167    let pattern_lookup: UnorderedHashMap<_, _> =
168        arenas.patterns.iter().map(|(id, pattern)| (pattern.stable_ptr(), id)).collect();
169    let resolver_data = Arc::new(resolver.data);
170    Ok(FunctionBodyData {
171        diagnostics: diagnostics.build(),
172        expr_lookup,
173        pattern_lookup,
174        resolver_data,
175        body: FunctionBody { arenas, body_expr },
176    })
177}
178
179/// Cycle handling for [FreeFunctionSemantic::priv_free_function_body_data].
180fn priv_free_function_body_data_cycle<'db>(
181    _db: &'db dyn Database,
182    _cycle: &salsa::Cycle<'_>,
183    last_provisional_value: &Maybe<FunctionBodyData<'db>>,
184    _value: Maybe<FunctionBodyData<'db>>,
185    _free_function_id: FreeFunctionId<'db>,
186) -> Maybe<FunctionBodyData<'db>> {
187    last_provisional_value.clone()
188}
189
190/// Cycle handling for [FreeFunctionSemantic::priv_free_function_body_data].
191fn priv_free_function_body_data_initial<'db>(
192    _db: &'db dyn Database,
193    _id: salsa::Id,
194    _free_function_id: FreeFunctionId<'db>,
195) -> Maybe<FunctionBodyData<'db>> {
196    Err(skip_diagnostic())
197}
198
199/// Trait for free function-related semantic queries.
200pub trait FreeFunctionSemantic<'db>: Database {
201    /// Returns the semantic diagnostics of a free function's declaration (signature).
202    fn free_function_declaration_diagnostics(
203        &'db self,
204        id: FreeFunctionId<'db>,
205    ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
206        free_function_declaration_data(self.as_dyn_database(), id)
207            .as_ref()
208            .map(|data| data.diagnostics.clone())
209            .unwrap_or_default()
210    }
211    /// Returns the signature of a free function.
212    fn free_function_signature(
213        &'db self,
214        id: FreeFunctionId<'db>,
215    ) -> Maybe<&'db semantic::Signature<'db>> {
216        Ok(&free_function_declaration_data(self.as_dyn_database(), id).maybe_as_ref()?.signature)
217    }
218    /// Returns the implicits precedence of a free function.
219    fn free_function_declaration_implicit_precedence(
220        &'db self,
221        id: FreeFunctionId<'db>,
222    ) -> Maybe<&'db ImplicitPrecedence<'db>> {
223        Ok(&free_function_declaration_data(self.as_dyn_database(), id)
224            .maybe_as_ref()?
225            .implicit_precedence)
226    }
227    /// Returns the generic params of a free function.
228    fn free_function_generic_params(
229        &'db self,
230        id: FreeFunctionId<'db>,
231    ) -> Maybe<&'db [GenericParam<'db>]> {
232        Ok(&free_function_generic_params_data(self.as_dyn_database(), id)
233            .maybe_as_ref()?
234            .generic_params)
235    }
236    /// Returns the attributes of a free function.
237    fn free_function_attributes(
238        &'db self,
239        id: FreeFunctionId<'db>,
240    ) -> Maybe<&'db [Attribute<'db>]> {
241        Ok(&free_function_declaration_data(self.as_dyn_database(), id).maybe_as_ref()?.attributes)
242    }
243    /// Returns the resolution resolved_items of a free function's declaration.
244    fn free_function_declaration_resolver_data(
245        &'db self,
246        id: FreeFunctionId<'db>,
247    ) -> Maybe<Arc<ResolverData<'db>>> {
248        Ok(free_function_declaration_data(self.as_dyn_database(), id)
249            .maybe_as_ref()?
250            .resolver_data
251            .clone())
252    }
253    /// Returns the inline configuration of a free function's declaration.
254    fn free_function_declaration_inline_config(
255        &'db self,
256        id: FreeFunctionId<'db>,
257    ) -> Maybe<InlineConfiguration<'db>> {
258        Ok(free_function_declaration_data(self.as_dyn_database(), id)
259            .maybe_as_ref()?
260            .inline_config
261            .clone())
262    }
263    /// Returns the semantic diagnostics of a free function's body.
264    fn free_function_body_diagnostics(
265        &'db self,
266        id: FreeFunctionId<'db>,
267    ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
268        self.priv_free_function_body_data(id)
269            .map(|data| data.diagnostics.clone())
270            .unwrap_or_default()
271    }
272    /// Returns the definition of a free function.
273    fn free_function_body(&'db self, id: FreeFunctionId<'db>) -> Maybe<&'db FunctionBody<'db>> {
274        Ok(&self.priv_free_function_body_data(id)?.body)
275    }
276    /// Returns the resolution resolved_items of a free function's body.
277    fn free_function_body_resolver_data(
278        &'db self,
279        id: FreeFunctionId<'db>,
280    ) -> Maybe<Arc<ResolverData<'db>>> {
281        Ok(self.priv_free_function_body_data(id)?.resolver_data.clone())
282    }
283    /// Returns the semantic body of a free function.
284    fn priv_free_function_body_data(
285        &'db self,
286        id: FreeFunctionId<'db>,
287    ) -> Maybe<&'db FunctionBodyData<'db>> {
288        priv_free_function_body_data(self.as_dyn_database(), id).maybe_as_ref()
289    }
290}
291impl<'db, T: Database + ?Sized> FreeFunctionSemantic<'db> for T {}