cairo_lang_semantic/items/
free_function.rs1use 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};
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#[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.module_id(db);
41 let mut diagnostics = SemanticDiagnostics::default();
42 let free_function_syntax = db.module_free_function_by_id(free_function_id)?;
43 let declaration = free_function_syntax.declaration(db);
44
45 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#[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::default();
74 let free_function_syntax = db.module_free_function_by_id(free_function_id)?;
75 let declaration = free_function_syntax.declaration(db);
76
77 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 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#[salsa::tracked(returns(ref))]
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::default();
133 let free_function_syntax = db.module_free_function_by_id(free_function_id)?;
134 let declaration = free_function_declaration_data(db, free_function_id).maybe_as_ref()?;
136
137 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 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
179pub trait FreeFunctionSemantic<'db>: Database {
181 fn free_function_declaration_diagnostics(
183 &'db self,
184 id: FreeFunctionId<'db>,
185 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
186 free_function_declaration_data(self.as_dyn_database(), id)
187 .as_ref()
188 .map(|data| data.diagnostics.clone())
189 .unwrap_or_default()
190 }
191 fn free_function_signature(
193 &'db self,
194 id: FreeFunctionId<'db>,
195 ) -> Maybe<&'db semantic::Signature<'db>> {
196 Ok(&free_function_declaration_data(self.as_dyn_database(), id).maybe_as_ref()?.signature)
197 }
198 fn free_function_declaration_implicit_precedence(
200 &'db self,
201 id: FreeFunctionId<'db>,
202 ) -> Maybe<&'db ImplicitPrecedence<'db>> {
203 Ok(&free_function_declaration_data(self.as_dyn_database(), id)
204 .maybe_as_ref()?
205 .implicit_precedence)
206 }
207 fn free_function_generic_params(
209 &'db self,
210 id: FreeFunctionId<'db>,
211 ) -> Maybe<&'db [GenericParam<'db>]> {
212 Ok(&free_function_generic_params_data(self.as_dyn_database(), id)
213 .maybe_as_ref()?
214 .generic_params)
215 }
216 fn free_function_attributes(
218 &'db self,
219 id: FreeFunctionId<'db>,
220 ) -> Maybe<&'db [Attribute<'db>]> {
221 Ok(&free_function_declaration_data(self.as_dyn_database(), id).maybe_as_ref()?.attributes)
222 }
223 fn free_function_declaration_resolver_data(
225 &'db self,
226 id: FreeFunctionId<'db>,
227 ) -> Maybe<Arc<ResolverData<'db>>> {
228 Ok(free_function_declaration_data(self.as_dyn_database(), id)
229 .maybe_as_ref()?
230 .resolver_data
231 .clone())
232 }
233 fn free_function_declaration_inline_config(
235 &'db self,
236 id: FreeFunctionId<'db>,
237 ) -> Maybe<InlineConfiguration<'db>> {
238 Ok(free_function_declaration_data(self.as_dyn_database(), id)
239 .maybe_as_ref()?
240 .inline_config
241 .clone())
242 }
243 fn free_function_body_diagnostics(
245 &'db self,
246 id: FreeFunctionId<'db>,
247 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
248 self.priv_free_function_body_data(id)
249 .map(|data| data.diagnostics.clone())
250 .unwrap_or_default()
251 }
252 fn free_function_body(&'db self, id: FreeFunctionId<'db>) -> Maybe<&'db FunctionBody<'db>> {
254 Ok(&self.priv_free_function_body_data(id)?.body)
255 }
256 fn free_function_body_resolver_data(
258 &'db self,
259 id: FreeFunctionId<'db>,
260 ) -> Maybe<Arc<ResolverData<'db>>> {
261 Ok(self.priv_free_function_body_data(id)?.resolver_data.clone())
262 }
263 fn priv_free_function_body_data(
265 &'db self,
266 id: FreeFunctionId<'db>,
267 ) -> Maybe<&'db FunctionBodyData<'db>> {
268 priv_free_function_body_data(self.as_dyn_database(), id).maybe_as_ref()
269 }
270}
271impl<'db, T: Database + ?Sized> FreeFunctionSemantic<'db> for T {}