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