Skip to main content

shape_vm/compiler/
statements.rs

1//! Statement and item compilation
2
3use 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                // Register as a normal function so call sites resolve the name
166                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                // Store the foreign function def so call sites can resolve
196                // the declared return type (must be Result<T> for dynamic languages).
197                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                    // Same registration as Item::ForeignFunction
212                    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                // Desugar extend methods to functions with implicit `self` receiver param.
250                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                // Impl blocks use scoped UFCS names.
258                // - default impl: "Type::method" (legacy compatibility)
259                // - named impl: "Trait::Type::ImplName::method"
260                // This prevents conflicts when multiple named impls exist.
261                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                // From/TryFrom impls use reverse-conversion desugaring:
272                // the method takes an explicit `value` param (no implicit self),
273                // and we auto-derive Into/TryInto trait symbols on the source type.
274                if trait_name == "From" || trait_name == "TryFrom" {
275                    return self.compile_from_impl(impl_block, trait_name, type_name);
276                }
277
278                // Collect names of methods explicitly provided in the impl block
279                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                    // Track drop kind per type (sync, async, or both)
300                    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                // Install default methods from the trait definition that were not overridden
319                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                // BUG-4.6 fix: Register the trait impl in the type inference
345                // environment so that `implements()` can see it at comptime.
346                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    /// Register a function definition
370    pub(super) fn register_function(&mut self, func_def: &FunctionDef) -> Result<()> {
371        // Detect duplicate function definitions (Shape does not support overloading).
372        // Skip names containing "::" (trait impl methods) or "." (extend methods)
373        // — those are type-qualified and live in separate namespaces.
374        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                // Allow idempotent re-registration from module inlining: when the
379                // prelude and an explicitly imported module both define the same helper
380                // function (e.g., `percentile`), silently keep the first definition
381                // if arities match. Different arities indicate a genuine conflict.
382                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, // Will be updated during compilation
452            entry_point: 0,  // Will be updated during compilation
453            body_length: 0,  // Will be updated during compilation
454            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        // Register function return type for typed opcode emission.
467        // When a function has an explicit return type annotation (e.g., `: int`),
468        // record it so that call sites can propagate NumericType through expressions
469        // like `fib(n-1) + fib(n-2)` and emit AddInt instead of generic Add.
470        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    /// Compile a top-level item with context about whether it's the last item
481    /// If is_last is true and the item is an expression, keep the result on the stack
482    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                // ModuleBinding variable — register the variable even if the initializer fails,
490                // to prevent cascading "Undefined variable" errors on later references.
491                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                            // Push null as placeholder so the variable still gets registered
496                            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                // Only pop if not the last item - keep last expression result on stack
525                if !is_last {
526                    self.emit(Instruction::simple(OpCode::Pop));
527                }
528            }
529            Item::Statement(stmt, _) => {
530                // For expression statements that are the last item, keep result on stack
531                if is_last {
532                    if let Statement::Expression(expr, _) = stmt {
533                        self.compile_expr(expr)?;
534                        // Don't emit Pop - keep result on stack
535                        return Ok(());
536                    }
537                }
538                self.compile_statement(stmt)?;
539            }
540            Item::Export(export, export_span) => {
541                // If the export has a source variable declaration (pub let/const/var),
542                // compile it so the initialization is actually executed.
543                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(_) => {} // no-op for now
570                    ExportItem::Trait(_) => {} // no-op for now (trait registration happens in type system)
571                    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                // Track type alias for meta validation
583                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                // Apply comptime field overrides from type alias
597                // e.g., type EUR = Currency { symbol: "€" } overrides Currency's comptime symbol
598                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                    // Start with base type's comptime fields (if any)
606                    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            // Meta/Format definitions removed — formatting now uses Display trait
653            Item::Import(import_stmt, _) => {
654                // Import handling is now done by executor pre-resolution
655                // via the unified runtime module loader.
656                // Imported module AST items are inlined via prelude injection
657                // before compilation (single-pass, no index remapping).
658                //
659                // At self point in compile_item, imports should already have been
660                // processed by pre-resolution. If we reach here, the import
661                // is either:
662                // 1. Being compiled standalone (no module context) - skip for now
663                // 2. A future extension point for runtime imports
664                //
665                // For now, we register the imported names as known functions
666                // that can be resolved later.
667                self.register_import_names(import_stmt)?;
668            }
669            Item::Extend(extend, _) => {
670                // Compile desugared extend methods
671                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                // Compile impl block methods with scoped names
678                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                // From/TryFrom: compile the from/tryFrom method + synthetic wrapper
689                if trait_name == "From" || trait_name == "TryFrom" {
690                    return self.compile_from_impl_bodies(impl_block, trait_name, type_name);
691                }
692
693                // Collect names of methods explicitly provided in the impl block
694                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                // Compile default methods from the trait definition that were not overridden
709                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                // Execute comptime block at compile time (side-effects only; result discarded)
732                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                // Pop the query result unless self is the last item
768                if !is_last {
769                    self.emit(Instruction::simple(OpCode::Pop));
770                }
771            }
772            Item::ForeignFunction(def, _) => self.compile_foreign_function(def)?,
773            _ => {} // Skip other items for now
774        }
775        Ok(())
776    }
777
778    /// Register imported names for symbol resolution
779    ///
780    /// This allows the compiler to recognize imported functions when
781    /// they are called later in the code.
782    fn register_import_names(&mut self, import_stmt: &shape_ast::ast::ImportStmt) -> Result<()> {
783        use shape_ast::ast::ImportItems;
784
785        // Check permissions before registering imports.
786        // Clone to avoid borrow conflict with &mut self in check_import_permissions.
787        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                    // Register as a known import - actual function resolution
796                    // happens when the imported module's bytecode is merged
797                    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                // `use module.path` or `use module.path as alias`
808                // Register the local namespace binding as a module_binding.
809                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                // Predeclare module object schema so runtime can instantiate
818                // module module_bindings without synthesizing schemas dynamically.
819                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                // The module object will be provided at runtime by the VM
830                let _ = binding_idx;
831            }
832        }
833        Ok(())
834    }
835
836    /// Check whether the imported symbols are allowed by the active permission set.
837    ///
838    /// For named imports (`from "file" import { read_text }`), checks each function
839    /// individually. For namespace imports (`use http`), checks the whole module.
840    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        // Extract the module name from the import path.
849        // Paths like "std::file", "file", "std/file" all resolve to "file".
850        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                // For namespace imports, check the entire module's permission envelope.
876                // If the module requires any permissions not granted, deny the import.
877                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                // Record module-level permissions for namespace imports in the current blob
893                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    /// Extract the leaf module name from an import path.
903    ///
904    /// `"std::file"` → `"file"`, `"file"` → `"file"`, `"std/io"` → `"io"`
905    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    /// Register an enum definition in the TypeSchemaRegistry
978    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    /// Pre-register items from an imported module (enums, struct types, functions).
999    ///
1000    /// Called by the LSP before compilation to make imported enums/types known
1001    /// to the compiler's type tracker. Reuses `register_enum` as single source of truth.
1002    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                            // Register struct type fields so the compiler knows about them
1012                            let _ = self.register_struct_type(struct_def, Span::DUMMY);
1013                        }
1014                        ExportItem::Function(func_def) => {
1015                            // Register function so it's known during compilation
1016                            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    /// Register a meta definition in the format registry
1030    ///
1031    // Meta compilation methods removed — formatting now uses Display trait
1032
1033    /// Desugar an extend method to a FunctionDef with implicit `self` first param.
1034    ///
1035    /// `extend Number { method double() { self * 2 } }`
1036    /// becomes: `function double(self) { self * 2 }`
1037    ///
1038    /// UFCS handles the rest: `(5).double()` → `double(5)` → self = 5
1039    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        // Extend methods use qualified "Type.method" names to avoid collisions
1048        // with free functions (e.g., prelude's `sum` vs extend Point { method sum() }).
1049        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    /// Desugar an impl method to a scoped FunctionDef.
1069    ///
1070    /// - Default impl:
1071    ///   `impl Queryable for DbTable { method filter(pred) { ... } }`
1072    ///   becomes: `function DbTable::filter(self, pred) { ... }`
1073    /// - Named impl:
1074    ///   `impl Display for User as JsonDisplay { method display() { ... } }`
1075    ///   becomes: `function Display::User::JsonDisplay::display(self) { ... }`
1076    ///
1077    /// Named impls use trait/type/impl prefixes to avoid collisions.
1078    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        // Async drop methods are named "drop_async" so both sync and async
1090        // variants can coexist in the function name index.
1091        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    /// Build desugared method params/body with implicit receiver handling.
1117    ///
1118    /// Canonical receiver is `self`.
1119    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    /// Compile a `From` or `TryFrom` impl block.
1161    ///
1162    /// Unlike normal impl methods (which inject implicit `self`), From/TryFrom
1163    /// methods are constructors: `from(value: Source) -> Target`. The value
1164    /// parameter sits at local slot 0 with no receiver.
1165    ///
1166    /// Auto-derives:
1167    /// - `impl From<S> for T`  → `Into<T>::into` on S (direct alias)
1168    ///                          + `TryInto<T>::tryInto` on S (wrapper → Ok())
1169    /// - `impl TryFrom<S> for T` → `TryInto<T>::tryInto` on S (direct alias)
1170    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        // Extract source type from generic args: From<Source> → Source
1177        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        // Named impl selector defaults to the source type name
1204        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            // Register From/TryFrom trait method symbol on the target type
1212            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            // Auto-derive Into/TryInto on the source type
1222            if trait_name == "From" {
1223                // From<S> for T → Into<T>::into on S = direct alias (same fn)
1224                self.program.register_trait_method_symbol(
1225                    "Into",
1226                    &source_type,
1227                    Some(selector),
1228                    "into",
1229                    &from_fn_name,
1230                );
1231
1232                // From<S> for T → TryInto<T>::tryInto on S = wrapper (from + Ok)
1233                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                // Register trait impls in type inference environment
1244                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                // TryFrom<S> for T → TryInto<T>::tryInto on S = direct alias
1258                self.program.register_trait_method_symbol(
1259                    "TryInto",
1260                    &source_type,
1261                    Some(selector),
1262                    "tryInto",
1263                    &from_fn_name,
1264                );
1265
1266                // Register TryInto trait impl in type inference environment
1267                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        // Register From/TryFrom trait impl on target type
1277        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    /// Compile From/TryFrom impl method bodies (and the synthetic TryInto wrapper).
1290    ///
1291    /// Called from `compile_item_with_context` — the registration pass already
1292    /// happened in `compile_from_impl` / `register_item_functions`.
1293    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(()), // error already reported in registration
1304                }
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        // Also compile the synthetic TryInto wrapper for From impls
1316        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                // The wrapper was already registered; now compile its body
1324                if let Some(func_def) = self.function_defs.get(&wrapper_name).cloned() {
1325                    let _ = self.compile_function(&func_def);
1326                    // Suppress errors: if Ok() or the from fn is not yet available, it
1327                    // will be resolved at link time.
1328                    let _ = from_fn_name; // used above in the format
1329                }
1330            }
1331        }
1332
1333        Ok(())
1334    }
1335
1336    /// Desugar a From/TryFrom method WITHOUT implicit self injection.
1337    ///
1338    /// `From::from(value: S)` is a constructor — `value` sits at local slot 0.
1339    /// Function name: `"From::TargetType::SourceType::method_name"`
1340    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        // Verify no explicit `self` parameter
1348        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    /// Emit a synthetic wrapper function that calls a From::from function
1384    /// and wraps its result in Ok() for TryInto compatibility.
1385    ///
1386    /// Generated function: `__from_tryinto_{source}_{target}(value) -> Ok(from(value))`
1387    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        // Create a synthetic FunctionDef whose body calls from() and wraps in Ok()
1396        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    /// Compile an annotation definition.
1454    ///
1455    /// Each handler is compiled as an internal function:
1456    /// - before(args, ctx) → `{name}___before(self, period, args, ctx)`
1457    /// - after(args, result, ctx) → `{name}___after(self, period, args, result, ctx)`
1458    ///
1459    /// `self` is the annotated item (function/method/property).
1460    /// Annotation params (e.g., `period`) are prepended after `self`.
1461    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            // Comptime handlers are stored as AST (not compiled to bytecode).
1483            // They are executed at compile time when the annotation is applied.
1484            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            // Build function params: self + annotation_params + handler_params
1517            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            // Add annotation params (e.g., period)
1529            for ann_param in &ann_def.params {
1530                params.push(ann_param.clone());
1531            }
1532            // Add handler params (e.g., args, ctx)
1533            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            // Convert handler body (Expr) to function body (Vec<Statement>)
1592            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 => {} // handled above
1618                AnnotationHandlerType::ComptimePost => {} // handled above
1619            }
1620        }
1621
1622        // Resolve allowed target kinds.
1623        // Explicit `targets: [...]` in the annotation definition has priority.
1624        // Otherwise infer from handlers:
1625        // before/after handlers only make sense on functions (they wrap calls),
1626        // lifecycle handlers (on_define/metadata) are definition-time only.
1627        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        // Enforce that definition-time lifecycle hooks only target definition
1645        // sites (`function` / `type`).
1646        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    /// Register a struct type definition.
1679    ///
1680    /// Comptime fields are baked at compile time and excluded from the runtime TypeSchema.
1681    /// Their values are stored in `self.comptime_fields` for constant-folded access.
1682    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        // Validate annotation target kinds before type registration.
1691        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        // Pre-register runtime field layout so comptime-generated methods on
1706        // `extend target { ... }` can resolve `self.field` statically.
1707        // If the target is later removed by comptime directives, these
1708        // placeholders are rolled back below.
1709        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        // Execute comptime annotation handlers before registration so
1753        // `remove target` can suppress type emission entirely.
1754        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        // Build TypeSchema for runtime fields only
1765        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        // Bake comptime field values
1795        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            // Comptime fields without a default are allowed — they must be
1824            // provided via type alias overrides (e.g., type EUR = Currency { symbol: "€" })
1825        }
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    /// Execute comptime annotation handlers for a struct type definition.
2340    ///
2341    /// Mirrors `execute_comptime_handlers` in functions.rs but uses
2342    /// `ComptimeTarget::from_type()` to build the target from struct fields.
2343    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                    // Build field info for ComptimeTarget::from_type()
2357                    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                // pub const/let/var: unwrap the source_decl and qualify it as a VariableDecl
2478                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    /// Compile a query (Backtest, Alert, or With/CTE).
2900    ///
2901    /// For CTE (WITH) queries:
2902    /// 1. Compile each CTE subquery and store the result in a named module_binding variable.
2903    /// 2. Compile the main query (which can reference CTEs by name as variables).
2904    ///
2905    /// For Backtest and Alert queries, emit a stub for now.
2906    fn compile_query(&mut self, query: &Query) -> Result<()> {
2907        match query {
2908            Query::With(with_query) => {
2909                // Compile each CTE: evaluate its subquery and store as a named variable
2910                for cte in &with_query.ctes {
2911                    // Recursively compile the CTE's subquery
2912                    self.compile_query(&cte.query)?;
2913
2914                    // Store the result in a module_binding variable with the CTE's name
2915                    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                // Compile the main query
2923                self.compile_query(&with_query.query)?;
2924            }
2925            Query::Backtest(_backtest) => {
2926                // Backtest queries require runtime context to evaluate.
2927                // Push null as placeholder — the runtime executor handles backtest
2928                // execution when given a full ExecutionContext.
2929                self.emit(Instruction::simple(OpCode::PushNull));
2930            }
2931            Query::Alert(alert) => {
2932                // Compile alert condition
2933                self.compile_expr(&alert.condition)?;
2934                // Push null as placeholder (alert evaluation requires runtime context)
2935                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    /// Compile a statement
2947    pub(super) fn compile_statement(&mut self, stmt: &Statement) -> Result<()> {
2948        match stmt {
2949            Statement::Return(expr_opt, _span) => {
2950                // Prevent returning references — refs are scoped borrows
2951                // that cannot escape the function (would create dangling refs).
2952                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                    // Note: returning a ref_local identifier is allowed — compile_expr
2960                    // emits DerefLoad which returns the dereferenced *value*, not the
2961                    // reference itself. Only returning `&x` (Expr::Reference) is blocked.
2962                    self.compile_expr(expr)?;
2963                } else {
2964                    self.emit(Instruction::simple(OpCode::PushNull));
2965                }
2966                // Emit drops for all active drop scopes before returning
2967                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                    // Emit drops for drop scopes inside the loop before breaking
2978                    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                    // Copy values we need before mutable borrow
3001                    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                    // Emit drops for drop scopes inside the loop before continuing
3007                    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                // Set pending variable name for hoisting integration.
3025                // compile_typed_object_literal uses self to include hoisted fields in the schema.
3026                self.pending_variable_name =
3027                    var_decl.pattern.as_identifier().map(|s| s.to_string());
3028
3029                // Compile-time range check: if the type annotation is a width type
3030                // (i8, u8, i16, etc.) and the initializer is a constant expression,
3031                // verify the value fits in the declared width.
3032                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                                    // Float → int truncation check
3044                                    let i = f as i64;
3045                                    (i as f64 == f) && w.in_range_i64(i)
3046                                } else {
3047                                    true // non-numeric, let runtime handle it
3048                                };
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                // Compile initializer — register the variable even if the initializer fails,
3066                // to prevent cascading "Undefined variable" errors on later references.
3067                let init_err = if let Some(init_expr) = &var_decl.value {
3068                    match self.compile_expr(init_expr) {
3069                        Ok(()) => None,
3070                        Err(e) => {
3071                            self.emit(Instruction::simple(OpCode::PushNull));
3072                            Some(e)
3073                        }
3074                    }
3075                } else {
3076                    self.emit(Instruction::simple(OpCode::PushNull));
3077                    None
3078                };
3079
3080                // Clear pending variable name after init expression is compiled
3081                self.pending_variable_name = None;
3082
3083                // Emit BindSchema for Table<T> annotations (runtime safety net)
3084                if let Some(ref type_ann) = var_decl.type_annotation {
3085                    if let Some(schema_id) = self.get_table_schema_id(type_ann) {
3086                        self.emit(Instruction::new(
3087                            OpCode::BindSchema,
3088                            Some(Operand::Count(schema_id)),
3089                        ));
3090                    }
3091                }
3092
3093                // At top-level (no current function), create module_bindings; otherwise create locals
3094                if self.current_function.is_none() {
3095                    // Top-level: create module_binding variable
3096                    if let Some(name) = var_decl.pattern.as_identifier() {
3097                        let binding_idx = self.get_or_create_module_binding(name);
3098                        self.emit(Instruction::new(
3099                            OpCode::StoreModuleBinding,
3100                            Some(Operand::ModuleBinding(binding_idx)),
3101                        ));
3102
3103                        // Track const module bindings for reassignment checks
3104                        if var_decl.kind == VarKind::Const {
3105                            self.const_module_bindings.insert(binding_idx);
3106                        }
3107
3108                        // Track immutable `let` bindings at module level
3109                        if var_decl.kind == VarKind::Let && !var_decl.is_mut {
3110                            self.immutable_module_bindings.insert(binding_idx);
3111                        }
3112
3113                        // Track type annotation if present (for type checker)
3114                        if let Some(ref type_ann) = var_decl.type_annotation {
3115                            if let Some(type_name) =
3116                                Self::tracked_type_name_from_annotation(type_ann)
3117                            {
3118                                self.set_module_binding_type_info(binding_idx, &type_name);
3119                            }
3120                            // Handle Table<T> generic annotation
3121                            self.try_track_datatable_type(type_ann, binding_idx, false)?;
3122                        } else {
3123                            let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
3124                            self.propagate_initializer_type_to_slot(binding_idx, false, is_mutable);
3125                        }
3126                    } else {
3127                        self.compile_destructure_pattern_global(&var_decl.pattern)?;
3128                    }
3129                } else {
3130                    // Inside function: create local variable
3131                    self.compile_destructure_pattern(&var_decl.pattern)?;
3132
3133                    // Patch StoreLocal → StoreLocalTyped for width-typed simple bindings.
3134                    // compile_destructure_pattern emits StoreLocal(idx) for Identifier patterns;
3135                    // we upgrade it here when the type annotation is a width type.
3136                    if let (Some(name), Some(TypeAnnotation::Basic(type_name))) = (
3137                        var_decl.pattern.as_identifier(),
3138                        var_decl.type_annotation.as_ref(),
3139                    ) {
3140                        if let Some(w) = shape_ast::IntWidth::from_name(type_name) {
3141                            if let Some(local_idx) = self.resolve_local(name) {
3142                                if let Some(last) = self.program.instructions.last_mut() {
3143                                    if last.opcode == OpCode::StoreLocal {
3144                                        last.opcode = OpCode::StoreLocalTyped;
3145                                        last.operand = Some(Operand::TypedLocal(
3146                                            local_idx,
3147                                            crate::bytecode::NumericWidth::from_int_width(w),
3148                                        ));
3149                                    }
3150                                }
3151                            }
3152                        }
3153                    }
3154
3155                    // Track const locals for reassignment checks
3156                    if var_decl.kind == VarKind::Const {
3157                        if let Some(name) = var_decl.pattern.as_identifier() {
3158                            if let Some(local_idx) = self.resolve_local(name) {
3159                                self.const_locals.insert(local_idx);
3160                            }
3161                        }
3162                    }
3163
3164                    // Track immutable `let` bindings (not `let mut` and not `var`)
3165                    // `let` without `mut` is immutable by default.
3166                    // `var` is always mutable (inferred from usage).
3167                    // `let mut` is explicitly mutable.
3168                    if var_decl.kind == VarKind::Let && !var_decl.is_mut {
3169                        if let Some(name) = var_decl.pattern.as_identifier() {
3170                            if let Some(local_idx) = self.resolve_local(name) {
3171                                self.immutable_locals.insert(local_idx);
3172                            }
3173                        }
3174                    }
3175
3176                    // Track type annotation first (so drop tracking can resolve the type)
3177                    if let Some(name) = var_decl.pattern.as_identifier() {
3178                        if let Some(ref type_ann) = var_decl.type_annotation {
3179                            if let Some(type_name) =
3180                                Self::tracked_type_name_from_annotation(type_ann)
3181                            {
3182                                // Get the local index for self variable
3183                                if let Some(local_idx) = self.resolve_local(name) {
3184                                    self.set_local_type_info(local_idx, &type_name);
3185                                }
3186                            }
3187                            // Handle Table<T> generic annotation
3188                            if let Some(local_idx) = self.resolve_local(name) {
3189                                self.try_track_datatable_type(type_ann, local_idx, true)?;
3190                            }
3191                        } else if let Some(local_idx) = self.resolve_local(name) {
3192                            let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
3193                            self.propagate_initializer_type_to_slot(local_idx, true, is_mutable);
3194                        }
3195                    }
3196
3197                    // Track for auto-drop at scope exit (DropCall silently skips non-Drop types).
3198                    // Select sync vs async opcode based on the type's DropKind.
3199                    if let Some(name) = var_decl.pattern.as_identifier() {
3200                        if let Some(local_idx) = self.resolve_local(name) {
3201                            let drop_kind = self.local_drop_kind(local_idx).or_else(|| {
3202                                var_decl
3203                                    .type_annotation
3204                                    .as_ref()
3205                                    .and_then(|ann| self.annotation_drop_kind(ann))
3206                            });
3207
3208                            let is_async = match drop_kind {
3209                                Some(DropKind::AsyncOnly) => {
3210                                    if !self.current_function_is_async {
3211                                        let tn = self
3212                                            .type_tracker
3213                                            .get_local_type(local_idx)
3214                                            .and_then(|info| info.type_name.clone())
3215                                            .unwrap_or_else(|| name.to_string());
3216                                        return Err(ShapeError::SemanticError {
3217                                            message: format!(
3218                                                "type '{}' has only an async drop() and cannot be used in a sync context; \
3219                                                 add a sync method drop(self) or use it inside an async function",
3220                                                tn
3221                                            ),
3222                                            location: None,
3223                                        });
3224                                    }
3225                                    true
3226                                }
3227                                Some(DropKind::Both) => self.current_function_is_async,
3228                                Some(DropKind::SyncOnly) | None => false,
3229                            };
3230                            self.track_drop_local(local_idx, is_async);
3231                        }
3232                    }
3233                }
3234
3235                if let Some(e) = init_err {
3236                    return Err(e);
3237                }
3238            }
3239
3240            Statement::Assignment(assign, _) => 'assign: {
3241                // Check for const reassignment
3242                if let Some(name) = assign.pattern.as_identifier() {
3243                    if let Some(local_idx) = self.resolve_local(name) {
3244                        if self.const_locals.contains(&local_idx) {
3245                            return Err(ShapeError::SemanticError {
3246                                message: format!("Cannot reassign const variable '{}'", name),
3247                                location: None,
3248                            });
3249                        }
3250                        // Check for immutable `let` reassignment
3251                        if self.immutable_locals.contains(&local_idx) {
3252                            return Err(ShapeError::SemanticError {
3253                                message: format!(
3254                                    "Cannot reassign immutable variable '{}'. Use `let mut` or `var` for mutable bindings",
3255                                    name
3256                                ),
3257                                location: None,
3258                            });
3259                        }
3260                    } else {
3261                        let scoped_name = self
3262                            .resolve_scoped_module_binding_name(name)
3263                            .unwrap_or_else(|| name.to_string());
3264                        if let Some(&binding_idx) = self.module_bindings.get(&scoped_name) {
3265                            if self.const_module_bindings.contains(&binding_idx) {
3266                                return Err(ShapeError::SemanticError {
3267                                    message: format!("Cannot reassign const variable '{}'", name),
3268                                    location: None,
3269                                });
3270                            }
3271                            // Check for immutable `let` reassignment at module level
3272                            if self.immutable_module_bindings.contains(&binding_idx) {
3273                                return Err(ShapeError::SemanticError {
3274                                    message: format!(
3275                                        "Cannot reassign immutable variable '{}'. Use `let mut` or `var` for mutable bindings",
3276                                        name
3277                                    ),
3278                                    location: None,
3279                                });
3280                            }
3281                        }
3282                    }
3283                }
3284
3285                // Optimization: x = x.push(val) → ArrayPushLocal (O(1) in-place mutation)
3286                if let Some(name) = assign.pattern.as_identifier() {
3287                    if let Expr::MethodCall {
3288                        receiver,
3289                        method,
3290                        args,
3291                        ..
3292                    } = &assign.value
3293                    {
3294                        if method == "push" && args.len() == 1 {
3295                            if let Expr::Identifier(recv_name, _) = receiver.as_ref() {
3296                                if recv_name == name {
3297                                    if let Some(local_idx) = self.resolve_local(name) {
3298                                        if !self.ref_locals.contains(&local_idx) {
3299                                            self.compile_expr(&args[0])?;
3300                                            let pushed_numeric = self.last_expr_numeric_type;
3301                                            self.emit(Instruction::new(
3302                                                OpCode::ArrayPushLocal,
3303                                                Some(Operand::Local(local_idx)),
3304                                            ));
3305                                            if let Some(numeric_type) = pushed_numeric {
3306                                                self.mark_slot_as_numeric_array(
3307                                                    local_idx,
3308                                                    true,
3309                                                    numeric_type,
3310                                                );
3311                                            }
3312                                            break 'assign;
3313                                        }
3314                                    } else {
3315                                        let binding_idx = self.get_or_create_module_binding(name);
3316                                        self.compile_expr(&args[0])?;
3317                                        let pushed_numeric = self.last_expr_numeric_type;
3318                                        self.emit(Instruction::new(
3319                                            OpCode::ArrayPushLocal,
3320                                            Some(Operand::ModuleBinding(binding_idx)),
3321                                        ));
3322                                        if let Some(numeric_type) = pushed_numeric {
3323                                            self.mark_slot_as_numeric_array(
3324                                                binding_idx,
3325                                                false,
3326                                                numeric_type,
3327                                            );
3328                                        }
3329                                        break 'assign;
3330                                    }
3331                                }
3332                            }
3333                        }
3334                    }
3335                }
3336
3337                // Compile value
3338                self.compile_expr(&assign.value)?;
3339                let assigned_ident = assign.pattern.as_identifier().map(str::to_string);
3340
3341                // Store in variable
3342                self.compile_destructure_assignment(&assign.pattern)?;
3343                if let Some(name) = assigned_ident.as_deref() {
3344                    self.propagate_assignment_type_to_identifier(name);
3345                }
3346            }
3347
3348            Statement::Expression(expr, _) => {
3349                // Fast path: arr.push(val) as standalone statement → in-place mutation
3350                // (avoids the LoadLocal+Pop overhead from the expression-level optimization)
3351                if let Expr::MethodCall {
3352                    receiver,
3353                    method,
3354                    args,
3355                    ..
3356                } = expr
3357                {
3358                    if method == "push" && args.len() == 1 {
3359                        if let Expr::Identifier(recv_name, _) = receiver.as_ref() {
3360                            if let Some(local_idx) = self.resolve_local(recv_name) {
3361                                if !self.ref_locals.contains(&local_idx) {
3362                                    self.compile_expr(&args[0])?;
3363                                    let pushed_numeric = self.last_expr_numeric_type;
3364                                    self.emit(Instruction::new(
3365                                        OpCode::ArrayPushLocal,
3366                                        Some(Operand::Local(local_idx)),
3367                                    ));
3368                                    if let Some(numeric_type) = pushed_numeric {
3369                                        self.mark_slot_as_numeric_array(
3370                                            local_idx,
3371                                            true,
3372                                            numeric_type,
3373                                        );
3374                                    }
3375                                    return Ok(());
3376                                }
3377                            } else if !self
3378                                .mutable_closure_captures
3379                                .contains_key(recv_name.as_str())
3380                            {
3381                                let binding_idx = self.get_or_create_module_binding(recv_name);
3382                                self.compile_expr(&args[0])?;
3383                                self.emit(Instruction::new(
3384                                    OpCode::ArrayPushLocal,
3385                                    Some(Operand::ModuleBinding(binding_idx)),
3386                                ));
3387                                return Ok(());
3388                            }
3389                        }
3390                    }
3391                }
3392                self.compile_expr(expr)?;
3393                self.emit(Instruction::simple(OpCode::Pop));
3394            }
3395
3396            Statement::For(for_loop, _) => {
3397                self.compile_for_loop(for_loop)?;
3398            }
3399
3400            Statement::While(while_loop, _) => {
3401                self.compile_while_loop(while_loop)?;
3402            }
3403
3404            Statement::If(if_stmt, _) => {
3405                self.compile_if_statement(if_stmt)?;
3406            }
3407            Statement::Extend(extend, span) => {
3408                if !self.comptime_mode {
3409                    return Err(ShapeError::SemanticError {
3410                        message:
3411                            "`extend` as a statement is only valid inside `comptime { }` context"
3412                                .to_string(),
3413                        location: Some(self.span_to_source_location(*span)),
3414                    });
3415                }
3416                self.emit_comptime_extend_directive(extend, *span)?;
3417            }
3418            Statement::RemoveTarget(span) => {
3419                if !self.comptime_mode {
3420                    return Err(ShapeError::SemanticError {
3421                        message: "`remove target` is only valid inside `comptime { }` context"
3422                            .to_string(),
3423                        location: Some(self.span_to_source_location(*span)),
3424                    });
3425                }
3426                self.emit_comptime_remove_directive(*span)?;
3427            }
3428            Statement::SetParamType {
3429                param_name,
3430                type_annotation,
3431                span,
3432            } => {
3433                if !self.comptime_mode {
3434                    return Err(ShapeError::SemanticError {
3435                        message: "`set param` is only valid inside `comptime { }` context"
3436                            .to_string(),
3437                        location: Some(self.span_to_source_location(*span)),
3438                    });
3439                }
3440                self.emit_comptime_set_param_type_directive(param_name, type_annotation, *span)?;
3441            }
3442            Statement::SetReturnType {
3443                type_annotation,
3444                span,
3445            } => {
3446                if !self.comptime_mode {
3447                    return Err(ShapeError::SemanticError {
3448                        message: "`set return` is only valid inside `comptime { }` context"
3449                            .to_string(),
3450                        location: Some(self.span_to_source_location(*span)),
3451                    });
3452                }
3453                self.emit_comptime_set_return_type_directive(type_annotation, *span)?;
3454            }
3455            Statement::SetReturnExpr { expression, span } => {
3456                if !self.comptime_mode {
3457                    return Err(ShapeError::SemanticError {
3458                        message: "`set return` is only valid inside `comptime { }` context"
3459                            .to_string(),
3460                        location: Some(self.span_to_source_location(*span)),
3461                    });
3462                }
3463                self.emit_comptime_set_return_expr_directive(expression, *span)?;
3464            }
3465            Statement::ReplaceBody { body, span } => {
3466                if !self.comptime_mode {
3467                    return Err(ShapeError::SemanticError {
3468                        message: "`replace body` is only valid inside `comptime { }` context"
3469                            .to_string(),
3470                        location: Some(self.span_to_source_location(*span)),
3471                    });
3472                }
3473                self.emit_comptime_replace_body_directive(body, *span)?;
3474            }
3475            Statement::ReplaceBodyExpr { expression, span } => {
3476                if !self.comptime_mode {
3477                    return Err(ShapeError::SemanticError {
3478                        message: "`replace body` is only valid inside `comptime { }` context"
3479                            .to_string(),
3480                        location: Some(self.span_to_source_location(*span)),
3481                    });
3482                }
3483                self.emit_comptime_replace_body_expr_directive(expression, *span)?;
3484            }
3485            Statement::ReplaceModuleExpr { expression, span } => {
3486                if !self.comptime_mode {
3487                    return Err(ShapeError::SemanticError {
3488                        message: "`replace module` is only valid inside `comptime { }` context"
3489                            .to_string(),
3490                        location: Some(self.span_to_source_location(*span)),
3491                    });
3492                }
3493                self.emit_comptime_replace_module_expr_directive(expression, *span)?;
3494            }
3495        }
3496        Ok(())
3497    }
3498}
3499
3500#[cfg(test)]
3501mod tests {
3502    use crate::compiler::BytecodeCompiler;
3503    use crate::executor::{VMConfig, VirtualMachine};
3504    use shape_ast::parser::parse_program;
3505
3506    #[test]
3507    fn test_module_decl_function_resolves_module_const() {
3508        let code = r#"
3509            mod math {
3510                const BASE = 21
3511                fn twice() {
3512                    BASE * 2
3513                }
3514            }
3515            math.twice()
3516        "#;
3517
3518        let program = parse_program(code).expect("Failed to parse");
3519        let bytecode = BytecodeCompiler::new()
3520            .compile(&program)
3521            .expect("Failed to compile");
3522
3523        let mut vm = VirtualMachine::new(VMConfig::default());
3524        vm.load_program(bytecode);
3525        vm.populate_module_objects();
3526        let result = vm.execute(None).expect("Failed to execute");
3527        assert_eq!(
3528            result
3529                .as_number_coerce()
3530                .expect("module call should return number"),
3531            42.0
3532        );
3533    }
3534
3535    #[test]
3536    fn test_module_annotation_can_replace_module_items() {
3537        let code = r#"
3538            annotation synth_module() {
3539                targets: [module]
3540                comptime post(target, ctx) {
3541                    replace module ("const ANSWER = 40; fn plus_two() { ANSWER + 2 }")
3542                }
3543            }
3544
3545            @synth_module()
3546            mod demo {}
3547
3548            demo.plus_two()
3549        "#;
3550
3551        let program = parse_program(code).expect("Failed to parse");
3552        let bytecode = BytecodeCompiler::new()
3553            .compile(&program)
3554            .expect("Failed to compile");
3555
3556        let mut vm = VirtualMachine::new(VMConfig::default());
3557        vm.load_program(bytecode);
3558        vm.populate_module_objects();
3559        let result = vm.execute(None).expect("Failed to execute");
3560        assert_eq!(
3561            result
3562                .as_number_coerce()
3563                .expect("module call should return number"),
3564            42.0
3565        );
3566    }
3567
3568    #[test]
3569    fn test_module_inline_comptime_can_replace_module_items() {
3570        let code = r#"
3571            mod demo {
3572                comptime {
3573                    replace module ("const ANSWER = 40; fn plus_two() { ANSWER + 2 }")
3574                }
3575            }
3576
3577            demo.plus_two()
3578        "#;
3579
3580        let program = parse_program(code).expect("Failed to parse");
3581        let bytecode = BytecodeCompiler::new()
3582            .compile(&program)
3583            .expect("Failed to compile");
3584
3585        let mut vm = VirtualMachine::new(VMConfig::default());
3586        vm.load_program(bytecode);
3587        vm.populate_module_objects();
3588        let result = vm.execute(None).expect("Failed to execute");
3589        assert_eq!(
3590            result
3591                .as_number_coerce()
3592                .expect("module call should return number"),
3593            42.0
3594        );
3595    }
3596
3597    #[test]
3598    fn test_module_inline_comptime_can_use_module_local_comptime_helper() {
3599        let code = r#"
3600            mod demo {
3601                comptime fn synth() {
3602                    "const ANSWER = 40; fn plus_two() { ANSWER + 2 }"
3603                }
3604
3605                comptime {
3606                    replace module (synth())
3607                }
3608            }
3609
3610            demo.plus_two()
3611        "#;
3612
3613        let program = parse_program(code).expect("Failed to parse");
3614        let bytecode = BytecodeCompiler::new()
3615            .compile(&program)
3616            .expect("Failed to compile");
3617
3618        let mut vm = VirtualMachine::new(VMConfig::default());
3619        vm.load_program(bytecode);
3620        vm.populate_module_objects();
3621        let result = vm.execute(None).expect("Failed to execute");
3622        assert_eq!(
3623            result
3624                .as_number_coerce()
3625                .expect("module call should return number"),
3626            42.0
3627        );
3628    }
3629
3630    #[test]
3631    fn test_type_annotated_variable_no_wrapping() {
3632        // BUG-1/BUG-2 fix: variable declarations must NOT emit WrapTypeAnnotation
3633        // (the wrapper broke arithmetic and comparisons)
3634        let code = r#"
3635            type Currency = Number
3636            let x: Currency = 123
3637        "#;
3638        let program = parse_program(code).expect("Failed to parse");
3639        let bytecode = BytecodeCompiler::new()
3640            .compile(&program)
3641            .expect("Failed to compile");
3642
3643        // WrapTypeAnnotation should NOT be emitted for variable declarations
3644        let has_wrap_instruction = bytecode
3645            .instructions
3646            .iter()
3647            .any(|instr| instr.opcode == crate::bytecode::OpCode::WrapTypeAnnotation);
3648        assert!(
3649            !has_wrap_instruction,
3650            "Should NOT emit WrapTypeAnnotation for type-annotated variable"
3651        );
3652    }
3653
3654    #[test]
3655    fn test_untyped_variable_no_wrapping() {
3656        // Variables without type annotations should NOT emit WrapTypeAnnotation
3657        let code = r#"
3658            let x = 123
3659        "#;
3660        let program = parse_program(code).expect("Failed to parse");
3661        let bytecode = BytecodeCompiler::new()
3662            .compile(&program)
3663            .expect("Failed to compile");
3664
3665        // Check that WrapTypeAnnotation instruction was NOT emitted
3666        let has_wrap_instruction = bytecode
3667            .instructions
3668            .iter()
3669            .any(|instr| instr.opcode == crate::bytecode::OpCode::WrapTypeAnnotation);
3670        assert!(
3671            !has_wrap_instruction,
3672            "Should NOT emit WrapTypeAnnotation for untyped variable"
3673        );
3674    }
3675
3676    // ===== Phase 2: Extend Block Compilation Tests =====
3677
3678    #[test]
3679    fn test_extend_block_compiles() {
3680        let code = r#"
3681            extend Number {
3682                method double() {
3683                    return self * 2
3684                }
3685            }
3686        "#;
3687        let program = parse_program(code).expect("Failed to parse extend block");
3688        let bytecode = BytecodeCompiler::new().compile(&program);
3689        assert!(
3690            bytecode.is_ok(),
3691            "Extend block should compile: {:?}",
3692            bytecode.err()
3693        );
3694
3695        // Verify a function named "Number.double" was generated (qualified extend name).
3696        let bytecode = bytecode.unwrap();
3697        let has_double = bytecode.functions.iter().any(|f| f.name == "Number.double");
3698        assert!(
3699            has_double,
3700            "Should generate 'Number.double' function from extend block"
3701        );
3702    }
3703
3704    #[test]
3705    fn test_extend_method_has_self_param() {
3706        let code = r#"
3707            extend Number {
3708                method add(n) {
3709                    return self + n
3710                }
3711            }
3712        "#;
3713        let program = parse_program(code).expect("Failed to parse");
3714        let bytecode = BytecodeCompiler::new()
3715            .compile(&program)
3716            .expect("Failed to compile");
3717
3718        let func = bytecode.functions.iter().find(|f| f.name == "Number.add");
3719        assert!(func.is_some(), "Should have 'Number.add' function");
3720        // The function should have 2 params: self + n
3721        assert_eq!(
3722            func.unwrap().arity,
3723            2,
3724            "add() should have arity 2 (self + n)"
3725        );
3726    }
3727
3728    #[test]
3729    fn test_extend_method_rejects_explicit_self_param() {
3730        let code = r#"
3731            extend Number {
3732                method add(self, n) {
3733                    return self + n
3734                }
3735            }
3736        "#;
3737        let program = parse_program(code).expect("Failed to parse");
3738        let err = BytecodeCompiler::new()
3739            .compile(&program)
3740            .expect_err("Compiler should reject explicit self receiver param in methods");
3741        let msg = format!("{err}");
3742        assert!(
3743            msg.contains("explicit `self` parameter"),
3744            "Expected explicit self error, got: {msg}"
3745        );
3746    }
3747
3748    // ===== Phase 3: Annotation Handler Compilation Tests =====
3749
3750    #[test]
3751    fn test_annotation_def_compiles_handlers() {
3752        let code = r#"
3753            annotation warmup(period) {
3754                before(args, ctx) {
3755                    args
3756                }
3757                after(args, result, ctx) {
3758                    result
3759                }
3760            }
3761            function test() { return 42; }
3762        "#;
3763        let program = parse_program(code).expect("Failed to parse annotation def");
3764        let bytecode = BytecodeCompiler::new().compile(&program);
3765        assert!(
3766            bytecode.is_ok(),
3767            "Annotation def should compile: {:?}",
3768            bytecode.err()
3769        );
3770
3771        let bytecode = bytecode.unwrap();
3772        // Verify CompiledAnnotation was registered
3773        assert!(
3774            bytecode.compiled_annotations.contains_key("warmup"),
3775            "Should have compiled 'warmup' annotation"
3776        );
3777
3778        let compiled = bytecode.compiled_annotations.get("warmup").unwrap();
3779        assert!(
3780            compiled.before_handler.is_some(),
3781            "Should have before handler"
3782        );
3783        assert!(
3784            compiled.after_handler.is_some(),
3785            "Should have after handler"
3786        );
3787    }
3788
3789    #[test]
3790    fn test_annotation_handler_function_names() {
3791        let code = r#"
3792            annotation my_ann(x) {
3793                before(args, ctx) {
3794                    args
3795                }
3796            }
3797            function test() { return 1; }
3798        "#;
3799        let program = parse_program(code).expect("Failed to parse");
3800        let bytecode = BytecodeCompiler::new()
3801            .compile(&program)
3802            .expect("Failed to compile");
3803
3804        // Handler should be compiled as an internal function
3805        let compiled = bytecode.compiled_annotations.get("my_ann").unwrap();
3806        let handler_id = compiled.before_handler.unwrap() as usize;
3807        assert!(
3808            handler_id < bytecode.functions.len(),
3809            "Handler function ID should be valid"
3810        );
3811
3812        let handler_fn = &bytecode.functions[handler_id];
3813        assert_eq!(
3814            handler_fn.name, "my_ann___before",
3815            "Handler function should be named my_ann___before"
3816        );
3817    }
3818
3819    // ===== Phase 4: Compile-Time Function Wrapping Tests =====
3820
3821    #[test]
3822    fn test_annotated_function_generates_wrapper() {
3823        let code = r#"
3824            annotation tracked(label) {
3825                before(args, ctx) {
3826                    args
3827                }
3828            }
3829            @tracked("my_func")
3830            function compute(x) {
3831                return x * 2
3832            }
3833            function test() { return 1; }
3834        "#;
3835        let program = parse_program(code).expect("Failed to parse");
3836        let bytecode = BytecodeCompiler::new().compile(&program);
3837        assert!(
3838            bytecode.is_ok(),
3839            "Annotated function should compile: {:?}",
3840            bytecode.err()
3841        );
3842
3843        let bytecode = bytecode.unwrap();
3844        // Should have the original function (wrapper) and the impl
3845        let has_impl = bytecode
3846            .functions
3847            .iter()
3848            .any(|f| f.name == "compute___impl");
3849        assert!(has_impl, "Should generate compute___impl function");
3850
3851        let has_wrapper = bytecode.functions.iter().any(|f| f.name == "compute");
3852        assert!(has_wrapper, "Should keep compute as wrapper");
3853    }
3854
3855    #[test]
3856    fn test_unannotated_function_no_wrapper() {
3857        let code = r#"
3858            function plain(x) {
3859                return x + 1
3860            }
3861        "#;
3862        let program = parse_program(code).expect("Failed to parse");
3863        let bytecode = BytecodeCompiler::new()
3864            .compile(&program)
3865            .expect("Failed to compile");
3866
3867        // Should NOT have an ___impl function
3868        let has_impl = bytecode
3869            .functions
3870            .iter()
3871            .any(|f| f.name.ends_with("___impl"));
3872        assert!(
3873            !has_impl,
3874            "Non-annotated function should not generate ___impl"
3875        );
3876    }
3877
3878    // ===== Sprint 10: Annotation chaining and target validation =====
3879
3880    #[test]
3881    fn test_annotation_chaining_generates_chain() {
3882        // Two annotations on the same function should generate chained wrappers
3883        let code = r#"
3884            annotation first() {
3885                before(args, ctx) {
3886                    return args
3887                }
3888            }
3889
3890            annotation second() {
3891                before(args, ctx) {
3892                    return args
3893                }
3894            }
3895
3896            @first
3897            @second
3898            function compute(x) {
3899                return x * 2
3900            }
3901        "#;
3902        let program = parse_program(code).expect("Failed to parse");
3903        let bytecode = BytecodeCompiler::new().compile(&program);
3904        assert!(
3905            bytecode.is_ok(),
3906            "Chained annotations should compile: {:?}",
3907            bytecode.err()
3908        );
3909        let bytecode = bytecode.unwrap();
3910
3911        // Should have: compute (outermost wrapper), compute___impl (body), compute___second (intermediate)
3912        let has_impl = bytecode
3913            .functions
3914            .iter()
3915            .any(|f| f.name == "compute___impl");
3916        assert!(has_impl, "Should generate compute___impl function");
3917        let has_wrapper = bytecode.functions.iter().any(|f| f.name == "compute");
3918        assert!(has_wrapper, "Should keep compute as outermost wrapper");
3919        let has_intermediate = bytecode
3920            .functions
3921            .iter()
3922            .any(|f| f.name == "compute___second");
3923        assert!(
3924            has_intermediate,
3925            "Should generate compute___second intermediate wrapper"
3926        );
3927    }
3928
3929    #[test]
3930    fn test_annotation_allowed_targets_inferred() {
3931        // An annotation with before/after should have allowed_targets = [Function]
3932        let code = r#"
3933            annotation traced() {
3934                before(args, ctx) {
3935                    return args
3936                }
3937            }
3938        "#;
3939        let program = parse_program(code).expect("Failed to parse");
3940        let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
3941        let ann = bytecode
3942            .compiled_annotations
3943            .get("traced")
3944            .expect("traced annotation");
3945        assert!(
3946            !ann.allowed_targets.is_empty(),
3947            "before handler should restrict targets"
3948        );
3949        assert!(
3950            ann.allowed_targets
3951                .contains(&shape_ast::ast::functions::AnnotationTargetKind::Function),
3952            "before handler should allow Function target"
3953        );
3954    }
3955
3956    #[test]
3957    fn test_annotation_allowed_targets_explicit_override() {
3958        // Explicit `targets: [...]` should override inferred defaults.
3959        let code = r#"
3960            annotation traced() {
3961                targets: [type]
3962                before(args, ctx) {
3963                    return args
3964                }
3965            }
3966        "#;
3967        let program = parse_program(code).expect("Failed to parse");
3968        let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
3969        let ann = bytecode
3970            .compiled_annotations
3971            .get("traced")
3972            .expect("traced annotation");
3973        assert_eq!(
3974            ann.allowed_targets,
3975            vec![shape_ast::ast::functions::AnnotationTargetKind::Type]
3976        );
3977    }
3978
3979    #[test]
3980    fn test_metadata_only_annotation_defaults_to_definition_targets() {
3981        // An annotation with only metadata handler should default to definition targets.
3982        let code = r#"
3983            annotation info() {
3984                metadata() {
3985                    return { version: 1 }
3986                }
3987            }
3988        "#;
3989        let program = parse_program(code).expect("Failed to parse");
3990        let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
3991        let ann = bytecode
3992            .compiled_annotations
3993            .get("info")
3994            .expect("info annotation");
3995        assert_eq!(
3996            ann.allowed_targets,
3997            vec![
3998                shape_ast::ast::functions::AnnotationTargetKind::Function,
3999                shape_ast::ast::functions::AnnotationTargetKind::Type,
4000                shape_ast::ast::functions::AnnotationTargetKind::Module
4001            ],
4002            "metadata-only annotation should default to definition targets"
4003        );
4004    }
4005
4006    #[test]
4007    fn test_definition_lifecycle_targets_reject_expression_target() {
4008        let code = r#"
4009            annotation info() {
4010                targets: [expression]
4011                metadata(target, ctx) {
4012                    target.name
4013                }
4014            }
4015        "#;
4016        let program = parse_program(code).expect("Failed to parse");
4017        let err = BytecodeCompiler::new()
4018            .compile(&program)
4019            .expect_err("metadata hooks on expression targets should fail");
4020        let msg = format!("{}", err);
4021        assert!(
4022            msg.contains("not a definition target"),
4023            "expected definition-target restriction error, got: {}",
4024            msg
4025        );
4026    }
4027
4028    #[test]
4029    fn test_annotation_target_validation_on_struct_type() {
4030        // Function-only annotation applied to a type should fail.
4031        let code = r#"
4032            annotation traced() {
4033                before(args, ctx) { return args }
4034            }
4035
4036            @traced()
4037            type Point { x: int }
4038        "#;
4039        let program = parse_program(code).expect("Failed to parse");
4040        let err = BytecodeCompiler::new()
4041            .compile(&program)
4042            .expect_err("function-only annotation on type should fail");
4043        let msg = format!("{}", err);
4044        assert!(
4045            msg.contains("cannot be applied to a type"),
4046            "expected type target validation error, got: {}",
4047            msg
4048        );
4049    }
4050
4051    #[test]
4052    fn test_type_c_emits_native_layout_metadata() {
4053        let bytecode = compiles_to(
4054            r#"
4055            type C Pair32 {
4056                left: i32,
4057                right: i32,
4058            }
4059            "#,
4060        );
4061
4062        assert_eq!(bytecode.native_struct_layouts.len(), 1);
4063        let layout = &bytecode.native_struct_layouts[0];
4064        assert_eq!(layout.name, "Pair32");
4065        assert_eq!(layout.abi, "C");
4066        assert_eq!(layout.size, 8);
4067        assert_eq!(layout.align, 4);
4068        assert_eq!(layout.fields.len(), 2);
4069        assert_eq!(layout.fields[0].name, "left");
4070        assert_eq!(layout.fields[0].offset, 0);
4071        assert_eq!(layout.fields[0].size, 4);
4072        assert_eq!(layout.fields[1].name, "right");
4073        assert_eq!(layout.fields[1].offset, 4);
4074        assert_eq!(layout.fields[1].size, 4);
4075    }
4076
4077    #[test]
4078    fn test_type_c_auto_generates_into_from_traits() {
4079        let bytecode = compiles_to(
4080            r#"
4081            type C QuoteC {
4082                bid: i64,
4083                ask: i64,
4084            }
4085
4086            type Quote {
4087                bid: i64,
4088                ask: i64,
4089            }
4090            "#,
4091        );
4092
4093        let c_to_shape =
4094            bytecode.lookup_trait_method_symbol("Into", "QuoteC", Some("Quote"), "into");
4095        let shape_to_c =
4096            bytecode.lookup_trait_method_symbol("Into", "Quote", Some("QuoteC"), "into");
4097        let from_c = bytecode.lookup_trait_method_symbol("From", "Quote", Some("QuoteC"), "from");
4098        let from_shape =
4099            bytecode.lookup_trait_method_symbol("From", "QuoteC", Some("Quote"), "from");
4100
4101        assert!(c_to_shape.is_some(), "expected Into<Quote> for QuoteC");
4102        assert!(shape_to_c.is_some(), "expected Into<QuoteC> for Quote");
4103        assert!(from_c.is_some(), "expected From<QuoteC> for Quote");
4104        assert!(from_shape.is_some(), "expected From<Quote> for QuoteC");
4105    }
4106
4107    #[test]
4108    fn test_type_c_auto_conversion_function_compiles() {
4109        let _ = compiles_to(
4110            r#"
4111            type Quote {
4112                bid: i64,
4113                ask: i64,
4114            }
4115
4116            type C QuoteC {
4117                bid: i64,
4118                ask: i64,
4119            }
4120
4121            fn spread(q: QuoteC) -> i64 {
4122                let q_shape = __auto_native_from_QuoteC_to_Quote(q);
4123                q_shape.ask - q_shape.bid
4124            }
4125
4126            spread(QuoteC { bid: 10, ask: 13 })
4127            "#,
4128        );
4129    }
4130
4131    #[test]
4132    fn test_type_c_auto_conversion_rejects_incompatible_fields() {
4133        let program = parse_program(
4134            r#"
4135            type Price {
4136                value: i64,
4137            }
4138
4139            type C PriceC {
4140                value: u64,
4141            }
4142            "#,
4143        )
4144        .expect("parse failed");
4145        let err = BytecodeCompiler::new()
4146            .compile(&program)
4147            .expect_err("incompatible type C conversion pair should fail");
4148        let msg = format!("{}", err);
4149        assert!(
4150            msg.contains("field type mismatch for auto conversion"),
4151            "expected type mismatch error, got: {}",
4152            msg
4153        );
4154    }
4155
4156    // ===== Task 1: Meta on traits =====
4157
4158    // ===== Drop Track: Sprint 2 Tests =====
4159
4160    fn compiles_to(code: &str) -> crate::bytecode::BytecodeProgram {
4161        let program = parse_program(code).expect("parse failed");
4162        let compiler = BytecodeCompiler::new();
4163        compiler.compile(&program).expect("compile failed")
4164    }
4165
4166    // --- Permission checking tests ---
4167
4168    #[test]
4169    fn test_extract_module_name() {
4170        assert_eq!(BytecodeCompiler::extract_module_name("file"), "file");
4171        assert_eq!(BytecodeCompiler::extract_module_name("std::file"), "file");
4172        assert_eq!(BytecodeCompiler::extract_module_name("std/io"), "io");
4173        assert_eq!(BytecodeCompiler::extract_module_name("a::b::c"), "c");
4174        assert_eq!(BytecodeCompiler::extract_module_name(""), "");
4175    }
4176
4177    #[test]
4178    fn test_permission_check_allows_pure_module_imports() {
4179        // json is a pure module — should compile even with empty permissions
4180        let code = "from json use { parse }";
4181        let program = parse_program(code).expect("parse failed");
4182        let mut compiler = BytecodeCompiler::new();
4183        compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4184        // Should not fail — json requires no permissions
4185        let _result = compiler.compile(&program);
4186    }
4187
4188    #[test]
4189    fn test_permission_check_blocks_file_import_under_pure() {
4190        let code = "from file use { read_text }";
4191        let program = parse_program(code).expect("parse failed");
4192        let mut compiler = BytecodeCompiler::new();
4193        compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4194        let result = compiler.compile(&program);
4195        assert!(
4196            result.is_err(),
4197            "Expected permission error for file::read_text under pure"
4198        );
4199        let err_msg = format!("{}", result.unwrap_err());
4200        assert!(
4201            err_msg.contains("Permission denied"),
4202            "Error should mention permission denied: {err_msg}"
4203        );
4204        assert!(
4205            err_msg.contains("fs.read"),
4206            "Error should mention fs.read: {err_msg}"
4207        );
4208    }
4209
4210    #[test]
4211    fn test_permission_check_allows_file_import_with_fs_read() {
4212        let code = "from file use { read_text }";
4213        let program = parse_program(code).expect("parse failed");
4214        let mut compiler = BytecodeCompiler::new();
4215        let pset = shape_abi_v1::PermissionSet::from_iter([shape_abi_v1::Permission::FsRead]);
4216        compiler.set_permission_set(Some(pset));
4217        // Should not fail
4218        let _result = compiler.compile(&program);
4219    }
4220
4221    #[test]
4222    fn test_permission_check_no_permission_set_allows_everything() {
4223        // When permission_set is None (default), no checking is done
4224        let code = "from file use { read_text }";
4225        let program = parse_program(code).expect("parse failed");
4226        let compiler = BytecodeCompiler::new();
4227        // permission_set is None by default — should compile fine
4228        let _result = compiler.compile(&program);
4229    }
4230
4231    #[test]
4232    fn test_permission_check_namespace_import_blocked() {
4233        let code = "use http";
4234        let program = parse_program(code).expect("parse failed");
4235        let mut compiler = BytecodeCompiler::new();
4236        compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
4237        let result = compiler.compile(&program);
4238        assert!(
4239            result.is_err(),
4240            "Expected permission error for `use http` under pure"
4241        );
4242        let err_msg = format!("{}", result.unwrap_err());
4243        assert!(
4244            err_msg.contains("Permission denied"),
4245            "Error should mention permission denied: {err_msg}"
4246        );
4247    }
4248
4249    #[test]
4250    fn test_permission_check_namespace_import_allowed() {
4251        let code = "use http";
4252        let program = parse_program(code).expect("parse failed");
4253        let mut compiler = BytecodeCompiler::new();
4254        compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::full()));
4255        // Should not fail
4256        let _result = compiler.compile(&program);
4257    }
4258}