shape_ast/ast/functions.rs
1//! Function definition and parameter types for Shape AST
2
3use super::DocComment;
4use super::expressions::Expr;
5use super::span::Span;
6use super::statements::Statement;
7use super::types::TypeAnnotation;
8use serde::{Deserialize, Serialize};
9// Re-export TypeParam from types to avoid duplication
10pub use super::types::TypeParam;
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct FunctionDef {
14 pub name: String,
15 pub name_span: Span,
16 /// Declaring module path for compiler/runtime provenance checks.
17 ///
18 /// This is injected by the module loader for loaded modules and is not part
19 /// of user-authored source syntax.
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub declaring_module_path: Option<String>,
22 #[serde(default)]
23 pub doc_comment: Option<DocComment>,
24 pub type_params: Option<Vec<TypeParam>>,
25 pub params: Vec<FunctionParameter>,
26 pub return_type: Option<TypeAnnotation>,
27 pub where_clause: Option<Vec<super::types::WherePredicate>>,
28 pub body: Vec<Statement>,
29 pub annotations: Vec<Annotation>,
30 pub is_async: bool,
31 /// Whether this function is compile-time-only (`comptime fn`).
32 ///
33 /// Comptime-only functions can only be called from comptime contexts.
34 #[serde(default)]
35 pub is_comptime: bool,
36}
37
38/// A foreign function definition: `fn <language> name(params) -> type { foreign_body }`
39///
40/// The body is raw source text in the foreign language, not parsed as Shape.
41#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub struct ForeignFunctionDef {
43 /// The language identifier (e.g., "python", "julia", "sql")
44 pub language: String,
45 pub language_span: Span,
46 pub name: String,
47 pub name_span: Span,
48 #[serde(default)]
49 pub doc_comment: Option<DocComment>,
50 pub type_params: Option<Vec<TypeParam>>,
51 pub params: Vec<FunctionParameter>,
52 pub return_type: Option<TypeAnnotation>,
53 /// The raw dedented source text of the foreign function body.
54 pub body_text: String,
55 /// Span of the body text in the original Shape source file.
56 pub body_span: Span,
57 pub annotations: Vec<Annotation>,
58 #[serde(default)]
59 pub is_async: bool,
60 /// Native ABI metadata for `extern "C"` declarations.
61 ///
62 /// When present, this foreign function is not compiled/invoked through a
63 /// language runtime extension. The VM links and invokes it via the native
64 /// C ABI path.
65 #[serde(default)]
66 pub native_abi: Option<NativeAbiBinding>,
67}
68
69/// Native ABI link metadata attached to a foreign function declaration.
70#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
71pub struct NativeAbiBinding {
72 /// ABI name (currently `"C"`).
73 pub abi: String,
74 /// Library path or logical dependency key.
75 pub library: String,
76 /// Symbol name to resolve in the library.
77 pub symbol: String,
78 /// Declaring package identity for package-scoped native resolution.
79 ///
80 /// This is compiler/runtime metadata, not source syntax.
81 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub package_key: Option<String>,
83}
84
85impl ForeignFunctionDef {
86 /// Whether the declared return type is `Result<T>`.
87 pub fn returns_result(&self) -> bool {
88 matches!(
89 &self.return_type,
90 Some(TypeAnnotation::Generic { name, .. }) if name == "Result"
91 )
92 }
93
94 /// Whether this function uses native ABI binding (e.g. `extern "C"`).
95 pub fn is_native_abi(&self) -> bool {
96 self.native_abi.is_some()
97 }
98
99 /// Validate that all parameter and return types are explicitly annotated,
100 /// and that dynamic-language foreign functions declare `Result<T>` as their
101 /// return type.
102 ///
103 /// Foreign function bodies are opaque — the type system cannot infer types
104 /// from them. This returns a list of `(message, span)` for each problem,
105 /// shared between the compiler and the LSP.
106 ///
107 /// `dynamic_language` should be `true` for languages like Python, JS, Ruby
108 /// where every call can fail at runtime. Currently all foreign languages
109 /// are treated as dynamic (the ABI declares this via `ErrorModel`).
110 pub fn validate_type_annotations(&self, dynamic_language: bool) -> Vec<(String, Span)> {
111 let mut errors = Vec::new();
112
113 for param in &self.params {
114 if param.type_annotation.is_none() {
115 let name = param.simple_name().unwrap_or("_");
116 errors.push((
117 format!(
118 "Foreign function '{}': parameter '{}' requires a type annotation \
119 (type inference is not available for foreign function bodies)",
120 self.name, name
121 ),
122 param.span(),
123 ));
124 }
125 }
126
127 if self.return_type.is_none() {
128 errors.push((
129 format!(
130 "Foreign function '{}' requires an explicit return type annotation \
131 (type inference is not available for foreign function bodies)",
132 self.name
133 ),
134 self.name_span,
135 ));
136 } else if dynamic_language && !self.returns_result() {
137 let inner_type = self
138 .return_type
139 .as_ref()
140 .map(|t| t.to_type_string())
141 .unwrap_or_else(|| "T".to_string());
142 errors.push((
143 format!(
144 "Foreign function '{}': return type must be Result<{}> \
145 (dynamic language runtimes can fail on every call)",
146 self.name, inner_type
147 ),
148 self.name_span,
149 ));
150 }
151
152 errors
153 }
154}
155
156#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
157pub struct FunctionParameter {
158 pub pattern: super::patterns::DestructurePattern,
159 #[serde(default)]
160 pub is_const: bool,
161 #[serde(default)]
162 pub is_reference: bool,
163 /// Whether this is an exclusive (mutable) reference: `&mut x`
164 /// Only meaningful when `is_reference` is true.
165 #[serde(default)]
166 pub is_mut_reference: bool,
167 /// Whether this is an `out` parameter (C out-pointer pattern).
168 /// Only valid on `extern C fn` declarations. The compiler auto-generates
169 /// cell allocation, C call, value readback, and cell cleanup.
170 #[serde(default)]
171 pub is_out: bool,
172 pub type_annotation: Option<TypeAnnotation>,
173 pub default_value: Option<Expr>,
174}
175
176impl FunctionParameter {
177 /// Get the simple parameter name if this is a simple identifier pattern
178 pub fn simple_name(&self) -> Option<&str> {
179 self.pattern.as_identifier()
180 }
181
182 /// Get all identifiers bound by this parameter (for destructuring patterns)
183 pub fn get_identifiers(&self) -> Vec<String> {
184 self.pattern.get_identifiers()
185 }
186
187 /// Get the span for this parameter
188 pub fn span(&self) -> Span {
189 match &self.pattern {
190 super::patterns::DestructurePattern::Identifier(_, span) => *span,
191 super::patterns::DestructurePattern::Array(_) => Span::default(),
192 super::patterns::DestructurePattern::Object(_) => Span::default(),
193 super::patterns::DestructurePattern::Rest(_) => Span::default(),
194 super::patterns::DestructurePattern::Decomposition(_) => Span::default(),
195 }
196 }
197}
198
199// Note: TypeParam is re-exported from types module above
200
201#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202pub struct Annotation {
203 pub name: String,
204 pub args: Vec<Expr>,
205 pub span: Span,
206}
207
208impl Annotation {
209 pub fn get<'a>(annotations: &'a [Annotation], name: &str) -> Option<&'a Annotation> {
210 annotations.iter().find(|a| a.name == name)
211 }
212}
213
214/// Annotation definition with lifecycle hooks
215///
216/// Annotations are Shape's aspect-oriented programming mechanism.
217/// They can define handlers for different lifecycle events:
218///
219/// ```shape
220/// annotation pattern() {
221/// on_define(fn, ctx) { ctx.registry("patterns").set(fn.name, fn); }
222/// metadata() { return { is_pattern: true }; }
223/// }
224/// ```
225#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
226pub struct AnnotationDef {
227 pub name: String,
228 pub name_span: Span,
229 #[serde(default)]
230 pub doc_comment: Option<DocComment>,
231 /// Annotation parameters (e.g., `period` in `@warmup(period)`)
232 pub params: Vec<FunctionParameter>,
233 /// Optional explicit target restrictions from `targets: [...]`.
234 /// If None, target applicability is inferred from handler kinds.
235 pub allowed_targets: Option<Vec<AnnotationTargetKind>>,
236 /// Lifecycle handlers (on_define, before, after, metadata)
237 pub handlers: Vec<AnnotationHandler>,
238 /// Full span of the annotation definition
239 pub span: Span,
240}
241
242/// Type of annotation lifecycle handler
243#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
244pub enum AnnotationHandlerType {
245 /// Called when function is defined (registration time)
246 OnDefine,
247 /// Called before each function invocation
248 Before,
249 /// Called after each function invocation
250 After,
251 /// Returns static metadata for tooling/optimization
252 Metadata,
253 /// Compile-time pre-inference handler: `comptime pre(target, ctx) { ... }`
254 /// Can emit directives to concretize untyped function parameters.
255 ComptimePre,
256 /// Compile-time post-inference handler: `comptime post(target, ctx) { ... }`
257 /// Can emit directives to synthesize return types and runtime bodies.
258 ComptimePost,
259}
260
261/// Describes what kind of syntax element an annotation is targeting.
262/// Used for compile-time validation of annotation applicability.
263#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
264pub enum AnnotationTargetKind {
265 /// @annotation before a function definition
266 Function,
267 /// @annotation before a type/struct/enum definition
268 Type,
269 /// @annotation before a module definition
270 Module,
271 /// @annotation before an arbitrary expression
272 Expression,
273 /// @annotation before a block expression
274 Block,
275 /// @annotation inside an await expression: `await @timeout(5s) expr`
276 AwaitExpr,
277 /// @annotation before a let/var/const binding
278 Binding,
279}
280
281/// A lifecycle handler within an annotation definition
282#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
283pub struct AnnotationHandler {
284 /// Type of handler (on_define, before, after, metadata)
285 pub handler_type: AnnotationHandlerType,
286 /// Handler parameters (e.g., `fn, ctx` for on_define)
287 pub params: Vec<AnnotationHandlerParam>,
288 /// Optional return type annotation
289 pub return_type: Option<TypeAnnotation>,
290 /// Handler body (a block expression)
291 pub body: Expr,
292 /// Span for error reporting
293 pub span: Span,
294}
295
296#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
297pub struct AnnotationHandlerParam {
298 pub name: String,
299 #[serde(default)]
300 pub is_variadic: bool,
301}