Skip to main content

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