cairo_lang_semantic/items/
functions.rs

1use std::fmt::Write;
2use std::sync::Arc;
3
4use cairo_lang_debug::DebugWithDb;
5use cairo_lang_defs::diagnostic_utils::StableLocation;
6use cairo_lang_defs::ids::{
7    ExternFunctionId, FreeFunctionId, FunctionTitleId, FunctionWithBodyId, ImplFunctionId,
8    LanguageElementId, ModuleId, ModuleItemId, NamedLanguageElementId, ParamLongId,
9    TopLevelLanguageElementId, TraitFunctionId,
10};
11use cairo_lang_diagnostics::{Diagnostics, Maybe, MaybeAsRef};
12use cairo_lang_filesystem::ids::{SmolStrId, Tracked, UnstableSalsaId};
13use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
14use cairo_lang_syntax as syntax;
15use cairo_lang_syntax::attribute::structured::Attribute;
16use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode, ast};
17use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
18use cairo_lang_utils::{Intern, OptionFrom, define_short_id, require, try_extract_matches};
19use itertools::{Itertools, chain};
20use salsa::Database;
21use syntax::attribute::consts::MUST_USE_ATTR;
22use syntax::node::TypedStablePtr;
23
24use super::attribute::SemanticQueryAttrs;
25use super::generics::{fmt_generic_args, generic_params_to_args};
26use super::imp::{ImplId, ImplLongId};
27use super::modifiers;
28use super::trt::ConcreteTraitGenericFunctionId;
29use crate::corelib::{CorelibSemantic, fn_traits, unit_ty};
30use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnostics, SemanticDiagnosticsBuilder};
31use crate::expr::compute::Environment;
32use crate::expr::fmt::CountingWriter;
33use crate::items::extern_function::ExternFunctionSemantic;
34use crate::items::free_function::FreeFunctionSemantic;
35use crate::items::imp::ImplSemantic;
36use crate::items::trt::TraitSemantic;
37use crate::resolve::{Resolver, ResolverData};
38use crate::substitution::GenericSubstitution;
39use crate::types::resolve_type;
40use crate::{
41    ConcreteImplId, ConcreteImplLongId, ConcreteTraitLongId, GenericArgumentId, GenericParam,
42    SemanticDiagnostic, TypeId, semantic, semantic_object_for_id,
43};
44
45/// A generic function of an impl.
46#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update)]
47pub struct ImplGenericFunctionId<'db> {
48    // TODO(spapini): Consider making these private and enforcing invariants in the ctor.
49    /// The impl the function is in.
50    pub impl_id: ImplId<'db>,
51    /// The trait function this impl function implements.
52    pub function: TraitFunctionId<'db>,
53}
54impl<'db> ImplGenericFunctionId<'db> {
55    /// Gets the impl function language element, if self.impl_id is of a concrete impl.
56    pub fn impl_function(&self, db: &'db dyn Database) -> Maybe<Option<ImplFunctionId<'db>>> {
57        match self.impl_id.long(db) {
58            ImplLongId::Concrete(concrete_impl_id) => {
59                concrete_impl_id.get_impl_function(db, self.function)
60            }
61            ImplLongId::GenericParameter(_)
62            | ImplLongId::ImplVar(_)
63            | ImplLongId::ImplImpl(_)
64            | ImplLongId::SelfImpl(_)
65            | ImplLongId::GeneratedImpl(_) => Ok(None),
66        }
67    }
68    pub fn format(&self, db: &dyn Database) -> String {
69        format!("{}::{}", self.impl_id.name(db), self.function.name(db).long(db))
70    }
71}
72impl<'db> DebugWithDb<'db> for ImplGenericFunctionId<'db> {
73    type Db = dyn Database;
74
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
76        write!(f, "{}", self.format(db))
77    }
78}
79
80/// The ID of a generic function that can be concretized.
81#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update)]
82pub enum GenericFunctionId<'db> {
83    /// A generic free function.
84    Free(FreeFunctionId<'db>),
85    /// A generic extern function.
86    Extern(ExternFunctionId<'db>),
87    /// A generic function of an impl.
88    Impl(ImplGenericFunctionId<'db>),
89}
90impl<'db> GenericFunctionId<'db> {
91    pub fn from_generic_with_body(
92        db: &'db dyn Database,
93        val: GenericFunctionWithBodyId<'db>,
94    ) -> Maybe<Self> {
95        Ok(match val {
96            GenericFunctionWithBodyId::Free(id) => GenericFunctionId::Free(id),
97            GenericFunctionWithBodyId::Impl(id) => {
98                let impl_id = ImplLongId::Concrete(id.concrete_impl_id).intern(db);
99                let function = match id.function_body {
100                    ImplFunctionBodyId::Impl(body_id) => {
101                        db.impl_function_trait_function(body_id)?
102                    }
103                    ImplFunctionBodyId::Trait(body_id) => body_id,
104                };
105                GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function })
106            }
107            GenericFunctionWithBodyId::Trait(id) => {
108                GenericFunctionId::Impl(ImplGenericFunctionId {
109                    impl_id: ImplLongId::SelfImpl(id.concrete_trait(db)).intern(db),
110                    function: id.trait_function(db),
111                })
112            }
113        })
114    }
115    pub fn format(&self, db: &dyn Database) -> String {
116        match self {
117            GenericFunctionId::Free(id) => id.full_path(db),
118            GenericFunctionId::Extern(id) => id.full_path(db),
119            GenericFunctionId::Impl(id) => {
120                format!("{:?}::{}", id.impl_id.debug(db), id.function.name(db).long(db))
121            }
122        }
123    }
124    pub fn generic_signature(&self, db: &'db dyn Database) -> Maybe<&'db Signature<'db>> {
125        match *self {
126            GenericFunctionId::Free(id) => db.free_function_signature(id),
127            GenericFunctionId::Extern(id) => db.extern_function_signature(id),
128            GenericFunctionId::Impl(id) => {
129                #[salsa::tracked(returns(ref))]
130                fn impl_function_signature_tracked<'db>(
131                    db: &'db dyn Database,
132                    impl_id: ImplId<'db>,
133                    function: TraitFunctionId<'db>,
134                ) -> Maybe<Signature<'db>> {
135                    let concrete_trait_id = impl_id.concrete_trait(db)?;
136                    let signature = db.concrete_trait_function_signature(
137                        ConcreteTraitGenericFunctionId::new_from_data(
138                            db,
139                            concrete_trait_id,
140                            function,
141                        ),
142                    )?;
143                    GenericSubstitution::from_impl(impl_id).substitute(db, signature.clone())
144                }
145                impl_function_signature_tracked(db, id.impl_id, id.function).maybe_as_ref()
146            }
147        }
148    }
149    pub fn generic_params(&self, db: &'db dyn Database) -> Maybe<&'db [GenericParam<'db>]> {
150        match *self {
151            GenericFunctionId::Free(id) => db.free_function_generic_params(id),
152            GenericFunctionId::Extern(id) => db.extern_function_declaration_generic_params(id),
153            GenericFunctionId::Impl(id) => {
154                #[salsa::tracked(returns(ref))]
155                fn impl_function_generic_params_tracked<'db>(
156                    db: &'db dyn Database,
157                    impl_id: ImplId<'db>,
158                    trait_function: TraitFunctionId<'db>,
159                ) -> Maybe<Vec<GenericParam<'db>>> {
160                    let concrete_trait_id = db.impl_concrete_trait(impl_id)?;
161                    let concrete_id = ConcreteTraitGenericFunctionId::new_from_data(
162                        db,
163                        concrete_trait_id,
164                        trait_function,
165                    );
166                    GenericSubstitution::from_impl(impl_id).substitute(
167                        db,
168                        db.concrete_trait_function_generic_params(concrete_id)?.to_vec(),
169                    )
170                }
171                Ok(impl_function_generic_params_tracked(db, id.impl_id, id.function)
172                    .maybe_as_ref()?)
173            }
174        }
175    }
176    pub fn name(&self, db: &dyn Database) -> String {
177        match self {
178            GenericFunctionId::Free(free_function) => free_function.name(db).to_string(db),
179            GenericFunctionId::Extern(extern_function) => extern_function.name(db).to_string(db),
180            GenericFunctionId::Impl(impl_function) => impl_function.format(db),
181        }
182    }
183    /// Returns the ModuleId of the function's definition if possible.
184    pub fn module_id(&self, db: &'db dyn Database) -> Option<ModuleId<'db>> {
185        match self {
186            GenericFunctionId::Free(free_function) => Some(free_function.module_id(db)),
187            GenericFunctionId::Extern(extern_function) => Some(extern_function.module_id(db)),
188            GenericFunctionId::Impl(impl_generic_function_id) => {
189                // Return the module file of the impl containing the function.
190                if let ImplLongId::Concrete(concrete_impl_id) =
191                    impl_generic_function_id.impl_id.long(db)
192                {
193                    Some(concrete_impl_id.impl_def_id(db).module_id(db))
194                } else {
195                    None
196                }
197            }
198        }
199    }
200    /// Returns whether the function has the `#[must_use]` attribute.
201    pub fn is_must_use(&self, db: &dyn Database) -> Maybe<bool> {
202        match self {
203            GenericFunctionId::Free(id) => id.has_attr(db, MUST_USE_ATTR),
204            GenericFunctionId::Impl(id) => id.function.has_attr(db, MUST_USE_ATTR),
205            GenericFunctionId::Extern(_) => Ok(false),
206        }
207    }
208    /// Returns true if the function does not depend on any generics.
209    pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
210        match self {
211            GenericFunctionId::Free(_) | GenericFunctionId::Extern(_) => true,
212            GenericFunctionId::Impl(impl_generic_function) => {
213                impl_generic_function.impl_id.is_fully_concrete(db)
214            }
215        }
216    }
217    /// Returns true if the function does not depend on impl or type variables.
218    pub fn is_var_free(&self, db: &dyn Database) -> bool {
219        match self {
220            GenericFunctionId::Free(_) | GenericFunctionId::Extern(_) => true,
221            GenericFunctionId::Impl(impl_generic_function) => {
222                impl_generic_function.impl_id.is_var_free(db)
223            }
224        }
225    }
226    /// Returns the concrete function of this generic function with the given generic args.
227    pub fn concretize(
228        &self,
229        db: &'db dyn Database,
230        generic_args: Vec<semantic::GenericArgumentId<'db>>,
231    ) -> FunctionId<'db> {
232        FunctionLongId { function: ConcreteFunction { generic_function: *self, generic_args } }
233            .intern(db)
234    }
235}
236/// Conversion from ModuleItemId to GenericFunctionId.
237impl<'db> OptionFrom<ModuleItemId<'db>> for GenericFunctionId<'db> {
238    fn option_from(item: ModuleItemId<'db>) -> Option<Self> {
239        match item {
240            ModuleItemId::FreeFunction(id) => Some(GenericFunctionId::Free(id)),
241            ModuleItemId::ExternFunction(id) => Some(GenericFunctionId::Extern(id)),
242            ModuleItemId::Constant(_)
243            | ModuleItemId::Submodule(_)
244            | ModuleItemId::Use(_)
245            | ModuleItemId::Trait(_)
246            | ModuleItemId::Impl(_)
247            | ModuleItemId::Struct(_)
248            | ModuleItemId::Enum(_)
249            | ModuleItemId::TypeAlias(_)
250            | ModuleItemId::ImplAlias(_)
251            | ModuleItemId::ExternType(_)
252            | ModuleItemId::MacroDeclaration(_) => None,
253        }
254    }
255}
256impl<'db> DebugWithDb<'db> for GenericFunctionId<'db> {
257    type Db = dyn Database;
258
259    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
260        match self {
261            GenericFunctionId::Free(func) => write!(f, "{:?}", func.debug(db)),
262            GenericFunctionId::Extern(func) => write!(f, "{:?}", func.debug(db)),
263            GenericFunctionId::Impl(func) => write!(f, "{:?}", func.debug(db)),
264        }
265    }
266}
267
268/// Function instance.
269/// For example: `ImplA::foo<A, B>`, or `bar<A>`.
270// TODO(spapini): Make it an enum and add a function pointer variant.
271#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
272pub struct FunctionLongId<'db> {
273    pub function: ConcreteFunction<'db>,
274}
275impl<'db> DebugWithDb<'db> for FunctionLongId<'db> {
276    type Db = dyn Database;
277
278    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &dyn Database) -> std::fmt::Result {
279        write!(f, "{:?}", self.function.debug(db))
280    }
281}
282
283define_short_id!(FunctionId, FunctionLongId<'db>);
284semantic_object_for_id!(FunctionId, FunctionLongId<'a>);
285impl<'db> FunctionId<'db> {
286    pub fn get_concrete(&self, db: &'db dyn Database) -> ConcreteFunction<'db> {
287        self.long(db).function.clone()
288    }
289
290    /// Returns the ExternFunctionId if this is an extern function. Otherwise returns none.
291    pub fn try_get_extern_function_id(
292        &self,
293        db: &'db dyn Database,
294    ) -> Option<ExternFunctionId<'db>> {
295        try_extract_matches!(self.get_concrete(db).generic_function, GenericFunctionId::Extern)
296    }
297
298    pub fn name(&self, db: &dyn Database) -> String {
299        format!("{:?}", self.get_concrete(db).generic_function.name(db))
300    }
301
302    pub fn full_path(&self, db: &dyn Database) -> String {
303        self.get_concrete(db).full_path(db)
304    }
305
306    /// Returns true if the function does not depend on any generics.
307    pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
308        let func = self.get_concrete(db);
309        func.generic_function.is_fully_concrete(db)
310            && func
311                .generic_args
312                .iter()
313                .all(|generic_argument_id| generic_argument_id.is_fully_concrete(db))
314    }
315    /// Returns true if the function does not depend on impl or type variables.
316    pub fn is_var_free(&self, db: &dyn Database) -> bool {
317        let func = self.get_concrete(db);
318        func.generic_function.is_var_free(db)
319            && func
320                .generic_args
321                .iter()
322                .all(|generic_argument_id| generic_argument_id.is_var_free(db))
323    }
324}
325impl<'db> FunctionLongId<'db> {
326    pub fn from_generic(
327        db: &'db dyn Database,
328        generic_function: GenericFunctionId<'db>,
329    ) -> Maybe<Self> {
330        let generic_params = generic_function.generic_params(db)?;
331
332        Ok(FunctionLongId {
333            function: ConcreteFunction {
334                generic_function,
335                generic_args: generic_params_to_args(generic_params, db),
336            },
337        })
338    }
339}
340
341/// A generic function of a concrete impl.
342#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
343pub struct ImplGenericFunctionWithBodyId<'db> {
344    pub concrete_impl_id: ConcreteImplId<'db>,
345    pub function_body: ImplFunctionBodyId<'db>,
346}
347
348/// The body of an impl function implementation.
349#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
350pub enum ImplFunctionBodyId<'db> {
351    /// A function that was implemented in the impl.
352    Impl(ImplFunctionId<'db>),
353    /// The default implementation of a trait function in the trait.
354    Trait(TraitFunctionId<'db>),
355}
356impl<'db> ImplFunctionBodyId<'db> {
357    pub fn name(&self, db: &'db dyn Database) -> SmolStrId<'db> {
358        match self {
359            Self::Impl(body_id) => body_id.name(db),
360            Self::Trait(body_id) => body_id.name(db),
361        }
362    }
363    pub fn stable_location(&self, db: &'db dyn Database) -> StableLocation<'db> {
364        match self {
365            Self::Impl(body_id) => body_id.stable_location(db),
366            Self::Trait(body_id) => body_id.stable_location(db),
367        }
368    }
369
370    pub fn trait_function(&self, db: &'db dyn Database) -> Maybe<TraitFunctionId<'db>> {
371        match self {
372            Self::Impl(impl_function) => db.impl_function_trait_function(*impl_function),
373            Self::Trait(trait_function) => Ok(*trait_function),
374        }
375    }
376}
377
378/// The ID of a generic function with body that can be concretized.
379#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
380pub enum GenericFunctionWithBodyId<'db> {
381    Free(FreeFunctionId<'db>),
382    Impl(ImplGenericFunctionWithBodyId<'db>),
383    Trait(ConcreteTraitGenericFunctionId<'db>),
384}
385impl<'db> GenericFunctionWithBodyId<'db> {
386    pub fn from_generic(
387        db: &'db dyn Database,
388        other: GenericFunctionId<'db>,
389    ) -> Maybe<Option<Self>> {
390        Ok(Some(match other {
391            GenericFunctionId::Free(id) => GenericFunctionWithBodyId::Free(id),
392            GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function }) => {
393                let ImplLongId::Concrete(concrete_impl_id) = impl_id.long(db) else {
394                    return Ok(None);
395                };
396                GenericFunctionWithBodyId::Impl(ImplGenericFunctionWithBodyId {
397                    concrete_impl_id: *concrete_impl_id,
398                    function_body: if let Some(impl_function) =
399                        concrete_impl_id.get_impl_function(db, function)?
400                    {
401                        ImplFunctionBodyId::Impl(impl_function)
402                    } else {
403                        ImplFunctionBodyId::Trait(function)
404                    },
405                })
406            }
407            _ => return Ok(None),
408        }))
409    }
410    pub fn name(&self, db: &dyn Database) -> String {
411        match self {
412            GenericFunctionWithBodyId::Free(free) => free.name(db).to_string(db),
413            GenericFunctionWithBodyId::Impl(imp) => {
414                format!(
415                    "{}::{}",
416                    imp.concrete_impl_id.name(db).long(db),
417                    imp.function_body.name(db).long(db)
418                )
419            }
420            GenericFunctionWithBodyId::Trait(trt) => {
421                format!(
422                    "{}::{}",
423                    trt.concrete_trait(db).name(db).long(db),
424                    trt.trait_function(db).name(db).long(db)
425                )
426            }
427        }
428    }
429
430    pub fn full_path(&self, db: &dyn Database) -> String {
431        match self {
432            GenericFunctionWithBodyId::Free(free) => free.full_path(db),
433            GenericFunctionWithBodyId::Impl(imp) => {
434                format!(
435                    "{}::{}",
436                    imp.concrete_impl_id.impl_def_id(db).full_path(db),
437                    imp.function_body.name(db).long(db)
438                )
439            }
440            GenericFunctionWithBodyId::Trait(trt) => format!(
441                "{}::{}",
442                trt.concrete_trait(db).full_path(db),
443                trt.trait_function(db).name(db).long(db)
444            ),
445        }
446    }
447    pub fn stable_location(&self, db: &'db dyn Database) -> StableLocation<'db> {
448        match self {
449            GenericFunctionWithBodyId::Free(free_function) => free_function.stable_location(db),
450            GenericFunctionWithBodyId::Impl(impl_function) => {
451                impl_function.function_body.stable_location(db)
452            }
453            GenericFunctionWithBodyId::Trait(trait_function) => {
454                trait_function.trait_function(db).stable_location(db)
455            }
456        }
457    }
458}
459
460/// A long Id of a concrete function with body.
461#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
462pub struct ConcreteFunctionWithBody<'db> {
463    pub generic_function: GenericFunctionWithBodyId<'db>,
464    pub generic_args: Vec<semantic::GenericArgumentId<'db>>,
465}
466impl<'db> ConcreteFunctionWithBody<'db> {
467    pub fn function_with_body_id(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db> {
468        match self.generic_function {
469            GenericFunctionWithBodyId::Free(id) => FunctionWithBodyId::Free(id),
470            GenericFunctionWithBodyId::Impl(id) => match id.function_body {
471                ImplFunctionBodyId::Impl(id) => FunctionWithBodyId::Impl(id),
472                ImplFunctionBodyId::Trait(id) => FunctionWithBodyId::Trait(id),
473            },
474            GenericFunctionWithBodyId::Trait(id) => {
475                FunctionWithBodyId::Trait(id.trait_function(db))
476            }
477        }
478    }
479    pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
480        Ok(match self.generic_function {
481            GenericFunctionWithBodyId::Free(f) => {
482                GenericSubstitution::new(db.free_function_generic_params(f)?, &self.generic_args)
483            }
484            GenericFunctionWithBodyId::Impl(f) => match f.function_body {
485                ImplFunctionBodyId::Impl(body_id) => {
486                    let concrete_impl = f.concrete_impl_id.long(db);
487                    GenericSubstitution::from_impl(
488                        ImplLongId::Concrete(f.concrete_impl_id).intern(db),
489                    )
490                    .concat(GenericSubstitution::new(
491                        &chain!(
492                            db.impl_function_generic_params(body_id)?,
493                            db.impl_def_generic_params(concrete_impl.impl_def_id)?
494                        )
495                        .cloned()
496                        .collect_vec(),
497                        &chain!(&self.generic_args, &concrete_impl.generic_args)
498                            .cloned()
499                            .collect_vec(),
500                    ))
501                }
502                ImplFunctionBodyId::Trait(body_id) => {
503                    let concrete_impl_id = ImplLongId::Concrete(f.concrete_impl_id).intern(db);
504                    let concrete_trait = concrete_impl_id.concrete_trait(db)?.long(db);
505                    GenericSubstitution::from_impl(concrete_impl_id).concat(
506                        GenericSubstitution::new(
507                            &chain!(
508                                db.trait_function_generic_params(body_id)?,
509                                db.trait_generic_params(concrete_trait.trait_id)?
510                            )
511                            .cloned()
512                            .collect_vec(),
513                            &chain!(&self.generic_args, &concrete_trait.generic_args)
514                                .cloned()
515                                .collect_vec(),
516                        ),
517                    )
518                }
519            },
520            GenericFunctionWithBodyId::Trait(f) => {
521                let concrete_trait = f.concrete_trait(db).long(db);
522                GenericSubstitution::new(
523                    &chain!(
524                        db.trait_function_generic_params(f.trait_function(db))?,
525                        db.trait_generic_params(concrete_trait.trait_id)?
526                    )
527                    .cloned()
528                    .collect_vec(),
529                    &chain!(&self.generic_args, &concrete_trait.generic_args)
530                        .cloned()
531                        .collect_vec(),
532                )
533            }
534        })
535    }
536    pub fn from_no_generics_free(
537        db: &dyn Database,
538        free_function_id: FreeFunctionId<'db>,
539    ) -> Option<Self> {
540        require(db.free_function_generic_params(free_function_id).ok()?.is_empty())?;
541        Some(ConcreteFunctionWithBody {
542            generic_function: GenericFunctionWithBodyId::Free(free_function_id),
543            generic_args: vec![],
544        })
545    }
546    pub fn from_generic(
547        db: &'db dyn Database,
548        function_id: FunctionWithBodyId<'db>,
549    ) -> Maybe<Self> {
550        Ok(match function_id {
551            FunctionWithBodyId::Free(free) => {
552                let params = db.free_function_generic_params(free)?;
553                let generic_args = generic_params_to_args(params, db);
554                ConcreteFunctionWithBody {
555                    generic_function: GenericFunctionWithBodyId::Free(free),
556                    generic_args,
557                }
558            }
559            FunctionWithBodyId::Impl(impl_function_id) => {
560                let params = db.impl_function_generic_params(impl_function_id)?;
561                let generic_args = generic_params_to_args(params, db);
562                let impl_def_id = impl_function_id.impl_def_id(db);
563                let impl_def_params = db.impl_def_generic_params(impl_def_id)?;
564                let impl_generic_args = generic_params_to_args(impl_def_params, db);
565                let impl_generic_function = ImplGenericFunctionWithBodyId {
566                    concrete_impl_id: ConcreteImplLongId {
567                        impl_def_id,
568                        generic_args: impl_generic_args,
569                    }
570                    .intern(db),
571                    function_body: ImplFunctionBodyId::Impl(impl_function_id),
572                };
573                ConcreteFunctionWithBody {
574                    generic_function: GenericFunctionWithBodyId::Impl(impl_generic_function),
575                    generic_args,
576                }
577            }
578            FunctionWithBodyId::Trait(trait_function_id) => {
579                let params = db.trait_function_generic_params(trait_function_id)?;
580                let generic_args = generic_params_to_args(params, db);
581                let trait_id = trait_function_id.trait_id(db);
582                let trait_generic_params = db.trait_generic_params(trait_id)?;
583                let trait_generic_args = generic_params_to_args(trait_generic_params, db);
584                let concrete_trait_id = ConcreteTraitLongId {
585                    generic_args: trait_generic_args,
586                    trait_id: trait_function_id.trait_id(db),
587                }
588                .intern(db);
589                let trait_generic_function = ConcreteTraitGenericFunctionId::new_from_data(
590                    db,
591                    concrete_trait_id,
592                    trait_function_id,
593                );
594                ConcreteFunctionWithBody {
595                    generic_function: GenericFunctionWithBodyId::Trait(trait_generic_function),
596                    generic_args,
597                }
598            }
599        })
600    }
601    pub fn concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunction<'db>> {
602        Ok(ConcreteFunction {
603            generic_function: GenericFunctionId::from_generic_with_body(db, self.generic_function)?,
604            generic_args: self.generic_args.clone(),
605        })
606    }
607    pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
608        Ok(FunctionLongId { function: self.concrete(db)? }.intern(db))
609    }
610    pub fn name(&self, db: &'db dyn Database) -> SmolStrId<'db> {
611        self.function_with_body_id(db).name(db)
612    }
613    pub fn full_path(&self, db: &dyn Database) -> String {
614        format!("{:?}", self.debug(db))
615    }
616}
617
618impl<'db> DebugWithDb<'db> for ConcreteFunctionWithBody<'db> {
619    type Db = dyn Database;
620
621    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
622        let f = &mut CountingWriter::new(f);
623        write!(f, "{}", self.generic_function.full_path(db))?;
624        fmt_generic_args(&self.generic_args, f, db)
625    }
626}
627
628define_short_id!(ConcreteFunctionWithBodyId, ConcreteFunctionWithBody<'db>);
629semantic_object_for_id!(ConcreteFunctionWithBodyId, ConcreteFunctionWithBody<'a>);
630impl<'db> ConcreteFunctionWithBodyId<'db> {
631    pub fn function_with_body_id(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db> {
632        self.long(db).function_with_body_id(db)
633    }
634    pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
635        self.long(db).substitution(db)
636    }
637    pub fn from_no_generics_free(
638        db: &'db dyn Database,
639        free_function_id: FreeFunctionId<'db>,
640    ) -> Option<Self> {
641        Some(ConcreteFunctionWithBody::from_no_generics_free(db, free_function_id)?.intern(db))
642    }
643    pub fn from_generic(
644        db: &'db dyn Database,
645        function_id: FunctionWithBodyId<'db>,
646    ) -> Maybe<Self> {
647        Ok(ConcreteFunctionWithBody::from_generic(db, function_id)?.intern(db))
648    }
649    pub fn concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunction<'db>> {
650        self.long(db).concrete(db)
651    }
652    pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
653        self.long(db).function_id(db)
654    }
655    pub fn generic_function(&self, db: &'db dyn Database) -> GenericFunctionWithBodyId<'db> {
656        self.long(db).generic_function
657    }
658    pub fn name(&self, db: &'db dyn Database) -> SmolStrId<'db> {
659        self.long(db).name(db)
660    }
661    pub fn full_path(&self, db: &dyn Database) -> String {
662        self.long(db).full_path(db)
663    }
664
665    pub fn stable_location(&self, db: &'db dyn Database) -> StableLocation<'db> {
666        self.long(db).generic_function.stable_location(db)
667    }
668
669    pub fn is_panic_destruct_fn(&self, db: &dyn Database) -> Maybe<bool> {
670        let trait_function = match self.generic_function(db) {
671            GenericFunctionWithBodyId::Free(_) => return Ok(false),
672            GenericFunctionWithBodyId::Impl(impl_func) => {
673                impl_func.function_body.trait_function(db)?
674            }
675            GenericFunctionWithBodyId::Trait(trait_func) => trait_func.trait_function(db),
676        };
677        Ok(trait_function == db.core_info().panic_destruct_fn)
678    }
679}
680
681impl<'db> UnstableSalsaId for ConcreteFunctionWithBodyId<'db> {
682    fn get_internal_id(&self) -> salsa::Id {
683        self.as_intern_id()
684    }
685}
686
687#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
688pub struct ConcreteFunction<'db> {
689    pub generic_function: GenericFunctionId<'db>,
690    pub generic_args: Vec<semantic::GenericArgumentId<'db>>,
691}
692impl<'db> ConcreteFunction<'db> {
693    pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
694        let Some(generic_function) =
695            GenericFunctionWithBodyId::from_generic(db, self.generic_function)?
696        else {
697            return Ok(None);
698        };
699        Ok(Some(
700            ConcreteFunctionWithBody { generic_function, generic_args: self.generic_args.clone() }
701                .intern(db),
702        ))
703    }
704    pub fn full_path(&self, db: &dyn Database) -> String {
705        format!("{:?}", self.debug(db))
706    }
707}
708impl<'db> DebugWithDb<'db> for ConcreteFunction<'db> {
709    type Db = dyn Database;
710
711    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
712        let f = &mut CountingWriter::new(f);
713        write!(f, "{}", self.generic_function.format(db))?;
714        fmt_generic_args(&self.generic_args, f, db)
715    }
716}
717
718#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, salsa::Update)]
719#[debug_db(dyn Database)]
720pub struct Signature<'db> {
721    pub params: Vec<semantic::Parameter<'db>>,
722    pub return_type: semantic::TypeId<'db>,
723    /// implicit parameters
724    pub implicits: Vec<semantic::TypeId<'db>>,
725    #[dont_rewrite]
726    pub panicable: bool,
727    #[dont_rewrite]
728    pub is_const: bool,
729    #[hide_field_debug_with_db]
730    #[dont_rewrite]
731    pub stable_ptr: ast::FunctionSignaturePtr<'db>,
732}
733
734impl<'db> Signature<'db> {
735    pub fn from_ast(
736        diagnostics: &mut SemanticDiagnostics<'db>,
737        db: &'db dyn Database,
738        resolver: &mut Resolver<'db>,
739        declaration_syntax: &ast::FunctionDeclaration<'db>,
740        function_title_id: FunctionTitleId<'db>,
741        environment: &mut Environment<'db>,
742    ) -> Self {
743        let signature_syntax = declaration_syntax.signature(db);
744        let params = function_signature_params(
745            diagnostics,
746            db,
747            resolver,
748            signature_syntax.parameters(db).elements(db),
749            Some(function_title_id),
750            environment,
751        );
752        let return_type =
753            function_signature_return_type(diagnostics, db, resolver, &signature_syntax);
754        let implicits =
755            function_signature_implicit_parameters(diagnostics, db, resolver, &signature_syntax);
756        let panicable = match signature_syntax.optional_no_panic(db) {
757            ast::OptionTerminalNoPanic::Empty(_) => true,
758            ast::OptionTerminalNoPanic::TerminalNoPanic(_) => false,
759        };
760        let stable_ptr = signature_syntax.stable_ptr(db);
761        let is_const = matches!(
762            declaration_syntax.optional_const(db),
763            ast::OptionTerminalConst::TerminalConst(_)
764        );
765        semantic::Signature { params, return_type, implicits, panicable, stable_ptr, is_const }
766    }
767}
768
769pub fn function_signature_return_type<'db>(
770    diagnostics: &mut SemanticDiagnostics<'db>,
771    db: &'db dyn Database,
772    resolver: &mut Resolver<'db>,
773    sig: &ast::FunctionSignature<'db>,
774) -> semantic::TypeId<'db> {
775    let ty_syntax = match sig.ret_ty(db) {
776        ast::OptionReturnTypeClause::Empty(_) => {
777            return unit_ty(db);
778        }
779        ast::OptionReturnTypeClause::ReturnTypeClause(ret_type_clause) => ret_type_clause.ty(db),
780    };
781    resolve_type(db, diagnostics, resolver, &ty_syntax)
782}
783
784/// Returns the implicit parameters of the given function signature's AST.
785pub fn function_signature_implicit_parameters<'db>(
786    diagnostics: &mut SemanticDiagnostics<'db>,
787    db: &'db dyn Database,
788    resolver: &mut Resolver<'db>,
789    sig: &ast::FunctionSignature<'db>,
790) -> Vec<semantic::TypeId<'db>> {
791    let implicits = match sig.implicits_clause(db) {
792        ast::OptionImplicitsClause::Empty(_) => return vec![],
793        ast::OptionImplicitsClause::ImplicitsClause(implicits_clause) => {
794            implicits_clause.implicits(db)
795        }
796    };
797    let ast_implicits = implicits.elements(db);
798    ast_implicits
799        .map(|implicit| resolve_type(db, diagnostics, resolver, &ast::Expr::Path(implicit)))
800        .collect()
801}
802
803/// Returns the parameters of the given function signature's AST.
804pub fn function_signature_params<'db>(
805    diagnostics: &mut SemanticDiagnostics<'db>,
806    db: &'db dyn Database,
807    resolver: &mut Resolver<'db>,
808    params: impl Iterator<Item = ast::Param<'db>>,
809    function_title_id: Option<FunctionTitleId<'db>>,
810    env: &mut Environment<'db>,
811) -> Vec<semantic::Parameter<'db>> {
812    update_env_with_ast_params(diagnostics, db, resolver, params, function_title_id, env)
813}
814
815/// Query implementation of [FunctionsSemantic::concrete_function_signature].
816#[salsa::tracked(returns(ref))]
817fn concrete_function_signature<'db>(
818    db: &'db dyn Database,
819    function_id: FunctionId<'db>,
820) -> Maybe<Signature<'db>> {
821    let ConcreteFunction { generic_function, generic_args, .. } = &function_id.long(db).function;
822    let generic_params = generic_function.generic_params(db)?;
823    let generic_signature = generic_function.generic_signature(db)?;
824    // TODO(spapini): When trait generics are supported, they need to be substituted
825    //   one by one, not together.
826    // Panic shouldn't occur since ConcreteFunction is assumed to be constructed correctly.
827    GenericSubstitution::new(generic_params, generic_args).substitute(db, generic_signature.clone())
828}
829
830/// Implementation of [FunctionsSemantic::concrete_function_closure_params].
831fn concrete_function_closure_params<'db>(
832    db: &'db dyn Database,
833    function_id: FunctionId<'db>,
834) -> Maybe<OrderedHashMap<semantic::TypeId<'db>, semantic::TypeId<'db>>> {
835    let ConcreteFunction { generic_function, generic_args, .. } =
836        function_id.long(db).function.clone();
837    let generic_params = generic_function.generic_params(db)?;
838    let mut generic_closure_params = db.get_closure_params(generic_function)?;
839    let substitution = GenericSubstitution::new(generic_params, &generic_args);
840    let mut changed_keys = vec![];
841    for (key, value) in generic_closure_params.iter_mut() {
842        *value = substitution.substitute(db, *value)?;
843        let updated_key = substitution.substitute(db, *key)?;
844        if updated_key != *key {
845            changed_keys.push((*key, updated_key));
846        }
847    }
848    for (old_key, new_key) in changed_keys {
849        let v = generic_closure_params.swap_remove(&old_key).unwrap();
850        generic_closure_params.insert(new_key, v);
851    }
852    Ok(generic_closure_params)
853}
854
855/// Query implementation of [FunctionsSemantic::concrete_function_closure_params].
856#[salsa::tracked]
857fn concrete_function_closure_params_tracked<'db>(
858    db: &'db dyn Database,
859    function_id: FunctionId<'db>,
860) -> Maybe<OrderedHashMap<semantic::TypeId<'db>, semantic::TypeId<'db>>> {
861    concrete_function_closure_params(db, function_id)
862}
863
864/// For a given list of AST parameters, returns the list of semantic parameters along with the
865/// corresponding environment.
866fn update_env_with_ast_params<'db>(
867    diagnostics: &mut SemanticDiagnostics<'db>,
868    db: &'db dyn Database,
869    resolver: &mut Resolver<'db>,
870    ast_params: impl Iterator<Item = ast::Param<'db>>,
871    function_title_id: Option<FunctionTitleId<'db>>,
872    env: &mut Environment<'db>,
873) -> Vec<semantic::Parameter<'db>> {
874    let mut semantic_params = Vec::new();
875    for ast_param in ast_params {
876        let semantic_param = ast_param_to_semantic(diagnostics, db, resolver, &ast_param);
877
878        if env
879            .add_param(db, diagnostics, semantic_param.clone(), &ast_param, function_title_id)
880            .is_ok()
881        {
882            semantic_params.push(semantic_param);
883        }
884    }
885    semantic_params
886}
887
888/// Returns a semantic parameter (and its name) for the given AST parameter.
889fn ast_param_to_semantic<'db>(
890    diagnostics: &mut SemanticDiagnostics<'db>,
891    db: &'db dyn Database,
892    resolver: &mut Resolver<'db>,
893    ast_param: &ast::Param<'db>,
894) -> semantic::Parameter<'db> {
895    let name = ast_param.name(db);
896
897    let id = ParamLongId(resolver.module_id, ast_param.stable_ptr(db)).intern(db);
898
899    let ty = match ast_param.type_clause(db) {
900        ast::OptionTypeClause::Empty(missing) => {
901            resolver.inference().new_type_var(Some(missing.stable_ptr(db).untyped()))
902        }
903        ast::OptionTypeClause::TypeClause(ty_syntax) => {
904            resolve_type(db, diagnostics, resolver, &ty_syntax.ty(db))
905        }
906    };
907
908    let mutability =
909        modifiers::compute_mutability(diagnostics, db, &ast_param.modifiers(db).elements_vec(db));
910
911    semantic::Parameter { id, name: name.text(db), ty, mutability, stable_ptr: name.stable_ptr(db) }
912}
913
914// === Function Declaration ===
915
916#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
917#[debug_db(dyn Database)]
918pub struct FunctionDeclarationData<'db> {
919    pub diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
920    pub signature: semantic::Signature<'db>,
921    /// The environment induced by the function's signature.
922    pub environment: Environment<'db>,
923    pub attributes: Vec<Attribute<'db>>,
924    pub resolver_data: Arc<ResolverData<'db>>,
925    pub inline_config: InlineConfiguration<'db>,
926    /// Order of implicits to follow by this function.
927    ///
928    /// For example, this can be used to enforce ABI compatibility with Starknet OS.
929    pub implicit_precedence: ImplicitPrecedence<'db>,
930}
931
932#[derive(Debug, PartialEq, Eq, Clone, salsa::Update)]
933pub enum InlineConfiguration<'db> {
934    /// The user did not specify any inlining preferences.
935    None,
936    Always(ast::AttributePtr<'db>),
937    Should(ast::AttributePtr<'db>),
938    Never(ast::AttributePtr<'db>),
939}
940
941/// If a function with impl generic parameters is marked as '#[inline(always)]', raise a diagnostic.
942pub fn forbid_inline_always_with_impl_generic_param<'db>(
943    diagnostics: &mut SemanticDiagnostics<'db>,
944    generic_params: &[GenericParam<'db>],
945    inline_config: &InlineConfiguration<'db>,
946) {
947    let has_impl_generic_param = generic_params.iter().any(|p| matches!(p, GenericParam::Impl(_)));
948    match &inline_config {
949        InlineConfiguration::Always(stable_ptr) if has_impl_generic_param => {
950            diagnostics.report(
951                stable_ptr.untyped(),
952                SemanticDiagnosticKind::InlineAlwaysWithImplGenericArgNotAllowed,
953            );
954        }
955        _ => {}
956    }
957}
958
959/// Extra information about sorting of implicit arguments of the function.
960///
961/// In most of the user written code, the implicits are not stated explicitly, but instead are
962/// inferred by the compiler. The order on how these implicit arguments are laid out on Sierra level
963/// is unspecified though for the users. Currently, the compiler sorts them alphabetically by name
964/// for reproducibility, but it can equally just randomize the order on each compilation.
965///
966/// Some compilation targets tend to expect that particular functions accept particular implicit
967/// arguments at fixed positions. For example, the Starknet OS has such assumptions. By reading the
968/// implicit precedence information attached to functions, the compiler can now reliably generate
969/// compatible code.
970///
971/// To set, add the `#[implicit_precedence(...)]` attribute to function declaration. Only free or
972/// impl functions can have this information defined. For extern functions, the compiler raises an
973/// error. It is recommended to always create this attribute from compiler plugins, and not force
974/// users to write it manually.
975///
976/// Use [ImplicitPrecedence::UNSPECIFIED] to represent lack of information.
977#[derive(Clone, Debug, Default, Eq, PartialEq, salsa::Update)]
978pub struct ImplicitPrecedence<'db>(Vec<TypeId<'db>>);
979
980impl<'db> ImplicitPrecedence<'db> {
981    /// A precedence that does not actually prefer any implicit.
982    ///
983    /// When applied to a sequence of implicits, they will just be reordered alphabetically.
984    pub const UNSPECIFIED: Self = Self(Vec::new());
985
986    /// Sort implicits according to this precedence: first the ones with precedence
987    /// (according to it), then the others by their name.
988    pub fn apply(&self, implicits: &mut [TypeId<'db>], db: &dyn Database) {
989        implicits.sort_by_cached_key(|implicit| {
990            if let Some(idx) = self.0.iter().position(|item| item == implicit) {
991                return (idx, "".to_string());
992            }
993
994            (self.0.len(), implicit.format(db))
995        });
996    }
997}
998
999impl<'db> FromIterator<TypeId<'db>> for ImplicitPrecedence<'db> {
1000    fn from_iter<T: IntoIterator<Item = TypeId<'db>>>(iter: T) -> Self {
1001        Self(Vec::from_iter(iter))
1002    }
1003}
1004
1005/// Implementation of [FunctionsSemantic::get_closure_params].
1006fn get_closure_params<'db>(
1007    db: &'db dyn Database,
1008    generic_function_id: GenericFunctionId<'db>,
1009) -> Maybe<OrderedHashMap<TypeId<'db>, TypeId<'db>>> {
1010    let mut closure_params_map = OrderedHashMap::default();
1011    let generic_params = generic_function_id.generic_params(db)?;
1012
1013    for param in generic_params {
1014        if let GenericParam::Impl(generic_param_impl) = param {
1015            let concrete_trait = generic_param_impl.concrete_trait?;
1016            if fn_traits(db).contains(&concrete_trait.trait_id(db)) {
1017                let [GenericArgumentId::Type(closure_type), GenericArgumentId::Type(params_type)] =
1018                    *concrete_trait.generic_args(db)
1019                else {
1020                    unreachable!(
1021                        "Fn trait must have exactly two generic arguments: closure type and \
1022                         parameter type."
1023                    )
1024                };
1025
1026                closure_params_map.insert(closure_type, params_type);
1027            }
1028        }
1029    }
1030    Ok(closure_params_map)
1031}
1032
1033/// Query implementation of [FunctionsSemantic::get_closure_params].
1034fn get_closure_params_tracked<'db>(
1035    db: &'db dyn Database,
1036    generic_function_id: GenericFunctionId<'db>,
1037) -> Maybe<OrderedHashMap<TypeId<'db>, TypeId<'db>>> {
1038    get_closure_params_helper(db, (), generic_function_id)
1039}
1040
1041#[salsa::tracked]
1042fn get_closure_params_helper<'db>(
1043    db: &'db dyn Database,
1044    _tracked: Tracked,
1045    generic_function_id: GenericFunctionId<'db>,
1046) -> Maybe<OrderedHashMap<TypeId<'db>, TypeId<'db>>> {
1047    get_closure_params(db, generic_function_id)
1048}
1049
1050/// Trait for functions-related semantic queries.
1051pub trait FunctionsSemantic<'db>: Database {
1052    /// Returns the signature of the given FunctionTitleId. This include free functions, extern
1053    /// functions, etc...
1054    fn function_title_signature(
1055        &'db self,
1056        function_title_id: FunctionTitleId<'db>,
1057    ) -> Maybe<&'db semantic::Signature<'db>> {
1058        match function_title_id {
1059            FunctionTitleId::Free(id) => self.free_function_signature(id),
1060            FunctionTitleId::Extern(id) => self.extern_function_signature(id),
1061            FunctionTitleId::Trait(id) => self.trait_function_signature(id),
1062            FunctionTitleId::Impl(id) => self.impl_function_signature(id),
1063        }
1064    }
1065    /// Returns the generic parameters of the given FunctionTitleId. This include free
1066    /// functions, extern functions, etc...
1067    fn function_title_generic_params(
1068        &'db self,
1069        function_title_id: FunctionTitleId<'db>,
1070    ) -> Maybe<&'db [GenericParam<'db>]> {
1071        match function_title_id {
1072            FunctionTitleId::Free(free_function) => {
1073                self.free_function_generic_params(free_function)
1074            }
1075            FunctionTitleId::Extern(extern_function) => {
1076                self.extern_function_declaration_generic_params(extern_function)
1077            }
1078            FunctionTitleId::Trait(trait_function) => {
1079                self.trait_function_generic_params(trait_function)
1080            }
1081            FunctionTitleId::Impl(impl_function) => {
1082                self.impl_function_generic_params(impl_function)
1083            }
1084        }
1085    }
1086    /// Returns the signature of a concrete function. This include free functions, extern functions,
1087    /// etc...
1088    fn concrete_function_signature(
1089        &'db self,
1090        function_id: FunctionId<'db>,
1091    ) -> Maybe<&'db semantic::Signature<'db>> {
1092        concrete_function_signature(self.as_dyn_database(), function_id).maybe_as_ref()
1093    }
1094    /// Returns a mapping of closure types to their associated parameter types for a concrete
1095    /// function.
1096    fn concrete_function_closure_params(
1097        &'db self,
1098        function_id: FunctionId<'db>,
1099    ) -> Maybe<OrderedHashMap<semantic::TypeId<'db>, semantic::TypeId<'db>>> {
1100        concrete_function_closure_params_tracked(self.as_dyn_database(), function_id)
1101    }
1102    /// Returns a mapping of closure types to their associated parameter types for a generic
1103    /// function.
1104    fn get_closure_params(
1105        &'db self,
1106        generic_function_id: GenericFunctionId<'db>,
1107    ) -> Maybe<OrderedHashMap<TypeId<'db>, TypeId<'db>>> {
1108        get_closure_params_tracked(self.as_dyn_database(), generic_function_id)
1109    }
1110}
1111impl<'db, T: Database + ?Sized> FunctionsSemantic<'db> for T {}