cairo_lang_semantic/items/
functions.rs

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