cairo_lang_semantic/items/
function_with_body.rs1use std::sync::Arc;
2
3use cairo_lang_defs::ids::FunctionWithBodyId;
4use cairo_lang_diagnostics::{DiagnosticAdded, Diagnostics, Maybe, ToMaybe};
5use cairo_lang_filesystem::ids::Tracked;
6use cairo_lang_proc_macros::DebugWithDb;
7use cairo_lang_syntax::attribute::consts::{IMPLICIT_PRECEDENCE_ATTR, INLINE_ATTR};
8use cairo_lang_syntax::attribute::structured::{Attribute, AttributeArg, AttributeArgVariant};
9use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast};
10use cairo_lang_utils::try_extract_matches;
11use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
12use itertools::{Itertools, chain};
13use salsa::Database;
14
15use super::functions::InlineConfiguration;
16use crate::diagnostic::{
17 NotFoundItemType, SemanticDiagnosticKind, SemanticDiagnostics, SemanticDiagnosticsBuilder,
18};
19use crate::items::free_function::FreeFunctionSemantic;
20use crate::items::functions::ImplicitPrecedence;
21use crate::items::imp::ImplSemantic;
22use crate::items::trt::TraitSemantic;
23use crate::resolve::{ResolvedConcreteItem, Resolver, ResolverData};
24use crate::{Arenas, ExprId, PatternId, SemanticDiagnostic, TypeId, semantic};
25
26#[salsa::tracked]
28fn function_with_body_generic_params<'db>(
29 db: &'db dyn Database,
30 _tracked: Tracked,
31 function_id: FunctionWithBodyId<'db>,
32) -> Maybe<Vec<semantic::GenericParam<'db>>> {
33 Ok(match function_id {
34 FunctionWithBodyId::Free(free_function_id) => {
35 db.free_function_generic_params(free_function_id)?.to_vec()
36 }
37 FunctionWithBodyId::Impl(impl_function_id) => chain!(
38 db.impl_def_generic_params(impl_function_id.impl_def_id(db))?,
39 db.impl_function_generic_params(impl_function_id)?
40 )
41 .cloned()
42 .collect(),
43 FunctionWithBodyId::Trait(trait_function_id) => chain!(
44 db.trait_generic_params(trait_function_id.trait_id(db))?,
45 db.trait_function_generic_params(trait_function_id)?
46 )
47 .cloned()
48 .collect(),
49 })
50}
51
52#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
53#[debug_db(dyn Database)]
54pub struct FunctionBodyData<'db> {
55 pub diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
56 pub expr_lookup: UnorderedHashMap<ast::ExprPtr<'db>, ExprId>,
57 pub pattern_lookup: UnorderedHashMap<ast::PatternPtr<'db>, PatternId>,
58 pub resolver_data: Arc<ResolverData<'db>>,
59 pub body: FunctionBody<'db>,
60}
61
62unsafe impl<'db> salsa::Update for FunctionBodyData<'db> {
63 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
67 let old_value = unsafe { &mut *old_pointer };
68 let res = unsafe {
69 Diagnostics::maybe_update(&mut old_value.diagnostics, new_value.diagnostics)
70 | Arc::maybe_update(&mut old_value.resolver_data, new_value.resolver_data)
71 | FunctionBody::maybe_update(&mut old_value.body, new_value.body)
72 };
73 if res {
74 old_value.expr_lookup = new_value.expr_lookup;
75 old_value.pattern_lookup = new_value.pattern_lookup;
76 return true;
77 }
78 false
79 }
80}
81
82#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
83#[debug_db(dyn Database)]
84pub struct FunctionBody<'db> {
85 pub arenas: Arenas<'db>,
86 pub body_expr: semantic::ExprId,
87}
88
89unsafe impl<'db> salsa::Update for FunctionBody<'db> {
90 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
91 let old_value = unsafe { &mut *old_pointer };
94
95 if old_value != &new_value {
96 *old_value = new_value;
97 return true;
98 }
99
100 false
101 }
102}
103
104pub trait SemanticExprLookup<'db>: Database {
105 fn lookup_expr_by_ptr(
106 &'db self,
107 function_id: FunctionWithBodyId<'db>,
108 ptr: ast::ExprPtr<'db>,
109 ) -> Maybe<ExprId> {
110 let body_data = match function_id {
111 FunctionWithBodyId::Free(id) => self.priv_free_function_body_data(id)?,
112 FunctionWithBodyId::Impl(id) => self.priv_impl_function_body_data(id)?,
113 FunctionWithBodyId::Trait(id) => {
114 self.priv_trait_function_body_data(id)?.ok_or(DiagnosticAdded)?
115 }
116 };
117 body_data.expr_lookup.get(&ptr).copied().to_maybe()
118 }
119 fn lookup_pattern_by_ptr(
120 &'db self,
121 function_id: FunctionWithBodyId<'db>,
122 ptr: ast::PatternPtr<'db>,
123 ) -> Maybe<PatternId> {
124 let body_data = match function_id {
125 FunctionWithBodyId::Free(id) => self.priv_free_function_body_data(id)?,
126 FunctionWithBodyId::Impl(id) => self.priv_impl_function_body_data(id)?,
127 FunctionWithBodyId::Trait(id) => {
128 self.priv_trait_function_body_data(id)?.ok_or(DiagnosticAdded)?
129 }
130 };
131 body_data.pattern_lookup.get(&ptr).copied().to_maybe()
132 }
133}
134impl<'db, T: Database + ?Sized> SemanticExprLookup<'db> for T {}
135
136pub fn get_inline_config<'db>(
138 db: &'db dyn Database,
139 diagnostics: &mut SemanticDiagnostics<'db>,
140 attributes: &[Attribute<'db>],
141) -> Maybe<InlineConfiguration<'db>> {
142 let mut config = InlineConfiguration::None;
143 let mut seen_inline_attr = false;
144 for attr in attributes {
145 if attr.id.long(db) != INLINE_ATTR {
146 continue;
147 }
148
149 match &attr.args[..] {
150 [
151 AttributeArg {
152 variant: AttributeArgVariant::Unnamed(ast::Expr::Path(path)), ..
153 },
154 ] if path.as_syntax_node().get_text(db) == "always" => {
155 config = InlineConfiguration::Always(attr.stable_ptr);
156 }
157 [
158 AttributeArg {
159 variant: AttributeArgVariant::Unnamed(ast::Expr::Path(path)), ..
160 },
161 ] if path.as_syntax_node().get_text(db) == "never" => {
162 config = InlineConfiguration::Never(attr.stable_ptr);
163 }
164 [] => {
165 config = InlineConfiguration::Should(attr.stable_ptr);
166 }
167 _ => {
168 diagnostics.report(
169 attr.args_stable_ptr.untyped(),
170 SemanticDiagnosticKind::UnsupportedInlineArguments,
171 );
172 }
173 }
174
175 if seen_inline_attr {
176 diagnostics.report(
177 attr.id_stable_ptr.untyped(),
178 SemanticDiagnosticKind::RedundantInlineAttribute,
179 );
180 config = InlineConfiguration::None;
182 }
183
184 seen_inline_attr = true;
185 }
186 Ok(config)
187}
188
189pub fn get_implicit_precedence<'a, 'r>(
195 db: &'a dyn Database,
196 diagnostics: &mut SemanticDiagnostics<'a>,
197 resolver: &mut Resolver<'a>,
198 attributes: &'r [Attribute<'a>],
199) -> (ImplicitPrecedence<'a>, Option<&'r Attribute<'a>>) {
200 let mut attributes =
201 attributes.iter().rev().filter(|attr| attr.id.long(db) == IMPLICIT_PRECEDENCE_ATTR);
202
203 let Some(attr) = attributes.next() else { return (ImplicitPrecedence::UNSPECIFIED, None) };
205
206 for attr in attributes {
208 diagnostics.report(
209 attr.id_stable_ptr,
210 SemanticDiagnosticKind::RedundantImplicitPrecedenceAttribute,
211 );
212 }
213
214 let Ok(types) =
215 attr.args
216 .iter()
217 .map(|arg| match &arg.variant {
218 AttributeArgVariant::Unnamed(value) => {
219 let ast::Expr::Path(path) = value else {
220 return Err(diagnostics.report(
221 value.stable_ptr(db),
222 SemanticDiagnosticKind::UnsupportedImplicitPrecedenceArguments,
223 ));
224 };
225
226 resolver
227 .resolve_concrete_path(diagnostics, path, NotFoundItemType::Type)
228 .and_then(|resolved_item: crate::resolve::ResolvedConcreteItem<'_>| {
229 try_extract_matches!(resolved_item, ResolvedConcreteItem::Type)
230 .ok_or_else(|| {
231 diagnostics.report(
232 value.stable_ptr(db),
233 SemanticDiagnosticKind::UnknownType,
234 )
235 })
236 })
237 }
238
239 _ => Err(diagnostics.report(
240 arg.arg.stable_ptr(db),
241 SemanticDiagnosticKind::UnsupportedImplicitPrecedenceArguments,
242 )),
243 })
244 .try_collect::<TypeId<'_>, Vec<_>, _>()
245 else {
246 return (ImplicitPrecedence::UNSPECIFIED, None);
247 };
248
249 let precedence = ImplicitPrecedence::from_iter(types);
250
251 (precedence, Some(attr))
252}
253
254pub trait FunctionWithBodySemantic<'db>: Database {
256 fn function_declaration_diagnostics(
258 &'db self,
259 function_id: FunctionWithBodyId<'db>,
260 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
261 match function_id {
262 FunctionWithBodyId::Free(id) => self.free_function_declaration_diagnostics(id),
263 FunctionWithBodyId::Impl(id) => self.impl_function_declaration_diagnostics(id),
264 FunctionWithBodyId::Trait(id) => self.trait_function_declaration_diagnostics(id),
265 }
266 }
267 fn function_declaration_inline_config(
269 &'db self,
270 function_id: FunctionWithBodyId<'db>,
271 ) -> Maybe<InlineConfiguration<'db>> {
272 match function_id {
273 FunctionWithBodyId::Free(id) => self.free_function_declaration_inline_config(id),
274 FunctionWithBodyId::Impl(id) => self.impl_function_declaration_inline_config(id),
275 FunctionWithBodyId::Trait(id) => self.trait_function_declaration_inline_config(id),
276 }
277 }
278 fn function_declaration_implicit_precedence(
280 &'db self,
281 function_id: FunctionWithBodyId<'db>,
282 ) -> Maybe<&'db ImplicitPrecedence<'db>> {
283 match function_id {
284 FunctionWithBodyId::Free(id) => self.free_function_declaration_implicit_precedence(id),
285 FunctionWithBodyId::Impl(id) => self.impl_function_declaration_implicit_precedence(id),
286 FunctionWithBodyId::Trait(id) => {
287 self.trait_function_declaration_implicit_precedence(id)
288 }
289 }
290 }
291 fn function_with_body_signature(
293 &'db self,
294 function_id: FunctionWithBodyId<'db>,
295 ) -> Maybe<&'db semantic::Signature<'db>> {
296 match function_id {
297 FunctionWithBodyId::Free(id) => self.free_function_signature(id),
298 FunctionWithBodyId::Impl(id) => self.impl_function_signature(id),
299 FunctionWithBodyId::Trait(id) => self.trait_function_signature(id),
300 }
301 }
302 fn function_with_body_generic_params(
304 &'db self,
305 function_id: FunctionWithBodyId<'db>,
306 ) -> Maybe<Vec<semantic::GenericParam<'db>>> {
307 function_with_body_generic_params(self.as_dyn_database(), (), function_id)
308 }
309 fn function_with_body_attributes(
311 &'db self,
312 function_id: FunctionWithBodyId<'db>,
313 ) -> Maybe<&'db [Attribute<'db>]> {
314 match function_id {
315 FunctionWithBodyId::Free(id) => self.free_function_attributes(id),
316 FunctionWithBodyId::Impl(id) => self.impl_function_attributes(id),
317 FunctionWithBodyId::Trait(id) => self.trait_function_attributes(id),
318 }
319 }
320 fn function_body_diagnostics(
322 &'db self,
323 function_id: FunctionWithBodyId<'db>,
324 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
325 match function_id {
326 FunctionWithBodyId::Free(id) => self.free_function_body_diagnostics(id),
327 FunctionWithBodyId::Impl(id) => self.impl_function_body_diagnostics(id),
328 FunctionWithBodyId::Trait(id) => self.trait_function_body_diagnostics(id),
329 }
330 }
331 fn function_body(
333 &'db self,
334 function_id: FunctionWithBodyId<'db>,
335 ) -> Maybe<&'db FunctionBody<'db>> {
336 match function_id {
337 FunctionWithBodyId::Free(id) => self.free_function_body(id),
338 FunctionWithBodyId::Impl(id) => self.impl_function_body(id),
339 FunctionWithBodyId::Trait(id) => self.trait_function_body(id)?.ok_or(DiagnosticAdded),
340 }
341 }
342 fn function_body_expr(
344 &'db self,
345 function_id: FunctionWithBodyId<'db>,
346 ) -> Maybe<semantic::ExprId> {
347 Ok(self.function_body(function_id)?.body_expr)
348 }
349 fn expr_semantic(
351 &'db self,
352 function_id: FunctionWithBodyId<'db>,
353 id: semantic::ExprId,
354 ) -> semantic::Expr<'db> {
355 self.function_body(function_id).unwrap().arenas.exprs.get(id).unwrap().clone()
356 }
357 fn statement_semantic(
359 &'db self,
360 function_id: FunctionWithBodyId<'db>,
361 id: semantic::StatementId,
362 ) -> semantic::Statement<'db> {
363 self.function_body(function_id).unwrap().arenas.statements.get(id).unwrap().clone()
364 }
365 fn pattern_semantic(
367 &'db self,
368 function_id: FunctionWithBodyId<'db>,
369 id: semantic::PatternId,
370 ) -> semantic::Pattern<'db> {
371 self.function_body(function_id).unwrap().arenas.patterns.get(id).unwrap().clone()
372 }
373}
374impl<'db, T: Database + ?Sized> FunctionWithBodySemantic<'db> for T {}