cairo_lang_semantic/items/
extern_function.rs1use 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#[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 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#[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 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 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
146pub trait ExternFunctionSemantic<'db>: Database {
148 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 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 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 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 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
191trait PrivExternFunctionSemantic<'db>: Database {
193 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 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 {}