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