cairo_lang_semantic/items/
extern_function.rs

1use std::sync::Arc;
2
3use cairo_lang_defs::db::DefsGroup;
4use cairo_lang_defs::ids::{
5    ExternFunctionId, FunctionTitleId, LanguageElementId, LookupItemId, ModuleItemId,
6};
7use cairo_lang_diagnostics::{Diagnostics, Maybe, MaybeAsRef};
8use cairo_lang_filesystem::ids::SmolStrId;
9use cairo_lang_syntax::attribute::structured::AttributeListStructurize;
10use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
11use cairo_lang_utils::extract_matches;
12use salsa::Database;
13
14use super::function_with_body::get_inline_config;
15use super::functions::{FunctionDeclarationData, GenericFunctionId, InlineConfiguration};
16use super::generics::{GenericParamsData, semantic_generic_params};
17use crate::corelib::get_core_generic_function_id;
18use crate::diagnostic::SemanticDiagnosticKind::*;
19use crate::diagnostic::{SemanticDiagnostics, SemanticDiagnosticsBuilder};
20use crate::expr::compute::Environment;
21use crate::expr::inference::InferenceId;
22use crate::expr::inference::canonic::ResultNoErrEx;
23use crate::items::function_with_body::get_implicit_precedence;
24use crate::items::functions::ImplicitPrecedence;
25use crate::resolve::{Resolver, ResolverData};
26use crate::substitution::SemanticRewriter;
27use crate::{GenericParam, SemanticDiagnostic, semantic};
28
29#[cfg(test)]
30#[path = "extern_function_test.rs"]
31mod test;
32
33/// Query implementation of
34/// [ExternFunctionSemantic::extern_function_declaration_generic_params_data].
35#[salsa::tracked(returns(ref))]
36fn extern_function_declaration_generic_params_data<'db>(
37    db: &'db dyn Database,
38    extern_function_id: ExternFunctionId<'db>,
39) -> Maybe<GenericParamsData<'db>> {
40    let module_id = extern_function_id.module_id(db);
41    let mut diagnostics = SemanticDiagnostics::default();
42    let extern_function_syntax = db.module_extern_function_by_id(extern_function_id)?;
43    let declaration = extern_function_syntax.declaration(db);
44
45    // Generic params.
46    let inference_id = InferenceId::LookupItemGenerics(LookupItemId::ModuleItem(
47        ModuleItemId::ExternFunction(extern_function_id),
48    ));
49    let mut resolver = Resolver::new(db, module_id, inference_id);
50    resolver.set_feature_config(&extern_function_id, &extern_function_syntax, &mut diagnostics);
51    let generic_params = semantic_generic_params(
52        db,
53        &mut diagnostics,
54        &mut resolver,
55        module_id,
56        &declaration.generic_params(db),
57    );
58
59    let inference = &mut resolver.inference();
60    inference.finalize(&mut diagnostics, extern_function_syntax.stable_ptr(db).untyped());
61
62    let generic_params = inference.rewrite(generic_params).no_err();
63    let resolver_data = Arc::new(resolver.data);
64    Ok(GenericParamsData { diagnostics: diagnostics.build(), generic_params, resolver_data })
65}
66
67/// Query implementation of [ExternFunctionSemantic::priv_extern_function_declaration_data].
68#[salsa::tracked(returns(ref))]
69fn priv_extern_function_declaration_data<'db>(
70    db: &'db dyn Database,
71    extern_function_id: ExternFunctionId<'db>,
72) -> Maybe<FunctionDeclarationData<'db>> {
73    let mut diagnostics = SemanticDiagnostics::default();
74    let extern_function_syntax = db.module_extern_function_by_id(extern_function_id)?;
75
76    let declaration = extern_function_syntax.declaration(db);
77
78    // Generic params.
79    let generic_params_data =
80        db.extern_function_declaration_generic_params_data(extern_function_id)?;
81    let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::ExternFunction(extern_function_id));
82    let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
83    let mut resolver = Resolver::with_data(
84        db,
85        (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
86    );
87    diagnostics.extend(generic_params_data.diagnostics.clone());
88    resolver.set_feature_config(&extern_function_id, &extern_function_syntax, &mut diagnostics);
89
90    let mut environment = Environment::empty();
91    let signature = semantic::Signature::from_ast(
92        &mut diagnostics,
93        db,
94        &mut resolver,
95        &declaration,
96        FunctionTitleId::Extern(extern_function_id),
97        &mut environment,
98    );
99
100    if signature.panicable {
101        let panic_function = extract_matches!(
102            get_core_generic_function_id(db, SmolStrId::from(db, "panic")),
103            GenericFunctionId::Extern
104        );
105        if extern_function_id != panic_function {
106            diagnostics.report(extern_function_syntax.stable_ptr(db), PanicableExternFunction);
107        }
108    }
109
110    let attributes = extern_function_syntax.attributes(db).structurize(db);
111    let inline_config = get_inline_config(db, &mut diagnostics, &attributes)?;
112
113    match &inline_config {
114        InlineConfiguration::None => {}
115        InlineConfiguration::Always(stable_ptr)
116        | InlineConfiguration::Never(stable_ptr)
117        | InlineConfiguration::Should(stable_ptr) => {
118            diagnostics.report(stable_ptr.untyped(), InlineAttrForExternFunctionNotAllowed);
119        }
120    }
121
122    let (_, implicit_precedence_attr) =
123        get_implicit_precedence(db, &mut diagnostics, &mut resolver, &attributes);
124    if let Some(attr) = implicit_precedence_attr {
125        diagnostics
126            .report(attr.stable_ptr.untyped(), ImplicitPrecedenceAttrForExternFunctionNotAllowed);
127    }
128
129    // Check fully resolved.
130    let inference = &mut resolver.inference();
131    inference.finalize(&mut diagnostics, extern_function_syntax.stable_ptr(db).untyped());
132
133    let signature = inference.rewrite(signature).no_err();
134
135    Ok(FunctionDeclarationData {
136        diagnostics: diagnostics.build(),
137        signature,
138        environment,
139        attributes,
140        resolver_data: Arc::new(resolver.data),
141        inline_config,
142        implicit_precedence: ImplicitPrecedence::UNSPECIFIED,
143    })
144}
145
146/// Trait for extern function-related semantic queries.
147pub trait ExternFunctionSemantic<'db>: Database {
148    /// Returns the inline configuration of an extern function's declaration.
149    fn extern_function_declaration_inline_config(
150        &'db self,
151        extern_function_id: ExternFunctionId<'db>,
152    ) -> Maybe<InlineConfiguration<'db>> {
153        Ok(self.priv_extern_function_declaration_data(extern_function_id)?.inline_config.clone())
154    }
155    /// Returns the semantic diagnostics of an extern function declaration. An extern function has
156    /// no body, and thus only has a declaration.
157    fn extern_function_declaration_diagnostics(
158        &'db self,
159        extern_function_id: ExternFunctionId<'db>,
160    ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
161        self.priv_extern_function_declaration_data(extern_function_id)
162            .map(|data| data.diagnostics.clone())
163            .unwrap_or_default()
164    }
165    /// Returns the signature of an extern function.
166    fn extern_function_signature(
167        &'db self,
168        extern_function_id: ExternFunctionId<'db>,
169    ) -> Maybe<&'db semantic::Signature<'db>> {
170        Ok(&self.priv_extern_function_declaration_data(extern_function_id)?.signature)
171    }
172    /// Returns the generic params of an extern function.
173    fn extern_function_declaration_generic_params(
174        &'db self,
175        extern_function_id: ExternFunctionId<'db>,
176    ) -> Maybe<&'db [GenericParam<'db>]> {
177        Ok(&self
178            .extern_function_declaration_generic_params_data(extern_function_id)?
179            .generic_params)
180    }
181    /// Returns the resolution resolved_items of an extern function.
182    fn extern_function_declaration_resolver_data(
183        &'db self,
184        extern_function_id: ExternFunctionId<'db>,
185    ) -> Maybe<Arc<ResolverData<'db>>> {
186        Ok(self.priv_extern_function_declaration_data(extern_function_id)?.resolver_data.clone())
187    }
188}
189impl<'db, T: Database + ?Sized> ExternFunctionSemantic<'db> for T {}
190
191/// Trait for extern function-related semantic queries.
192trait PrivExternFunctionSemantic<'db>: Database {
193    /// Private query to compute data about an extern function declaration. An extern function has
194    /// no body, and thus only has a declaration.
195    fn priv_extern_function_declaration_data(
196        &'db self,
197        function_id: ExternFunctionId<'db>,
198    ) -> Maybe<&'db FunctionDeclarationData<'db>> {
199        priv_extern_function_declaration_data(self.as_dyn_database(), function_id).maybe_as_ref()
200    }
201    /// Returns the generic params data of an extern function.
202    fn extern_function_declaration_generic_params_data(
203        &'db self,
204        extern_function_id: ExternFunctionId<'db>,
205    ) -> Maybe<&'db GenericParamsData<'db>> {
206        extern_function_declaration_generic_params_data(self.as_dyn_database(), extern_function_id)
207            .maybe_as_ref()
208    }
209}
210impl<'db, T: Database + ?Sized> PrivExternFunctionSemantic<'db> for T {}