1use crate::bytecode::{Function, Instruction, OpCode, Operand};
4use shape_ast::ast::{
5 AnnotationTargetKind, DestructurePattern, EnumDef, EnumMemberKind, ExportItem, Expr,
6 FunctionDef, FunctionParameter, Item, Literal, ModuleDecl, ObjectEntry, Query, Span, Statement,
7 TypeAnnotation, VarKind,
8};
9use shape_ast::error::{Result, ShapeError};
10use shape_runtime::type_schema::{EnumVariantInfo, FieldType};
11
12use super::{BytecodeCompiler, DropKind, ImportedSymbol, ParamPassMode, StructGenericInfo};
13
14#[derive(Debug, Clone)]
15struct NativeFieldLayoutSpec {
16 c_type: String,
17 size: u64,
18 align: u64,
19}
20
21impl BytecodeCompiler {
22 fn emit_comptime_internal_call(
23 &mut self,
24 method: &str,
25 args: Vec<Expr>,
26 span: Span,
27 ) -> Result<()> {
28 let call = Expr::MethodCall {
29 receiver: Box::new(Expr::Identifier("__comptime__".to_string(), span)),
30 method: method.to_string(),
31 args,
32 named_args: Vec::new(),
33 span,
34 };
35 let prev = self.allow_internal_comptime_namespace;
36 self.allow_internal_comptime_namespace = true;
37 let compile_result = self.compile_expr(&call);
38 self.allow_internal_comptime_namespace = prev;
39 compile_result?;
40 self.emit(Instruction::simple(OpCode::Pop));
41 Ok(())
42 }
43
44 fn emit_comptime_extend_directive(
45 &mut self,
46 extend: &shape_ast::ast::ExtendStatement,
47 span: Span,
48 ) -> Result<()> {
49 let payload = serde_json::to_string(extend).map_err(|e| ShapeError::RuntimeError {
50 message: format!("Failed to serialize comptime extend directive: {}", e),
51 location: Some(self.span_to_source_location(span)),
52 })?;
53 self.emit_comptime_internal_call(
54 "__emit_extend",
55 vec![Expr::Literal(Literal::String(payload), span)],
56 span,
57 )
58 }
59
60 fn emit_comptime_remove_directive(&mut self, span: Span) -> Result<()> {
61 self.emit_comptime_internal_call("__emit_remove", Vec::new(), span)
62 }
63
64 fn emit_comptime_set_param_value_directive(
65 &mut self,
66 param_name: &str,
67 expression: &Expr,
68 span: Span,
69 ) -> Result<()> {
70 self.emit_comptime_internal_call(
71 "__emit_set_param_value",
72 vec![
73 Expr::Literal(Literal::String(param_name.to_string()), span),
74 expression.clone(),
75 ],
76 span,
77 )
78 }
79
80 fn emit_comptime_set_param_type_directive(
81 &mut self,
82 param_name: &str,
83 type_annotation: &TypeAnnotation,
84 span: Span,
85 ) -> Result<()> {
86 let payload =
87 serde_json::to_string(type_annotation).map_err(|e| ShapeError::RuntimeError {
88 message: format!("Failed to serialize comptime param type directive: {}", e),
89 location: Some(self.span_to_source_location(span)),
90 })?;
91 self.emit_comptime_internal_call(
92 "__emit_set_param_type",
93 vec![
94 Expr::Literal(Literal::String(param_name.to_string()), span),
95 Expr::Literal(Literal::String(payload), span),
96 ],
97 span,
98 )
99 }
100
101 fn emit_comptime_set_return_type_directive(
102 &mut self,
103 type_annotation: &TypeAnnotation,
104 span: Span,
105 ) -> Result<()> {
106 let payload =
107 serde_json::to_string(type_annotation).map_err(|e| ShapeError::RuntimeError {
108 message: format!("Failed to serialize comptime return type directive: {}", e),
109 location: Some(self.span_to_source_location(span)),
110 })?;
111 self.emit_comptime_internal_call(
112 "__emit_set_return_type",
113 vec![Expr::Literal(Literal::String(payload), span)],
114 span,
115 )
116 }
117
118 fn emit_comptime_set_return_expr_directive(
119 &mut self,
120 expression: &Expr,
121 span: Span,
122 ) -> Result<()> {
123 self.emit_comptime_internal_call("__emit_set_return_type", vec![expression.clone()], span)
124 }
125
126 fn emit_comptime_replace_body_directive(
127 &mut self,
128 body: &[Statement],
129 span: Span,
130 ) -> Result<()> {
131 let payload = serde_json::to_string(body).map_err(|e| ShapeError::RuntimeError {
132 message: format!("Failed to serialize comptime replace-body directive: {}", e),
133 location: Some(self.span_to_source_location(span)),
134 })?;
135 self.emit_comptime_internal_call(
136 "__emit_replace_body",
137 vec![Expr::Literal(Literal::String(payload), span)],
138 span,
139 )
140 }
141
142 fn emit_comptime_replace_body_expr_directive(
143 &mut self,
144 expression: &Expr,
145 span: Span,
146 ) -> Result<()> {
147 self.emit_comptime_internal_call("__emit_replace_body", vec![expression.clone()], span)
148 }
149
150 fn emit_comptime_replace_module_expr_directive(
151 &mut self,
152 expression: &Expr,
153 span: Span,
154 ) -> Result<()> {
155 self.emit_comptime_internal_call("__emit_replace_module", vec![expression.clone()], span)
156 }
157
158 pub(super) fn register_item_functions(&mut self, item: &Item) -> Result<()> {
159 match item {
160 Item::Function(func_def, _) => self.register_function(func_def),
161 Item::Module(module_def, _) => {
162 let module_path = self.current_module_path_for(module_def.name.as_str());
163 self.module_scope_stack.push(module_path.clone());
164 let register_result = (|| -> Result<()> {
165 for inner in &module_def.items {
166 let qualified = self.qualify_module_item(inner, &module_path)?;
167 self.register_item_functions(&qualified)?;
168 }
169 Ok(())
170 })();
171 self.module_scope_stack.pop();
172 register_result
173 }
174 Item::Trait(trait_def, _) => {
175 self.known_traits.insert(trait_def.name.clone());
176 self.trait_defs
177 .insert(trait_def.name.clone(), trait_def.clone());
178 Ok(())
179 }
180 Item::ForeignFunction(def, _) => {
181 let caller_visible = def.params.iter().filter(|p| !p.is_out).count();
184 self.function_arity_bounds
185 .insert(def.name.clone(), (caller_visible, caller_visible));
186 self.function_const_params
187 .insert(def.name.clone(), Vec::new());
188 let (ref_params, ref_mutates) = Self::native_param_reference_contract(def);
189 let (vis_ref_params, vis_ref_mutates) = if def.params.iter().any(|p| p.is_out) {
190 let mut vrp = Vec::new();
191 let mut vrm = Vec::new();
192 for (i, p) in def.params.iter().enumerate() {
193 if !p.is_out {
194 vrp.push(ref_params.get(i).copied().unwrap_or(false));
195 vrm.push(ref_mutates.get(i).copied().unwrap_or(false));
196 }
197 }
198 (vrp, vrm)
199 } else {
200 (ref_params, ref_mutates)
201 };
202
203 let func = crate::bytecode::Function {
204 name: def.name.clone(),
205 arity: caller_visible as u16,
206 param_names: def
207 .params
208 .iter()
209 .filter(|p| !p.is_out)
210 .flat_map(|p| p.get_identifiers())
211 .collect(),
212 locals_count: 0,
213 entry_point: 0,
214 body_length: 0,
215 is_closure: false,
216 captures_count: 0,
217 is_async: def.is_async,
218 ref_params: vis_ref_params,
219 ref_mutates: vis_ref_mutates,
220 mutable_captures: Vec::new(),
221 frame_descriptor: None,
222 osr_entry_points: Vec::new(),
223 };
224 self.program.functions.push(func);
225
226 self.foreign_function_defs
229 .insert(def.name.clone(), def.clone());
230
231 Ok(())
232 }
233 Item::Export(export, _) => match &export.item {
234 ExportItem::Function(func_def) => self.register_function(func_def),
235 ExportItem::Trait(trait_def) => {
236 self.known_traits.insert(trait_def.name.clone());
237 self.trait_defs
238 .insert(trait_def.name.clone(), trait_def.clone());
239 Ok(())
240 }
241 ExportItem::ForeignFunction(def) => {
242 let caller_visible = def.params.iter().filter(|p| !p.is_out).count();
244 self.function_arity_bounds
245 .insert(def.name.clone(), (caller_visible, caller_visible));
246 self.function_const_params
247 .insert(def.name.clone(), Vec::new());
248 let (ref_params, ref_mutates) = Self::native_param_reference_contract(def);
249 let (vis_ref_params, vis_ref_mutates) =
250 if def.params.iter().any(|p| p.is_out) {
251 let mut vrp = Vec::new();
252 let mut vrm = Vec::new();
253 for (i, p) in def.params.iter().enumerate() {
254 if !p.is_out {
255 vrp.push(ref_params.get(i).copied().unwrap_or(false));
256 vrm.push(ref_mutates.get(i).copied().unwrap_or(false));
257 }
258 }
259 (vrp, vrm)
260 } else {
261 (ref_params, ref_mutates)
262 };
263
264 let func = crate::bytecode::Function {
265 name: def.name.clone(),
266 arity: caller_visible as u16,
267 param_names: def
268 .params
269 .iter()
270 .filter(|p| !p.is_out)
271 .flat_map(|p| p.get_identifiers())
272 .collect(),
273 locals_count: 0,
274 entry_point: 0,
275 body_length: 0,
276 is_closure: false,
277 captures_count: 0,
278 is_async: def.is_async,
279 ref_params: vis_ref_params,
280 ref_mutates: vis_ref_mutates,
281 mutable_captures: Vec::new(),
282 frame_descriptor: None,
283 osr_entry_points: Vec::new(),
284 };
285 self.program.functions.push(func);
286
287 self.foreign_function_defs
288 .insert(def.name.clone(), def.clone());
289
290 Ok(())
291 }
292 _ => Ok(()),
293 },
294 Item::Extend(extend, _) => {
295 for method in &extend.methods {
297 let func_def = self.desugar_extend_method(method, &extend.type_name)?;
298 self.register_function(&func_def)?;
299 }
300 Ok(())
301 }
302 Item::Impl(impl_block, _) => {
303 let trait_name = match &impl_block.trait_name {
308 shape_ast::ast::types::TypeName::Simple(n) => n.as_str(),
309 shape_ast::ast::types::TypeName::Generic { name, .. } => name.as_str(),
310 };
311 let type_name = match &impl_block.target_type {
312 shape_ast::ast::types::TypeName::Simple(n) => n.as_str(),
313 shape_ast::ast::types::TypeName::Generic { name, .. } => name.as_str(),
314 };
315 let impl_name = impl_block.impl_name.as_deref();
316
317 if trait_name == "From" || trait_name == "TryFrom" {
321 return self.compile_from_impl(impl_block, trait_name, type_name);
322 }
323
324 let overridden: std::collections::HashSet<&str> =
326 impl_block.methods.iter().map(|m| m.name.as_str()).collect();
327
328 for method in &impl_block.methods {
329 let func_def = self.desugar_impl_method(
330 method,
331 trait_name,
332 type_name,
333 impl_name,
334 &impl_block.target_type,
335 )?;
336 self.program.register_trait_method_symbol(
337 trait_name,
338 type_name,
339 impl_name,
340 &method.name,
341 &func_def.name,
342 );
343 self.register_function(&func_def)?;
344
345 if trait_name == "Drop" && method.name == "drop" {
347 let type_key = type_name.to_string();
348 let existing = self.drop_type_info.get(&type_key).copied();
349 let new_kind = if method.is_async {
350 match existing {
351 Some(DropKind::SyncOnly) | Some(DropKind::Both) => DropKind::Both,
352 _ => DropKind::AsyncOnly,
353 }
354 } else {
355 match existing {
356 Some(DropKind::AsyncOnly) | Some(DropKind::Both) => DropKind::Both,
357 _ => DropKind::SyncOnly,
358 }
359 };
360 self.drop_type_info.insert(type_key, new_kind);
361 }
362 }
363
364 if let Some(trait_def) = self.trait_defs.get(trait_name).cloned() {
366 for member in &trait_def.members {
367 if let shape_ast::ast::types::TraitMember::Default(default_method) = member
368 {
369 if !overridden.contains(default_method.name.as_str()) {
370 let func_def = self.desugar_impl_method(
371 default_method,
372 trait_name,
373 type_name,
374 impl_name,
375 &impl_block.target_type,
376 )?;
377 self.program.register_trait_method_symbol(
378 trait_name,
379 type_name,
380 impl_name,
381 &default_method.name,
382 &func_def.name,
383 );
384 self.register_function(&func_def)?;
385 }
386 }
387 }
388 }
389
390 let all_method_names: Vec<String> =
393 impl_block.methods.iter().map(|m| m.name.clone()).collect();
394 if let Some(selector) = impl_name {
395 let _ = self.type_inference.env.register_trait_impl_named(
396 trait_name,
397 type_name,
398 selector,
399 all_method_names,
400 );
401 } else {
402 let _ = self.type_inference.env.register_trait_impl(
403 trait_name,
404 type_name,
405 all_method_names,
406 );
407 }
408
409 Ok(())
410 }
411 _ => Ok(()),
412 }
413 }
414
415 pub(super) fn register_function(&mut self, func_def: &FunctionDef) -> Result<()> {
417 if !func_def.name.contains("::")
421 && !func_def.name.contains('.')
422 {
423 if let Some(existing) = self.program.functions.iter().find(|f| f.name == func_def.name) {
424 if existing.arity == func_def.params.len() as u16 {
429 return Ok(());
430 }
431 return Err(ShapeError::SemanticError {
432 message: format!(
433 "Duplicate function definition: '{}' is already defined",
434 func_def.name
435 ),
436 location: Some(self.span_to_source_location(func_def.name_span)),
437 });
438 }
439 }
440
441 self.function_defs
442 .insert(func_def.name.clone(), func_def.clone());
443
444 let total_params = func_def.params.len();
445 let mut required_params = total_params;
446 let mut saw_default = false;
447 let mut const_params = Vec::new();
448 for (idx, param) in func_def.params.iter().enumerate() {
449 if param.is_const {
450 const_params.push(idx);
451 }
452 if param.default_value.is_some() {
453 if !saw_default {
454 required_params = idx;
455 saw_default = true;
456 }
457 } else if saw_default {
458 return Err(ShapeError::SemanticError {
459 message: "Required parameter cannot follow a parameter with a default value"
460 .to_string(),
461 location: Some(self.span_to_source_location(param.span())),
462 });
463 }
464 }
465
466 self.function_arity_bounds
467 .insert(func_def.name.clone(), (required_params, total_params));
468 self.function_const_params
469 .insert(func_def.name.clone(), const_params);
470
471 let inferred_param_modes = self
472 .inferred_param_pass_modes
473 .get(&func_def.name)
474 .cloned()
475 .unwrap_or_default();
476 let mut ref_params: Vec<bool> = Vec::with_capacity(func_def.params.len());
477 let mut ref_mutates: Vec<bool> = Vec::with_capacity(func_def.params.len());
478 for (idx, param) in func_def.params.iter().enumerate() {
479 let fallback = if param.is_reference {
480 ParamPassMode::ByRefShared
481 } else {
482 ParamPassMode::ByValue
483 };
484 let mode = inferred_param_modes.get(idx).copied().unwrap_or(fallback);
485 ref_params.push(mode.is_reference());
486 ref_mutates.push(mode.is_exclusive());
487 }
488
489 let func = Function {
490 name: func_def.name.clone(),
491 arity: func_def.params.len() as u16,
492 param_names: func_def
493 .params
494 .iter()
495 .flat_map(|p| p.get_identifiers())
496 .collect(),
497 locals_count: 0, entry_point: 0, body_length: 0, is_closure: false,
501 captures_count: 0,
502 is_async: func_def.is_async,
503 ref_params,
504 ref_mutates,
505 mutable_captures: Vec::new(),
506 frame_descriptor: None,
507 osr_entry_points: Vec::new(),
508 };
509
510 self.program.functions.push(func);
511
512 if let Some(ref return_type) = func_def.return_type {
517 if let Some(type_name) = return_type.as_simple_name() {
518 self.type_tracker
519 .register_function_return_type(&func_def.name, type_name);
520 }
521 }
522
523 Ok(())
524 }
525
526 pub(super) fn compile_item_with_context(&mut self, item: &Item, is_last: bool) -> Result<()> {
529 match item {
530 Item::Function(func_def, _) => self.compile_function(func_def)?,
531 Item::Module(module_def, span) => {
532 self.compile_module_decl(module_def, *span)?;
533 }
534 Item::VariableDecl(var_decl, _) => {
535 let init_err = if let Some(init_expr) = &var_decl.value {
538 match self.compile_expr(init_expr) {
539 Ok(()) => None,
540 Err(e) => {
541 self.emit(Instruction::simple(OpCode::PushNull));
543 Some(e)
544 }
545 }
546 } else {
547 self.emit(Instruction::simple(OpCode::PushNull));
548 None
549 };
550
551 if let Some(name) = var_decl.pattern.as_identifier() {
552 let binding_idx = self.get_or_create_module_binding(name);
553 self.emit(Instruction::new(
554 OpCode::StoreModuleBinding,
555 Some(Operand::ModuleBinding(binding_idx)),
556 ));
557
558 if let Some(ref type_ann) = var_decl.type_annotation {
560 if let Some(type_name) =
561 Self::tracked_type_name_from_annotation(type_ann)
562 {
563 self.set_module_binding_type_info(binding_idx, &type_name);
564 }
565 } else {
566 let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
567 self.propagate_initializer_type_to_slot(binding_idx, false, is_mutable);
568 }
569
570 let binding_type_name = self
572 .type_tracker
573 .get_binding_type(binding_idx)
574 .and_then(|info| info.type_name.clone());
575 let drop_kind = binding_type_name
576 .as_ref()
577 .and_then(|tn| self.drop_type_info.get(tn).copied())
578 .or_else(|| {
579 var_decl
580 .type_annotation
581 .as_ref()
582 .and_then(|ann| self.annotation_drop_kind(ann))
583 });
584 if drop_kind.is_some() {
585 let is_async = match drop_kind {
586 Some(DropKind::AsyncOnly) => true,
587 Some(DropKind::Both) => false,
588 Some(DropKind::SyncOnly) | None => false,
589 };
590 self.track_drop_module_binding(binding_idx, is_async);
591 }
592 } else {
593 self.compile_destructure_pattern_global(&var_decl.pattern)?;
594 }
595
596 if let Some(e) = init_err {
597 return Err(e);
598 }
599 }
600 Item::Assignment(assign, _) => {
601 self.compile_statement(&Statement::Assignment(assign.clone(), Span::DUMMY))?;
602 }
603 Item::Expression(expr, _) => {
604 self.compile_expr(expr)?;
605 if !is_last {
607 self.emit(Instruction::simple(OpCode::Pop));
608 }
609 }
610 Item::Statement(stmt, _) => {
611 if is_last {
613 if let Statement::Expression(expr, _) = stmt {
614 self.compile_expr(expr)?;
615 return Ok(());
617 }
618 }
619 self.compile_statement(stmt)?;
620 }
621 Item::Export(export, export_span) => {
622 if let Some(ref var_decl) = export.source_decl {
625 if let Some(init_expr) = &var_decl.value {
626 self.compile_expr(init_expr)?;
627 } else {
628 self.emit(Instruction::simple(OpCode::PushNull));
629 }
630 if let Some(name) = var_decl.pattern.as_identifier() {
631 let binding_idx = self.get_or_create_module_binding(name);
632 self.emit(Instruction::new(
633 OpCode::StoreModuleBinding,
634 Some(Operand::ModuleBinding(binding_idx)),
635 ));
636 }
637 }
638 match &export.item {
639 ExportItem::Function(func_def) => self.compile_function(func_def)?,
640 ExportItem::Enum(enum_def) => self.register_enum(enum_def)?,
641 ExportItem::Struct(struct_def) => {
642 self.register_struct_type(struct_def, *export_span)?;
643 if self.struct_types.contains_key(&struct_def.name) {
644 self.emit_annotation_lifecycle_calls_for_type(
645 &struct_def.name,
646 &struct_def.annotations,
647 )?;
648 }
649 }
650 ExportItem::Interface(_) => {} ExportItem::Trait(_) => {} ExportItem::ForeignFunction(def) => self.compile_foreign_function(def)?,
653 _ => {}
654 }
655 }
656 Item::Stream(_stream, _) => {
657 return Err(ShapeError::StreamError {
658 message: "Streaming functionality has been removed".to_string(),
659 stream_name: None,
660 });
661 }
662 Item::TypeAlias(type_alias, _) => {
663 let base_type_name = match &type_alias.type_annotation {
665 TypeAnnotation::Reference(name) | TypeAnnotation::Basic(name) => {
666 Some(name.clone())
667 }
668 _ => None,
669 };
670 self.type_aliases.insert(
671 type_alias.name.clone(),
672 base_type_name
673 .clone()
674 .unwrap_or_else(|| format!("{:?}", type_alias.type_annotation)),
675 );
676
677 if let (Some(base_name), Some(overrides)) =
680 (&base_type_name, &type_alias.meta_param_overrides)
681 {
682 use shape_ast::ast::Literal;
683 use shape_value::ValueWord;
684 use std::sync::Arc;
685
686 let mut alias_comptime = self
688 .comptime_fields
689 .get(base_name)
690 .cloned()
691 .unwrap_or_default();
692
693 for (field_name, expr) in overrides {
694 let value = match expr {
695 Expr::Literal(Literal::Number(n), _) => ValueWord::from_f64(*n),
696 Expr::Literal(Literal::Int(n), _) => ValueWord::from_f64(*n as f64),
697 Expr::Literal(Literal::String(s), _) => {
698 ValueWord::from_string(Arc::new(s.clone()))
699 }
700 Expr::Literal(Literal::Bool(b), _) => ValueWord::from_bool(*b),
701 Expr::Literal(Literal::None, _) => ValueWord::none(),
702 _ => {
703 return Err(ShapeError::SemanticError {
704 message: format!(
705 "Comptime field override '{}' on type alias '{}' must be a literal",
706 field_name, type_alias.name
707 ),
708 location: None,
709 });
710 }
711 };
712 alias_comptime.insert(field_name.clone(), value);
713 }
714
715 if !alias_comptime.is_empty() {
716 self.comptime_fields
717 .insert(type_alias.name.clone(), alias_comptime);
718 }
719 }
720 }
721 Item::StructType(struct_def, span) => {
722 self.register_struct_type(struct_def, *span)?;
723 if self.struct_types.contains_key(&struct_def.name) {
724 self.emit_annotation_lifecycle_calls_for_type(
725 &struct_def.name,
726 &struct_def.annotations,
727 )?;
728 }
729 }
730 Item::Enum(enum_def, _) => {
731 self.register_enum(enum_def)?;
732 }
733 Item::Import(import_stmt, _) => {
735 self.register_import_names(import_stmt)?;
749 }
750 Item::Extend(extend, _) => {
751 for method in &extend.methods {
753 let func_def = self.desugar_extend_method(method, &extend.type_name)?;
754 self.compile_function(&func_def)?;
755 }
756 }
757 Item::Impl(impl_block, _) => {
758 let trait_name = match &impl_block.trait_name {
760 shape_ast::ast::types::TypeName::Simple(n) => n.as_str(),
761 shape_ast::ast::types::TypeName::Generic { name, .. } => name.as_str(),
762 };
763 let type_name = match &impl_block.target_type {
764 shape_ast::ast::types::TypeName::Simple(n) => n.as_str(),
765 shape_ast::ast::types::TypeName::Generic { name, .. } => name.as_str(),
766 };
767 let impl_name = impl_block.impl_name.as_deref();
768
769 if trait_name == "From" || trait_name == "TryFrom" {
771 return self.compile_from_impl_bodies(impl_block, trait_name, type_name);
772 }
773
774 let overridden: std::collections::HashSet<&str> =
776 impl_block.methods.iter().map(|m| m.name.as_str()).collect();
777
778 for method in &impl_block.methods {
779 let func_def = self.desugar_impl_method(
780 method,
781 trait_name,
782 type_name,
783 impl_name,
784 &impl_block.target_type,
785 )?;
786 self.compile_function(&func_def)?;
787 }
788
789 if let Some(trait_def) = self.trait_defs.get(trait_name).cloned() {
791 for member in &trait_def.members {
792 if let shape_ast::ast::types::TraitMember::Default(default_method) = member
793 {
794 if !overridden.contains(default_method.name.as_str()) {
795 let func_def = self.desugar_impl_method(
796 default_method,
797 trait_name,
798 type_name,
799 impl_name,
800 &impl_block.target_type,
801 )?;
802 self.compile_function(&func_def)?;
803 }
804 }
805 }
806 }
807 }
808 Item::AnnotationDef(ann_def, _) => {
809 self.compile_annotation_def(ann_def)?;
810 }
811 Item::Comptime(stmts, span) => {
812 let extensions: Vec<_> = self
814 .extension_registry
815 .as_ref()
816 .map(|r| r.as_ref().clone())
817 .unwrap_or_default();
818 let trait_impls = self.type_inference.env.trait_impl_keys();
819 let known_type_symbols: std::collections::HashSet<String> = self
820 .struct_types
821 .keys()
822 .chain(self.type_aliases.keys())
823 .cloned()
824 .collect();
825 let comptime_helpers = self.collect_comptime_helpers();
826 let execution = super::comptime::execute_comptime(
827 stmts,
828 &comptime_helpers,
829 &extensions,
830 trait_impls,
831 known_type_symbols,
832 )
833 .map_err(|e| ShapeError::RuntimeError {
834 message: format!(
835 "Comptime block evaluation failed: {}",
836 super::helpers::strip_error_prefix(&e)
837 ),
838 location: Some(self.span_to_source_location(*span)),
839 })?;
840 self.process_comptime_directives(execution.directives, "")
841 .map_err(|e| ShapeError::RuntimeError {
842 message: format!("Comptime block directive processing failed: {}", e),
843 location: Some(self.span_to_source_location(*span)),
844 })?;
845 }
846 Item::Query(query, _span) => {
847 self.compile_query(query)?;
848 if !is_last {
850 self.emit(Instruction::simple(OpCode::Pop));
851 }
852 }
853 Item::ForeignFunction(def, _) => self.compile_foreign_function(def)?,
854 _ => {} }
856 Ok(())
857 }
858
859 fn register_import_names(&mut self, import_stmt: &shape_ast::ast::ImportStmt) -> Result<()> {
864 use shape_ast::ast::ImportItems;
865
866 if let Some(pset) = self.permission_set.clone() {
869 self.check_import_permissions(import_stmt, &pset)?;
870 }
871
872 match &import_stmt.items {
873 ImportItems::Named(specs) => {
874 for spec in specs {
875 let local_name = spec.alias.as_ref().unwrap_or(&spec.name);
876 self.imported_names.insert(
879 local_name.clone(),
880 ImportedSymbol {
881 original_name: spec.name.clone(),
882 module_path: import_stmt.from.clone(),
883 },
884 );
885 }
886 }
887 ImportItems::Namespace { name, alias } => {
888 let local_name = alias.as_ref().unwrap_or(name);
891 let binding_idx = self.get_or_create_module_binding(local_name);
892 self.module_namespace_bindings.insert(local_name.clone());
893 let module_path = if import_stmt.from.is_empty() {
894 name.as_str()
895 } else {
896 import_stmt.from.as_str()
897 };
898 self.register_extension_module_schema(module_path);
901 let module_schema_name = format!("__mod_{}", module_path);
902 if self
903 .type_tracker
904 .schema_registry()
905 .get(&module_schema_name)
906 .is_some()
907 {
908 self.set_module_binding_type_info(binding_idx, &module_schema_name);
909 }
910 let _ = binding_idx;
912 }
913 }
914 Ok(())
915 }
916
917 fn check_import_permissions(
922 &mut self,
923 import_stmt: &shape_ast::ast::ImportStmt,
924 pset: &shape_abi_v1::PermissionSet,
925 ) -> Result<()> {
926 use shape_ast::ast::ImportItems;
927 use shape_runtime::stdlib::capability_tags;
928
929 let module_name = Self::extract_module_name(&import_stmt.from);
932
933 match &import_stmt.items {
934 ImportItems::Named(specs) => {
935 for spec in specs {
936 let required = capability_tags::required_permissions(module_name, &spec.name);
937 if !required.is_empty() && !required.is_subset(pset) {
938 let missing = required.difference(pset);
939 let missing_names: Vec<&str> = missing.iter().map(|p| p.name()).collect();
940 return Err(ShapeError::SemanticError {
941 message: format!(
942 "Permission denied: {module_name}::{} requires {} capability, \
943 but the active permission set does not include it. \
944 Add the permission to [permissions] in shape.toml or use a less \
945 restrictive preset.",
946 spec.name,
947 missing_names.join(", "),
948 ),
949 location: None,
950 });
951 }
952 self.record_blob_permissions(module_name, &spec.name);
953 }
954 }
955 ImportItems::Namespace { .. } => {
956 let required = capability_tags::module_permissions(module_name);
959 if !required.is_empty() && !required.is_subset(pset) {
960 let missing = required.difference(pset);
961 let missing_names: Vec<&str> = missing.iter().map(|p| p.name()).collect();
962 return Err(ShapeError::SemanticError {
963 message: format!(
964 "Permission denied: module '{module_name}' requires {} capabilities, \
965 but the active permission set does not include them. \
966 Add the permissions to [permissions] in shape.toml or use a less \
967 restrictive preset.",
968 missing_names.join(", "),
969 ),
970 location: None,
971 });
972 }
973 if let Some(ref mut blob) = self.current_blob_builder {
975 let module_perms = capability_tags::module_permissions(module_name);
976 blob.record_permissions(&module_perms);
977 }
978 }
979 }
980 Ok(())
981 }
982
983 fn extract_module_name(path: &str) -> &str {
987 path.rsplit(|c| c == ':' || c == '/')
988 .find(|s| !s.is_empty())
989 .unwrap_or(path)
990 }
991
992 pub(super) fn register_extension_module_schema(&mut self, module_path: &str) {
993 let Some(registry) = self.extension_registry.as_ref() else {
994 return;
995 };
996 let Some(module) = registry.iter().rev().find(|m| m.name == module_path) else {
997 return;
998 };
999
1000 for schema in &module.type_schemas {
1001 if self
1002 .type_tracker
1003 .schema_registry()
1004 .get(&schema.name)
1005 .is_none()
1006 {
1007 self.type_tracker
1008 .schema_registry_mut()
1009 .register(schema.clone());
1010 }
1011 }
1012
1013 let schema_name = format!("__mod_{}", module_path);
1014 if self
1015 .type_tracker
1016 .schema_registry()
1017 .get(&schema_name)
1018 .is_some()
1019 {
1020 return;
1021 }
1022
1023 let mut export_names: Vec<String> = module
1024 .export_names_available(self.comptime_mode)
1025 .into_iter()
1026 .map(|name| name.to_string())
1027 .collect();
1028
1029 for artifact in &module.module_artifacts {
1030 if artifact.module_path != module_path {
1031 continue;
1032 }
1033 let Some(source) = artifact.source.as_deref() else {
1034 continue;
1035 };
1036 if let Ok(names) =
1037 shape_runtime::module_loader::collect_exported_function_names_from_source(
1038 &artifact.module_path,
1039 source,
1040 )
1041 {
1042 export_names.extend(names);
1043 }
1044 }
1045
1046 export_names.sort();
1047 export_names.dedup();
1048
1049 let fields: Vec<(String, FieldType)> = export_names
1050 .into_iter()
1051 .map(|name| (name, FieldType::Any))
1052 .collect();
1053 self.type_tracker
1054 .schema_registry_mut()
1055 .register_type(schema_name, fields);
1056 }
1057
1058 fn register_enum(&mut self, enum_def: &EnumDef) -> Result<()> {
1060 let variants: Vec<EnumVariantInfo> = enum_def
1061 .members
1062 .iter()
1063 .enumerate()
1064 .map(|(id, member)| {
1065 let payload_fields = match &member.kind {
1066 EnumMemberKind::Unit { .. } => 0,
1067 EnumMemberKind::Tuple(types) => types.len() as u16,
1068 EnumMemberKind::Struct(fields) => fields.len() as u16,
1069 };
1070 EnumVariantInfo::new(&member.name, id as u16, payload_fields)
1071 })
1072 .collect();
1073
1074 let schema = shape_runtime::type_schema::TypeSchema::new_enum(&enum_def.name, variants);
1075 self.type_tracker.schema_registry_mut().register(schema);
1076 Ok(())
1077 }
1078
1079 pub fn register_imported_items(&mut self, items: &[Item]) {
1084 for item in items {
1085 match item {
1086 Item::Export(export, _) => {
1087 match &export.item {
1088 ExportItem::Enum(enum_def) => {
1089 let _ = self.register_enum(enum_def);
1090 }
1091 ExportItem::Struct(struct_def) => {
1092 let _ = self.register_struct_type(struct_def, Span::DUMMY);
1094 }
1095 ExportItem::Function(func_def) => {
1096 let _ = self.register_function(func_def);
1098 }
1099 _ => {}
1100 }
1101 }
1102 Item::Enum(enum_def, _) => {
1103 let _ = self.register_enum(enum_def);
1104 }
1105 _ => {}
1106 }
1107 }
1108 }
1109
1110 pub(super) fn desugar_extend_method(
1121 &self,
1122 method: &shape_ast::ast::types::MethodDef,
1123 target_type: &shape_ast::ast::TypeName,
1124 ) -> Result<FunctionDef> {
1125 let receiver_type = Some(Self::type_name_to_annotation(target_type));
1126 let (params, body) = self.desugar_method_signature_and_body(method, receiver_type)?;
1127
1128 let type_str = match target_type {
1131 shape_ast::ast::TypeName::Simple(n) => n.clone(),
1132 shape_ast::ast::TypeName::Generic { name, .. } => name.clone(),
1133 };
1134
1135 Ok(FunctionDef {
1136 name: format!("{}.{}", type_str, method.name),
1137 name_span: Span::DUMMY,
1138 declaring_module_path: method.declaring_module_path.clone(),
1139 doc_comment: None,
1140 params,
1141 return_type: method.return_type.clone(),
1142 body,
1143 type_params: Some(Vec::new()),
1144 annotations: method.annotations.clone(),
1145 is_async: method.is_async,
1146 is_comptime: false,
1147 where_clause: None,
1148 })
1149 }
1150
1151 fn desugar_impl_method(
1162 &self,
1163 method: &shape_ast::ast::types::MethodDef,
1164 trait_name: &str,
1165 type_name: &str,
1166 impl_name: Option<&str>,
1167 target_type: &shape_ast::ast::TypeName,
1168 ) -> Result<FunctionDef> {
1169 let receiver_type = Some(Self::type_name_to_annotation(target_type));
1170 let (params, body) = self.desugar_method_signature_and_body(method, receiver_type)?;
1171
1172 let method_name = if trait_name == "Drop" && method.name == "drop" && method.is_async {
1175 "drop_async".to_string()
1176 } else {
1177 method.name.clone()
1178 };
1179 let fn_name = if let Some(name) = impl_name {
1180 format!("{}::{}::{}::{}", trait_name, type_name, name, method_name)
1181 } else {
1182 format!("{}::{}", type_name, method_name)
1183 };
1184
1185 Ok(FunctionDef {
1186 name: fn_name,
1187 name_span: Span::DUMMY,
1188 declaring_module_path: method.declaring_module_path.clone(),
1189 doc_comment: None,
1190 params,
1191 return_type: method.return_type.clone(),
1192 body,
1193 type_params: Some(Vec::new()),
1194 annotations: method.annotations.clone(),
1195 is_async: method.is_async,
1196 is_comptime: false,
1197 where_clause: None,
1198 })
1199 }
1200
1201 fn desugar_method_signature_and_body(
1205 &self,
1206 method: &shape_ast::ast::types::MethodDef,
1207 receiver_type: Option<shape_ast::ast::TypeAnnotation>,
1208 ) -> Result<(Vec<FunctionParameter>, Vec<Statement>)> {
1209 if let Some(receiver) = method
1210 .params
1211 .first()
1212 .and_then(|p| p.pattern.as_identifier())
1213 {
1214 if receiver == "self" {
1215 let location = method
1216 .params
1217 .first()
1218 .map(|p| self.span_to_source_location(p.span()));
1219 return Err(ShapeError::SemanticError {
1220 message: format!(
1221 "Method '{}' has an explicit `self` parameter, but method receivers are implicit. Use `method {}(...)` without `self`.",
1222 method.name, method.name
1223 ),
1224 location,
1225 });
1226 }
1227 }
1228
1229 let mut params = vec![FunctionParameter {
1230 pattern: shape_ast::ast::DestructurePattern::Identifier(
1231 "self".to_string(),
1232 Span::DUMMY,
1233 ),
1234 is_const: false,
1235 is_reference: false,
1236 is_mut_reference: false,
1237 is_out: false,
1238 type_annotation: receiver_type,
1239 default_value: None,
1240 }];
1241 params.extend(method.params.clone());
1242
1243 Ok((params, method.body.clone()))
1244 }
1245
1246 fn compile_from_impl(
1257 &mut self,
1258 impl_block: &shape_ast::ast::types::ImplBlock,
1259 trait_name: &str,
1260 target_type: &str,
1261 ) -> Result<()> {
1262 let source_type = match &impl_block.trait_name {
1264 shape_ast::ast::types::TypeName::Generic { type_args, .. } if !type_args.is_empty() => {
1265 match &type_args[0] {
1266 TypeAnnotation::Basic(name) | TypeAnnotation::Reference(name) => name.clone(),
1267 other => {
1268 return Err(ShapeError::SemanticError {
1269 message: format!(
1270 "{} impl requires a simple source type, found {:?}",
1271 trait_name, other
1272 ),
1273 location: None,
1274 });
1275 }
1276 }
1277 }
1278 _ => {
1279 return Err(ShapeError::SemanticError {
1280 message: format!(
1281 "{} impl requires a generic type argument, e.g., {}<string>",
1282 trait_name, trait_name
1283 ),
1284 location: None,
1285 });
1286 }
1287 };
1288
1289 let selector = impl_block.impl_name.as_deref().unwrap_or(&source_type);
1291
1292 for method in &impl_block.methods {
1293 let func_def =
1294 self.desugar_from_method(method, trait_name, target_type, &source_type)?;
1295 let from_fn_name = func_def.name.clone();
1296
1297 self.program.register_trait_method_symbol(
1299 trait_name,
1300 target_type,
1301 Some(&source_type),
1302 &method.name,
1303 &from_fn_name,
1304 );
1305 self.register_function(&func_def)?;
1306
1307 if trait_name == "From" {
1309 self.program.register_trait_method_symbol(
1311 "Into",
1312 &source_type,
1313 Some(selector),
1314 "into",
1315 &from_fn_name,
1316 );
1317
1318 let wrapper_name =
1320 self.emit_from_to_tryinto_wrapper(&from_fn_name, &source_type, target_type)?;
1321 self.program.register_trait_method_symbol(
1322 "TryInto",
1323 &source_type,
1324 Some(selector),
1325 "tryInto",
1326 &wrapper_name,
1327 );
1328
1329 let _ = self.type_inference.env.register_trait_impl_named(
1331 "Into",
1332 &source_type,
1333 selector,
1334 vec!["into".to_string()],
1335 );
1336 let _ = self.type_inference.env.register_trait_impl_named(
1337 "TryInto",
1338 &source_type,
1339 selector,
1340 vec!["tryInto".to_string()],
1341 );
1342 } else {
1343 self.program.register_trait_method_symbol(
1345 "TryInto",
1346 &source_type,
1347 Some(selector),
1348 "tryInto",
1349 &from_fn_name,
1350 );
1351
1352 let _ = self.type_inference.env.register_trait_impl_named(
1354 "TryInto",
1355 &source_type,
1356 selector,
1357 vec!["tryInto".to_string()],
1358 );
1359 }
1360 }
1361
1362 let all_method_names: Vec<String> =
1364 impl_block.methods.iter().map(|m| m.name.clone()).collect();
1365 let _ = self.type_inference.env.register_trait_impl_named(
1366 trait_name,
1367 target_type,
1368 &source_type,
1369 all_method_names,
1370 );
1371
1372 Ok(())
1373 }
1374
1375 fn compile_from_impl_bodies(
1380 &mut self,
1381 impl_block: &shape_ast::ast::types::ImplBlock,
1382 trait_name: &str,
1383 target_type: &str,
1384 ) -> Result<()> {
1385 let source_type = match &impl_block.trait_name {
1386 shape_ast::ast::types::TypeName::Generic { type_args, .. } if !type_args.is_empty() => {
1387 match &type_args[0] {
1388 TypeAnnotation::Basic(name) | TypeAnnotation::Reference(name) => name.clone(),
1389 _ => return Ok(()), }
1391 }
1392 _ => return Ok(()),
1393 };
1394
1395 for method in &impl_block.methods {
1396 let func_def =
1397 self.desugar_from_method(method, trait_name, target_type, &source_type)?;
1398 self.compile_function(&func_def)?;
1399 }
1400
1401 if trait_name == "From" {
1403 for method in &impl_block.methods {
1404 let from_fn_name = format!(
1405 "{}::{}::{}::{}",
1406 trait_name, target_type, source_type, method.name
1407 );
1408 let wrapper_name = format!("__from_tryinto_{}_{}", source_type, target_type);
1409 if let Some(func_def) = self.function_defs.get(&wrapper_name).cloned() {
1411 let _ = self.compile_function(&func_def);
1412 let _ = from_fn_name; }
1416 }
1417 }
1418
1419 Ok(())
1420 }
1421
1422 fn desugar_from_method(
1427 &self,
1428 method: &shape_ast::ast::types::MethodDef,
1429 trait_name: &str,
1430 target_type: &str,
1431 source_type: &str,
1432 ) -> Result<FunctionDef> {
1433 if let Some(first) = method
1435 .params
1436 .first()
1437 .and_then(|p| p.pattern.as_identifier())
1438 {
1439 if first == "self" {
1440 return Err(ShapeError::SemanticError {
1441 message: format!(
1442 "{}::{} methods are constructors and must not have a `self` parameter",
1443 trait_name, method.name
1444 ),
1445 location: None,
1446 });
1447 }
1448 }
1449
1450 let fn_name = format!(
1451 "{}::{}::{}::{}",
1452 trait_name, target_type, source_type, method.name
1453 );
1454
1455 Ok(FunctionDef {
1456 name: fn_name,
1457 name_span: Span::DUMMY,
1458 declaring_module_path: method.declaring_module_path.clone(),
1459 doc_comment: None,
1460 params: method.params.clone(),
1461 return_type: method.return_type.clone(),
1462 body: method.body.clone(),
1463 type_params: Some(Vec::new()),
1464 annotations: Vec::new(),
1465 is_async: method.is_async,
1466 is_comptime: false,
1467 where_clause: None,
1468 })
1469 }
1470
1471 fn emit_from_to_tryinto_wrapper(
1476 &mut self,
1477 from_fn_name: &str,
1478 source_type: &str,
1479 target_type: &str,
1480 ) -> Result<String> {
1481 let wrapper_name = format!("__from_tryinto_{}_{}", source_type, target_type);
1482
1483 let span = Span::DUMMY;
1485 let body = vec![Statement::Return(
1486 Some(Expr::FunctionCall {
1487 name: "Ok".to_string(),
1488 args: vec![Expr::FunctionCall {
1489 name: from_fn_name.to_string(),
1490 args: vec![Expr::Identifier("value".to_string(), span)],
1491 named_args: Vec::new(),
1492 span,
1493 }],
1494 named_args: Vec::new(),
1495 span,
1496 }),
1497 span,
1498 )];
1499
1500 let func_def = FunctionDef {
1501 name: wrapper_name.clone(),
1502 name_span: span,
1503 declaring_module_path: None,
1504 doc_comment: None,
1505 params: vec![FunctionParameter {
1506 pattern: DestructurePattern::Identifier("value".to_string(), span),
1507 is_const: false,
1508 is_reference: false,
1509 is_mut_reference: false,
1510 is_out: false,
1511 type_annotation: None,
1512 default_value: None,
1513 }],
1514 return_type: None,
1515 body,
1516 type_params: Some(Vec::new()),
1517 annotations: Vec::new(),
1518 is_async: false,
1519 is_comptime: false,
1520 where_clause: None,
1521 };
1522
1523 self.register_function(&func_def)?;
1524
1525 Ok(wrapper_name)
1526 }
1527
1528 fn type_name_to_annotation(
1529 type_name: &shape_ast::ast::TypeName,
1530 ) -> shape_ast::ast::TypeAnnotation {
1531 match type_name {
1532 shape_ast::ast::TypeName::Simple(name) => {
1533 shape_ast::ast::TypeAnnotation::Basic(name.clone())
1534 }
1535 shape_ast::ast::TypeName::Generic { name, type_args } => {
1536 shape_ast::ast::TypeAnnotation::Generic {
1537 name: name.clone(),
1538 args: type_args.clone(),
1539 }
1540 }
1541 }
1542 }
1543
1544 fn compile_annotation_def(&mut self, ann_def: &shape_ast::ast::AnnotationDef) -> Result<()> {
1553 use crate::bytecode::CompiledAnnotation;
1554 use shape_ast::ast::AnnotationHandlerType;
1555
1556 let mut compiled = CompiledAnnotation {
1557 name: ann_def.name.clone(),
1558 param_names: ann_def
1559 .params
1560 .iter()
1561 .flat_map(|p| p.get_identifiers())
1562 .collect(),
1563 before_handler: None,
1564 after_handler: None,
1565 on_define_handler: None,
1566 metadata_handler: None,
1567 comptime_pre_handler: None,
1568 comptime_post_handler: None,
1569 allowed_targets: Vec::new(),
1570 };
1571
1572 for handler in &ann_def.handlers {
1573 match handler.handler_type {
1576 AnnotationHandlerType::ComptimePre => {
1577 compiled.comptime_pre_handler = Some(handler.clone());
1578 continue;
1579 }
1580 AnnotationHandlerType::ComptimePost => {
1581 compiled.comptime_post_handler = Some(handler.clone());
1582 continue;
1583 }
1584 _ => {}
1585 }
1586
1587 if handler.params.iter().any(|p| p.is_variadic) {
1588 return Err(ShapeError::SemanticError {
1589 message:
1590 "Variadic annotation handler params (`...args`) are only supported on comptime handlers"
1591 .to_string(),
1592 location: Some(self.span_to_source_location(handler.span)),
1593 });
1594 }
1595
1596 let handler_type_str = match handler.handler_type {
1597 AnnotationHandlerType::Before => "before",
1598 AnnotationHandlerType::After => "after",
1599 AnnotationHandlerType::OnDefine => "on_define",
1600 AnnotationHandlerType::Metadata => "metadata",
1601 AnnotationHandlerType::ComptimePre => unreachable!(),
1602 AnnotationHandlerType::ComptimePost => unreachable!(),
1603 };
1604
1605 let func_name = format!("{}___{}", ann_def.name, handler_type_str);
1606
1607 let mut params = vec![FunctionParameter {
1609 pattern: shape_ast::ast::DestructurePattern::Identifier(
1610 "self".to_string(),
1611 Span::DUMMY,
1612 ),
1613 is_const: false,
1614 is_reference: false,
1615 is_mut_reference: false,
1616 is_out: false,
1617 type_annotation: None,
1618 default_value: None,
1619 }];
1620 for ann_param in &ann_def.params {
1622 params.push(ann_param.clone());
1623 }
1624 for param in &handler.params {
1626 let inferred_type = if param.name == "ctx" {
1627 Some(TypeAnnotation::Object(vec![
1628 shape_ast::ast::ObjectTypeField {
1629 name: "state".to_string(),
1630 optional: false,
1631 type_annotation: TypeAnnotation::Basic("unknown".to_string()),
1632 annotations: vec![],
1633 },
1634 shape_ast::ast::ObjectTypeField {
1635 name: "event_log".to_string(),
1636 optional: false,
1637 type_annotation: TypeAnnotation::Array(Box::new(TypeAnnotation::Basic("unknown".to_string()))),
1638 annotations: vec![],
1639 },
1640 ]))
1641 } else if matches!(
1642 handler.handler_type,
1643 AnnotationHandlerType::OnDefine | AnnotationHandlerType::Metadata
1644 ) && (param.name == "fn" || param.name == "target")
1645 {
1646 Some(TypeAnnotation::Object(vec![
1647 shape_ast::ast::ObjectTypeField {
1648 name: "name".to_string(),
1649 optional: false,
1650 type_annotation: TypeAnnotation::Basic("string".to_string()),
1651 annotations: vec![],
1652 },
1653 shape_ast::ast::ObjectTypeField {
1654 name: "kind".to_string(),
1655 optional: false,
1656 type_annotation: TypeAnnotation::Basic("string".to_string()),
1657 annotations: vec![],
1658 },
1659 shape_ast::ast::ObjectTypeField {
1660 name: "id".to_string(),
1661 optional: false,
1662 type_annotation: TypeAnnotation::Basic("int".to_string()),
1663 annotations: vec![],
1664 },
1665 ]))
1666 } else {
1667 None
1668 };
1669
1670 params.push(FunctionParameter {
1671 pattern: shape_ast::ast::DestructurePattern::Identifier(
1672 param.name.clone(),
1673 Span::DUMMY,
1674 ),
1675 is_const: false,
1676 is_reference: false,
1677 is_mut_reference: false,
1678 is_out: false,
1679 type_annotation: inferred_type,
1680 default_value: None,
1681 });
1682 }
1683
1684 let body = vec![Statement::Return(Some(handler.body.clone()), Span::DUMMY)];
1686
1687 let func_def = FunctionDef {
1688 name: func_name,
1689 name_span: Span::DUMMY,
1690 declaring_module_path: None,
1691 doc_comment: None,
1692 params,
1693 return_type: handler.return_type.clone(),
1694 body,
1695 type_params: Some(Vec::new()),
1696 annotations: Vec::new(),
1697 is_async: false,
1698 is_comptime: false,
1699 where_clause: None,
1700 };
1701
1702 self.register_function(&func_def)?;
1703 self.compile_function(&func_def)?;
1704
1705 let func_id = (self.program.functions.len() - 1) as u16;
1706
1707 match handler.handler_type {
1708 AnnotationHandlerType::Before => compiled.before_handler = Some(func_id),
1709 AnnotationHandlerType::After => compiled.after_handler = Some(func_id),
1710 AnnotationHandlerType::OnDefine => compiled.on_define_handler = Some(func_id),
1711 AnnotationHandlerType::Metadata => compiled.metadata_handler = Some(func_id),
1712 AnnotationHandlerType::ComptimePre => {} AnnotationHandlerType::ComptimePost => {} }
1715 }
1716
1717 if let Some(explicit) = &ann_def.allowed_targets {
1723 compiled.allowed_targets = explicit.clone();
1724 } else if compiled.before_handler.is_some()
1725 || compiled.after_handler.is_some()
1726 || compiled.comptime_pre_handler.is_some()
1727 || compiled.comptime_post_handler.is_some()
1728 {
1729 compiled.allowed_targets =
1730 vec![shape_ast::ast::functions::AnnotationTargetKind::Function];
1731 } else if compiled.on_define_handler.is_some() || compiled.metadata_handler.is_some() {
1732 compiled.allowed_targets = vec![
1733 shape_ast::ast::functions::AnnotationTargetKind::Function,
1734 shape_ast::ast::functions::AnnotationTargetKind::Type,
1735 shape_ast::ast::functions::AnnotationTargetKind::Module,
1736 ];
1737 }
1738
1739 if compiled.on_define_handler.is_some() || compiled.metadata_handler.is_some() {
1742 if compiled.allowed_targets.is_empty() {
1743 return Err(ShapeError::SemanticError {
1744 message: format!(
1745 "Annotation '{}' uses `on_define`/`metadata` and cannot have unrestricted targets. Allowed targets are: function, type, module",
1746 ann_def.name
1747 ),
1748 location: Some(self.span_to_source_location(ann_def.span)),
1749 });
1750 }
1751 if let Some(invalid) = compiled
1752 .allowed_targets
1753 .iter()
1754 .find(|kind| !Self::is_definition_annotation_target(**kind))
1755 {
1756 let invalid_label = format!("{:?}", invalid).to_lowercase();
1757 return Err(ShapeError::SemanticError {
1758 message: format!(
1759 "Annotation '{}' uses `on_define`/`metadata`, but target '{}' is not a definition target. Allowed targets are: function, type, module",
1760 ann_def.name, invalid_label
1761 ),
1762 location: Some(self.span_to_source_location(ann_def.span)),
1763 });
1764 }
1765 }
1766
1767 self.program
1768 .compiled_annotations
1769 .insert(ann_def.name.clone(), compiled);
1770 Ok(())
1771 }
1772
1773 fn register_struct_type(
1778 &mut self,
1779 struct_def: &shape_ast::ast::StructTypeDef,
1780 span: shape_ast::ast::Span,
1781 ) -> Result<()> {
1782 use shape_ast::ast::Literal;
1783 use shape_runtime::type_schema::{FieldAnnotation, TypeSchemaBuilder};
1784
1785 for ann in &struct_def.annotations {
1787 self.validate_annotation_target_usage(
1788 ann,
1789 shape_ast::ast::functions::AnnotationTargetKind::Type,
1790 span,
1791 )?;
1792 }
1793
1794 if struct_def.native_layout.is_some() {
1795 self.native_layout_types.insert(struct_def.name.clone());
1796 } else {
1797 self.native_layout_types.remove(&struct_def.name);
1798 }
1799
1800 let runtime_field_names: Vec<String> = struct_def
1805 .fields
1806 .iter()
1807 .filter(|f| !f.is_comptime)
1808 .map(|f| f.name.clone())
1809 .collect();
1810 let runtime_field_types = struct_def
1811 .fields
1812 .iter()
1813 .filter(|f| !f.is_comptime)
1814 .map(|f| (f.name.clone(), f.type_annotation.clone()))
1815 .collect::<std::collections::HashMap<_, _>>();
1816 self.struct_types
1817 .insert(struct_def.name.clone(), (runtime_field_names, span));
1818 self.struct_generic_info.insert(
1819 struct_def.name.clone(),
1820 StructGenericInfo {
1821 type_params: struct_def.type_params.clone().unwrap_or_default(),
1822 runtime_field_types,
1823 },
1824 );
1825 if self
1826 .type_tracker
1827 .schema_registry()
1828 .get(&struct_def.name)
1829 .is_none()
1830 {
1831 let runtime_fields: Vec<(String, shape_runtime::type_schema::FieldType)> = struct_def
1832 .fields
1833 .iter()
1834 .filter(|f| !f.is_comptime)
1835 .map(|f| {
1836 (
1837 f.name.clone(),
1838 Self::type_annotation_to_field_type(&f.type_annotation),
1839 )
1840 })
1841 .collect();
1842 self.type_tracker
1843 .schema_registry_mut()
1844 .register_type(struct_def.name.clone(), runtime_fields);
1845 }
1846
1847 if self.execute_struct_comptime_handlers(struct_def)? {
1850 self.struct_types.remove(&struct_def.name);
1851 self.struct_generic_info.remove(&struct_def.name);
1852 return Ok(());
1853 }
1854
1855 if struct_def.native_layout.is_some() {
1856 self.register_native_struct_layout(struct_def, span)?;
1857 }
1858
1859 if self
1861 .type_tracker
1862 .schema_registry()
1863 .get(&struct_def.name)
1864 .is_none()
1865 {
1866 let mut builder = TypeSchemaBuilder::new(struct_def.name.clone());
1867 for field in &struct_def.fields {
1868 if field.is_comptime {
1869 continue;
1870 }
1871 let field_type = Self::type_annotation_to_field_type(&field.type_annotation);
1872 let mut annotations = Vec::new();
1873 for ann in &field.annotations {
1874 let args: Vec<String> = ann
1875 .args
1876 .iter()
1877 .filter_map(Self::eval_annotation_arg)
1878 .collect();
1879 annotations.push(FieldAnnotation {
1880 name: ann.name.clone(),
1881 args,
1882 });
1883 }
1884 builder = builder.field_with_meta(field.name.clone(), field_type, annotations);
1885 }
1886 builder.register(self.type_tracker.schema_registry_mut());
1887 }
1888
1889 let mut comptime_values = std::collections::HashMap::new();
1891 for field in &struct_def.fields {
1892 if !field.is_comptime {
1893 continue;
1894 }
1895 if let Some(ref default_expr) = field.default_value {
1896 let value = match default_expr {
1897 Expr::Literal(Literal::Number(n), _) => shape_value::ValueWord::from_f64(*n),
1898 Expr::Literal(Literal::Int(n), _) => {
1899 shape_value::ValueWord::from_f64(*n as f64)
1900 }
1901 Expr::Literal(Literal::String(s), _) => {
1902 shape_value::ValueWord::from_string(std::sync::Arc::new(s.clone()))
1903 }
1904 Expr::Literal(Literal::Bool(b), _) => shape_value::ValueWord::from_bool(*b),
1905 Expr::Literal(Literal::None, _) => shape_value::ValueWord::none(),
1906 _ => {
1907 return Err(ShapeError::SemanticError {
1908 message: format!(
1909 "Comptime field '{}' on type '{}' must have a literal default value",
1910 field.name, struct_def.name
1911 ),
1912 location: None,
1913 });
1914 }
1915 };
1916 comptime_values.insert(field.name.clone(), value);
1917 }
1918 }
1921
1922 if !comptime_values.is_empty() {
1923 self.comptime_fields
1924 .insert(struct_def.name.clone(), comptime_values);
1925 }
1926
1927 self.maybe_generate_native_type_conversions(&struct_def.name, span)?;
1928
1929 Ok(())
1930 }
1931
1932 fn register_native_struct_layout(
1933 &mut self,
1934 struct_def: &shape_ast::ast::StructTypeDef,
1935 span: shape_ast::ast::Span,
1936 ) -> Result<()> {
1937 if struct_def.type_params.is_some() {
1938 return Err(ShapeError::SemanticError {
1939 message: format!(
1940 "type C '{}' cannot be generic in this version",
1941 struct_def.name
1942 ),
1943 location: Some(self.span_to_source_location(span)),
1944 });
1945 }
1946
1947 if struct_def.fields.iter().any(|f| f.is_comptime) {
1948 return Err(ShapeError::SemanticError {
1949 message: format!(
1950 "type C '{}' cannot contain comptime fields",
1951 struct_def.name
1952 ),
1953 location: Some(self.span_to_source_location(span)),
1954 });
1955 }
1956
1957 let abi = struct_def
1958 .native_layout
1959 .as_ref()
1960 .map(|b| b.abi.clone())
1961 .unwrap_or_else(|| "C".to_string());
1962 if abi != "C" {
1963 return Err(ShapeError::SemanticError {
1964 message: format!(
1965 "type '{}' uses unsupported native ABI '{}'; only C is supported",
1966 struct_def.name, abi
1967 ),
1968 location: Some(self.span_to_source_location(span)),
1969 });
1970 }
1971
1972 let mut struct_align: u64 = 1;
1973 let mut offset: u64 = 0;
1974 let mut field_layouts = Vec::with_capacity(struct_def.fields.len());
1975
1976 for field in &struct_def.fields {
1977 let field_spec =
1978 self.native_field_layout_spec(&field.type_annotation, span, &struct_def.name)?;
1979 struct_align = struct_align.max(field_spec.align);
1980 offset = Self::align_to(offset, field_spec.align);
1981 if offset > u32::MAX as u64
1982 || field_spec.size > u32::MAX as u64
1983 || field_spec.align > u32::MAX as u64
1984 {
1985 return Err(ShapeError::SemanticError {
1986 message: format!(
1987 "type C '{}' layout exceeds supported size/alignment limits",
1988 struct_def.name
1989 ),
1990 location: Some(self.span_to_source_location(span)),
1991 });
1992 }
1993 field_layouts.push(crate::bytecode::NativeStructFieldLayout {
1994 name: field.name.clone(),
1995 c_type: field_spec.c_type,
1996 offset: offset as u32,
1997 size: field_spec.size as u32,
1998 align: field_spec.align as u32,
1999 });
2000 offset = offset.saturating_add(field_spec.size);
2001 }
2002
2003 let size = Self::align_to(offset, struct_align);
2004 if size > u32::MAX as u64 || struct_align > u32::MAX as u64 {
2005 return Err(ShapeError::SemanticError {
2006 message: format!(
2007 "type C '{}' layout exceeds supported size/alignment limits",
2008 struct_def.name
2009 ),
2010 location: Some(self.span_to_source_location(span)),
2011 });
2012 }
2013
2014 let entry = crate::bytecode::NativeStructLayoutEntry {
2015 name: struct_def.name.clone(),
2016 abi,
2017 size: size as u32,
2018 align: struct_align as u32,
2019 fields: field_layouts,
2020 };
2021
2022 if let Some(existing) = self
2023 .program
2024 .native_struct_layouts
2025 .iter_mut()
2026 .find(|existing| existing.name == entry.name)
2027 {
2028 *existing = entry;
2029 } else {
2030 self.program.native_struct_layouts.push(entry);
2031 }
2032
2033 Ok(())
2034 }
2035
2036 fn align_to(value: u64, align: u64) -> u64 {
2037 debug_assert!(align > 0);
2038 let mask = align - 1;
2039 (value + mask) & !mask
2040 }
2041
2042 fn native_field_layout_spec(
2043 &self,
2044 ann: &shape_ast::ast::TypeAnnotation,
2045 span: shape_ast::ast::Span,
2046 struct_name: &str,
2047 ) -> Result<NativeFieldLayoutSpec> {
2048 use shape_ast::ast::TypeAnnotation;
2049
2050 let pointer = std::mem::size_of::<usize>() as u64;
2051
2052 let fail = || -> Result<NativeFieldLayoutSpec> {
2053 Err(ShapeError::SemanticError {
2054 message: format!(
2055 "unsupported type C field type '{}' in '{}'",
2056 ann.to_type_string(),
2057 struct_name
2058 ),
2059 location: Some(self.span_to_source_location(span)),
2060 })
2061 };
2062
2063 match ann {
2064 TypeAnnotation::Basic(name) | TypeAnnotation::Reference(name) => {
2065 if let Some(existing) = self
2066 .program
2067 .native_struct_layouts
2068 .iter()
2069 .find(|layout| &layout.name == name)
2070 {
2071 return Ok(NativeFieldLayoutSpec {
2072 c_type: name.clone(),
2073 size: existing.size as u64,
2074 align: existing.align as u64,
2075 });
2076 }
2077
2078 let spec = match name.as_str() {
2079 "f64" | "number" | "Number" | "float" => ("f64", 8, 8),
2080 "f32" => ("f32", 4, 4),
2081 "i64" | "int" | "integer" | "Int" | "Integer" => ("i64", 8, 8),
2082 "i32" => ("i32", 4, 4),
2083 "i16" => ("i16", 2, 2),
2084 "i8" | "char" => ("i8", 1, 1),
2085 "u64" => ("u64", 8, 8),
2086 "u32" => ("u32", 4, 4),
2087 "u16" => ("u16", 2, 2),
2088 "u8" | "byte" => ("u8", 1, 1),
2089 "bool" | "boolean" => ("bool", 1, 1),
2090 "isize" => ("isize", pointer, pointer),
2091 "usize" | "ptr" | "pointer" => ("ptr", pointer, pointer),
2092 "string" | "str" | "cstring" => ("cstring", pointer, pointer),
2093 _ => return fail(),
2094 };
2095 Ok(NativeFieldLayoutSpec {
2096 c_type: spec.0.to_string(),
2097 size: spec.1,
2098 align: spec.2,
2099 })
2100 }
2101 TypeAnnotation::Generic { name, args }
2102 if name == "Option" && args.len() == 1 =>
2103 {
2104 let inner = self.native_field_layout_spec(&args[0], span, struct_name)?;
2105 if inner.c_type == "cstring" {
2106 Ok(NativeFieldLayoutSpec {
2107 c_type: "cstring?".to_string(),
2108 size: pointer,
2109 align: pointer,
2110 })
2111 } else {
2112 fail()
2113 }
2114 }
2115 _ => fail(),
2116 }
2117 }
2118
2119 fn maybe_generate_native_type_conversions(
2120 &mut self,
2121 type_name: &str,
2122 span: shape_ast::ast::Span,
2123 ) -> Result<()> {
2124 let pair = if self.native_layout_types.contains(type_name) {
2125 let Some(object_type) = Self::object_type_name_for_native_layout(type_name) else {
2126 return Ok(());
2127 };
2128 if !self.struct_types.contains_key(&object_type)
2129 || self.native_layout_types.contains(&object_type)
2130 {
2131 return Ok(());
2132 }
2133 (type_name.to_string(), object_type)
2134 } else {
2135 let candidates: Vec<String> = Self::native_layout_name_candidates_for_object(type_name)
2136 .into_iter()
2137 .filter(|candidate| self.native_layout_types.contains(candidate))
2138 .collect();
2139 if candidates.is_empty() {
2140 return Ok(());
2141 }
2142 if candidates.len() > 1 {
2143 return Err(ShapeError::SemanticError {
2144 message: format!(
2145 "type '{}' matches multiple `type C` companions ({}) - use one canonical name",
2146 type_name,
2147 candidates.join(", ")
2148 ),
2149 location: Some(self.span_to_source_location(span)),
2150 });
2151 }
2152 (candidates[0].clone(), type_name.to_string())
2153 };
2154
2155 let pair_key = format!("{}::{}", pair.0, pair.1);
2156 if self.generated_native_conversion_pairs.contains(&pair_key) {
2157 return Ok(());
2158 }
2159
2160 self.validate_native_conversion_pair(&pair.0, &pair.1, span)?;
2161 self.generate_native_conversion_direction(&pair.0, &pair.1, span)?;
2162 self.generate_native_conversion_direction(&pair.1, &pair.0, span)?;
2163 self.generated_native_conversion_pairs.insert(pair_key);
2164 Ok(())
2165 }
2166
2167 fn object_type_name_for_native_layout(name: &str) -> Option<String> {
2168 if let Some(base) = name.strip_suffix("Layout")
2169 && !base.is_empty()
2170 {
2171 return Some(base.to_string());
2172 }
2173 if let Some(base) = name.strip_suffix('C')
2174 && !base.is_empty()
2175 {
2176 return Some(base.to_string());
2177 }
2178 if let Some(base) = name.strip_prefix('C')
2179 && !base.is_empty()
2180 && base
2181 .chars()
2182 .next()
2183 .map(|ch| ch.is_ascii_uppercase())
2184 .unwrap_or(false)
2185 {
2186 return Some(base.to_string());
2187 }
2188 None
2189 }
2190
2191 fn native_layout_name_candidates_for_object(name: &str) -> Vec<String> {
2192 vec![
2193 format!("{}Layout", name),
2194 format!("{}C", name),
2195 format!("C{}", name),
2196 ]
2197 }
2198
2199 fn validate_native_conversion_pair(
2200 &self,
2201 c_type: &str,
2202 object_type: &str,
2203 span: shape_ast::ast::Span,
2204 ) -> Result<()> {
2205 if !self.native_layout_types.contains(c_type) {
2206 return Err(ShapeError::SemanticError {
2207 message: format!("'{}' is not declared as `type C`", c_type),
2208 location: Some(self.span_to_source_location(span)),
2209 });
2210 }
2211 if self.native_layout_types.contains(object_type) {
2212 return Err(ShapeError::SemanticError {
2213 message: format!(
2214 "auto conversion target '{}' cannot also be declared as `type C`",
2215 object_type
2216 ),
2217 location: Some(self.span_to_source_location(span)),
2218 });
2219 }
2220
2221 let c_type_info =
2222 self.struct_generic_info
2223 .get(c_type)
2224 .ok_or_else(|| ShapeError::SemanticError {
2225 message: format!("missing compiler metadata for `type C {}`", c_type),
2226 location: Some(self.span_to_source_location(span)),
2227 })?;
2228 let object_type_info =
2229 self.struct_generic_info
2230 .get(object_type)
2231 .ok_or_else(|| ShapeError::SemanticError {
2232 message: format!(
2233 "missing compiler metadata for companion type '{}'",
2234 object_type
2235 ),
2236 location: Some(self.span_to_source_location(span)),
2237 })?;
2238
2239 if !c_type_info.type_params.is_empty() || !object_type_info.type_params.is_empty() {
2240 return Err(ShapeError::SemanticError {
2241 message: format!(
2242 "auto `type C` conversions currently require non-generic types (`{}` <-> `{}`)",
2243 c_type, object_type
2244 ),
2245 location: Some(self.span_to_source_location(span)),
2246 });
2247 }
2248
2249 let c_fields = self
2250 .struct_types
2251 .get(c_type)
2252 .map(|(fields, _)| fields)
2253 .ok_or_else(|| ShapeError::SemanticError {
2254 message: format!("missing field metadata for `type C {}`", c_type),
2255 location: Some(self.span_to_source_location(span)),
2256 })?;
2257 let object_fields = self
2258 .struct_types
2259 .get(object_type)
2260 .map(|(fields, _)| fields)
2261 .ok_or_else(|| ShapeError::SemanticError {
2262 message: format!(
2263 "missing field metadata for companion type '{}'",
2264 object_type
2265 ),
2266 location: Some(self.span_to_source_location(span)),
2267 })?;
2268
2269 let c_field_set: std::collections::HashSet<&str> =
2270 c_fields.iter().map(String::as_str).collect();
2271 let object_field_set: std::collections::HashSet<&str> =
2272 object_fields.iter().map(String::as_str).collect();
2273 if c_field_set != object_field_set {
2274 return Err(ShapeError::SemanticError {
2275 message: format!(
2276 "auto conversion pair '{}' <-> '{}' must have identical runtime fields",
2277 c_type, object_type
2278 ),
2279 location: Some(self.span_to_source_location(span)),
2280 });
2281 }
2282
2283 for field_name in c_field_set {
2284 let c_ann = c_type_info
2285 .runtime_field_types
2286 .get(field_name)
2287 .ok_or_else(|| ShapeError::SemanticError {
2288 message: format!(
2289 "missing type metadata for field '{}.{}'",
2290 c_type, field_name
2291 ),
2292 location: Some(self.span_to_source_location(span)),
2293 })?;
2294 let object_ann = object_type_info
2295 .runtime_field_types
2296 .get(field_name)
2297 .ok_or_else(|| ShapeError::SemanticError {
2298 message: format!(
2299 "missing type metadata for field '{}.{}'",
2300 object_type, field_name
2301 ),
2302 location: Some(self.span_to_source_location(span)),
2303 })?;
2304 if c_ann != object_ann {
2305 return Err(ShapeError::SemanticError {
2306 message: format!(
2307 "field type mismatch for auto conversion '{}.{}' (`{}`) vs '{}.{}' (`{}`)",
2308 c_type,
2309 field_name,
2310 c_ann.to_type_string(),
2311 object_type,
2312 field_name,
2313 object_ann.to_type_string()
2314 ),
2315 location: Some(self.span_to_source_location(span)),
2316 });
2317 }
2318 }
2319
2320 Ok(())
2321 }
2322
2323 fn generate_native_conversion_direction(
2324 &mut self,
2325 source_type: &str,
2326 target_type: &str,
2327 span: shape_ast::ast::Span,
2328 ) -> Result<()> {
2329 let fn_name = format!(
2330 "__auto_native_from_{}_to_{}",
2331 Self::sanitize_auto_symbol(source_type),
2332 Self::sanitize_auto_symbol(target_type)
2333 );
2334 if self.function_defs.contains_key(&fn_name) {
2335 return Ok(());
2336 }
2337
2338 let target_fields = self
2339 .struct_types
2340 .get(target_type)
2341 .map(|(fields, _)| fields.clone())
2342 .ok_or_else(|| ShapeError::SemanticError {
2343 message: format!(
2344 "missing target type metadata for auto conversion '{}'",
2345 target_type
2346 ),
2347 location: Some(self.span_to_source_location(span)),
2348 })?;
2349
2350 let source_expr = Expr::Identifier("value".to_string(), span);
2351 let struct_fields = target_fields
2352 .iter()
2353 .map(|field| {
2354 (
2355 field.clone(),
2356 Expr::PropertyAccess {
2357 object: Box::new(source_expr.clone()),
2358 property: field.clone(),
2359 optional: false,
2360 span,
2361 },
2362 )
2363 })
2364 .collect::<Vec<_>>();
2365 let body = vec![Statement::Return(
2366 Some(Expr::StructLiteral {
2367 type_name: target_type.to_string(),
2368 fields: struct_fields,
2369 span,
2370 }),
2371 span,
2372 )];
2373 let fn_def = FunctionDef {
2374 name: fn_name.clone(),
2375 name_span: span,
2376 declaring_module_path: None,
2377 doc_comment: None,
2378 params: vec![FunctionParameter {
2379 pattern: DestructurePattern::Identifier("value".to_string(), span),
2380 is_const: false,
2381 is_reference: false,
2382 is_mut_reference: false,
2383 is_out: false,
2384 type_annotation: Some(TypeAnnotation::Reference(source_type.to_string())),
2385 default_value: None,
2386 }],
2387 return_type: Some(TypeAnnotation::Reference(target_type.to_string())),
2388 body,
2389 type_params: Some(Vec::new()),
2390 annotations: Vec::new(),
2391 is_async: false,
2392 is_comptime: false,
2393 where_clause: None,
2394 };
2395 self.register_function(&fn_def)?;
2396 self.compile_function(&fn_def)?;
2397
2398 self.program.register_trait_method_symbol(
2399 "From",
2400 target_type,
2401 Some(source_type),
2402 "from",
2403 &fn_name,
2404 );
2405 self.program.register_trait_method_symbol(
2406 "Into",
2407 source_type,
2408 Some(target_type),
2409 "into",
2410 &fn_name,
2411 );
2412 let _ = self.type_inference.env.register_trait_impl_named(
2413 "From",
2414 target_type,
2415 source_type,
2416 vec!["from".to_string()],
2417 );
2418 let _ = self.type_inference.env.register_trait_impl_named(
2419 "Into",
2420 source_type,
2421 target_type,
2422 vec!["into".to_string()],
2423 );
2424 Ok(())
2425 }
2426
2427 fn sanitize_auto_symbol(name: &str) -> String {
2428 let mut out = String::with_capacity(name.len());
2429 for ch in name.chars() {
2430 if ch.is_ascii_alphanumeric() {
2431 out.push(ch);
2432 } else {
2433 out.push('_');
2434 }
2435 }
2436 out
2437 }
2438
2439 fn execute_struct_comptime_handlers(
2444 &mut self,
2445 struct_def: &shape_ast::ast::StructTypeDef,
2446 ) -> Result<bool> {
2447 let mut removed = false;
2448 for ann in &struct_def.annotations {
2449 let compiled = self.program.compiled_annotations.get(&ann.name).cloned();
2450 if let Some(compiled) = compiled {
2451 let handlers = [
2452 compiled.comptime_pre_handler,
2453 compiled.comptime_post_handler,
2454 ];
2455 for handler in handlers.into_iter().flatten() {
2456 let fields: Vec<(
2459 String,
2460 Option<shape_ast::ast::TypeAnnotation>,
2461 Vec<shape_ast::ast::functions::Annotation>,
2462 )> = struct_def
2463 .fields
2464 .iter()
2465 .map(|f| {
2466 (
2467 f.name.clone(),
2468 Some(f.type_annotation.clone()),
2469 f.annotations.clone(),
2470 )
2471 })
2472 .collect();
2473
2474 let target = super::comptime_target::ComptimeTarget::from_type(
2475 &struct_def.name,
2476 &fields,
2477 );
2478 let target_value = target.to_nanboxed();
2479 let target_name = struct_def.name.clone();
2480 let handler_span = handler.span;
2481 let execution =
2482 self.execute_comptime_annotation_handler(ann, &handler, target_value, &compiled.param_names, &[])?;
2483
2484 if self
2485 .process_comptime_directives(execution.directives, &target_name)
2486 .map_err(|e| ShapeError::RuntimeError {
2487 message: format!(
2488 "Comptime handler '{}' directive processing failed: {}",
2489 ann.name, e
2490 ),
2491 location: Some(self.span_to_source_location(handler_span)),
2492 })?
2493 {
2494 removed = true;
2495 break;
2496 }
2497 }
2498 }
2499 if removed {
2500 break;
2501 }
2502 }
2503 Ok(removed)
2504 }
2505
2506 fn current_module_path_for(&self, module_name: &str) -> String {
2507 if let Some(parent) = self.module_scope_stack.last() {
2508 format!("{}::{}", parent, module_name)
2509 } else {
2510 module_name.to_string()
2511 }
2512 }
2513
2514 fn qualify_module_symbol(module_path: &str, name: &str) -> String {
2515 format!("{}::{}", module_path, name)
2516 }
2517
2518 fn qualify_module_item(&self, item: &Item, module_path: &str) -> Result<Item> {
2519 match item {
2520 Item::Function(func, span) => {
2521 let mut qualified = func.clone();
2522 qualified.name = Self::qualify_module_symbol(module_path, &func.name);
2523 Ok(Item::Function(qualified, *span))
2524 }
2525 Item::VariableDecl(decl, span) => {
2526 if decl.kind != VarKind::Const {
2527 return Err(ShapeError::SemanticError {
2528 message: "module-level variable declarations currently require `const`"
2529 .to_string(),
2530 location: Some(self.span_to_source_location(*span)),
2531 });
2532 }
2533 let mut qualified = decl.clone();
2534 let Some(name) = decl.pattern.as_identifier() else {
2535 return Err(ShapeError::SemanticError {
2536 message:
2537 "module-level constants currently require a simple identifier binding"
2538 .to_string(),
2539 location: Some(self.span_to_source_location(*span)),
2540 });
2541 };
2542 qualified.pattern = DestructurePattern::Identifier(
2543 Self::qualify_module_symbol(module_path, name),
2544 *span,
2545 );
2546 Ok(Item::VariableDecl(qualified, *span))
2547 }
2548 Item::Statement(Statement::VariableDecl(decl, stmt_span), item_span) => {
2549 if decl.kind != VarKind::Const {
2550 return Err(ShapeError::SemanticError {
2551 message: "module-level variable declarations currently require `const`"
2552 .to_string(),
2553 location: Some(self.span_to_source_location(*stmt_span)),
2554 });
2555 }
2556 let mut qualified = decl.clone();
2557 let Some(name) = decl.pattern.as_identifier() else {
2558 return Err(ShapeError::SemanticError {
2559 message:
2560 "module-level constants currently require a simple identifier binding"
2561 .to_string(),
2562 location: Some(self.span_to_source_location(*stmt_span)),
2563 });
2564 };
2565 qualified.pattern = DestructurePattern::Identifier(
2566 Self::qualify_module_symbol(module_path, name),
2567 *stmt_span,
2568 );
2569 Ok(Item::Statement(
2570 Statement::VariableDecl(qualified, *stmt_span),
2571 *item_span,
2572 ))
2573 }
2574 Item::Statement(Statement::Assignment(assign, stmt_span), item_span) => {
2575 let mut qualified = assign.clone();
2576 if let Some(name) = assign.pattern.as_identifier() {
2577 qualified.pattern = DestructurePattern::Identifier(
2578 Self::qualify_module_symbol(module_path, name),
2579 *stmt_span,
2580 );
2581 }
2582 Ok(Item::Statement(
2583 Statement::Assignment(qualified, *stmt_span),
2584 *item_span,
2585 ))
2586 }
2587 Item::Export(export, span) if export.source_decl.is_some() => {
2588 let decl = export.source_decl.as_ref().unwrap();
2590 if decl.kind != VarKind::Const {
2591 return Err(ShapeError::SemanticError {
2592 message: "module-level variable declarations currently require `const`"
2593 .to_string(),
2594 location: Some(self.span_to_source_location(*span)),
2595 });
2596 }
2597 let mut qualified = decl.clone();
2598 let Some(name) = decl.pattern.as_identifier() else {
2599 return Err(ShapeError::SemanticError {
2600 message:
2601 "module-level constants currently require a simple identifier binding"
2602 .to_string(),
2603 location: Some(self.span_to_source_location(*span)),
2604 });
2605 };
2606 qualified.pattern = DestructurePattern::Identifier(
2607 Self::qualify_module_symbol(module_path, name),
2608 *span,
2609 );
2610 Ok(Item::VariableDecl(qualified, *span))
2611 }
2612 _ => Ok(item.clone()),
2613 }
2614 }
2615
2616 fn collect_module_runtime_exports(
2617 &self,
2618 items: &[Item],
2619 module_path: &str,
2620 ) -> Vec<(String, String)> {
2621 let mut exports = Vec::new();
2622 for item in items {
2623 match item {
2624 Item::Function(func, _) => {
2625 exports.push((
2626 func.name.clone(),
2627 Self::qualify_module_symbol(module_path, &func.name),
2628 ));
2629 }
2630 Item::VariableDecl(decl, _) => {
2631 if decl.kind == VarKind::Const
2632 && let Some(name) = decl.pattern.as_identifier()
2633 {
2634 exports.push((
2635 name.to_string(),
2636 Self::qualify_module_symbol(module_path, name),
2637 ));
2638 }
2639 }
2640 Item::Statement(Statement::VariableDecl(decl, _), _) => {
2641 if decl.kind == VarKind::Const
2642 && let Some(name) = decl.pattern.as_identifier()
2643 {
2644 exports.push((
2645 name.to_string(),
2646 Self::qualify_module_symbol(module_path, name),
2647 ));
2648 }
2649 }
2650 Item::Export(export, _) => {
2651 if let Some(ref decl) = export.source_decl {
2652 if let Some(name) = decl.pattern.as_identifier() {
2653 exports.push((
2654 name.to_string(),
2655 Self::qualify_module_symbol(module_path, name),
2656 ));
2657 }
2658 }
2659 }
2660 Item::Module(module, _) => {
2661 exports.push((
2662 module.name.clone(),
2663 Self::qualify_module_symbol(module_path, &module.name),
2664 ));
2665 }
2666 _ => {}
2667 }
2668 }
2669 exports.sort_by(|a, b| a.0.cmp(&b.0));
2670 exports.dedup_by(|a, b| a.0 == b.0);
2671 exports
2672 }
2673
2674 fn module_target_fields(items: &[Item]) -> Vec<(String, String)> {
2675 let mut fields = Vec::new();
2676 for item in items {
2677 match item {
2678 Item::Function(func, _) => fields.push((func.name.clone(), "function".to_string())),
2679 Item::VariableDecl(decl, _) => {
2680 if let Some(name) = decl.pattern.as_identifier() {
2681 let type_name = decl
2682 .type_annotation
2683 .as_ref()
2684 .and_then(TypeAnnotation::as_simple_name)
2685 .unwrap_or("any")
2686 .to_string();
2687 fields.push((name.to_string(), type_name));
2688 }
2689 }
2690 Item::Statement(Statement::VariableDecl(decl, _), _) => {
2691 if let Some(name) = decl.pattern.as_identifier() {
2692 let type_name = decl
2693 .type_annotation
2694 .as_ref()
2695 .and_then(TypeAnnotation::as_simple_name)
2696 .unwrap_or("any")
2697 .to_string();
2698 fields.push((name.to_string(), type_name));
2699 }
2700 }
2701 Item::Export(export, _) => {
2702 if let Some(ref decl) = export.source_decl {
2703 if let Some(name) = decl.pattern.as_identifier() {
2704 let type_name = decl
2705 .type_annotation
2706 .as_ref()
2707 .and_then(TypeAnnotation::as_simple_name)
2708 .unwrap_or("any")
2709 .to_string();
2710 fields.push((name.to_string(), type_name));
2711 }
2712 }
2713 }
2714 Item::StructType(def, _) => fields.push((def.name.clone(), "type".to_string())),
2715 Item::Enum(def, _) => fields.push((def.name.clone(), "type".to_string())),
2716 Item::TypeAlias(def, _) => fields.push((def.name.clone(), "type".to_string())),
2717 Item::Module(def, _) => fields.push((def.name.clone(), "module".to_string())),
2718 _ => {}
2719 }
2720 }
2721 fields
2722 }
2723
2724 fn process_comptime_directives_for_module(
2725 &mut self,
2726 directives: Vec<super::comptime_builtins::ComptimeDirective>,
2727 module_name: &str,
2728 module_items: &mut Vec<Item>,
2729 ) -> std::result::Result<bool, String> {
2730 let mut removed = false;
2731 for directive in directives {
2732 match directive {
2733 super::comptime_builtins::ComptimeDirective::Extend(extend) => {
2734 self.apply_comptime_extend(extend, module_name)
2735 .map_err(|e| e.to_string())?;
2736 }
2737 super::comptime_builtins::ComptimeDirective::RemoveTarget => {
2738 removed = true;
2739 break;
2740 }
2741 super::comptime_builtins::ComptimeDirective::ReplaceModule { items } => {
2742 *module_items = items;
2743 }
2744 super::comptime_builtins::ComptimeDirective::SetParamType { .. }
2745 | super::comptime_builtins::ComptimeDirective::SetParamValue { .. } => {
2746 return Err(
2747 "`set param` directives are only valid when compiling function targets"
2748 .to_string(),
2749 );
2750 }
2751 super::comptime_builtins::ComptimeDirective::SetReturnType { .. } => {
2752 return Err(
2753 "`set return` directives are only valid when compiling function targets"
2754 .to_string(),
2755 );
2756 }
2757 super::comptime_builtins::ComptimeDirective::ReplaceBody { .. } => {
2758 return Err(
2759 "`replace body` directives are only valid when compiling function targets"
2760 .to_string(),
2761 );
2762 }
2763 }
2764 }
2765 Ok(removed)
2766 }
2767
2768 fn execute_module_comptime_handlers(
2769 &mut self,
2770 module_def: &ModuleDecl,
2771 module_path: &str,
2772 module_items: &mut Vec<Item>,
2773 ) -> Result<bool> {
2774 let mut removed = false;
2775 for ann in &module_def.annotations {
2776 let compiled = self.program.compiled_annotations.get(&ann.name).cloned();
2777 if let Some(compiled) = compiled {
2778 let handlers = [
2779 compiled.comptime_pre_handler,
2780 compiled.comptime_post_handler,
2781 ];
2782 for handler in handlers.into_iter().flatten() {
2783 let target = super::comptime_target::ComptimeTarget::from_module(
2784 module_path,
2785 &Self::module_target_fields(module_items),
2786 );
2787 let target_value = target.to_nanboxed();
2788 let handler_span = handler.span;
2789 let execution =
2790 self.execute_comptime_annotation_handler(ann, &handler, target_value, &compiled.param_names, &[])?;
2791 if self
2792 .process_comptime_directives_for_module(
2793 execution.directives,
2794 module_path,
2795 module_items,
2796 )
2797 .map_err(|e| ShapeError::RuntimeError {
2798 message: format!(
2799 "Comptime handler '{}' directive processing failed: {}",
2800 ann.name, e
2801 ),
2802 location: Some(self.span_to_source_location(handler_span)),
2803 })?
2804 {
2805 removed = true;
2806 break;
2807 }
2808 }
2809 }
2810 if removed {
2811 break;
2812 }
2813 }
2814 Ok(removed)
2815 }
2816
2817 fn inject_module_local_comptime_helper_aliases(
2818 &self,
2819 module_path: &str,
2820 helpers: &mut Vec<FunctionDef>,
2821 ) {
2822 let module_prefix = format!("{}::", module_path);
2823 let mut seen: std::collections::HashSet<String> =
2824 helpers.iter().map(|h| h.name.clone()).collect();
2825 let mut aliases = Vec::new();
2826
2827 for helper in helpers.iter() {
2828 let Some(local_name) = helper.name.strip_prefix(&module_prefix) else {
2829 continue;
2830 };
2831 if local_name.contains("::") || !seen.insert(local_name.to_string()) {
2832 continue;
2833 }
2834 let mut alias = helper.clone();
2835 alias.name = local_name.to_string();
2836 aliases.push(alias);
2837 }
2838
2839 helpers.extend(aliases);
2840 }
2841
2842 fn execute_module_inline_comptime_blocks(
2843 &mut self,
2844 module_path: &str,
2845 module_items: &mut Vec<Item>,
2846 ) -> Result<bool> {
2847 loop {
2848 let Some(idx) = module_items
2849 .iter()
2850 .position(|item| matches!(item, Item::Comptime(_, _)))
2851 else {
2852 break;
2853 };
2854
2855 let (stmts, span) = match module_items[idx].clone() {
2856 Item::Comptime(stmts, span) => (stmts, span),
2857 _ => unreachable!("index is guarded by position() matcher"),
2858 };
2859
2860 let extensions: Vec<_> = self
2861 .extension_registry
2862 .as_ref()
2863 .map(|r| r.as_ref().clone())
2864 .unwrap_or_default();
2865 let trait_impls = self.type_inference.env.trait_impl_keys();
2866 let known_type_symbols: std::collections::HashSet<String> = self
2867 .struct_types
2868 .keys()
2869 .chain(self.type_aliases.keys())
2870 .cloned()
2871 .collect();
2872 let mut comptime_helpers = self.collect_comptime_helpers();
2873 self.inject_module_local_comptime_helper_aliases(module_path, &mut comptime_helpers);
2874
2875 let execution = super::comptime::execute_comptime(
2876 &stmts,
2877 &comptime_helpers,
2878 &extensions,
2879 trait_impls,
2880 known_type_symbols,
2881 )
2882 .map_err(|e| ShapeError::RuntimeError {
2883 message: format!(
2884 "Comptime block evaluation failed: {}",
2885 super::helpers::strip_error_prefix(&e)
2886 ),
2887 location: Some(self.span_to_source_location(span)),
2888 })?;
2889
2890 if self
2891 .process_comptime_directives_for_module(
2892 execution.directives,
2893 module_path,
2894 module_items,
2895 )
2896 .map_err(|e| ShapeError::RuntimeError {
2897 message: format!("Comptime block directive processing failed: {}", e),
2898 location: Some(self.span_to_source_location(span)),
2899 })?
2900 {
2901 return Ok(true);
2902 }
2903
2904 if idx < module_items.len() && matches!(module_items[idx], Item::Comptime(_, _)) {
2905 module_items.remove(idx);
2906 }
2907 }
2908
2909 Ok(false)
2910 }
2911
2912 fn register_missing_module_functions(&mut self, item: &Item) -> Result<()> {
2913 match item {
2914 Item::Function(func, _) => {
2915 if !self.function_defs.contains_key(&func.name) {
2916 self.register_function(func)?;
2917 }
2918 Ok(())
2919 }
2920 Item::Export(export, _) => match &export.item {
2921 ExportItem::Function(func) => {
2922 if !self.function_defs.contains_key(&func.name) {
2923 self.register_function(func)?;
2924 }
2925 Ok(())
2926 }
2927 _ => Ok(()),
2928 },
2929 Item::Module(module, _) => {
2930 let module_path = self.current_module_path_for(module.name.as_str());
2931 self.module_scope_stack.push(module_path.clone());
2932 let register_result = (|| -> Result<()> {
2933 for inner in &module.items {
2934 let qualified = self.qualify_module_item(inner, &module_path)?;
2935 self.register_missing_module_functions(&qualified)?;
2936 }
2937 Ok(())
2938 })();
2939 self.module_scope_stack.pop();
2940 register_result
2941 }
2942 _ => Ok(()),
2943 }
2944 }
2945
2946 fn compile_module_decl(&mut self, module_def: &ModuleDecl, span: Span) -> Result<()> {
2947 for ann in &module_def.annotations {
2948 self.validate_annotation_target_usage(ann, AnnotationTargetKind::Module, span)?;
2949 }
2950
2951 let module_path = self.current_module_path_for(&module_def.name);
2952 self.module_scope_stack.push(module_path.clone());
2953
2954 let mut module_items = module_def.items.clone();
2955 if self.execute_module_comptime_handlers(module_def, &module_path, &mut module_items)? {
2956 self.module_scope_stack.pop();
2957 return Ok(());
2958 }
2959 if self.execute_module_inline_comptime_blocks(&module_path, &mut module_items)? {
2960 self.module_scope_stack.pop();
2961 return Ok(());
2962 }
2963
2964 let mut qualified_items = Vec::with_capacity(module_items.len());
2965 for inner in &module_items {
2966 qualified_items.push(self.qualify_module_item(inner, &module_path)?);
2967 }
2968
2969 for qualified in &qualified_items {
2970 self.register_missing_module_functions(qualified)?;
2971 }
2972
2973 for qualified in &qualified_items {
2974 self.compile_item_with_context(qualified, false)?;
2975 }
2976
2977 let exports = self.collect_module_runtime_exports(&module_items, &module_path);
2978 let entries: Vec<ObjectEntry> = exports
2979 .into_iter()
2980 .map(|(name, value_ident)| ObjectEntry::Field {
2981 key: name,
2982 value: Expr::Identifier(value_ident, span),
2983 type_annotation: None,
2984 })
2985 .collect();
2986 let module_object = Expr::Object(entries, span);
2987 self.compile_expr(&module_object)?;
2988
2989 let binding_idx = self.get_or_create_module_binding(&module_path);
2990 self.emit(Instruction::new(
2991 OpCode::StoreModuleBinding,
2992 Some(Operand::ModuleBinding(binding_idx)),
2993 ));
2994 self.propagate_initializer_type_to_slot(binding_idx, false, false);
2995
2996 if self.module_scope_stack.len() == 1 {
2997 self.module_namespace_bindings
2998 .insert(module_def.name.clone());
2999 }
3000
3001 self.emit_annotation_lifecycle_calls_for_module(
3002 &module_path,
3003 &module_def.annotations,
3004 Some(binding_idx),
3005 )?;
3006
3007 self.module_scope_stack.pop();
3008 Ok(())
3009 }
3010
3011 fn compile_query(&mut self, query: &Query) -> Result<()> {
3019 match query {
3020 Query::With(with_query) => {
3021 for cte in &with_query.ctes {
3023 self.compile_query(&cte.query)?;
3025
3026 let binding_idx = self.get_or_create_module_binding(&cte.name);
3028 self.emit(Instruction::new(
3029 OpCode::StoreModuleBinding,
3030 Some(Operand::ModuleBinding(binding_idx)),
3031 ));
3032 }
3033
3034 self.compile_query(&with_query.query)?;
3036 }
3037 Query::Backtest(_backtest) => {
3038 self.emit(Instruction::simple(OpCode::PushNull));
3042 }
3043 Query::Alert(alert) => {
3044 self.compile_expr(&alert.condition)?;
3046 self.emit(Instruction::simple(OpCode::Pop));
3048 self.emit(Instruction::simple(OpCode::PushNull));
3049 }
3050 }
3051 Ok(())
3052 }
3053
3054 pub(super) fn propagate_initializer_type_to_slot(&mut self, slot: u16, is_local: bool, _is_mutable: bool) {
3055 self.propagate_assignment_type_to_slot(slot, is_local, true);
3056 }
3057
3058 pub(super) fn compile_statement(&mut self, stmt: &Statement) -> Result<()> {
3060 match stmt {
3061 Statement::Return(expr_opt, _span) => {
3062 if let Some(expr) = expr_opt {
3065 if let Expr::Reference { span: ref_span, .. } = expr {
3066 return Err(ShapeError::SemanticError {
3067 message: "cannot return a reference — references are scoped borrows that cannot escape the function. Return an owned value instead".to_string(),
3068 location: Some(self.span_to_source_location(*ref_span)),
3069 });
3070 }
3071 self.compile_expr(expr)?;
3075 } else {
3076 self.emit(Instruction::simple(OpCode::PushNull));
3077 }
3078 let total_scopes = self.drop_locals.len();
3080 if total_scopes > 0 {
3081 self.emit_drops_for_early_exit(total_scopes)?;
3082 }
3083 self.emit(Instruction::simple(OpCode::ReturnValue));
3084 }
3085
3086 Statement::Break(_) => {
3087 let in_loop = !self.loop_stack.is_empty();
3088 if in_loop {
3089 let scopes_to_exit = self
3091 .loop_stack
3092 .last()
3093 .map(|ctx| self.drop_locals.len().saturating_sub(ctx.drop_scope_depth))
3094 .unwrap_or(0);
3095 if scopes_to_exit > 0 {
3096 self.emit_drops_for_early_exit(scopes_to_exit)?;
3097 }
3098 let jump_idx = self.emit_jump(OpCode::Jump, 0);
3099 if let Some(loop_ctx) = self.loop_stack.last_mut() {
3100 loop_ctx.break_jumps.push(jump_idx);
3101 }
3102 } else {
3103 return Err(ShapeError::RuntimeError {
3104 message: "break statement outside of loop".to_string(),
3105 location: None,
3106 });
3107 }
3108 }
3109
3110 Statement::Continue(_) => {
3111 if let Some(loop_ctx) = self.loop_stack.last() {
3112 let scopes_to_exit = self
3114 .drop_locals
3115 .len()
3116 .saturating_sub(loop_ctx.drop_scope_depth);
3117 let continue_target = loop_ctx.continue_target;
3118 if scopes_to_exit > 0 {
3120 self.emit_drops_for_early_exit(scopes_to_exit)?;
3121 }
3122 let offset = continue_target as i32 - self.program.current_offset() as i32 - 1;
3123 self.emit(Instruction::new(
3124 OpCode::Jump,
3125 Some(Operand::Offset(offset)),
3126 ));
3127 } else {
3128 return Err(ShapeError::RuntimeError {
3129 message: "continue statement outside of loop".to_string(),
3130 location: None,
3131 });
3132 }
3133 }
3134
3135 Statement::VariableDecl(var_decl, _) => {
3136 self.pending_variable_name =
3139 var_decl.pattern.as_identifier().map(|s| s.to_string());
3140
3141 if let (Some(type_ann), Some(init_expr)) =
3145 (&var_decl.type_annotation, &var_decl.value)
3146 {
3147 if let shape_ast::ast::TypeAnnotation::Basic(type_name) = type_ann {
3148 if let Some(w) = shape_ast::IntWidth::from_name(type_name) {
3149 if let Some(const_val) =
3150 crate::compiler::expressions::function_calls::eval_const_expr_to_nanboxed(init_expr)
3151 {
3152 let in_range = if let Some(i) = const_val.as_i64() {
3153 w.in_range_i64(i)
3154 } else if let Some(f) = const_val.as_f64() {
3155 let i = f as i64;
3157 (i as f64 == f) && w.in_range_i64(i)
3158 } else {
3159 true };
3161 if !in_range {
3162 return Err(shape_ast::error::ShapeError::SemanticError {
3163 message: format!(
3164 "value does not fit in `{}` (range {}..={})",
3165 type_name,
3166 w.min_value(),
3167 w.max_value()
3168 ),
3169 location: Some(self.span_to_source_location(shape_ast::ast::Spanned::span(init_expr))),
3170 });
3171 }
3172 }
3173 }
3174 }
3175 }
3176
3177 let init_err = if let Some(init_expr) = &var_decl.value {
3180 if let Expr::TableRows(rows, tr_span) = init_expr {
3183 match self.compile_table_rows(rows, &var_decl.type_annotation, *tr_span) {
3184 Ok(()) => None,
3185 Err(e) => {
3186 self.emit(Instruction::simple(OpCode::PushNull));
3187 Some(e)
3188 }
3189 }
3190 } else {
3191 match self.compile_expr(init_expr) {
3192 Ok(()) => None,
3193 Err(e) => {
3194 self.emit(Instruction::simple(OpCode::PushNull));
3195 Some(e)
3196 }
3197 }
3198 }
3199 } else {
3200 self.emit(Instruction::simple(OpCode::PushNull));
3201 None
3202 };
3203
3204 self.pending_variable_name = None;
3206
3207 if let Some(ref type_ann) = var_decl.type_annotation {
3209 if let Some(schema_id) = self.get_table_schema_id(type_ann) {
3210 self.emit(Instruction::new(
3211 OpCode::BindSchema,
3212 Some(Operand::Count(schema_id)),
3213 ));
3214 }
3215 }
3216
3217 if self.current_function.is_none() {
3219 if let Some(name) = var_decl.pattern.as_identifier() {
3221 let binding_idx = self.get_or_create_module_binding(name);
3222 self.emit(Instruction::new(
3223 OpCode::StoreModuleBinding,
3224 Some(Operand::ModuleBinding(binding_idx)),
3225 ));
3226
3227 if var_decl.kind == VarKind::Const {
3229 self.const_module_bindings.insert(binding_idx);
3230 }
3231
3232 if var_decl.kind == VarKind::Let && !var_decl.is_mut {
3234 self.immutable_module_bindings.insert(binding_idx);
3235 }
3236
3237 if let Some(ref type_ann) = var_decl.type_annotation {
3239 if let Some(type_name) =
3240 Self::tracked_type_name_from_annotation(type_ann)
3241 {
3242 self.set_module_binding_type_info(binding_idx, &type_name);
3243 }
3244 self.try_track_datatable_type(type_ann, binding_idx, false)?;
3246 } else {
3247 let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
3248 self.propagate_initializer_type_to_slot(binding_idx, false, is_mutable);
3249 }
3250
3251 let binding_type_name = self
3253 .type_tracker
3254 .get_binding_type(binding_idx)
3255 .and_then(|info| info.type_name.clone());
3256 let drop_kind = binding_type_name
3257 .as_ref()
3258 .and_then(|tn| self.drop_type_info.get(tn).copied())
3259 .or_else(|| {
3260 var_decl
3261 .type_annotation
3262 .as_ref()
3263 .and_then(|ann| self.annotation_drop_kind(ann))
3264 });
3265 if drop_kind.is_some() {
3266 let is_async = match drop_kind {
3267 Some(DropKind::AsyncOnly) => true,
3268 Some(DropKind::Both) => false,
3269 Some(DropKind::SyncOnly) | None => false,
3270 };
3271 self.track_drop_module_binding(binding_idx, is_async);
3272 }
3273 } else {
3274 self.compile_destructure_pattern_global(&var_decl.pattern)?;
3275 }
3276 } else {
3277 self.compile_destructure_pattern(&var_decl.pattern)?;
3279
3280 if let (Some(name), Some(TypeAnnotation::Basic(type_name))) = (
3284 var_decl.pattern.as_identifier(),
3285 var_decl.type_annotation.as_ref(),
3286 ) {
3287 if let Some(w) = shape_ast::IntWidth::from_name(type_name) {
3288 if let Some(local_idx) = self.resolve_local(name) {
3289 if let Some(last) = self.program.instructions.last_mut() {
3290 if last.opcode == OpCode::StoreLocal {
3291 last.opcode = OpCode::StoreLocalTyped;
3292 last.operand = Some(Operand::TypedLocal(
3293 local_idx,
3294 crate::bytecode::NumericWidth::from_int_width(w),
3295 ));
3296 }
3297 }
3298 }
3299 }
3300 }
3301
3302 if var_decl.kind == VarKind::Const {
3304 if let Some(name) = var_decl.pattern.as_identifier() {
3305 if let Some(local_idx) = self.resolve_local(name) {
3306 self.const_locals.insert(local_idx);
3307 }
3308 }
3309 }
3310
3311 if var_decl.kind == VarKind::Let && !var_decl.is_mut {
3316 if let Some(name) = var_decl.pattern.as_identifier() {
3317 if let Some(local_idx) = self.resolve_local(name) {
3318 self.immutable_locals.insert(local_idx);
3319 }
3320 }
3321 }
3322
3323 if let Some(name) = var_decl.pattern.as_identifier() {
3325 if let Some(ref type_ann) = var_decl.type_annotation {
3326 if let Some(type_name) =
3327 Self::tracked_type_name_from_annotation(type_ann)
3328 {
3329 if let Some(local_idx) = self.resolve_local(name) {
3331 self.set_local_type_info(local_idx, &type_name);
3332 }
3333 }
3334 if let Some(local_idx) = self.resolve_local(name) {
3336 self.try_track_datatable_type(type_ann, local_idx, true)?;
3337 }
3338 } else if let Some(local_idx) = self.resolve_local(name) {
3339 let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
3340 self.propagate_initializer_type_to_slot(local_idx, true, is_mutable);
3341 }
3342 }
3343
3344 if let Some(name) = var_decl.pattern.as_identifier() {
3347 if let Some(local_idx) = self.resolve_local(name) {
3348 let drop_kind = self.local_drop_kind(local_idx).or_else(|| {
3349 var_decl
3350 .type_annotation
3351 .as_ref()
3352 .and_then(|ann| self.annotation_drop_kind(ann))
3353 });
3354
3355 let is_async = match drop_kind {
3356 Some(DropKind::AsyncOnly) => {
3357 if !self.current_function_is_async {
3358 let tn = self
3359 .type_tracker
3360 .get_local_type(local_idx)
3361 .and_then(|info| info.type_name.clone())
3362 .unwrap_or_else(|| name.to_string());
3363 return Err(ShapeError::SemanticError {
3364 message: format!(
3365 "type '{}' has only an async drop() and cannot be used in a sync context; \
3366 add a sync method drop(self) or use it inside an async function",
3367 tn
3368 ),
3369 location: None,
3370 });
3371 }
3372 true
3373 }
3374 Some(DropKind::Both) => self.current_function_is_async,
3375 Some(DropKind::SyncOnly) | None => false,
3376 };
3377 self.track_drop_local(local_idx, is_async);
3378 }
3379 }
3380 }
3381
3382 if let Some(e) = init_err {
3383 return Err(e);
3384 }
3385 }
3386
3387 Statement::Assignment(assign, _) => 'assign: {
3388 if let Some(name) = assign.pattern.as_identifier() {
3390 if let Some(local_idx) = self.resolve_local(name) {
3391 if self.const_locals.contains(&local_idx) {
3392 return Err(ShapeError::SemanticError {
3393 message: format!("Cannot reassign const variable '{}'", name),
3394 location: None,
3395 });
3396 }
3397 if self.immutable_locals.contains(&local_idx) {
3399 return Err(ShapeError::SemanticError {
3400 message: format!(
3401 "Cannot reassign immutable variable '{}'. Use `let mut` or `var` for mutable bindings",
3402 name
3403 ),
3404 location: None,
3405 });
3406 }
3407 } else {
3408 let scoped_name = self
3409 .resolve_scoped_module_binding_name(name)
3410 .unwrap_or_else(|| name.to_string());
3411 if let Some(&binding_idx) = self.module_bindings.get(&scoped_name) {
3412 if self.const_module_bindings.contains(&binding_idx) {
3413 return Err(ShapeError::SemanticError {
3414 message: format!("Cannot reassign const variable '{}'", name),
3415 location: None,
3416 });
3417 }
3418 if self.immutable_module_bindings.contains(&binding_idx) {
3420 return Err(ShapeError::SemanticError {
3421 message: format!(
3422 "Cannot reassign immutable variable '{}'. Use `let mut` or `var` for mutable bindings",
3423 name
3424 ),
3425 location: None,
3426 });
3427 }
3428 }
3429 }
3430 }
3431
3432 if let Some(name) = assign.pattern.as_identifier() {
3434 if let Expr::MethodCall {
3435 receiver,
3436 method,
3437 args,
3438 ..
3439 } = &assign.value
3440 {
3441 if method == "push" && args.len() == 1 {
3442 if let Expr::Identifier(recv_name, _) = receiver.as_ref() {
3443 if recv_name == name {
3444 if let Some(local_idx) = self.resolve_local(name) {
3445 if !self.ref_locals.contains(&local_idx) {
3446 self.compile_expr(&args[0])?;
3447 let pushed_numeric = self.last_expr_numeric_type;
3448 self.emit(Instruction::new(
3449 OpCode::ArrayPushLocal,
3450 Some(Operand::Local(local_idx)),
3451 ));
3452 if let Some(numeric_type) = pushed_numeric {
3453 self.mark_slot_as_numeric_array(
3454 local_idx,
3455 true,
3456 numeric_type,
3457 );
3458 }
3459 break 'assign;
3460 }
3461 } else {
3462 let binding_idx = self.get_or_create_module_binding(name);
3463 self.compile_expr(&args[0])?;
3464 let pushed_numeric = self.last_expr_numeric_type;
3465 self.emit(Instruction::new(
3466 OpCode::ArrayPushLocal,
3467 Some(Operand::ModuleBinding(binding_idx)),
3468 ));
3469 if let Some(numeric_type) = pushed_numeric {
3470 self.mark_slot_as_numeric_array(
3471 binding_idx,
3472 false,
3473 numeric_type,
3474 );
3475 }
3476 break 'assign;
3477 }
3478 }
3479 }
3480 }
3481 }
3482 }
3483
3484 self.compile_expr(&assign.value)?;
3486 let assigned_ident = assign.pattern.as_identifier().map(str::to_string);
3487
3488 self.compile_destructure_assignment(&assign.pattern)?;
3490 if let Some(name) = assigned_ident.as_deref() {
3491 self.propagate_assignment_type_to_identifier(name);
3492 }
3493 }
3494
3495 Statement::Expression(expr, _) => {
3496 if let Expr::MethodCall {
3499 receiver,
3500 method,
3501 args,
3502 ..
3503 } = expr
3504 {
3505 if method == "push" && args.len() == 1 {
3506 if let Expr::Identifier(recv_name, _) = receiver.as_ref() {
3507 if let Some(local_idx) = self.resolve_local(recv_name) {
3508 if !self.ref_locals.contains(&local_idx) {
3509 self.compile_expr(&args[0])?;
3510 let pushed_numeric = self.last_expr_numeric_type;
3511 self.emit(Instruction::new(
3512 OpCode::ArrayPushLocal,
3513 Some(Operand::Local(local_idx)),
3514 ));
3515 if let Some(numeric_type) = pushed_numeric {
3516 self.mark_slot_as_numeric_array(
3517 local_idx,
3518 true,
3519 numeric_type,
3520 );
3521 }
3522 return Ok(());
3523 }
3524 } else if !self
3525 .mutable_closure_captures
3526 .contains_key(recv_name.as_str())
3527 {
3528 let binding_idx = self.get_or_create_module_binding(recv_name);
3529 self.compile_expr(&args[0])?;
3530 self.emit(Instruction::new(
3531 OpCode::ArrayPushLocal,
3532 Some(Operand::ModuleBinding(binding_idx)),
3533 ));
3534 return Ok(());
3535 }
3536 }
3537 }
3538 }
3539 self.compile_expr(expr)?;
3540 self.emit(Instruction::simple(OpCode::Pop));
3541 }
3542
3543 Statement::For(for_loop, _) => {
3544 self.compile_for_loop(for_loop)?;
3545 }
3546
3547 Statement::While(while_loop, _) => {
3548 self.compile_while_loop(while_loop)?;
3549 }
3550
3551 Statement::If(if_stmt, _) => {
3552 self.compile_if_statement(if_stmt)?;
3553 }
3554 Statement::Extend(extend, span) => {
3555 if !self.comptime_mode {
3556 return Err(ShapeError::SemanticError {
3557 message:
3558 "`extend` as a statement is only valid inside `comptime { }` context"
3559 .to_string(),
3560 location: Some(self.span_to_source_location(*span)),
3561 });
3562 }
3563 self.emit_comptime_extend_directive(extend, *span)?;
3564 }
3565 Statement::RemoveTarget(span) => {
3566 if !self.comptime_mode {
3567 return Err(ShapeError::SemanticError {
3568 message: "`remove target` is only valid inside `comptime { }` context"
3569 .to_string(),
3570 location: Some(self.span_to_source_location(*span)),
3571 });
3572 }
3573 self.emit_comptime_remove_directive(*span)?;
3574 }
3575 Statement::SetParamType {
3576 param_name,
3577 type_annotation,
3578 span,
3579 } => {
3580 if !self.comptime_mode {
3581 return Err(ShapeError::SemanticError {
3582 message: "`set param` is only valid inside `comptime { }` context"
3583 .to_string(),
3584 location: Some(self.span_to_source_location(*span)),
3585 });
3586 }
3587 self.emit_comptime_set_param_type_directive(param_name, type_annotation, *span)?;
3588 }
3589 Statement::SetParamValue {
3590 param_name,
3591 expression,
3592 span,
3593 } => {
3594 if !self.comptime_mode {
3595 return Err(ShapeError::SemanticError {
3596 message: "`set param` is only valid inside `comptime { }` context"
3597 .to_string(),
3598 location: Some(self.span_to_source_location(*span)),
3599 });
3600 }
3601 self.emit_comptime_set_param_value_directive(param_name, expression, *span)?;
3602 }
3603 Statement::SetReturnType {
3604 type_annotation,
3605 span,
3606 } => {
3607 if !self.comptime_mode {
3608 return Err(ShapeError::SemanticError {
3609 message: "`set return` is only valid inside `comptime { }` context"
3610 .to_string(),
3611 location: Some(self.span_to_source_location(*span)),
3612 });
3613 }
3614 self.emit_comptime_set_return_type_directive(type_annotation, *span)?;
3615 }
3616 Statement::SetReturnExpr { expression, span } => {
3617 if !self.comptime_mode {
3618 return Err(ShapeError::SemanticError {
3619 message: "`set return` is only valid inside `comptime { }` context"
3620 .to_string(),
3621 location: Some(self.span_to_source_location(*span)),
3622 });
3623 }
3624 self.emit_comptime_set_return_expr_directive(expression, *span)?;
3625 }
3626 Statement::ReplaceBody { body, span } => {
3627 if !self.comptime_mode {
3628 return Err(ShapeError::SemanticError {
3629 message: "`replace body` is only valid inside `comptime { }` context"
3630 .to_string(),
3631 location: Some(self.span_to_source_location(*span)),
3632 });
3633 }
3634 self.emit_comptime_replace_body_directive(body, *span)?;
3635 }
3636 Statement::ReplaceBodyExpr { expression, span } => {
3637 if !self.comptime_mode {
3638 return Err(ShapeError::SemanticError {
3639 message: "`replace body` is only valid inside `comptime { }` context"
3640 .to_string(),
3641 location: Some(self.span_to_source_location(*span)),
3642 });
3643 }
3644 self.emit_comptime_replace_body_expr_directive(expression, *span)?;
3645 }
3646 Statement::ReplaceModuleExpr { expression, span } => {
3647 if !self.comptime_mode {
3648 return Err(ShapeError::SemanticError {
3649 message: "`replace module` is only valid inside `comptime { }` context"
3650 .to_string(),
3651 location: Some(self.span_to_source_location(*span)),
3652 });
3653 }
3654 self.emit_comptime_replace_module_expr_directive(expression, *span)?;
3655 }
3656 }
3657 Ok(())
3658 }
3659}
3660
3661#[cfg(test)]
3662mod tests {
3663 use crate::compiler::BytecodeCompiler;
3664 use crate::executor::{VMConfig, VirtualMachine};
3665 use shape_ast::parser::parse_program;
3666
3667 #[test]
3668 fn test_module_decl_function_resolves_module_const() {
3669 let code = r#"
3670 mod math {
3671 const BASE = 21
3672 fn twice() {
3673 BASE * 2
3674 }
3675 }
3676 math.twice()
3677 "#;
3678
3679 let program = parse_program(code).expect("Failed to parse");
3680 let bytecode = BytecodeCompiler::new()
3681 .compile(&program)
3682 .expect("Failed to compile");
3683
3684 let mut vm = VirtualMachine::new(VMConfig::default());
3685 vm.load_program(bytecode);
3686 vm.populate_module_objects();
3687 let result = vm.execute(None).expect("Failed to execute");
3688 assert_eq!(
3689 result
3690 .as_number_coerce()
3691 .expect("module call should return number"),
3692 42.0
3693 );
3694 }
3695
3696 #[test]
3697 fn test_module_annotation_can_replace_module_items() {
3698 let code = r#"
3699 annotation synth_module() {
3700 targets: [module]
3701 comptime post(target, ctx) {
3702 replace module ("const ANSWER = 40; fn plus_two() { ANSWER + 2 }")
3703 }
3704 }
3705
3706 @synth_module()
3707 mod demo {}
3708
3709 demo.plus_two()
3710 "#;
3711
3712 let program = parse_program(code).expect("Failed to parse");
3713 let bytecode = BytecodeCompiler::new()
3714 .compile(&program)
3715 .expect("Failed to compile");
3716
3717 let mut vm = VirtualMachine::new(VMConfig::default());
3718 vm.load_program(bytecode);
3719 vm.populate_module_objects();
3720 let result = vm.execute(None).expect("Failed to execute");
3721 assert_eq!(
3722 result
3723 .as_number_coerce()
3724 .expect("module call should return number"),
3725 42.0
3726 );
3727 }
3728
3729 #[test]
3730 fn test_module_inline_comptime_can_replace_module_items() {
3731 let code = r#"
3732 mod demo {
3733 comptime {
3734 replace module ("const ANSWER = 40; fn plus_two() { ANSWER + 2 }")
3735 }
3736 }
3737
3738 demo.plus_two()
3739 "#;
3740
3741 let program = parse_program(code).expect("Failed to parse");
3742 let bytecode = BytecodeCompiler::new()
3743 .compile(&program)
3744 .expect("Failed to compile");
3745
3746 let mut vm = VirtualMachine::new(VMConfig::default());
3747 vm.load_program(bytecode);
3748 vm.populate_module_objects();
3749 let result = vm.execute(None).expect("Failed to execute");
3750 assert_eq!(
3751 result
3752 .as_number_coerce()
3753 .expect("module call should return number"),
3754 42.0
3755 );
3756 }
3757
3758 #[test]
3759 fn test_module_inline_comptime_can_use_module_local_comptime_helper() {
3760 let code = r#"
3761 mod demo {
3762 comptime fn synth() {
3763 "const ANSWER = 40; fn plus_two() { ANSWER + 2 }"
3764 }
3765
3766 comptime {
3767 replace module (synth())
3768 }
3769 }
3770
3771 demo.plus_two()
3772 "#;
3773
3774 let program = parse_program(code).expect("Failed to parse");
3775 let bytecode = BytecodeCompiler::new()
3776 .compile(&program)
3777 .expect("Failed to compile");
3778
3779 let mut vm = VirtualMachine::new(VMConfig::default());
3780 vm.load_program(bytecode);
3781 vm.populate_module_objects();
3782 let result = vm.execute(None).expect("Failed to execute");
3783 assert_eq!(
3784 result
3785 .as_number_coerce()
3786 .expect("module call should return number"),
3787 42.0
3788 );
3789 }
3790
3791 #[test]
3792 fn test_type_annotated_variable_no_wrapping() {
3793 let code = r#"
3796 type Currency = Number
3797 let x: Currency = 123
3798 "#;
3799 let program = parse_program(code).expect("Failed to parse");
3800 let bytecode = BytecodeCompiler::new()
3801 .compile(&program)
3802 .expect("Failed to compile");
3803
3804 let has_wrap_instruction = bytecode
3806 .instructions
3807 .iter()
3808 .any(|instr| instr.opcode == crate::bytecode::OpCode::WrapTypeAnnotation);
3809 assert!(
3810 !has_wrap_instruction,
3811 "Should NOT emit WrapTypeAnnotation for type-annotated variable"
3812 );
3813 }
3814
3815 #[test]
3816 fn test_untyped_variable_no_wrapping() {
3817 let code = r#"
3819 let x = 123
3820 "#;
3821 let program = parse_program(code).expect("Failed to parse");
3822 let bytecode = BytecodeCompiler::new()
3823 .compile(&program)
3824 .expect("Failed to compile");
3825
3826 let has_wrap_instruction = bytecode
3828 .instructions
3829 .iter()
3830 .any(|instr| instr.opcode == crate::bytecode::OpCode::WrapTypeAnnotation);
3831 assert!(
3832 !has_wrap_instruction,
3833 "Should NOT emit WrapTypeAnnotation for untyped variable"
3834 );
3835 }
3836
3837 #[test]
3840 fn test_extend_block_compiles() {
3841 let code = r#"
3842 extend Number {
3843 method double() {
3844 return self * 2
3845 }
3846 }
3847 "#;
3848 let program = parse_program(code).expect("Failed to parse extend block");
3849 let bytecode = BytecodeCompiler::new().compile(&program);
3850 assert!(
3851 bytecode.is_ok(),
3852 "Extend block should compile: {:?}",
3853 bytecode.err()
3854 );
3855
3856 let bytecode = bytecode.unwrap();
3858 let has_double = bytecode.functions.iter().any(|f| f.name == "Number.double");
3859 assert!(
3860 has_double,
3861 "Should generate 'Number.double' function from extend block"
3862 );
3863 }
3864
3865 #[test]
3866 fn test_extend_method_has_self_param() {
3867 let code = r#"
3868 extend Number {
3869 method add(n) {
3870 return self + n
3871 }
3872 }
3873 "#;
3874 let program = parse_program(code).expect("Failed to parse");
3875 let bytecode = BytecodeCompiler::new()
3876 .compile(&program)
3877 .expect("Failed to compile");
3878
3879 let func = bytecode.functions.iter().find(|f| f.name == "Number.add");
3880 assert!(func.is_some(), "Should have 'Number.add' function");
3881 assert_eq!(
3883 func.unwrap().arity,
3884 2,
3885 "add() should have arity 2 (self + n)"
3886 );
3887 }
3888
3889 #[test]
3890 fn test_extend_method_rejects_explicit_self_param() {
3891 let code = r#"
3892 extend Number {
3893 method add(self, n) {
3894 return self + n
3895 }
3896 }
3897 "#;
3898 let program = parse_program(code).expect("Failed to parse");
3899 let err = BytecodeCompiler::new()
3900 .compile(&program)
3901 .expect_err("Compiler should reject explicit self receiver param in methods");
3902 let msg = format!("{err}");
3903 assert!(
3904 msg.contains("explicit `self` parameter"),
3905 "Expected explicit self error, got: {msg}"
3906 );
3907 }
3908
3909 #[test]
3912 fn test_annotation_def_compiles_handlers() {
3913 let code = r#"
3914 annotation warmup(period) {
3915 before(args, ctx) {
3916 args
3917 }
3918 after(args, result, ctx) {
3919 result
3920 }
3921 }
3922 function test() { return 42; }
3923 "#;
3924 let program = parse_program(code).expect("Failed to parse annotation def");
3925 let bytecode = BytecodeCompiler::new().compile(&program);
3926 assert!(
3927 bytecode.is_ok(),
3928 "Annotation def should compile: {:?}",
3929 bytecode.err()
3930 );
3931
3932 let bytecode = bytecode.unwrap();
3933 assert!(
3935 bytecode.compiled_annotations.contains_key("warmup"),
3936 "Should have compiled 'warmup' annotation"
3937 );
3938
3939 let compiled = bytecode.compiled_annotations.get("warmup").unwrap();
3940 assert!(
3941 compiled.before_handler.is_some(),
3942 "Should have before handler"
3943 );
3944 assert!(
3945 compiled.after_handler.is_some(),
3946 "Should have after handler"
3947 );
3948 }
3949
3950 #[test]
3951 fn test_annotation_handler_function_names() {
3952 let code = r#"
3953 annotation my_ann(x) {
3954 before(args, ctx) {
3955 args
3956 }
3957 }
3958 function test() { return 1; }
3959 "#;
3960 let program = parse_program(code).expect("Failed to parse");
3961 let bytecode = BytecodeCompiler::new()
3962 .compile(&program)
3963 .expect("Failed to compile");
3964
3965 let compiled = bytecode.compiled_annotations.get("my_ann").unwrap();
3967 let handler_id = compiled.before_handler.unwrap() as usize;
3968 assert!(
3969 handler_id < bytecode.functions.len(),
3970 "Handler function ID should be valid"
3971 );
3972
3973 let handler_fn = &bytecode.functions[handler_id];
3974 assert_eq!(
3975 handler_fn.name, "my_ann___before",
3976 "Handler function should be named my_ann___before"
3977 );
3978 }
3979
3980 #[test]
3983 fn test_annotated_function_generates_wrapper() {
3984 let code = r#"
3985 annotation tracked(label) {
3986 before(args, ctx) {
3987 args
3988 }
3989 }
3990 @tracked("my_func")
3991 function compute(x) {
3992 return x * 2
3993 }
3994 function test() { return 1; }
3995 "#;
3996 let program = parse_program(code).expect("Failed to parse");
3997 let bytecode = BytecodeCompiler::new().compile(&program);
3998 assert!(
3999 bytecode.is_ok(),
4000 "Annotated function should compile: {:?}",
4001 bytecode.err()
4002 );
4003
4004 let bytecode = bytecode.unwrap();
4005 let has_impl = bytecode
4007 .functions
4008 .iter()
4009 .any(|f| f.name == "compute___impl");
4010 assert!(has_impl, "Should generate compute___impl function");
4011
4012 let has_wrapper = bytecode.functions.iter().any(|f| f.name == "compute");
4013 assert!(has_wrapper, "Should keep compute as wrapper");
4014 }
4015
4016 #[test]
4017 fn test_unannotated_function_no_wrapper() {
4018 let code = r#"
4019 function plain(x) {
4020 return x + 1
4021 }
4022 "#;
4023 let program = parse_program(code).expect("Failed to parse");
4024 let bytecode = BytecodeCompiler::new()
4025 .compile(&program)
4026 .expect("Failed to compile");
4027
4028 let has_impl = bytecode
4030 .functions
4031 .iter()
4032 .any(|f| f.name.ends_with("___impl"));
4033 assert!(
4034 !has_impl,
4035 "Non-annotated function should not generate ___impl"
4036 );
4037 }
4038
4039 #[test]
4042 fn test_annotation_chaining_generates_chain() {
4043 let code = r#"
4045 annotation first() {
4046 before(args, ctx) {
4047 return args
4048 }
4049 }
4050
4051 annotation second() {
4052 before(args, ctx) {
4053 return args
4054 }
4055 }
4056
4057 @first
4058 @second
4059 function compute(x) {
4060 return x * 2
4061 }
4062 "#;
4063 let program = parse_program(code).expect("Failed to parse");
4064 let bytecode = BytecodeCompiler::new().compile(&program);
4065 assert!(
4066 bytecode.is_ok(),
4067 "Chained annotations should compile: {:?}",
4068 bytecode.err()
4069 );
4070 let bytecode = bytecode.unwrap();
4071
4072 let has_impl = bytecode
4074 .functions
4075 .iter()
4076 .any(|f| f.name == "compute___impl");
4077 assert!(has_impl, "Should generate compute___impl function");
4078 let has_wrapper = bytecode.functions.iter().any(|f| f.name == "compute");
4079 assert!(has_wrapper, "Should keep compute as outermost wrapper");
4080 let has_intermediate = bytecode
4081 .functions
4082 .iter()
4083 .any(|f| f.name == "compute___second");
4084 assert!(
4085 has_intermediate,
4086 "Should generate compute___second intermediate wrapper"
4087 );
4088 }
4089
4090 #[test]
4091 fn test_annotation_allowed_targets_inferred() {
4092 let code = r#"
4094 annotation traced() {
4095 before(args, ctx) {
4096 return args
4097 }
4098 }
4099 "#;
4100 let program = parse_program(code).expect("Failed to parse");
4101 let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
4102 let ann = bytecode
4103 .compiled_annotations
4104 .get("traced")
4105 .expect("traced annotation");
4106 assert!(
4107 !ann.allowed_targets.is_empty(),
4108 "before handler should restrict targets"
4109 );
4110 assert!(
4111 ann.allowed_targets
4112 .contains(&shape_ast::ast::functions::AnnotationTargetKind::Function),
4113 "before handler should allow Function target"
4114 );
4115 }
4116
4117 #[test]
4118 fn test_annotation_allowed_targets_explicit_override() {
4119 let code = r#"
4121 annotation traced() {
4122 targets: [type]
4123 before(args, ctx) {
4124 return args
4125 }
4126 }
4127 "#;
4128 let program = parse_program(code).expect("Failed to parse");
4129 let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
4130 let ann = bytecode
4131 .compiled_annotations
4132 .get("traced")
4133 .expect("traced annotation");
4134 assert_eq!(
4135 ann.allowed_targets,
4136 vec![shape_ast::ast::functions::AnnotationTargetKind::Type]
4137 );
4138 }
4139
4140 #[test]
4141 fn test_metadata_only_annotation_defaults_to_definition_targets() {
4142 let code = r#"
4144 annotation info() {
4145 metadata() {
4146 return { version: 1 }
4147 }
4148 }
4149 "#;
4150 let program = parse_program(code).expect("Failed to parse");
4151 let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
4152 let ann = bytecode
4153 .compiled_annotations
4154 .get("info")
4155 .expect("info annotation");
4156 assert_eq!(
4157 ann.allowed_targets,
4158 vec![
4159 shape_ast::ast::functions::AnnotationTargetKind::Function,
4160 shape_ast::ast::functions::AnnotationTargetKind::Type,
4161 shape_ast::ast::functions::AnnotationTargetKind::Module
4162 ],
4163 "metadata-only annotation should default to definition targets"
4164 );
4165 }
4166
4167 #[test]
4168 fn test_definition_lifecycle_targets_reject_expression_target() {
4169 let code = r#"
4170 annotation info() {
4171 targets: [expression]
4172 metadata(target, ctx) {
4173 target.name
4174 }
4175 }
4176 "#;
4177 let program = parse_program(code).expect("Failed to parse");
4178 let err = BytecodeCompiler::new()
4179 .compile(&program)
4180 .expect_err("metadata hooks on expression targets should fail");
4181 let msg = format!("{}", err);
4182 assert!(
4183 msg.contains("not a definition target"),
4184 "expected definition-target restriction error, got: {}",
4185 msg
4186 );
4187 }
4188
4189 #[test]
4190 fn test_annotation_target_validation_on_struct_type() {
4191 let code = r#"
4193 annotation traced() {
4194 before(args, ctx) { return args }
4195 }
4196
4197 @traced()
4198 type Point { x: int }
4199 "#;
4200 let program = parse_program(code).expect("Failed to parse");
4201 let err = BytecodeCompiler::new()
4202 .compile(&program)
4203 .expect_err("function-only annotation on type should fail");
4204 let msg = format!("{}", err);
4205 assert!(
4206 msg.contains("cannot be applied to a type"),
4207 "expected type target validation error, got: {}",
4208 msg
4209 );
4210 }
4211
4212 #[test]
4213 fn test_type_c_emits_native_layout_metadata() {
4214 let bytecode = compiles_to(
4215 r#"
4216 type C Pair32 {
4217 left: i32,
4218 right: i32,
4219 }
4220 "#,
4221 );
4222
4223 assert_eq!(bytecode.native_struct_layouts.len(), 1);
4224 let layout = &bytecode.native_struct_layouts[0];
4225 assert_eq!(layout.name, "Pair32");
4226 assert_eq!(layout.abi, "C");
4227 assert_eq!(layout.size, 8);
4228 assert_eq!(layout.align, 4);
4229 assert_eq!(layout.fields.len(), 2);
4230 assert_eq!(layout.fields[0].name, "left");
4231 assert_eq!(layout.fields[0].offset, 0);
4232 assert_eq!(layout.fields[0].size, 4);
4233 assert_eq!(layout.fields[1].name, "right");
4234 assert_eq!(layout.fields[1].offset, 4);
4235 assert_eq!(layout.fields[1].size, 4);
4236 }
4237
4238 #[test]
4239 fn test_type_c_auto_generates_into_from_traits() {
4240 let bytecode = compiles_to(
4241 r#"
4242 type C QuoteC {
4243 bid: i64,
4244 ask: i64,
4245 }
4246
4247 type Quote {
4248 bid: i64,
4249 ask: i64,
4250 }
4251 "#,
4252 );
4253
4254 let c_to_shape =
4255 bytecode.lookup_trait_method_symbol("Into", "QuoteC", Some("Quote"), "into");
4256 let shape_to_c =
4257 bytecode.lookup_trait_method_symbol("Into", "Quote", Some("QuoteC"), "into");
4258 let from_c = bytecode.lookup_trait_method_symbol("From", "Quote", Some("QuoteC"), "from");
4259 let from_shape =
4260 bytecode.lookup_trait_method_symbol("From", "QuoteC", Some("Quote"), "from");
4261
4262 assert!(c_to_shape.is_some(), "expected Into<Quote> for QuoteC");
4263 assert!(shape_to_c.is_some(), "expected Into<QuoteC> for Quote");
4264 assert!(from_c.is_some(), "expected From<QuoteC> for Quote");
4265 assert!(from_shape.is_some(), "expected From<Quote> for QuoteC");
4266 }
4267
4268 #[test]
4269 fn test_type_c_auto_conversion_function_compiles() {
4270 let _ = compiles_to(
4271 r#"
4272 type Quote {
4273 bid: i64,
4274 ask: i64,
4275 }
4276
4277 type C QuoteC {
4278 bid: i64,
4279 ask: i64,
4280 }
4281
4282 fn spread(q: QuoteC) -> i64 {
4283 let q_shape = __auto_native_from_QuoteC_to_Quote(q);
4284 q_shape.ask - q_shape.bid
4285 }
4286
4287 spread(QuoteC { bid: 10, ask: 13 })
4288 "#,
4289 );
4290 }
4291
4292 #[test]
4293 fn test_type_c_auto_conversion_rejects_incompatible_fields() {
4294 let program = parse_program(
4295 r#"
4296 type Price {
4297 value: i64,
4298 }
4299
4300 type C PriceC {
4301 value: u64,
4302 }
4303 "#,
4304 )
4305 .expect("parse failed");
4306 let err = BytecodeCompiler::new()
4307 .compile(&program)
4308 .expect_err("incompatible type C conversion pair should fail");
4309 let msg = format!("{}", err);
4310 assert!(
4311 msg.contains("field type mismatch for auto conversion"),
4312 "expected type mismatch error, got: {}",
4313 msg
4314 );
4315 }
4316
4317 fn compiles_to(code: &str) -> crate::bytecode::BytecodeProgram {
4322 let program = parse_program(code).expect("parse failed");
4323 let compiler = BytecodeCompiler::new();
4324 compiler.compile(&program).expect("compile failed")
4325 }
4326
4327 #[test]
4330 fn test_extract_module_name() {
4331 assert_eq!(BytecodeCompiler::extract_module_name("file"), "file");
4332 assert_eq!(BytecodeCompiler::extract_module_name("std::file"), "file");
4333 assert_eq!(BytecodeCompiler::extract_module_name("std/io"), "io");
4334 assert_eq!(BytecodeCompiler::extract_module_name("a::b::c"), "c");
4335 assert_eq!(BytecodeCompiler::extract_module_name(""), "");
4336 }
4337
4338 #[test]
4339 fn test_permission_check_allows_pure_module_imports() {
4340 let code = "from json use { parse }";
4342 let program = parse_program(code).expect("parse failed");
4343 let mut compiler = BytecodeCompiler::new();
4344 compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4345 let _result = compiler.compile(&program);
4347 }
4348
4349 #[test]
4350 fn test_permission_check_blocks_file_import_under_pure() {
4351 let code = "from file use { read_text }";
4352 let program = parse_program(code).expect("parse failed");
4353 let mut compiler = BytecodeCompiler::new();
4354 compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4355 let result = compiler.compile(&program);
4356 assert!(
4357 result.is_err(),
4358 "Expected permission error for file::read_text under pure"
4359 );
4360 let err_msg = format!("{}", result.unwrap_err());
4361 assert!(
4362 err_msg.contains("Permission denied"),
4363 "Error should mention permission denied: {err_msg}"
4364 );
4365 assert!(
4366 err_msg.contains("fs.read"),
4367 "Error should mention fs.read: {err_msg}"
4368 );
4369 }
4370
4371 #[test]
4372 fn test_permission_check_allows_file_import_with_fs_read() {
4373 let code = "from file use { read_text }";
4374 let program = parse_program(code).expect("parse failed");
4375 let mut compiler = BytecodeCompiler::new();
4376 let pset = shape_abi_v1::PermissionSet::from_iter([shape_abi_v1::Permission::FsRead]);
4377 compiler.set_permission_set(Some(pset));
4378 let _result = compiler.compile(&program);
4380 }
4381
4382 #[test]
4383 fn test_permission_check_no_permission_set_allows_everything() {
4384 let code = "from file use { read_text }";
4386 let program = parse_program(code).expect("parse failed");
4387 let compiler = BytecodeCompiler::new();
4388 let _result = compiler.compile(&program);
4390 }
4391
4392 #[test]
4393 fn test_permission_check_namespace_import_blocked() {
4394 let code = "use http";
4395 let program = parse_program(code).expect("parse failed");
4396 let mut compiler = BytecodeCompiler::new();
4397 compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4398 let result = compiler.compile(&program);
4399 assert!(
4400 result.is_err(),
4401 "Expected permission error for `use http` under pure"
4402 );
4403 let err_msg = format!("{}", result.unwrap_err());
4404 assert!(
4405 err_msg.contains("Permission denied"),
4406 "Error should mention permission denied: {err_msg}"
4407 );
4408 }
4409
4410 #[test]
4411 fn test_permission_check_namespace_import_allowed() {
4412 let code = "use http";
4413 let program = parse_program(code).expect("parse failed");
4414 let mut compiler = BytecodeCompiler::new();
4415 compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::full()));
4416 let _result = compiler.compile(&program);
4418 }
4419}