Skip to main content

cairo_lang_semantic/items/
functions.rs

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