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