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