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 doc_comment: None,
1139 params,
1140 return_type: method.return_type.clone(),
1141 body,
1142 type_params: Some(Vec::new()),
1143 annotations: method.annotations.clone(),
1144 is_async: method.is_async,
1145 is_comptime: false,
1146 where_clause: None,
1147 })
1148 }
1149
1150 fn desugar_impl_method(
1161 &self,
1162 method: &shape_ast::ast::types::MethodDef,
1163 trait_name: &str,
1164 type_name: &str,
1165 impl_name: Option<&str>,
1166 target_type: &shape_ast::ast::TypeName,
1167 ) -> Result<FunctionDef> {
1168 let receiver_type = Some(Self::type_name_to_annotation(target_type));
1169 let (params, body) = self.desugar_method_signature_and_body(method, receiver_type)?;
1170
1171 let method_name = if trait_name == "Drop" && method.name == "drop" && method.is_async {
1174 "drop_async".to_string()
1175 } else {
1176 method.name.clone()
1177 };
1178 let fn_name = if let Some(name) = impl_name {
1179 format!("{}::{}::{}::{}", trait_name, type_name, name, method_name)
1180 } else {
1181 format!("{}::{}", type_name, method_name)
1182 };
1183
1184 Ok(FunctionDef {
1185 name: fn_name,
1186 name_span: Span::DUMMY,
1187 doc_comment: None,
1188 params,
1189 return_type: method.return_type.clone(),
1190 body,
1191 type_params: Some(Vec::new()),
1192 annotations: method.annotations.clone(),
1193 is_async: method.is_async,
1194 is_comptime: false,
1195 where_clause: None,
1196 })
1197 }
1198
1199 fn desugar_method_signature_and_body(
1203 &self,
1204 method: &shape_ast::ast::types::MethodDef,
1205 receiver_type: Option<shape_ast::ast::TypeAnnotation>,
1206 ) -> Result<(Vec<FunctionParameter>, Vec<Statement>)> {
1207 if let Some(receiver) = method
1208 .params
1209 .first()
1210 .and_then(|p| p.pattern.as_identifier())
1211 {
1212 if receiver == "self" {
1213 let location = method
1214 .params
1215 .first()
1216 .map(|p| self.span_to_source_location(p.span()));
1217 return Err(ShapeError::SemanticError {
1218 message: format!(
1219 "Method '{}' has an explicit `self` parameter, but method receivers are implicit. Use `method {}(...)` without `self`.",
1220 method.name, method.name
1221 ),
1222 location,
1223 });
1224 }
1225 }
1226
1227 let mut params = vec![FunctionParameter {
1228 pattern: shape_ast::ast::DestructurePattern::Identifier(
1229 "self".to_string(),
1230 Span::DUMMY,
1231 ),
1232 is_const: false,
1233 is_reference: false,
1234 is_mut_reference: false,
1235 is_out: false,
1236 type_annotation: receiver_type,
1237 default_value: None,
1238 }];
1239 params.extend(method.params.clone());
1240
1241 Ok((params, method.body.clone()))
1242 }
1243
1244 fn compile_from_impl(
1255 &mut self,
1256 impl_block: &shape_ast::ast::types::ImplBlock,
1257 trait_name: &str,
1258 target_type: &str,
1259 ) -> Result<()> {
1260 let source_type = match &impl_block.trait_name {
1262 shape_ast::ast::types::TypeName::Generic { type_args, .. } if !type_args.is_empty() => {
1263 match &type_args[0] {
1264 TypeAnnotation::Basic(name) | TypeAnnotation::Reference(name) => name.clone(),
1265 other => {
1266 return Err(ShapeError::SemanticError {
1267 message: format!(
1268 "{} impl requires a simple source type, found {:?}",
1269 trait_name, other
1270 ),
1271 location: None,
1272 });
1273 }
1274 }
1275 }
1276 _ => {
1277 return Err(ShapeError::SemanticError {
1278 message: format!(
1279 "{} impl requires a generic type argument, e.g., {}<string>",
1280 trait_name, trait_name
1281 ),
1282 location: None,
1283 });
1284 }
1285 };
1286
1287 let selector = impl_block.impl_name.as_deref().unwrap_or(&source_type);
1289
1290 for method in &impl_block.methods {
1291 let func_def =
1292 self.desugar_from_method(method, trait_name, target_type, &source_type)?;
1293 let from_fn_name = func_def.name.clone();
1294
1295 self.program.register_trait_method_symbol(
1297 trait_name,
1298 target_type,
1299 Some(&source_type),
1300 &method.name,
1301 &from_fn_name,
1302 );
1303 self.register_function(&func_def)?;
1304
1305 if trait_name == "From" {
1307 self.program.register_trait_method_symbol(
1309 "Into",
1310 &source_type,
1311 Some(selector),
1312 "into",
1313 &from_fn_name,
1314 );
1315
1316 let wrapper_name =
1318 self.emit_from_to_tryinto_wrapper(&from_fn_name, &source_type, target_type)?;
1319 self.program.register_trait_method_symbol(
1320 "TryInto",
1321 &source_type,
1322 Some(selector),
1323 "tryInto",
1324 &wrapper_name,
1325 );
1326
1327 let _ = self.type_inference.env.register_trait_impl_named(
1329 "Into",
1330 &source_type,
1331 selector,
1332 vec!["into".to_string()],
1333 );
1334 let _ = self.type_inference.env.register_trait_impl_named(
1335 "TryInto",
1336 &source_type,
1337 selector,
1338 vec!["tryInto".to_string()],
1339 );
1340 } else {
1341 self.program.register_trait_method_symbol(
1343 "TryInto",
1344 &source_type,
1345 Some(selector),
1346 "tryInto",
1347 &from_fn_name,
1348 );
1349
1350 let _ = self.type_inference.env.register_trait_impl_named(
1352 "TryInto",
1353 &source_type,
1354 selector,
1355 vec!["tryInto".to_string()],
1356 );
1357 }
1358 }
1359
1360 let all_method_names: Vec<String> =
1362 impl_block.methods.iter().map(|m| m.name.clone()).collect();
1363 let _ = self.type_inference.env.register_trait_impl_named(
1364 trait_name,
1365 target_type,
1366 &source_type,
1367 all_method_names,
1368 );
1369
1370 Ok(())
1371 }
1372
1373 fn compile_from_impl_bodies(
1378 &mut self,
1379 impl_block: &shape_ast::ast::types::ImplBlock,
1380 trait_name: &str,
1381 target_type: &str,
1382 ) -> Result<()> {
1383 let source_type = match &impl_block.trait_name {
1384 shape_ast::ast::types::TypeName::Generic { type_args, .. } if !type_args.is_empty() => {
1385 match &type_args[0] {
1386 TypeAnnotation::Basic(name) | TypeAnnotation::Reference(name) => name.clone(),
1387 _ => return Ok(()), }
1389 }
1390 _ => return Ok(()),
1391 };
1392
1393 for method in &impl_block.methods {
1394 let func_def =
1395 self.desugar_from_method(method, trait_name, target_type, &source_type)?;
1396 self.compile_function(&func_def)?;
1397 }
1398
1399 if trait_name == "From" {
1401 for method in &impl_block.methods {
1402 let from_fn_name = format!(
1403 "{}::{}::{}::{}",
1404 trait_name, target_type, source_type, method.name
1405 );
1406 let wrapper_name = format!("__from_tryinto_{}_{}", source_type, target_type);
1407 if let Some(func_def) = self.function_defs.get(&wrapper_name).cloned() {
1409 let _ = self.compile_function(&func_def);
1410 let _ = from_fn_name; }
1414 }
1415 }
1416
1417 Ok(())
1418 }
1419
1420 fn desugar_from_method(
1425 &self,
1426 method: &shape_ast::ast::types::MethodDef,
1427 trait_name: &str,
1428 target_type: &str,
1429 source_type: &str,
1430 ) -> Result<FunctionDef> {
1431 if let Some(first) = method
1433 .params
1434 .first()
1435 .and_then(|p| p.pattern.as_identifier())
1436 {
1437 if first == "self" {
1438 return Err(ShapeError::SemanticError {
1439 message: format!(
1440 "{}::{} methods are constructors and must not have a `self` parameter",
1441 trait_name, method.name
1442 ),
1443 location: None,
1444 });
1445 }
1446 }
1447
1448 let fn_name = format!(
1449 "{}::{}::{}::{}",
1450 trait_name, target_type, source_type, method.name
1451 );
1452
1453 Ok(FunctionDef {
1454 name: fn_name,
1455 name_span: Span::DUMMY,
1456 doc_comment: None,
1457 params: method.params.clone(),
1458 return_type: method.return_type.clone(),
1459 body: method.body.clone(),
1460 type_params: Some(Vec::new()),
1461 annotations: Vec::new(),
1462 is_async: method.is_async,
1463 is_comptime: false,
1464 where_clause: None,
1465 })
1466 }
1467
1468 fn emit_from_to_tryinto_wrapper(
1473 &mut self,
1474 from_fn_name: &str,
1475 source_type: &str,
1476 target_type: &str,
1477 ) -> Result<String> {
1478 let wrapper_name = format!("__from_tryinto_{}_{}", source_type, target_type);
1479
1480 let span = Span::DUMMY;
1482 let body = vec![Statement::Return(
1483 Some(Expr::FunctionCall {
1484 name: "Ok".to_string(),
1485 args: vec![Expr::FunctionCall {
1486 name: from_fn_name.to_string(),
1487 args: vec![Expr::Identifier("value".to_string(), span)],
1488 named_args: Vec::new(),
1489 span,
1490 }],
1491 named_args: Vec::new(),
1492 span,
1493 }),
1494 span,
1495 )];
1496
1497 let func_def = FunctionDef {
1498 name: wrapper_name.clone(),
1499 name_span: span,
1500 doc_comment: None,
1501 params: vec![FunctionParameter {
1502 pattern: DestructurePattern::Identifier("value".to_string(), span),
1503 is_const: false,
1504 is_reference: false,
1505 is_mut_reference: false,
1506 is_out: false,
1507 type_annotation: None,
1508 default_value: None,
1509 }],
1510 return_type: None,
1511 body,
1512 type_params: Some(Vec::new()),
1513 annotations: Vec::new(),
1514 is_async: false,
1515 is_comptime: false,
1516 where_clause: None,
1517 };
1518
1519 self.register_function(&func_def)?;
1520
1521 Ok(wrapper_name)
1522 }
1523
1524 fn type_name_to_annotation(
1525 type_name: &shape_ast::ast::TypeName,
1526 ) -> shape_ast::ast::TypeAnnotation {
1527 match type_name {
1528 shape_ast::ast::TypeName::Simple(name) => {
1529 shape_ast::ast::TypeAnnotation::Basic(name.clone())
1530 }
1531 shape_ast::ast::TypeName::Generic { name, type_args } => {
1532 shape_ast::ast::TypeAnnotation::Generic {
1533 name: name.clone(),
1534 args: type_args.clone(),
1535 }
1536 }
1537 }
1538 }
1539
1540 fn compile_annotation_def(&mut self, ann_def: &shape_ast::ast::AnnotationDef) -> Result<()> {
1549 use crate::bytecode::CompiledAnnotation;
1550 use shape_ast::ast::AnnotationHandlerType;
1551
1552 let mut compiled = CompiledAnnotation {
1553 name: ann_def.name.clone(),
1554 param_names: ann_def
1555 .params
1556 .iter()
1557 .flat_map(|p| p.get_identifiers())
1558 .collect(),
1559 before_handler: None,
1560 after_handler: None,
1561 on_define_handler: None,
1562 metadata_handler: None,
1563 comptime_pre_handler: None,
1564 comptime_post_handler: None,
1565 allowed_targets: Vec::new(),
1566 };
1567
1568 for handler in &ann_def.handlers {
1569 match handler.handler_type {
1572 AnnotationHandlerType::ComptimePre => {
1573 compiled.comptime_pre_handler = Some(handler.clone());
1574 continue;
1575 }
1576 AnnotationHandlerType::ComptimePost => {
1577 compiled.comptime_post_handler = Some(handler.clone());
1578 continue;
1579 }
1580 _ => {}
1581 }
1582
1583 if handler.params.iter().any(|p| p.is_variadic) {
1584 return Err(ShapeError::SemanticError {
1585 message:
1586 "Variadic annotation handler params (`...args`) are only supported on comptime handlers"
1587 .to_string(),
1588 location: Some(self.span_to_source_location(handler.span)),
1589 });
1590 }
1591
1592 let handler_type_str = match handler.handler_type {
1593 AnnotationHandlerType::Before => "before",
1594 AnnotationHandlerType::After => "after",
1595 AnnotationHandlerType::OnDefine => "on_define",
1596 AnnotationHandlerType::Metadata => "metadata",
1597 AnnotationHandlerType::ComptimePre => unreachable!(),
1598 AnnotationHandlerType::ComptimePost => unreachable!(),
1599 };
1600
1601 let func_name = format!("{}___{}", ann_def.name, handler_type_str);
1602
1603 let mut params = vec![FunctionParameter {
1605 pattern: shape_ast::ast::DestructurePattern::Identifier(
1606 "self".to_string(),
1607 Span::DUMMY,
1608 ),
1609 is_const: false,
1610 is_reference: false,
1611 is_mut_reference: false,
1612 is_out: false,
1613 type_annotation: None,
1614 default_value: None,
1615 }];
1616 for ann_param in &ann_def.params {
1618 params.push(ann_param.clone());
1619 }
1620 for param in &handler.params {
1622 let inferred_type = if param.name == "ctx" {
1623 Some(TypeAnnotation::Object(vec![
1624 shape_ast::ast::ObjectTypeField {
1625 name: "state".to_string(),
1626 optional: false,
1627 type_annotation: TypeAnnotation::Basic("unknown".to_string()),
1628 annotations: vec![],
1629 },
1630 shape_ast::ast::ObjectTypeField {
1631 name: "event_log".to_string(),
1632 optional: false,
1633 type_annotation: TypeAnnotation::Array(Box::new(TypeAnnotation::Basic("unknown".to_string()))),
1634 annotations: vec![],
1635 },
1636 ]))
1637 } else if matches!(
1638 handler.handler_type,
1639 AnnotationHandlerType::OnDefine | AnnotationHandlerType::Metadata
1640 ) && (param.name == "fn" || param.name == "target")
1641 {
1642 Some(TypeAnnotation::Object(vec![
1643 shape_ast::ast::ObjectTypeField {
1644 name: "name".to_string(),
1645 optional: false,
1646 type_annotation: TypeAnnotation::Basic("string".to_string()),
1647 annotations: vec![],
1648 },
1649 shape_ast::ast::ObjectTypeField {
1650 name: "kind".to_string(),
1651 optional: false,
1652 type_annotation: TypeAnnotation::Basic("string".to_string()),
1653 annotations: vec![],
1654 },
1655 shape_ast::ast::ObjectTypeField {
1656 name: "id".to_string(),
1657 optional: false,
1658 type_annotation: TypeAnnotation::Basic("int".to_string()),
1659 annotations: vec![],
1660 },
1661 ]))
1662 } else {
1663 None
1664 };
1665
1666 params.push(FunctionParameter {
1667 pattern: shape_ast::ast::DestructurePattern::Identifier(
1668 param.name.clone(),
1669 Span::DUMMY,
1670 ),
1671 is_const: false,
1672 is_reference: false,
1673 is_mut_reference: false,
1674 is_out: false,
1675 type_annotation: inferred_type,
1676 default_value: None,
1677 });
1678 }
1679
1680 let body = vec![Statement::Return(Some(handler.body.clone()), Span::DUMMY)];
1682
1683 let func_def = FunctionDef {
1684 name: func_name,
1685 name_span: Span::DUMMY,
1686 doc_comment: None,
1687 params,
1688 return_type: handler.return_type.clone(),
1689 body,
1690 type_params: Some(Vec::new()),
1691 annotations: Vec::new(),
1692 is_async: false,
1693 is_comptime: false,
1694 where_clause: None,
1695 };
1696
1697 self.register_function(&func_def)?;
1698 self.compile_function(&func_def)?;
1699
1700 let func_id = (self.program.functions.len() - 1) as u16;
1701
1702 match handler.handler_type {
1703 AnnotationHandlerType::Before => compiled.before_handler = Some(func_id),
1704 AnnotationHandlerType::After => compiled.after_handler = Some(func_id),
1705 AnnotationHandlerType::OnDefine => compiled.on_define_handler = Some(func_id),
1706 AnnotationHandlerType::Metadata => compiled.metadata_handler = Some(func_id),
1707 AnnotationHandlerType::ComptimePre => {} AnnotationHandlerType::ComptimePost => {} }
1710 }
1711
1712 if let Some(explicit) = &ann_def.allowed_targets {
1718 compiled.allowed_targets = explicit.clone();
1719 } else if compiled.before_handler.is_some()
1720 || compiled.after_handler.is_some()
1721 || compiled.comptime_pre_handler.is_some()
1722 || compiled.comptime_post_handler.is_some()
1723 {
1724 compiled.allowed_targets =
1725 vec![shape_ast::ast::functions::AnnotationTargetKind::Function];
1726 } else if compiled.on_define_handler.is_some() || compiled.metadata_handler.is_some() {
1727 compiled.allowed_targets = vec![
1728 shape_ast::ast::functions::AnnotationTargetKind::Function,
1729 shape_ast::ast::functions::AnnotationTargetKind::Type,
1730 shape_ast::ast::functions::AnnotationTargetKind::Module,
1731 ];
1732 }
1733
1734 if compiled.on_define_handler.is_some() || compiled.metadata_handler.is_some() {
1737 if compiled.allowed_targets.is_empty() {
1738 return Err(ShapeError::SemanticError {
1739 message: format!(
1740 "Annotation '{}' uses `on_define`/`metadata` and cannot have unrestricted targets. Allowed targets are: function, type, module",
1741 ann_def.name
1742 ),
1743 location: Some(self.span_to_source_location(ann_def.span)),
1744 });
1745 }
1746 if let Some(invalid) = compiled
1747 .allowed_targets
1748 .iter()
1749 .find(|kind| !Self::is_definition_annotation_target(**kind))
1750 {
1751 let invalid_label = format!("{:?}", invalid).to_lowercase();
1752 return Err(ShapeError::SemanticError {
1753 message: format!(
1754 "Annotation '{}' uses `on_define`/`metadata`, but target '{}' is not a definition target. Allowed targets are: function, type, module",
1755 ann_def.name, invalid_label
1756 ),
1757 location: Some(self.span_to_source_location(ann_def.span)),
1758 });
1759 }
1760 }
1761
1762 self.program
1763 .compiled_annotations
1764 .insert(ann_def.name.clone(), compiled);
1765 Ok(())
1766 }
1767
1768 fn register_struct_type(
1773 &mut self,
1774 struct_def: &shape_ast::ast::StructTypeDef,
1775 span: shape_ast::ast::Span,
1776 ) -> Result<()> {
1777 use shape_ast::ast::Literal;
1778 use shape_runtime::type_schema::{FieldAnnotation, TypeSchemaBuilder};
1779
1780 for ann in &struct_def.annotations {
1782 self.validate_annotation_target_usage(
1783 ann,
1784 shape_ast::ast::functions::AnnotationTargetKind::Type,
1785 span,
1786 )?;
1787 }
1788
1789 if struct_def.native_layout.is_some() {
1790 self.native_layout_types.insert(struct_def.name.clone());
1791 } else {
1792 self.native_layout_types.remove(&struct_def.name);
1793 }
1794
1795 let runtime_field_names: Vec<String> = struct_def
1800 .fields
1801 .iter()
1802 .filter(|f| !f.is_comptime)
1803 .map(|f| f.name.clone())
1804 .collect();
1805 let runtime_field_types = struct_def
1806 .fields
1807 .iter()
1808 .filter(|f| !f.is_comptime)
1809 .map(|f| (f.name.clone(), f.type_annotation.clone()))
1810 .collect::<std::collections::HashMap<_, _>>();
1811 self.struct_types
1812 .insert(struct_def.name.clone(), (runtime_field_names, span));
1813 self.struct_generic_info.insert(
1814 struct_def.name.clone(),
1815 StructGenericInfo {
1816 type_params: struct_def.type_params.clone().unwrap_or_default(),
1817 runtime_field_types,
1818 },
1819 );
1820 if self
1821 .type_tracker
1822 .schema_registry()
1823 .get(&struct_def.name)
1824 .is_none()
1825 {
1826 let runtime_fields: Vec<(String, shape_runtime::type_schema::FieldType)> = struct_def
1827 .fields
1828 .iter()
1829 .filter(|f| !f.is_comptime)
1830 .map(|f| {
1831 (
1832 f.name.clone(),
1833 Self::type_annotation_to_field_type(&f.type_annotation),
1834 )
1835 })
1836 .collect();
1837 self.type_tracker
1838 .schema_registry_mut()
1839 .register_type(struct_def.name.clone(), runtime_fields);
1840 }
1841
1842 if self.execute_struct_comptime_handlers(struct_def)? {
1845 self.struct_types.remove(&struct_def.name);
1846 self.struct_generic_info.remove(&struct_def.name);
1847 return Ok(());
1848 }
1849
1850 if struct_def.native_layout.is_some() {
1851 self.register_native_struct_layout(struct_def, span)?;
1852 }
1853
1854 if self
1856 .type_tracker
1857 .schema_registry()
1858 .get(&struct_def.name)
1859 .is_none()
1860 {
1861 let mut builder = TypeSchemaBuilder::new(struct_def.name.clone());
1862 for field in &struct_def.fields {
1863 if field.is_comptime {
1864 continue;
1865 }
1866 let field_type = Self::type_annotation_to_field_type(&field.type_annotation);
1867 let mut annotations = Vec::new();
1868 for ann in &field.annotations {
1869 let args: Vec<String> = ann
1870 .args
1871 .iter()
1872 .filter_map(Self::eval_annotation_arg)
1873 .collect();
1874 annotations.push(FieldAnnotation {
1875 name: ann.name.clone(),
1876 args,
1877 });
1878 }
1879 builder = builder.field_with_meta(field.name.clone(), field_type, annotations);
1880 }
1881 builder.register(self.type_tracker.schema_registry_mut());
1882 }
1883
1884 let mut comptime_values = std::collections::HashMap::new();
1886 for field in &struct_def.fields {
1887 if !field.is_comptime {
1888 continue;
1889 }
1890 if let Some(ref default_expr) = field.default_value {
1891 let value = match default_expr {
1892 Expr::Literal(Literal::Number(n), _) => shape_value::ValueWord::from_f64(*n),
1893 Expr::Literal(Literal::Int(n), _) => {
1894 shape_value::ValueWord::from_f64(*n as f64)
1895 }
1896 Expr::Literal(Literal::String(s), _) => {
1897 shape_value::ValueWord::from_string(std::sync::Arc::new(s.clone()))
1898 }
1899 Expr::Literal(Literal::Bool(b), _) => shape_value::ValueWord::from_bool(*b),
1900 Expr::Literal(Literal::None, _) => shape_value::ValueWord::none(),
1901 _ => {
1902 return Err(ShapeError::SemanticError {
1903 message: format!(
1904 "Comptime field '{}' on type '{}' must have a literal default value",
1905 field.name, struct_def.name
1906 ),
1907 location: None,
1908 });
1909 }
1910 };
1911 comptime_values.insert(field.name.clone(), value);
1912 }
1913 }
1916
1917 if !comptime_values.is_empty() {
1918 self.comptime_fields
1919 .insert(struct_def.name.clone(), comptime_values);
1920 }
1921
1922 self.maybe_generate_native_type_conversions(&struct_def.name, span)?;
1923
1924 Ok(())
1925 }
1926
1927 fn register_native_struct_layout(
1928 &mut self,
1929 struct_def: &shape_ast::ast::StructTypeDef,
1930 span: shape_ast::ast::Span,
1931 ) -> Result<()> {
1932 if struct_def.type_params.is_some() {
1933 return Err(ShapeError::SemanticError {
1934 message: format!(
1935 "type C '{}' cannot be generic in this version",
1936 struct_def.name
1937 ),
1938 location: Some(self.span_to_source_location(span)),
1939 });
1940 }
1941
1942 if struct_def.fields.iter().any(|f| f.is_comptime) {
1943 return Err(ShapeError::SemanticError {
1944 message: format!(
1945 "type C '{}' cannot contain comptime fields",
1946 struct_def.name
1947 ),
1948 location: Some(self.span_to_source_location(span)),
1949 });
1950 }
1951
1952 let abi = struct_def
1953 .native_layout
1954 .as_ref()
1955 .map(|b| b.abi.clone())
1956 .unwrap_or_else(|| "C".to_string());
1957 if abi != "C" {
1958 return Err(ShapeError::SemanticError {
1959 message: format!(
1960 "type '{}' uses unsupported native ABI '{}'; only C is supported",
1961 struct_def.name, abi
1962 ),
1963 location: Some(self.span_to_source_location(span)),
1964 });
1965 }
1966
1967 let mut struct_align: u64 = 1;
1968 let mut offset: u64 = 0;
1969 let mut field_layouts = Vec::with_capacity(struct_def.fields.len());
1970
1971 for field in &struct_def.fields {
1972 let field_spec =
1973 self.native_field_layout_spec(&field.type_annotation, span, &struct_def.name)?;
1974 struct_align = struct_align.max(field_spec.align);
1975 offset = Self::align_to(offset, field_spec.align);
1976 if offset > u32::MAX as u64
1977 || field_spec.size > u32::MAX as u64
1978 || field_spec.align > u32::MAX as u64
1979 {
1980 return Err(ShapeError::SemanticError {
1981 message: format!(
1982 "type C '{}' layout exceeds supported size/alignment limits",
1983 struct_def.name
1984 ),
1985 location: Some(self.span_to_source_location(span)),
1986 });
1987 }
1988 field_layouts.push(crate::bytecode::NativeStructFieldLayout {
1989 name: field.name.clone(),
1990 c_type: field_spec.c_type,
1991 offset: offset as u32,
1992 size: field_spec.size as u32,
1993 align: field_spec.align as u32,
1994 });
1995 offset = offset.saturating_add(field_spec.size);
1996 }
1997
1998 let size = Self::align_to(offset, struct_align);
1999 if size > u32::MAX as u64 || struct_align > u32::MAX as u64 {
2000 return Err(ShapeError::SemanticError {
2001 message: format!(
2002 "type C '{}' layout exceeds supported size/alignment limits",
2003 struct_def.name
2004 ),
2005 location: Some(self.span_to_source_location(span)),
2006 });
2007 }
2008
2009 let entry = crate::bytecode::NativeStructLayoutEntry {
2010 name: struct_def.name.clone(),
2011 abi,
2012 size: size as u32,
2013 align: struct_align as u32,
2014 fields: field_layouts,
2015 };
2016
2017 if let Some(existing) = self
2018 .program
2019 .native_struct_layouts
2020 .iter_mut()
2021 .find(|existing| existing.name == entry.name)
2022 {
2023 *existing = entry;
2024 } else {
2025 self.program.native_struct_layouts.push(entry);
2026 }
2027
2028 Ok(())
2029 }
2030
2031 fn align_to(value: u64, align: u64) -> u64 {
2032 debug_assert!(align > 0);
2033 let mask = align - 1;
2034 (value + mask) & !mask
2035 }
2036
2037 fn native_field_layout_spec(
2038 &self,
2039 ann: &shape_ast::ast::TypeAnnotation,
2040 span: shape_ast::ast::Span,
2041 struct_name: &str,
2042 ) -> Result<NativeFieldLayoutSpec> {
2043 use shape_ast::ast::TypeAnnotation;
2044
2045 let pointer = std::mem::size_of::<usize>() as u64;
2046
2047 let fail = || -> Result<NativeFieldLayoutSpec> {
2048 Err(ShapeError::SemanticError {
2049 message: format!(
2050 "unsupported type C field type '{}' in '{}'",
2051 ann.to_type_string(),
2052 struct_name
2053 ),
2054 location: Some(self.span_to_source_location(span)),
2055 })
2056 };
2057
2058 match ann {
2059 TypeAnnotation::Basic(name) | TypeAnnotation::Reference(name) => {
2060 if let Some(existing) = self
2061 .program
2062 .native_struct_layouts
2063 .iter()
2064 .find(|layout| &layout.name == name)
2065 {
2066 return Ok(NativeFieldLayoutSpec {
2067 c_type: name.clone(),
2068 size: existing.size as u64,
2069 align: existing.align as u64,
2070 });
2071 }
2072
2073 let spec = match name.as_str() {
2074 "f64" | "number" | "Number" | "float" => ("f64", 8, 8),
2075 "f32" => ("f32", 4, 4),
2076 "i64" | "int" | "integer" | "Int" | "Integer" => ("i64", 8, 8),
2077 "i32" => ("i32", 4, 4),
2078 "i16" => ("i16", 2, 2),
2079 "i8" | "char" => ("i8", 1, 1),
2080 "u64" => ("u64", 8, 8),
2081 "u32" => ("u32", 4, 4),
2082 "u16" => ("u16", 2, 2),
2083 "u8" | "byte" => ("u8", 1, 1),
2084 "bool" | "boolean" => ("bool", 1, 1),
2085 "isize" => ("isize", pointer, pointer),
2086 "usize" | "ptr" | "pointer" => ("ptr", pointer, pointer),
2087 "string" | "str" | "cstring" => ("cstring", pointer, pointer),
2088 _ => return fail(),
2089 };
2090 Ok(NativeFieldLayoutSpec {
2091 c_type: spec.0.to_string(),
2092 size: spec.1,
2093 align: spec.2,
2094 })
2095 }
2096 TypeAnnotation::Generic { name, args }
2097 if name == "Option" && args.len() == 1 =>
2098 {
2099 let inner = self.native_field_layout_spec(&args[0], span, struct_name)?;
2100 if inner.c_type == "cstring" {
2101 Ok(NativeFieldLayoutSpec {
2102 c_type: "cstring?".to_string(),
2103 size: pointer,
2104 align: pointer,
2105 })
2106 } else {
2107 fail()
2108 }
2109 }
2110 _ => fail(),
2111 }
2112 }
2113
2114 fn maybe_generate_native_type_conversions(
2115 &mut self,
2116 type_name: &str,
2117 span: shape_ast::ast::Span,
2118 ) -> Result<()> {
2119 let pair = if self.native_layout_types.contains(type_name) {
2120 let Some(object_type) = Self::object_type_name_for_native_layout(type_name) else {
2121 return Ok(());
2122 };
2123 if !self.struct_types.contains_key(&object_type)
2124 || self.native_layout_types.contains(&object_type)
2125 {
2126 return Ok(());
2127 }
2128 (type_name.to_string(), object_type)
2129 } else {
2130 let candidates: Vec<String> = Self::native_layout_name_candidates_for_object(type_name)
2131 .into_iter()
2132 .filter(|candidate| self.native_layout_types.contains(candidate))
2133 .collect();
2134 if candidates.is_empty() {
2135 return Ok(());
2136 }
2137 if candidates.len() > 1 {
2138 return Err(ShapeError::SemanticError {
2139 message: format!(
2140 "type '{}' matches multiple `type C` companions ({}) - use one canonical name",
2141 type_name,
2142 candidates.join(", ")
2143 ),
2144 location: Some(self.span_to_source_location(span)),
2145 });
2146 }
2147 (candidates[0].clone(), type_name.to_string())
2148 };
2149
2150 let pair_key = format!("{}::{}", pair.0, pair.1);
2151 if self.generated_native_conversion_pairs.contains(&pair_key) {
2152 return Ok(());
2153 }
2154
2155 self.validate_native_conversion_pair(&pair.0, &pair.1, span)?;
2156 self.generate_native_conversion_direction(&pair.0, &pair.1, span)?;
2157 self.generate_native_conversion_direction(&pair.1, &pair.0, span)?;
2158 self.generated_native_conversion_pairs.insert(pair_key);
2159 Ok(())
2160 }
2161
2162 fn object_type_name_for_native_layout(name: &str) -> Option<String> {
2163 if let Some(base) = name.strip_suffix("Layout")
2164 && !base.is_empty()
2165 {
2166 return Some(base.to_string());
2167 }
2168 if let Some(base) = name.strip_suffix('C')
2169 && !base.is_empty()
2170 {
2171 return Some(base.to_string());
2172 }
2173 if let Some(base) = name.strip_prefix('C')
2174 && !base.is_empty()
2175 && base
2176 .chars()
2177 .next()
2178 .map(|ch| ch.is_ascii_uppercase())
2179 .unwrap_or(false)
2180 {
2181 return Some(base.to_string());
2182 }
2183 None
2184 }
2185
2186 fn native_layout_name_candidates_for_object(name: &str) -> Vec<String> {
2187 vec![
2188 format!("{}Layout", name),
2189 format!("{}C", name),
2190 format!("C{}", name),
2191 ]
2192 }
2193
2194 fn validate_native_conversion_pair(
2195 &self,
2196 c_type: &str,
2197 object_type: &str,
2198 span: shape_ast::ast::Span,
2199 ) -> Result<()> {
2200 if !self.native_layout_types.contains(c_type) {
2201 return Err(ShapeError::SemanticError {
2202 message: format!("'{}' is not declared as `type C`", c_type),
2203 location: Some(self.span_to_source_location(span)),
2204 });
2205 }
2206 if self.native_layout_types.contains(object_type) {
2207 return Err(ShapeError::SemanticError {
2208 message: format!(
2209 "auto conversion target '{}' cannot also be declared as `type C`",
2210 object_type
2211 ),
2212 location: Some(self.span_to_source_location(span)),
2213 });
2214 }
2215
2216 let c_type_info =
2217 self.struct_generic_info
2218 .get(c_type)
2219 .ok_or_else(|| ShapeError::SemanticError {
2220 message: format!("missing compiler metadata for `type C {}`", c_type),
2221 location: Some(self.span_to_source_location(span)),
2222 })?;
2223 let object_type_info =
2224 self.struct_generic_info
2225 .get(object_type)
2226 .ok_or_else(|| ShapeError::SemanticError {
2227 message: format!(
2228 "missing compiler metadata for companion type '{}'",
2229 object_type
2230 ),
2231 location: Some(self.span_to_source_location(span)),
2232 })?;
2233
2234 if !c_type_info.type_params.is_empty() || !object_type_info.type_params.is_empty() {
2235 return Err(ShapeError::SemanticError {
2236 message: format!(
2237 "auto `type C` conversions currently require non-generic types (`{}` <-> `{}`)",
2238 c_type, object_type
2239 ),
2240 location: Some(self.span_to_source_location(span)),
2241 });
2242 }
2243
2244 let c_fields = self
2245 .struct_types
2246 .get(c_type)
2247 .map(|(fields, _)| fields)
2248 .ok_or_else(|| ShapeError::SemanticError {
2249 message: format!("missing field metadata for `type C {}`", c_type),
2250 location: Some(self.span_to_source_location(span)),
2251 })?;
2252 let object_fields = self
2253 .struct_types
2254 .get(object_type)
2255 .map(|(fields, _)| fields)
2256 .ok_or_else(|| ShapeError::SemanticError {
2257 message: format!(
2258 "missing field metadata for companion type '{}'",
2259 object_type
2260 ),
2261 location: Some(self.span_to_source_location(span)),
2262 })?;
2263
2264 let c_field_set: std::collections::HashSet<&str> =
2265 c_fields.iter().map(String::as_str).collect();
2266 let object_field_set: std::collections::HashSet<&str> =
2267 object_fields.iter().map(String::as_str).collect();
2268 if c_field_set != object_field_set {
2269 return Err(ShapeError::SemanticError {
2270 message: format!(
2271 "auto conversion pair '{}' <-> '{}' must have identical runtime fields",
2272 c_type, object_type
2273 ),
2274 location: Some(self.span_to_source_location(span)),
2275 });
2276 }
2277
2278 for field_name in c_field_set {
2279 let c_ann = c_type_info
2280 .runtime_field_types
2281 .get(field_name)
2282 .ok_or_else(|| ShapeError::SemanticError {
2283 message: format!(
2284 "missing type metadata for field '{}.{}'",
2285 c_type, field_name
2286 ),
2287 location: Some(self.span_to_source_location(span)),
2288 })?;
2289 let object_ann = object_type_info
2290 .runtime_field_types
2291 .get(field_name)
2292 .ok_or_else(|| ShapeError::SemanticError {
2293 message: format!(
2294 "missing type metadata for field '{}.{}'",
2295 object_type, field_name
2296 ),
2297 location: Some(self.span_to_source_location(span)),
2298 })?;
2299 if c_ann != object_ann {
2300 return Err(ShapeError::SemanticError {
2301 message: format!(
2302 "field type mismatch for auto conversion '{}.{}' (`{}`) vs '{}.{}' (`{}`)",
2303 c_type,
2304 field_name,
2305 c_ann.to_type_string(),
2306 object_type,
2307 field_name,
2308 object_ann.to_type_string()
2309 ),
2310 location: Some(self.span_to_source_location(span)),
2311 });
2312 }
2313 }
2314
2315 Ok(())
2316 }
2317
2318 fn generate_native_conversion_direction(
2319 &mut self,
2320 source_type: &str,
2321 target_type: &str,
2322 span: shape_ast::ast::Span,
2323 ) -> Result<()> {
2324 let fn_name = format!(
2325 "__auto_native_from_{}_to_{}",
2326 Self::sanitize_auto_symbol(source_type),
2327 Self::sanitize_auto_symbol(target_type)
2328 );
2329 if self.function_defs.contains_key(&fn_name) {
2330 return Ok(());
2331 }
2332
2333 let target_fields = self
2334 .struct_types
2335 .get(target_type)
2336 .map(|(fields, _)| fields.clone())
2337 .ok_or_else(|| ShapeError::SemanticError {
2338 message: format!(
2339 "missing target type metadata for auto conversion '{}'",
2340 target_type
2341 ),
2342 location: Some(self.span_to_source_location(span)),
2343 })?;
2344
2345 let source_expr = Expr::Identifier("value".to_string(), span);
2346 let struct_fields = target_fields
2347 .iter()
2348 .map(|field| {
2349 (
2350 field.clone(),
2351 Expr::PropertyAccess {
2352 object: Box::new(source_expr.clone()),
2353 property: field.clone(),
2354 optional: false,
2355 span,
2356 },
2357 )
2358 })
2359 .collect::<Vec<_>>();
2360 let body = vec![Statement::Return(
2361 Some(Expr::StructLiteral {
2362 type_name: target_type.to_string(),
2363 fields: struct_fields,
2364 span,
2365 }),
2366 span,
2367 )];
2368 let fn_def = FunctionDef {
2369 name: fn_name.clone(),
2370 name_span: span,
2371 doc_comment: None,
2372 params: vec![FunctionParameter {
2373 pattern: DestructurePattern::Identifier("value".to_string(), span),
2374 is_const: false,
2375 is_reference: false,
2376 is_mut_reference: false,
2377 is_out: false,
2378 type_annotation: Some(TypeAnnotation::Reference(source_type.to_string())),
2379 default_value: None,
2380 }],
2381 return_type: Some(TypeAnnotation::Reference(target_type.to_string())),
2382 body,
2383 type_params: Some(Vec::new()),
2384 annotations: Vec::new(),
2385 is_async: false,
2386 is_comptime: false,
2387 where_clause: None,
2388 };
2389 self.register_function(&fn_def)?;
2390 self.compile_function(&fn_def)?;
2391
2392 self.program.register_trait_method_symbol(
2393 "From",
2394 target_type,
2395 Some(source_type),
2396 "from",
2397 &fn_name,
2398 );
2399 self.program.register_trait_method_symbol(
2400 "Into",
2401 source_type,
2402 Some(target_type),
2403 "into",
2404 &fn_name,
2405 );
2406 let _ = self.type_inference.env.register_trait_impl_named(
2407 "From",
2408 target_type,
2409 source_type,
2410 vec!["from".to_string()],
2411 );
2412 let _ = self.type_inference.env.register_trait_impl_named(
2413 "Into",
2414 source_type,
2415 target_type,
2416 vec!["into".to_string()],
2417 );
2418 Ok(())
2419 }
2420
2421 fn sanitize_auto_symbol(name: &str) -> String {
2422 let mut out = String::with_capacity(name.len());
2423 for ch in name.chars() {
2424 if ch.is_ascii_alphanumeric() {
2425 out.push(ch);
2426 } else {
2427 out.push('_');
2428 }
2429 }
2430 out
2431 }
2432
2433 fn execute_struct_comptime_handlers(
2438 &mut self,
2439 struct_def: &shape_ast::ast::StructTypeDef,
2440 ) -> Result<bool> {
2441 let mut removed = false;
2442 for ann in &struct_def.annotations {
2443 let compiled = self.program.compiled_annotations.get(&ann.name).cloned();
2444 if let Some(compiled) = compiled {
2445 let handlers = [
2446 compiled.comptime_pre_handler,
2447 compiled.comptime_post_handler,
2448 ];
2449 for handler in handlers.into_iter().flatten() {
2450 let fields: Vec<(
2453 String,
2454 Option<shape_ast::ast::TypeAnnotation>,
2455 Vec<shape_ast::ast::functions::Annotation>,
2456 )> = struct_def
2457 .fields
2458 .iter()
2459 .map(|f| {
2460 (
2461 f.name.clone(),
2462 Some(f.type_annotation.clone()),
2463 f.annotations.clone(),
2464 )
2465 })
2466 .collect();
2467
2468 let target = super::comptime_target::ComptimeTarget::from_type(
2469 &struct_def.name,
2470 &fields,
2471 );
2472 let target_value = target.to_nanboxed();
2473 let target_name = struct_def.name.clone();
2474 let handler_span = handler.span;
2475 let execution =
2476 self.execute_comptime_annotation_handler(ann, &handler, target_value, &compiled.param_names, &[])?;
2477
2478 if self
2479 .process_comptime_directives(execution.directives, &target_name)
2480 .map_err(|e| ShapeError::RuntimeError {
2481 message: format!(
2482 "Comptime handler '{}' directive processing failed: {}",
2483 ann.name, e
2484 ),
2485 location: Some(self.span_to_source_location(handler_span)),
2486 })?
2487 {
2488 removed = true;
2489 break;
2490 }
2491 }
2492 }
2493 if removed {
2494 break;
2495 }
2496 }
2497 Ok(removed)
2498 }
2499
2500 fn current_module_path_for(&self, module_name: &str) -> String {
2501 if let Some(parent) = self.module_scope_stack.last() {
2502 format!("{}::{}", parent, module_name)
2503 } else {
2504 module_name.to_string()
2505 }
2506 }
2507
2508 fn qualify_module_symbol(module_path: &str, name: &str) -> String {
2509 format!("{}::{}", module_path, name)
2510 }
2511
2512 fn qualify_module_item(&self, item: &Item, module_path: &str) -> Result<Item> {
2513 match item {
2514 Item::Function(func, span) => {
2515 let mut qualified = func.clone();
2516 qualified.name = Self::qualify_module_symbol(module_path, &func.name);
2517 Ok(Item::Function(qualified, *span))
2518 }
2519 Item::VariableDecl(decl, span) => {
2520 if decl.kind != VarKind::Const {
2521 return Err(ShapeError::SemanticError {
2522 message: "module-level variable declarations currently require `const`"
2523 .to_string(),
2524 location: Some(self.span_to_source_location(*span)),
2525 });
2526 }
2527 let mut qualified = decl.clone();
2528 let Some(name) = decl.pattern.as_identifier() else {
2529 return Err(ShapeError::SemanticError {
2530 message:
2531 "module-level constants currently require a simple identifier binding"
2532 .to_string(),
2533 location: Some(self.span_to_source_location(*span)),
2534 });
2535 };
2536 qualified.pattern = DestructurePattern::Identifier(
2537 Self::qualify_module_symbol(module_path, name),
2538 *span,
2539 );
2540 Ok(Item::VariableDecl(qualified, *span))
2541 }
2542 Item::Statement(Statement::VariableDecl(decl, stmt_span), item_span) => {
2543 if decl.kind != VarKind::Const {
2544 return Err(ShapeError::SemanticError {
2545 message: "module-level variable declarations currently require `const`"
2546 .to_string(),
2547 location: Some(self.span_to_source_location(*stmt_span)),
2548 });
2549 }
2550 let mut qualified = decl.clone();
2551 let Some(name) = decl.pattern.as_identifier() else {
2552 return Err(ShapeError::SemanticError {
2553 message:
2554 "module-level constants currently require a simple identifier binding"
2555 .to_string(),
2556 location: Some(self.span_to_source_location(*stmt_span)),
2557 });
2558 };
2559 qualified.pattern = DestructurePattern::Identifier(
2560 Self::qualify_module_symbol(module_path, name),
2561 *stmt_span,
2562 );
2563 Ok(Item::Statement(
2564 Statement::VariableDecl(qualified, *stmt_span),
2565 *item_span,
2566 ))
2567 }
2568 Item::Statement(Statement::Assignment(assign, stmt_span), item_span) => {
2569 let mut qualified = assign.clone();
2570 if let Some(name) = assign.pattern.as_identifier() {
2571 qualified.pattern = DestructurePattern::Identifier(
2572 Self::qualify_module_symbol(module_path, name),
2573 *stmt_span,
2574 );
2575 }
2576 Ok(Item::Statement(
2577 Statement::Assignment(qualified, *stmt_span),
2578 *item_span,
2579 ))
2580 }
2581 Item::Export(export, span) if export.source_decl.is_some() => {
2582 let decl = export.source_decl.as_ref().unwrap();
2584 if decl.kind != VarKind::Const {
2585 return Err(ShapeError::SemanticError {
2586 message: "module-level variable declarations currently require `const`"
2587 .to_string(),
2588 location: Some(self.span_to_source_location(*span)),
2589 });
2590 }
2591 let mut qualified = decl.clone();
2592 let Some(name) = decl.pattern.as_identifier() else {
2593 return Err(ShapeError::SemanticError {
2594 message:
2595 "module-level constants currently require a simple identifier binding"
2596 .to_string(),
2597 location: Some(self.span_to_source_location(*span)),
2598 });
2599 };
2600 qualified.pattern = DestructurePattern::Identifier(
2601 Self::qualify_module_symbol(module_path, name),
2602 *span,
2603 );
2604 Ok(Item::VariableDecl(qualified, *span))
2605 }
2606 _ => Ok(item.clone()),
2607 }
2608 }
2609
2610 fn collect_module_runtime_exports(
2611 &self,
2612 items: &[Item],
2613 module_path: &str,
2614 ) -> Vec<(String, String)> {
2615 let mut exports = Vec::new();
2616 for item in items {
2617 match item {
2618 Item::Function(func, _) => {
2619 exports.push((
2620 func.name.clone(),
2621 Self::qualify_module_symbol(module_path, &func.name),
2622 ));
2623 }
2624 Item::VariableDecl(decl, _) => {
2625 if decl.kind == VarKind::Const
2626 && let Some(name) = decl.pattern.as_identifier()
2627 {
2628 exports.push((
2629 name.to_string(),
2630 Self::qualify_module_symbol(module_path, name),
2631 ));
2632 }
2633 }
2634 Item::Statement(Statement::VariableDecl(decl, _), _) => {
2635 if decl.kind == VarKind::Const
2636 && let Some(name) = decl.pattern.as_identifier()
2637 {
2638 exports.push((
2639 name.to_string(),
2640 Self::qualify_module_symbol(module_path, name),
2641 ));
2642 }
2643 }
2644 Item::Export(export, _) => {
2645 if let Some(ref decl) = export.source_decl {
2646 if let Some(name) = decl.pattern.as_identifier() {
2647 exports.push((
2648 name.to_string(),
2649 Self::qualify_module_symbol(module_path, name),
2650 ));
2651 }
2652 }
2653 }
2654 Item::Module(module, _) => {
2655 exports.push((
2656 module.name.clone(),
2657 Self::qualify_module_symbol(module_path, &module.name),
2658 ));
2659 }
2660 _ => {}
2661 }
2662 }
2663 exports.sort_by(|a, b| a.0.cmp(&b.0));
2664 exports.dedup_by(|a, b| a.0 == b.0);
2665 exports
2666 }
2667
2668 fn module_target_fields(items: &[Item]) -> Vec<(String, String)> {
2669 let mut fields = Vec::new();
2670 for item in items {
2671 match item {
2672 Item::Function(func, _) => fields.push((func.name.clone(), "function".to_string())),
2673 Item::VariableDecl(decl, _) => {
2674 if let Some(name) = decl.pattern.as_identifier() {
2675 let type_name = decl
2676 .type_annotation
2677 .as_ref()
2678 .and_then(TypeAnnotation::as_simple_name)
2679 .unwrap_or("any")
2680 .to_string();
2681 fields.push((name.to_string(), type_name));
2682 }
2683 }
2684 Item::Statement(Statement::VariableDecl(decl, _), _) => {
2685 if let Some(name) = decl.pattern.as_identifier() {
2686 let type_name = decl
2687 .type_annotation
2688 .as_ref()
2689 .and_then(TypeAnnotation::as_simple_name)
2690 .unwrap_or("any")
2691 .to_string();
2692 fields.push((name.to_string(), type_name));
2693 }
2694 }
2695 Item::Export(export, _) => {
2696 if let Some(ref decl) = export.source_decl {
2697 if let Some(name) = decl.pattern.as_identifier() {
2698 let type_name = decl
2699 .type_annotation
2700 .as_ref()
2701 .and_then(TypeAnnotation::as_simple_name)
2702 .unwrap_or("any")
2703 .to_string();
2704 fields.push((name.to_string(), type_name));
2705 }
2706 }
2707 }
2708 Item::StructType(def, _) => fields.push((def.name.clone(), "type".to_string())),
2709 Item::Enum(def, _) => fields.push((def.name.clone(), "type".to_string())),
2710 Item::TypeAlias(def, _) => fields.push((def.name.clone(), "type".to_string())),
2711 Item::Module(def, _) => fields.push((def.name.clone(), "module".to_string())),
2712 _ => {}
2713 }
2714 }
2715 fields
2716 }
2717
2718 fn process_comptime_directives_for_module(
2719 &mut self,
2720 directives: Vec<super::comptime_builtins::ComptimeDirective>,
2721 module_name: &str,
2722 module_items: &mut Vec<Item>,
2723 ) -> std::result::Result<bool, String> {
2724 let mut removed = false;
2725 for directive in directives {
2726 match directive {
2727 super::comptime_builtins::ComptimeDirective::Extend(extend) => {
2728 self.apply_comptime_extend(extend, module_name)
2729 .map_err(|e| e.to_string())?;
2730 }
2731 super::comptime_builtins::ComptimeDirective::RemoveTarget => {
2732 removed = true;
2733 break;
2734 }
2735 super::comptime_builtins::ComptimeDirective::ReplaceModule { items } => {
2736 *module_items = items;
2737 }
2738 super::comptime_builtins::ComptimeDirective::SetParamType { .. }
2739 | super::comptime_builtins::ComptimeDirective::SetParamValue { .. } => {
2740 return Err(
2741 "`set param` directives are only valid when compiling function targets"
2742 .to_string(),
2743 );
2744 }
2745 super::comptime_builtins::ComptimeDirective::SetReturnType { .. } => {
2746 return Err(
2747 "`set return` directives are only valid when compiling function targets"
2748 .to_string(),
2749 );
2750 }
2751 super::comptime_builtins::ComptimeDirective::ReplaceBody { .. } => {
2752 return Err(
2753 "`replace body` directives are only valid when compiling function targets"
2754 .to_string(),
2755 );
2756 }
2757 }
2758 }
2759 Ok(removed)
2760 }
2761
2762 fn execute_module_comptime_handlers(
2763 &mut self,
2764 module_def: &ModuleDecl,
2765 module_path: &str,
2766 module_items: &mut Vec<Item>,
2767 ) -> Result<bool> {
2768 let mut removed = false;
2769 for ann in &module_def.annotations {
2770 let compiled = self.program.compiled_annotations.get(&ann.name).cloned();
2771 if let Some(compiled) = compiled {
2772 let handlers = [
2773 compiled.comptime_pre_handler,
2774 compiled.comptime_post_handler,
2775 ];
2776 for handler in handlers.into_iter().flatten() {
2777 let target = super::comptime_target::ComptimeTarget::from_module(
2778 module_path,
2779 &Self::module_target_fields(module_items),
2780 );
2781 let target_value = target.to_nanboxed();
2782 let handler_span = handler.span;
2783 let execution =
2784 self.execute_comptime_annotation_handler(ann, &handler, target_value, &compiled.param_names, &[])?;
2785 if self
2786 .process_comptime_directives_for_module(
2787 execution.directives,
2788 module_path,
2789 module_items,
2790 )
2791 .map_err(|e| ShapeError::RuntimeError {
2792 message: format!(
2793 "Comptime handler '{}' directive processing failed: {}",
2794 ann.name, e
2795 ),
2796 location: Some(self.span_to_source_location(handler_span)),
2797 })?
2798 {
2799 removed = true;
2800 break;
2801 }
2802 }
2803 }
2804 if removed {
2805 break;
2806 }
2807 }
2808 Ok(removed)
2809 }
2810
2811 fn inject_module_local_comptime_helper_aliases(
2812 &self,
2813 module_path: &str,
2814 helpers: &mut Vec<FunctionDef>,
2815 ) {
2816 let module_prefix = format!("{}::", module_path);
2817 let mut seen: std::collections::HashSet<String> =
2818 helpers.iter().map(|h| h.name.clone()).collect();
2819 let mut aliases = Vec::new();
2820
2821 for helper in helpers.iter() {
2822 let Some(local_name) = helper.name.strip_prefix(&module_prefix) else {
2823 continue;
2824 };
2825 if local_name.contains("::") || !seen.insert(local_name.to_string()) {
2826 continue;
2827 }
2828 let mut alias = helper.clone();
2829 alias.name = local_name.to_string();
2830 aliases.push(alias);
2831 }
2832
2833 helpers.extend(aliases);
2834 }
2835
2836 fn execute_module_inline_comptime_blocks(
2837 &mut self,
2838 module_path: &str,
2839 module_items: &mut Vec<Item>,
2840 ) -> Result<bool> {
2841 loop {
2842 let Some(idx) = module_items
2843 .iter()
2844 .position(|item| matches!(item, Item::Comptime(_, _)))
2845 else {
2846 break;
2847 };
2848
2849 let (stmts, span) = match module_items[idx].clone() {
2850 Item::Comptime(stmts, span) => (stmts, span),
2851 _ => unreachable!("index is guarded by position() matcher"),
2852 };
2853
2854 let extensions: Vec<_> = self
2855 .extension_registry
2856 .as_ref()
2857 .map(|r| r.as_ref().clone())
2858 .unwrap_or_default();
2859 let trait_impls = self.type_inference.env.trait_impl_keys();
2860 let known_type_symbols: std::collections::HashSet<String> = self
2861 .struct_types
2862 .keys()
2863 .chain(self.type_aliases.keys())
2864 .cloned()
2865 .collect();
2866 let mut comptime_helpers = self.collect_comptime_helpers();
2867 self.inject_module_local_comptime_helper_aliases(module_path, &mut comptime_helpers);
2868
2869 let execution = super::comptime::execute_comptime(
2870 &stmts,
2871 &comptime_helpers,
2872 &extensions,
2873 trait_impls,
2874 known_type_symbols,
2875 )
2876 .map_err(|e| ShapeError::RuntimeError {
2877 message: format!(
2878 "Comptime block evaluation failed: {}",
2879 super::helpers::strip_error_prefix(&e)
2880 ),
2881 location: Some(self.span_to_source_location(span)),
2882 })?;
2883
2884 if self
2885 .process_comptime_directives_for_module(
2886 execution.directives,
2887 module_path,
2888 module_items,
2889 )
2890 .map_err(|e| ShapeError::RuntimeError {
2891 message: format!("Comptime block directive processing failed: {}", e),
2892 location: Some(self.span_to_source_location(span)),
2893 })?
2894 {
2895 return Ok(true);
2896 }
2897
2898 if idx < module_items.len() && matches!(module_items[idx], Item::Comptime(_, _)) {
2899 module_items.remove(idx);
2900 }
2901 }
2902
2903 Ok(false)
2904 }
2905
2906 fn register_missing_module_functions(&mut self, item: &Item) -> Result<()> {
2907 match item {
2908 Item::Function(func, _) => {
2909 if !self.function_defs.contains_key(&func.name) {
2910 self.register_function(func)?;
2911 }
2912 Ok(())
2913 }
2914 Item::Export(export, _) => match &export.item {
2915 ExportItem::Function(func) => {
2916 if !self.function_defs.contains_key(&func.name) {
2917 self.register_function(func)?;
2918 }
2919 Ok(())
2920 }
2921 _ => Ok(()),
2922 },
2923 Item::Module(module, _) => {
2924 let module_path = self.current_module_path_for(module.name.as_str());
2925 self.module_scope_stack.push(module_path.clone());
2926 let register_result = (|| -> Result<()> {
2927 for inner in &module.items {
2928 let qualified = self.qualify_module_item(inner, &module_path)?;
2929 self.register_missing_module_functions(&qualified)?;
2930 }
2931 Ok(())
2932 })();
2933 self.module_scope_stack.pop();
2934 register_result
2935 }
2936 _ => Ok(()),
2937 }
2938 }
2939
2940 fn compile_module_decl(&mut self, module_def: &ModuleDecl, span: Span) -> Result<()> {
2941 for ann in &module_def.annotations {
2942 self.validate_annotation_target_usage(ann, AnnotationTargetKind::Module, span)?;
2943 }
2944
2945 let module_path = self.current_module_path_for(&module_def.name);
2946 self.module_scope_stack.push(module_path.clone());
2947
2948 let mut module_items = module_def.items.clone();
2949 if self.execute_module_comptime_handlers(module_def, &module_path, &mut module_items)? {
2950 self.module_scope_stack.pop();
2951 return Ok(());
2952 }
2953 if self.execute_module_inline_comptime_blocks(&module_path, &mut module_items)? {
2954 self.module_scope_stack.pop();
2955 return Ok(());
2956 }
2957
2958 let mut qualified_items = Vec::with_capacity(module_items.len());
2959 for inner in &module_items {
2960 qualified_items.push(self.qualify_module_item(inner, &module_path)?);
2961 }
2962
2963 for qualified in &qualified_items {
2964 self.register_missing_module_functions(qualified)?;
2965 }
2966
2967 for qualified in &qualified_items {
2968 self.compile_item_with_context(qualified, false)?;
2969 }
2970
2971 let exports = self.collect_module_runtime_exports(&module_items, &module_path);
2972 let entries: Vec<ObjectEntry> = exports
2973 .into_iter()
2974 .map(|(name, value_ident)| ObjectEntry::Field {
2975 key: name,
2976 value: Expr::Identifier(value_ident, span),
2977 type_annotation: None,
2978 })
2979 .collect();
2980 let module_object = Expr::Object(entries, span);
2981 self.compile_expr(&module_object)?;
2982
2983 let binding_idx = self.get_or_create_module_binding(&module_path);
2984 self.emit(Instruction::new(
2985 OpCode::StoreModuleBinding,
2986 Some(Operand::ModuleBinding(binding_idx)),
2987 ));
2988 self.propagate_initializer_type_to_slot(binding_idx, false, false);
2989
2990 if self.module_scope_stack.len() == 1 {
2991 self.module_namespace_bindings
2992 .insert(module_def.name.clone());
2993 }
2994
2995 self.emit_annotation_lifecycle_calls_for_module(
2996 &module_path,
2997 &module_def.annotations,
2998 Some(binding_idx),
2999 )?;
3000
3001 self.module_scope_stack.pop();
3002 Ok(())
3003 }
3004
3005 fn compile_query(&mut self, query: &Query) -> Result<()> {
3013 match query {
3014 Query::With(with_query) => {
3015 for cte in &with_query.ctes {
3017 self.compile_query(&cte.query)?;
3019
3020 let binding_idx = self.get_or_create_module_binding(&cte.name);
3022 self.emit(Instruction::new(
3023 OpCode::StoreModuleBinding,
3024 Some(Operand::ModuleBinding(binding_idx)),
3025 ));
3026 }
3027
3028 self.compile_query(&with_query.query)?;
3030 }
3031 Query::Backtest(_backtest) => {
3032 self.emit(Instruction::simple(OpCode::PushNull));
3036 }
3037 Query::Alert(alert) => {
3038 self.compile_expr(&alert.condition)?;
3040 self.emit(Instruction::simple(OpCode::Pop));
3042 self.emit(Instruction::simple(OpCode::PushNull));
3043 }
3044 }
3045 Ok(())
3046 }
3047
3048 pub(super) fn propagate_initializer_type_to_slot(&mut self, slot: u16, is_local: bool, _is_mutable: bool) {
3049 self.propagate_assignment_type_to_slot(slot, is_local, true);
3050 }
3051
3052 pub(super) fn compile_statement(&mut self, stmt: &Statement) -> Result<()> {
3054 match stmt {
3055 Statement::Return(expr_opt, _span) => {
3056 if let Some(expr) = expr_opt {
3059 if let Expr::Reference { span: ref_span, .. } = expr {
3060 return Err(ShapeError::SemanticError {
3061 message: "cannot return a reference — references are scoped borrows that cannot escape the function. Return an owned value instead".to_string(),
3062 location: Some(self.span_to_source_location(*ref_span)),
3063 });
3064 }
3065 self.compile_expr(expr)?;
3069 } else {
3070 self.emit(Instruction::simple(OpCode::PushNull));
3071 }
3072 let total_scopes = self.drop_locals.len();
3074 if total_scopes > 0 {
3075 self.emit_drops_for_early_exit(total_scopes)?;
3076 }
3077 self.emit(Instruction::simple(OpCode::ReturnValue));
3078 }
3079
3080 Statement::Break(_) => {
3081 let in_loop = !self.loop_stack.is_empty();
3082 if in_loop {
3083 let scopes_to_exit = self
3085 .loop_stack
3086 .last()
3087 .map(|ctx| self.drop_locals.len().saturating_sub(ctx.drop_scope_depth))
3088 .unwrap_or(0);
3089 if scopes_to_exit > 0 {
3090 self.emit_drops_for_early_exit(scopes_to_exit)?;
3091 }
3092 let jump_idx = self.emit_jump(OpCode::Jump, 0);
3093 if let Some(loop_ctx) = self.loop_stack.last_mut() {
3094 loop_ctx.break_jumps.push(jump_idx);
3095 }
3096 } else {
3097 return Err(ShapeError::RuntimeError {
3098 message: "break statement outside of loop".to_string(),
3099 location: None,
3100 });
3101 }
3102 }
3103
3104 Statement::Continue(_) => {
3105 if let Some(loop_ctx) = self.loop_stack.last() {
3106 let scopes_to_exit = self
3108 .drop_locals
3109 .len()
3110 .saturating_sub(loop_ctx.drop_scope_depth);
3111 let continue_target = loop_ctx.continue_target;
3112 if scopes_to_exit > 0 {
3114 self.emit_drops_for_early_exit(scopes_to_exit)?;
3115 }
3116 let offset = continue_target as i32 - self.program.current_offset() as i32 - 1;
3117 self.emit(Instruction::new(
3118 OpCode::Jump,
3119 Some(Operand::Offset(offset)),
3120 ));
3121 } else {
3122 return Err(ShapeError::RuntimeError {
3123 message: "continue statement outside of loop".to_string(),
3124 location: None,
3125 });
3126 }
3127 }
3128
3129 Statement::VariableDecl(var_decl, _) => {
3130 self.pending_variable_name =
3133 var_decl.pattern.as_identifier().map(|s| s.to_string());
3134
3135 if let (Some(type_ann), Some(init_expr)) =
3139 (&var_decl.type_annotation, &var_decl.value)
3140 {
3141 if let shape_ast::ast::TypeAnnotation::Basic(type_name) = type_ann {
3142 if let Some(w) = shape_ast::IntWidth::from_name(type_name) {
3143 if let Some(const_val) =
3144 crate::compiler::expressions::function_calls::eval_const_expr_to_nanboxed(init_expr)
3145 {
3146 let in_range = if let Some(i) = const_val.as_i64() {
3147 w.in_range_i64(i)
3148 } else if let Some(f) = const_val.as_f64() {
3149 let i = f as i64;
3151 (i as f64 == f) && w.in_range_i64(i)
3152 } else {
3153 true };
3155 if !in_range {
3156 return Err(shape_ast::error::ShapeError::SemanticError {
3157 message: format!(
3158 "value does not fit in `{}` (range {}..={})",
3159 type_name,
3160 w.min_value(),
3161 w.max_value()
3162 ),
3163 location: Some(self.span_to_source_location(shape_ast::ast::Spanned::span(init_expr))),
3164 });
3165 }
3166 }
3167 }
3168 }
3169 }
3170
3171 let init_err = if let Some(init_expr) = &var_decl.value {
3174 if let Expr::TableRows(rows, tr_span) = init_expr {
3177 match self.compile_table_rows(rows, &var_decl.type_annotation, *tr_span) {
3178 Ok(()) => None,
3179 Err(e) => {
3180 self.emit(Instruction::simple(OpCode::PushNull));
3181 Some(e)
3182 }
3183 }
3184 } else {
3185 match self.compile_expr(init_expr) {
3186 Ok(()) => None,
3187 Err(e) => {
3188 self.emit(Instruction::simple(OpCode::PushNull));
3189 Some(e)
3190 }
3191 }
3192 }
3193 } else {
3194 self.emit(Instruction::simple(OpCode::PushNull));
3195 None
3196 };
3197
3198 self.pending_variable_name = None;
3200
3201 if let Some(ref type_ann) = var_decl.type_annotation {
3203 if let Some(schema_id) = self.get_table_schema_id(type_ann) {
3204 self.emit(Instruction::new(
3205 OpCode::BindSchema,
3206 Some(Operand::Count(schema_id)),
3207 ));
3208 }
3209 }
3210
3211 if self.current_function.is_none() {
3213 if let Some(name) = var_decl.pattern.as_identifier() {
3215 let binding_idx = self.get_or_create_module_binding(name);
3216 self.emit(Instruction::new(
3217 OpCode::StoreModuleBinding,
3218 Some(Operand::ModuleBinding(binding_idx)),
3219 ));
3220
3221 if var_decl.kind == VarKind::Const {
3223 self.const_module_bindings.insert(binding_idx);
3224 }
3225
3226 if var_decl.kind == VarKind::Let && !var_decl.is_mut {
3228 self.immutable_module_bindings.insert(binding_idx);
3229 }
3230
3231 if let Some(ref type_ann) = var_decl.type_annotation {
3233 if let Some(type_name) =
3234 Self::tracked_type_name_from_annotation(type_ann)
3235 {
3236 self.set_module_binding_type_info(binding_idx, &type_name);
3237 }
3238 self.try_track_datatable_type(type_ann, binding_idx, false)?;
3240 } else {
3241 let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
3242 self.propagate_initializer_type_to_slot(binding_idx, false, is_mutable);
3243 }
3244
3245 let binding_type_name = self
3247 .type_tracker
3248 .get_binding_type(binding_idx)
3249 .and_then(|info| info.type_name.clone());
3250 let drop_kind = binding_type_name
3251 .as_ref()
3252 .and_then(|tn| self.drop_type_info.get(tn).copied())
3253 .or_else(|| {
3254 var_decl
3255 .type_annotation
3256 .as_ref()
3257 .and_then(|ann| self.annotation_drop_kind(ann))
3258 });
3259 if drop_kind.is_some() {
3260 let is_async = match drop_kind {
3261 Some(DropKind::AsyncOnly) => true,
3262 Some(DropKind::Both) => false,
3263 Some(DropKind::SyncOnly) | None => false,
3264 };
3265 self.track_drop_module_binding(binding_idx, is_async);
3266 }
3267 } else {
3268 self.compile_destructure_pattern_global(&var_decl.pattern)?;
3269 }
3270 } else {
3271 self.compile_destructure_pattern(&var_decl.pattern)?;
3273
3274 if let (Some(name), Some(TypeAnnotation::Basic(type_name))) = (
3278 var_decl.pattern.as_identifier(),
3279 var_decl.type_annotation.as_ref(),
3280 ) {
3281 if let Some(w) = shape_ast::IntWidth::from_name(type_name) {
3282 if let Some(local_idx) = self.resolve_local(name) {
3283 if let Some(last) = self.program.instructions.last_mut() {
3284 if last.opcode == OpCode::StoreLocal {
3285 last.opcode = OpCode::StoreLocalTyped;
3286 last.operand = Some(Operand::TypedLocal(
3287 local_idx,
3288 crate::bytecode::NumericWidth::from_int_width(w),
3289 ));
3290 }
3291 }
3292 }
3293 }
3294 }
3295
3296 if var_decl.kind == VarKind::Const {
3298 if let Some(name) = var_decl.pattern.as_identifier() {
3299 if let Some(local_idx) = self.resolve_local(name) {
3300 self.const_locals.insert(local_idx);
3301 }
3302 }
3303 }
3304
3305 if var_decl.kind == VarKind::Let && !var_decl.is_mut {
3310 if let Some(name) = var_decl.pattern.as_identifier() {
3311 if let Some(local_idx) = self.resolve_local(name) {
3312 self.immutable_locals.insert(local_idx);
3313 }
3314 }
3315 }
3316
3317 if let Some(name) = var_decl.pattern.as_identifier() {
3319 if let Some(ref type_ann) = var_decl.type_annotation {
3320 if let Some(type_name) =
3321 Self::tracked_type_name_from_annotation(type_ann)
3322 {
3323 if let Some(local_idx) = self.resolve_local(name) {
3325 self.set_local_type_info(local_idx, &type_name);
3326 }
3327 }
3328 if let Some(local_idx) = self.resolve_local(name) {
3330 self.try_track_datatable_type(type_ann, local_idx, true)?;
3331 }
3332 } else if let Some(local_idx) = self.resolve_local(name) {
3333 let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
3334 self.propagate_initializer_type_to_slot(local_idx, true, is_mutable);
3335 }
3336 }
3337
3338 if let Some(name) = var_decl.pattern.as_identifier() {
3341 if let Some(local_idx) = self.resolve_local(name) {
3342 let drop_kind = self.local_drop_kind(local_idx).or_else(|| {
3343 var_decl
3344 .type_annotation
3345 .as_ref()
3346 .and_then(|ann| self.annotation_drop_kind(ann))
3347 });
3348
3349 let is_async = match drop_kind {
3350 Some(DropKind::AsyncOnly) => {
3351 if !self.current_function_is_async {
3352 let tn = self
3353 .type_tracker
3354 .get_local_type(local_idx)
3355 .and_then(|info| info.type_name.clone())
3356 .unwrap_or_else(|| name.to_string());
3357 return Err(ShapeError::SemanticError {
3358 message: format!(
3359 "type '{}' has only an async drop() and cannot be used in a sync context; \
3360 add a sync method drop(self) or use it inside an async function",
3361 tn
3362 ),
3363 location: None,
3364 });
3365 }
3366 true
3367 }
3368 Some(DropKind::Both) => self.current_function_is_async,
3369 Some(DropKind::SyncOnly) | None => false,
3370 };
3371 self.track_drop_local(local_idx, is_async);
3372 }
3373 }
3374 }
3375
3376 if let Some(e) = init_err {
3377 return Err(e);
3378 }
3379 }
3380
3381 Statement::Assignment(assign, _) => 'assign: {
3382 if let Some(name) = assign.pattern.as_identifier() {
3384 if let Some(local_idx) = self.resolve_local(name) {
3385 if self.const_locals.contains(&local_idx) {
3386 return Err(ShapeError::SemanticError {
3387 message: format!("Cannot reassign const variable '{}'", name),
3388 location: None,
3389 });
3390 }
3391 if self.immutable_locals.contains(&local_idx) {
3393 return Err(ShapeError::SemanticError {
3394 message: format!(
3395 "Cannot reassign immutable variable '{}'. Use `let mut` or `var` for mutable bindings",
3396 name
3397 ),
3398 location: None,
3399 });
3400 }
3401 } else {
3402 let scoped_name = self
3403 .resolve_scoped_module_binding_name(name)
3404 .unwrap_or_else(|| name.to_string());
3405 if let Some(&binding_idx) = self.module_bindings.get(&scoped_name) {
3406 if self.const_module_bindings.contains(&binding_idx) {
3407 return Err(ShapeError::SemanticError {
3408 message: format!("Cannot reassign const variable '{}'", name),
3409 location: None,
3410 });
3411 }
3412 if self.immutable_module_bindings.contains(&binding_idx) {
3414 return Err(ShapeError::SemanticError {
3415 message: format!(
3416 "Cannot reassign immutable variable '{}'. Use `let mut` or `var` for mutable bindings",
3417 name
3418 ),
3419 location: None,
3420 });
3421 }
3422 }
3423 }
3424 }
3425
3426 if let Some(name) = assign.pattern.as_identifier() {
3428 if let Expr::MethodCall {
3429 receiver,
3430 method,
3431 args,
3432 ..
3433 } = &assign.value
3434 {
3435 if method == "push" && args.len() == 1 {
3436 if let Expr::Identifier(recv_name, _) = receiver.as_ref() {
3437 if recv_name == name {
3438 if let Some(local_idx) = self.resolve_local(name) {
3439 if !self.ref_locals.contains(&local_idx) {
3440 self.compile_expr(&args[0])?;
3441 let pushed_numeric = self.last_expr_numeric_type;
3442 self.emit(Instruction::new(
3443 OpCode::ArrayPushLocal,
3444 Some(Operand::Local(local_idx)),
3445 ));
3446 if let Some(numeric_type) = pushed_numeric {
3447 self.mark_slot_as_numeric_array(
3448 local_idx,
3449 true,
3450 numeric_type,
3451 );
3452 }
3453 break 'assign;
3454 }
3455 } else {
3456 let binding_idx = self.get_or_create_module_binding(name);
3457 self.compile_expr(&args[0])?;
3458 let pushed_numeric = self.last_expr_numeric_type;
3459 self.emit(Instruction::new(
3460 OpCode::ArrayPushLocal,
3461 Some(Operand::ModuleBinding(binding_idx)),
3462 ));
3463 if let Some(numeric_type) = pushed_numeric {
3464 self.mark_slot_as_numeric_array(
3465 binding_idx,
3466 false,
3467 numeric_type,
3468 );
3469 }
3470 break 'assign;
3471 }
3472 }
3473 }
3474 }
3475 }
3476 }
3477
3478 self.compile_expr(&assign.value)?;
3480 let assigned_ident = assign.pattern.as_identifier().map(str::to_string);
3481
3482 self.compile_destructure_assignment(&assign.pattern)?;
3484 if let Some(name) = assigned_ident.as_deref() {
3485 self.propagate_assignment_type_to_identifier(name);
3486 }
3487 }
3488
3489 Statement::Expression(expr, _) => {
3490 if let Expr::MethodCall {
3493 receiver,
3494 method,
3495 args,
3496 ..
3497 } = expr
3498 {
3499 if method == "push" && args.len() == 1 {
3500 if let Expr::Identifier(recv_name, _) = receiver.as_ref() {
3501 if let Some(local_idx) = self.resolve_local(recv_name) {
3502 if !self.ref_locals.contains(&local_idx) {
3503 self.compile_expr(&args[0])?;
3504 let pushed_numeric = self.last_expr_numeric_type;
3505 self.emit(Instruction::new(
3506 OpCode::ArrayPushLocal,
3507 Some(Operand::Local(local_idx)),
3508 ));
3509 if let Some(numeric_type) = pushed_numeric {
3510 self.mark_slot_as_numeric_array(
3511 local_idx,
3512 true,
3513 numeric_type,
3514 );
3515 }
3516 return Ok(());
3517 }
3518 } else if !self
3519 .mutable_closure_captures
3520 .contains_key(recv_name.as_str())
3521 {
3522 let binding_idx = self.get_or_create_module_binding(recv_name);
3523 self.compile_expr(&args[0])?;
3524 self.emit(Instruction::new(
3525 OpCode::ArrayPushLocal,
3526 Some(Operand::ModuleBinding(binding_idx)),
3527 ));
3528 return Ok(());
3529 }
3530 }
3531 }
3532 }
3533 self.compile_expr(expr)?;
3534 self.emit(Instruction::simple(OpCode::Pop));
3535 }
3536
3537 Statement::For(for_loop, _) => {
3538 self.compile_for_loop(for_loop)?;
3539 }
3540
3541 Statement::While(while_loop, _) => {
3542 self.compile_while_loop(while_loop)?;
3543 }
3544
3545 Statement::If(if_stmt, _) => {
3546 self.compile_if_statement(if_stmt)?;
3547 }
3548 Statement::Extend(extend, span) => {
3549 if !self.comptime_mode {
3550 return Err(ShapeError::SemanticError {
3551 message:
3552 "`extend` as a statement is only valid inside `comptime { }` context"
3553 .to_string(),
3554 location: Some(self.span_to_source_location(*span)),
3555 });
3556 }
3557 self.emit_comptime_extend_directive(extend, *span)?;
3558 }
3559 Statement::RemoveTarget(span) => {
3560 if !self.comptime_mode {
3561 return Err(ShapeError::SemanticError {
3562 message: "`remove target` is only valid inside `comptime { }` context"
3563 .to_string(),
3564 location: Some(self.span_to_source_location(*span)),
3565 });
3566 }
3567 self.emit_comptime_remove_directive(*span)?;
3568 }
3569 Statement::SetParamType {
3570 param_name,
3571 type_annotation,
3572 span,
3573 } => {
3574 if !self.comptime_mode {
3575 return Err(ShapeError::SemanticError {
3576 message: "`set param` is only valid inside `comptime { }` context"
3577 .to_string(),
3578 location: Some(self.span_to_source_location(*span)),
3579 });
3580 }
3581 self.emit_comptime_set_param_type_directive(param_name, type_annotation, *span)?;
3582 }
3583 Statement::SetParamValue {
3584 param_name,
3585 expression,
3586 span,
3587 } => {
3588 if !self.comptime_mode {
3589 return Err(ShapeError::SemanticError {
3590 message: "`set param` is only valid inside `comptime { }` context"
3591 .to_string(),
3592 location: Some(self.span_to_source_location(*span)),
3593 });
3594 }
3595 self.emit_comptime_set_param_value_directive(param_name, expression, *span)?;
3596 }
3597 Statement::SetReturnType {
3598 type_annotation,
3599 span,
3600 } => {
3601 if !self.comptime_mode {
3602 return Err(ShapeError::SemanticError {
3603 message: "`set return` is only valid inside `comptime { }` context"
3604 .to_string(),
3605 location: Some(self.span_to_source_location(*span)),
3606 });
3607 }
3608 self.emit_comptime_set_return_type_directive(type_annotation, *span)?;
3609 }
3610 Statement::SetReturnExpr { expression, span } => {
3611 if !self.comptime_mode {
3612 return Err(ShapeError::SemanticError {
3613 message: "`set return` is only valid inside `comptime { }` context"
3614 .to_string(),
3615 location: Some(self.span_to_source_location(*span)),
3616 });
3617 }
3618 self.emit_comptime_set_return_expr_directive(expression, *span)?;
3619 }
3620 Statement::ReplaceBody { body, span } => {
3621 if !self.comptime_mode {
3622 return Err(ShapeError::SemanticError {
3623 message: "`replace body` is only valid inside `comptime { }` context"
3624 .to_string(),
3625 location: Some(self.span_to_source_location(*span)),
3626 });
3627 }
3628 self.emit_comptime_replace_body_directive(body, *span)?;
3629 }
3630 Statement::ReplaceBodyExpr { expression, span } => {
3631 if !self.comptime_mode {
3632 return Err(ShapeError::SemanticError {
3633 message: "`replace body` is only valid inside `comptime { }` context"
3634 .to_string(),
3635 location: Some(self.span_to_source_location(*span)),
3636 });
3637 }
3638 self.emit_comptime_replace_body_expr_directive(expression, *span)?;
3639 }
3640 Statement::ReplaceModuleExpr { expression, span } => {
3641 if !self.comptime_mode {
3642 return Err(ShapeError::SemanticError {
3643 message: "`replace module` is only valid inside `comptime { }` context"
3644 .to_string(),
3645 location: Some(self.span_to_source_location(*span)),
3646 });
3647 }
3648 self.emit_comptime_replace_module_expr_directive(expression, *span)?;
3649 }
3650 }
3651 Ok(())
3652 }
3653}
3654
3655#[cfg(test)]
3656mod tests {
3657 use crate::compiler::BytecodeCompiler;
3658 use crate::executor::{VMConfig, VirtualMachine};
3659 use shape_ast::parser::parse_program;
3660
3661 #[test]
3662 fn test_module_decl_function_resolves_module_const() {
3663 let code = r#"
3664 mod math {
3665 const BASE = 21
3666 fn twice() {
3667 BASE * 2
3668 }
3669 }
3670 math.twice()
3671 "#;
3672
3673 let program = parse_program(code).expect("Failed to parse");
3674 let bytecode = BytecodeCompiler::new()
3675 .compile(&program)
3676 .expect("Failed to compile");
3677
3678 let mut vm = VirtualMachine::new(VMConfig::default());
3679 vm.load_program(bytecode);
3680 vm.populate_module_objects();
3681 let result = vm.execute(None).expect("Failed to execute");
3682 assert_eq!(
3683 result
3684 .as_number_coerce()
3685 .expect("module call should return number"),
3686 42.0
3687 );
3688 }
3689
3690 #[test]
3691 fn test_module_annotation_can_replace_module_items() {
3692 let code = r#"
3693 annotation synth_module() {
3694 targets: [module]
3695 comptime post(target, ctx) {
3696 replace module ("const ANSWER = 40; fn plus_two() { ANSWER + 2 }")
3697 }
3698 }
3699
3700 @synth_module()
3701 mod demo {}
3702
3703 demo.plus_two()
3704 "#;
3705
3706 let program = parse_program(code).expect("Failed to parse");
3707 let bytecode = BytecodeCompiler::new()
3708 .compile(&program)
3709 .expect("Failed to compile");
3710
3711 let mut vm = VirtualMachine::new(VMConfig::default());
3712 vm.load_program(bytecode);
3713 vm.populate_module_objects();
3714 let result = vm.execute(None).expect("Failed to execute");
3715 assert_eq!(
3716 result
3717 .as_number_coerce()
3718 .expect("module call should return number"),
3719 42.0
3720 );
3721 }
3722
3723 #[test]
3724 fn test_module_inline_comptime_can_replace_module_items() {
3725 let code = r#"
3726 mod demo {
3727 comptime {
3728 replace module ("const ANSWER = 40; fn plus_two() { ANSWER + 2 }")
3729 }
3730 }
3731
3732 demo.plus_two()
3733 "#;
3734
3735 let program = parse_program(code).expect("Failed to parse");
3736 let bytecode = BytecodeCompiler::new()
3737 .compile(&program)
3738 .expect("Failed to compile");
3739
3740 let mut vm = VirtualMachine::new(VMConfig::default());
3741 vm.load_program(bytecode);
3742 vm.populate_module_objects();
3743 let result = vm.execute(None).expect("Failed to execute");
3744 assert_eq!(
3745 result
3746 .as_number_coerce()
3747 .expect("module call should return number"),
3748 42.0
3749 );
3750 }
3751
3752 #[test]
3753 fn test_module_inline_comptime_can_use_module_local_comptime_helper() {
3754 let code = r#"
3755 mod demo {
3756 comptime fn synth() {
3757 "const ANSWER = 40; fn plus_two() { ANSWER + 2 }"
3758 }
3759
3760 comptime {
3761 replace module (synth())
3762 }
3763 }
3764
3765 demo.plus_two()
3766 "#;
3767
3768 let program = parse_program(code).expect("Failed to parse");
3769 let bytecode = BytecodeCompiler::new()
3770 .compile(&program)
3771 .expect("Failed to compile");
3772
3773 let mut vm = VirtualMachine::new(VMConfig::default());
3774 vm.load_program(bytecode);
3775 vm.populate_module_objects();
3776 let result = vm.execute(None).expect("Failed to execute");
3777 assert_eq!(
3778 result
3779 .as_number_coerce()
3780 .expect("module call should return number"),
3781 42.0
3782 );
3783 }
3784
3785 #[test]
3786 fn test_type_annotated_variable_no_wrapping() {
3787 let code = r#"
3790 type Currency = Number
3791 let x: Currency = 123
3792 "#;
3793 let program = parse_program(code).expect("Failed to parse");
3794 let bytecode = BytecodeCompiler::new()
3795 .compile(&program)
3796 .expect("Failed to compile");
3797
3798 let has_wrap_instruction = bytecode
3800 .instructions
3801 .iter()
3802 .any(|instr| instr.opcode == crate::bytecode::OpCode::WrapTypeAnnotation);
3803 assert!(
3804 !has_wrap_instruction,
3805 "Should NOT emit WrapTypeAnnotation for type-annotated variable"
3806 );
3807 }
3808
3809 #[test]
3810 fn test_untyped_variable_no_wrapping() {
3811 let code = r#"
3813 let x = 123
3814 "#;
3815 let program = parse_program(code).expect("Failed to parse");
3816 let bytecode = BytecodeCompiler::new()
3817 .compile(&program)
3818 .expect("Failed to compile");
3819
3820 let has_wrap_instruction = bytecode
3822 .instructions
3823 .iter()
3824 .any(|instr| instr.opcode == crate::bytecode::OpCode::WrapTypeAnnotation);
3825 assert!(
3826 !has_wrap_instruction,
3827 "Should NOT emit WrapTypeAnnotation for untyped variable"
3828 );
3829 }
3830
3831 #[test]
3834 fn test_extend_block_compiles() {
3835 let code = r#"
3836 extend Number {
3837 method double() {
3838 return self * 2
3839 }
3840 }
3841 "#;
3842 let program = parse_program(code).expect("Failed to parse extend block");
3843 let bytecode = BytecodeCompiler::new().compile(&program);
3844 assert!(
3845 bytecode.is_ok(),
3846 "Extend block should compile: {:?}",
3847 bytecode.err()
3848 );
3849
3850 let bytecode = bytecode.unwrap();
3852 let has_double = bytecode.functions.iter().any(|f| f.name == "Number.double");
3853 assert!(
3854 has_double,
3855 "Should generate 'Number.double' function from extend block"
3856 );
3857 }
3858
3859 #[test]
3860 fn test_extend_method_has_self_param() {
3861 let code = r#"
3862 extend Number {
3863 method add(n) {
3864 return self + n
3865 }
3866 }
3867 "#;
3868 let program = parse_program(code).expect("Failed to parse");
3869 let bytecode = BytecodeCompiler::new()
3870 .compile(&program)
3871 .expect("Failed to compile");
3872
3873 let func = bytecode.functions.iter().find(|f| f.name == "Number.add");
3874 assert!(func.is_some(), "Should have 'Number.add' function");
3875 assert_eq!(
3877 func.unwrap().arity,
3878 2,
3879 "add() should have arity 2 (self + n)"
3880 );
3881 }
3882
3883 #[test]
3884 fn test_extend_method_rejects_explicit_self_param() {
3885 let code = r#"
3886 extend Number {
3887 method add(self, n) {
3888 return self + n
3889 }
3890 }
3891 "#;
3892 let program = parse_program(code).expect("Failed to parse");
3893 let err = BytecodeCompiler::new()
3894 .compile(&program)
3895 .expect_err("Compiler should reject explicit self receiver param in methods");
3896 let msg = format!("{err}");
3897 assert!(
3898 msg.contains("explicit `self` parameter"),
3899 "Expected explicit self error, got: {msg}"
3900 );
3901 }
3902
3903 #[test]
3906 fn test_annotation_def_compiles_handlers() {
3907 let code = r#"
3908 annotation warmup(period) {
3909 before(args, ctx) {
3910 args
3911 }
3912 after(args, result, ctx) {
3913 result
3914 }
3915 }
3916 function test() { return 42; }
3917 "#;
3918 let program = parse_program(code).expect("Failed to parse annotation def");
3919 let bytecode = BytecodeCompiler::new().compile(&program);
3920 assert!(
3921 bytecode.is_ok(),
3922 "Annotation def should compile: {:?}",
3923 bytecode.err()
3924 );
3925
3926 let bytecode = bytecode.unwrap();
3927 assert!(
3929 bytecode.compiled_annotations.contains_key("warmup"),
3930 "Should have compiled 'warmup' annotation"
3931 );
3932
3933 let compiled = bytecode.compiled_annotations.get("warmup").unwrap();
3934 assert!(
3935 compiled.before_handler.is_some(),
3936 "Should have before handler"
3937 );
3938 assert!(
3939 compiled.after_handler.is_some(),
3940 "Should have after handler"
3941 );
3942 }
3943
3944 #[test]
3945 fn test_annotation_handler_function_names() {
3946 let code = r#"
3947 annotation my_ann(x) {
3948 before(args, ctx) {
3949 args
3950 }
3951 }
3952 function test() { return 1; }
3953 "#;
3954 let program = parse_program(code).expect("Failed to parse");
3955 let bytecode = BytecodeCompiler::new()
3956 .compile(&program)
3957 .expect("Failed to compile");
3958
3959 let compiled = bytecode.compiled_annotations.get("my_ann").unwrap();
3961 let handler_id = compiled.before_handler.unwrap() as usize;
3962 assert!(
3963 handler_id < bytecode.functions.len(),
3964 "Handler function ID should be valid"
3965 );
3966
3967 let handler_fn = &bytecode.functions[handler_id];
3968 assert_eq!(
3969 handler_fn.name, "my_ann___before",
3970 "Handler function should be named my_ann___before"
3971 );
3972 }
3973
3974 #[test]
3977 fn test_annotated_function_generates_wrapper() {
3978 let code = r#"
3979 annotation tracked(label) {
3980 before(args, ctx) {
3981 args
3982 }
3983 }
3984 @tracked("my_func")
3985 function compute(x) {
3986 return x * 2
3987 }
3988 function test() { return 1; }
3989 "#;
3990 let program = parse_program(code).expect("Failed to parse");
3991 let bytecode = BytecodeCompiler::new().compile(&program);
3992 assert!(
3993 bytecode.is_ok(),
3994 "Annotated function should compile: {:?}",
3995 bytecode.err()
3996 );
3997
3998 let bytecode = bytecode.unwrap();
3999 let has_impl = bytecode
4001 .functions
4002 .iter()
4003 .any(|f| f.name == "compute___impl");
4004 assert!(has_impl, "Should generate compute___impl function");
4005
4006 let has_wrapper = bytecode.functions.iter().any(|f| f.name == "compute");
4007 assert!(has_wrapper, "Should keep compute as wrapper");
4008 }
4009
4010 #[test]
4011 fn test_unannotated_function_no_wrapper() {
4012 let code = r#"
4013 function plain(x) {
4014 return x + 1
4015 }
4016 "#;
4017 let program = parse_program(code).expect("Failed to parse");
4018 let bytecode = BytecodeCompiler::new()
4019 .compile(&program)
4020 .expect("Failed to compile");
4021
4022 let has_impl = bytecode
4024 .functions
4025 .iter()
4026 .any(|f| f.name.ends_with("___impl"));
4027 assert!(
4028 !has_impl,
4029 "Non-annotated function should not generate ___impl"
4030 );
4031 }
4032
4033 #[test]
4036 fn test_annotation_chaining_generates_chain() {
4037 let code = r#"
4039 annotation first() {
4040 before(args, ctx) {
4041 return args
4042 }
4043 }
4044
4045 annotation second() {
4046 before(args, ctx) {
4047 return args
4048 }
4049 }
4050
4051 @first
4052 @second
4053 function compute(x) {
4054 return x * 2
4055 }
4056 "#;
4057 let program = parse_program(code).expect("Failed to parse");
4058 let bytecode = BytecodeCompiler::new().compile(&program);
4059 assert!(
4060 bytecode.is_ok(),
4061 "Chained annotations should compile: {:?}",
4062 bytecode.err()
4063 );
4064 let bytecode = bytecode.unwrap();
4065
4066 let has_impl = bytecode
4068 .functions
4069 .iter()
4070 .any(|f| f.name == "compute___impl");
4071 assert!(has_impl, "Should generate compute___impl function");
4072 let has_wrapper = bytecode.functions.iter().any(|f| f.name == "compute");
4073 assert!(has_wrapper, "Should keep compute as outermost wrapper");
4074 let has_intermediate = bytecode
4075 .functions
4076 .iter()
4077 .any(|f| f.name == "compute___second");
4078 assert!(
4079 has_intermediate,
4080 "Should generate compute___second intermediate wrapper"
4081 );
4082 }
4083
4084 #[test]
4085 fn test_annotation_allowed_targets_inferred() {
4086 let code = r#"
4088 annotation traced() {
4089 before(args, ctx) {
4090 return args
4091 }
4092 }
4093 "#;
4094 let program = parse_program(code).expect("Failed to parse");
4095 let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
4096 let ann = bytecode
4097 .compiled_annotations
4098 .get("traced")
4099 .expect("traced annotation");
4100 assert!(
4101 !ann.allowed_targets.is_empty(),
4102 "before handler should restrict targets"
4103 );
4104 assert!(
4105 ann.allowed_targets
4106 .contains(&shape_ast::ast::functions::AnnotationTargetKind::Function),
4107 "before handler should allow Function target"
4108 );
4109 }
4110
4111 #[test]
4112 fn test_annotation_allowed_targets_explicit_override() {
4113 let code = r#"
4115 annotation traced() {
4116 targets: [type]
4117 before(args, ctx) {
4118 return args
4119 }
4120 }
4121 "#;
4122 let program = parse_program(code).expect("Failed to parse");
4123 let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
4124 let ann = bytecode
4125 .compiled_annotations
4126 .get("traced")
4127 .expect("traced annotation");
4128 assert_eq!(
4129 ann.allowed_targets,
4130 vec![shape_ast::ast::functions::AnnotationTargetKind::Type]
4131 );
4132 }
4133
4134 #[test]
4135 fn test_metadata_only_annotation_defaults_to_definition_targets() {
4136 let code = r#"
4138 annotation info() {
4139 metadata() {
4140 return { version: 1 }
4141 }
4142 }
4143 "#;
4144 let program = parse_program(code).expect("Failed to parse");
4145 let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
4146 let ann = bytecode
4147 .compiled_annotations
4148 .get("info")
4149 .expect("info annotation");
4150 assert_eq!(
4151 ann.allowed_targets,
4152 vec![
4153 shape_ast::ast::functions::AnnotationTargetKind::Function,
4154 shape_ast::ast::functions::AnnotationTargetKind::Type,
4155 shape_ast::ast::functions::AnnotationTargetKind::Module
4156 ],
4157 "metadata-only annotation should default to definition targets"
4158 );
4159 }
4160
4161 #[test]
4162 fn test_definition_lifecycle_targets_reject_expression_target() {
4163 let code = r#"
4164 annotation info() {
4165 targets: [expression]
4166 metadata(target, ctx) {
4167 target.name
4168 }
4169 }
4170 "#;
4171 let program = parse_program(code).expect("Failed to parse");
4172 let err = BytecodeCompiler::new()
4173 .compile(&program)
4174 .expect_err("metadata hooks on expression targets should fail");
4175 let msg = format!("{}", err);
4176 assert!(
4177 msg.contains("not a definition target"),
4178 "expected definition-target restriction error, got: {}",
4179 msg
4180 );
4181 }
4182
4183 #[test]
4184 fn test_annotation_target_validation_on_struct_type() {
4185 let code = r#"
4187 annotation traced() {
4188 before(args, ctx) { return args }
4189 }
4190
4191 @traced()
4192 type Point { x: int }
4193 "#;
4194 let program = parse_program(code).expect("Failed to parse");
4195 let err = BytecodeCompiler::new()
4196 .compile(&program)
4197 .expect_err("function-only annotation on type should fail");
4198 let msg = format!("{}", err);
4199 assert!(
4200 msg.contains("cannot be applied to a type"),
4201 "expected type target validation error, got: {}",
4202 msg
4203 );
4204 }
4205
4206 #[test]
4207 fn test_type_c_emits_native_layout_metadata() {
4208 let bytecode = compiles_to(
4209 r#"
4210 type C Pair32 {
4211 left: i32,
4212 right: i32,
4213 }
4214 "#,
4215 );
4216
4217 assert_eq!(bytecode.native_struct_layouts.len(), 1);
4218 let layout = &bytecode.native_struct_layouts[0];
4219 assert_eq!(layout.name, "Pair32");
4220 assert_eq!(layout.abi, "C");
4221 assert_eq!(layout.size, 8);
4222 assert_eq!(layout.align, 4);
4223 assert_eq!(layout.fields.len(), 2);
4224 assert_eq!(layout.fields[0].name, "left");
4225 assert_eq!(layout.fields[0].offset, 0);
4226 assert_eq!(layout.fields[0].size, 4);
4227 assert_eq!(layout.fields[1].name, "right");
4228 assert_eq!(layout.fields[1].offset, 4);
4229 assert_eq!(layout.fields[1].size, 4);
4230 }
4231
4232 #[test]
4233 fn test_type_c_auto_generates_into_from_traits() {
4234 let bytecode = compiles_to(
4235 r#"
4236 type C QuoteC {
4237 bid: i64,
4238 ask: i64,
4239 }
4240
4241 type Quote {
4242 bid: i64,
4243 ask: i64,
4244 }
4245 "#,
4246 );
4247
4248 let c_to_shape =
4249 bytecode.lookup_trait_method_symbol("Into", "QuoteC", Some("Quote"), "into");
4250 let shape_to_c =
4251 bytecode.lookup_trait_method_symbol("Into", "Quote", Some("QuoteC"), "into");
4252 let from_c = bytecode.lookup_trait_method_symbol("From", "Quote", Some("QuoteC"), "from");
4253 let from_shape =
4254 bytecode.lookup_trait_method_symbol("From", "QuoteC", Some("Quote"), "from");
4255
4256 assert!(c_to_shape.is_some(), "expected Into<Quote> for QuoteC");
4257 assert!(shape_to_c.is_some(), "expected Into<QuoteC> for Quote");
4258 assert!(from_c.is_some(), "expected From<QuoteC> for Quote");
4259 assert!(from_shape.is_some(), "expected From<Quote> for QuoteC");
4260 }
4261
4262 #[test]
4263 fn test_type_c_auto_conversion_function_compiles() {
4264 let _ = compiles_to(
4265 r#"
4266 type Quote {
4267 bid: i64,
4268 ask: i64,
4269 }
4270
4271 type C QuoteC {
4272 bid: i64,
4273 ask: i64,
4274 }
4275
4276 fn spread(q: QuoteC) -> i64 {
4277 let q_shape = __auto_native_from_QuoteC_to_Quote(q);
4278 q_shape.ask - q_shape.bid
4279 }
4280
4281 spread(QuoteC { bid: 10, ask: 13 })
4282 "#,
4283 );
4284 }
4285
4286 #[test]
4287 fn test_type_c_auto_conversion_rejects_incompatible_fields() {
4288 let program = parse_program(
4289 r#"
4290 type Price {
4291 value: i64,
4292 }
4293
4294 type C PriceC {
4295 value: u64,
4296 }
4297 "#,
4298 )
4299 .expect("parse failed");
4300 let err = BytecodeCompiler::new()
4301 .compile(&program)
4302 .expect_err("incompatible type C conversion pair should fail");
4303 let msg = format!("{}", err);
4304 assert!(
4305 msg.contains("field type mismatch for auto conversion"),
4306 "expected type mismatch error, got: {}",
4307 msg
4308 );
4309 }
4310
4311 fn compiles_to(code: &str) -> crate::bytecode::BytecodeProgram {
4316 let program = parse_program(code).expect("parse failed");
4317 let compiler = BytecodeCompiler::new();
4318 compiler.compile(&program).expect("compile failed")
4319 }
4320
4321 #[test]
4324 fn test_extract_module_name() {
4325 assert_eq!(BytecodeCompiler::extract_module_name("file"), "file");
4326 assert_eq!(BytecodeCompiler::extract_module_name("std::file"), "file");
4327 assert_eq!(BytecodeCompiler::extract_module_name("std/io"), "io");
4328 assert_eq!(BytecodeCompiler::extract_module_name("a::b::c"), "c");
4329 assert_eq!(BytecodeCompiler::extract_module_name(""), "");
4330 }
4331
4332 #[test]
4333 fn test_permission_check_allows_pure_module_imports() {
4334 let code = "from json use { parse }";
4336 let program = parse_program(code).expect("parse failed");
4337 let mut compiler = BytecodeCompiler::new();
4338 compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4339 let _result = compiler.compile(&program);
4341 }
4342
4343 #[test]
4344 fn test_permission_check_blocks_file_import_under_pure() {
4345 let code = "from file use { read_text }";
4346 let program = parse_program(code).expect("parse failed");
4347 let mut compiler = BytecodeCompiler::new();
4348 compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4349 let result = compiler.compile(&program);
4350 assert!(
4351 result.is_err(),
4352 "Expected permission error for file::read_text under pure"
4353 );
4354 let err_msg = format!("{}", result.unwrap_err());
4355 assert!(
4356 err_msg.contains("Permission denied"),
4357 "Error should mention permission denied: {err_msg}"
4358 );
4359 assert!(
4360 err_msg.contains("fs.read"),
4361 "Error should mention fs.read: {err_msg}"
4362 );
4363 }
4364
4365 #[test]
4366 fn test_permission_check_allows_file_import_with_fs_read() {
4367 let code = "from file use { read_text }";
4368 let program = parse_program(code).expect("parse failed");
4369 let mut compiler = BytecodeCompiler::new();
4370 let pset = shape_abi_v1::PermissionSet::from_iter([shape_abi_v1::Permission::FsRead]);
4371 compiler.set_permission_set(Some(pset));
4372 let _result = compiler.compile(&program);
4374 }
4375
4376 #[test]
4377 fn test_permission_check_no_permission_set_allows_everything() {
4378 let code = "from file use { read_text }";
4380 let program = parse_program(code).expect("parse failed");
4381 let compiler = BytecodeCompiler::new();
4382 let _result = compiler.compile(&program);
4384 }
4385
4386 #[test]
4387 fn test_permission_check_namespace_import_blocked() {
4388 let code = "use http";
4389 let program = parse_program(code).expect("parse failed");
4390 let mut compiler = BytecodeCompiler::new();
4391 compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4392 let result = compiler.compile(&program);
4393 assert!(
4394 result.is_err(),
4395 "Expected permission error for `use http` under pure"
4396 );
4397 let err_msg = format!("{}", result.unwrap_err());
4398 assert!(
4399 err_msg.contains("Permission denied"),
4400 "Error should mention permission denied: {err_msg}"
4401 );
4402 }
4403
4404 #[test]
4405 fn test_permission_check_namespace_import_allowed() {
4406 let code = "use http";
4407 let program = parse_program(code).expect("parse failed");
4408 let mut compiler = BytecodeCompiler::new();
4409 compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::full()));
4410 let _result = compiler.compile(&program);
4412 }
4413}