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, Spanned,
7    Statement, TypeAnnotation, VarKind,
8};
9use shape_ast::error::{Result, ShapeError};
10use shape_runtime::type_schema::{EnumVariantInfo, FieldType};
11
12use super::{
13    BytecodeCompiler, DropKind, ImportedAnnotationSymbol, ImportedSymbol, ModuleBuiltinFunction,
14    ParamPassMode, StructGenericInfo,
15};
16
17#[derive(Debug, Clone)]
18struct NativeFieldLayoutSpec {
19    c_type: String,
20    size: u64,
21    align: u64,
22}
23
24impl BytecodeCompiler {
25    fn register_builtin_function_decl(
26        &mut self,
27        def: &shape_ast::ast::BuiltinFunctionDecl,
28    ) -> Result<()> {
29        let export_name = def
30            .name
31            .rsplit("::")
32            .next()
33            .unwrap_or(def.name.as_str())
34            .to_string();
35        let source_module_path = if let Some((owner_module, _)) = def.name.rsplit_once("::") {
36            self.resolve_canonical_module_path(owner_module)
37                .unwrap_or_else(|| owner_module.to_string())
38        } else {
39            return Ok(());
40        };
41
42        self.module_builtin_functions.insert(
43            def.name.clone(),
44            ModuleBuiltinFunction {
45                export_name,
46                source_module_path,
47            },
48        );
49        Ok(())
50    }
51
52    fn emit_comptime_internal_call(
53        &mut self,
54        method: &str,
55        args: Vec<Expr>,
56        span: Span,
57    ) -> Result<()> {
58        let call = Expr::QualifiedFunctionCall {
59            namespace: "__comptime__".to_string(),
60            function: method.to_string(),
61            args,
62            named_args: Vec::new(),
63            span,
64        };
65        let prev = self.allow_internal_comptime_namespace;
66        self.allow_internal_comptime_namespace = true;
67        let compile_result = self.compile_expr(&call);
68        self.allow_internal_comptime_namespace = prev;
69        compile_result?;
70        self.emit(Instruction::simple(OpCode::Pop));
71        Ok(())
72    }
73
74    /// Serialize a value to JSON for comptime directive payloads.
75    ///
76    /// Wraps serde_json serialization errors into ShapeError with the given
77    /// directive label for diagnostics.
78    fn serialize_directive_payload(
79        &self,
80        value: &(impl serde::Serialize + ?Sized),
81        directive_label: &str,
82        span: Span,
83    ) -> Result<String> {
84        serde_json::to_string(value).map_err(|e| ShapeError::RuntimeError {
85            message: format!("Failed to serialize comptime {} directive: {}", directive_label, e),
86            location: Some(self.span_to_source_location(span)),
87        })
88    }
89
90    /// Check that the compiler is in comptime mode, returning an error otherwise.
91    fn require_comptime_mode(&self, directive_name: &str, span: Span) -> Result<()> {
92        if !self.comptime_mode {
93            return Err(ShapeError::SemanticError {
94                message: format!("`{}` is only valid inside `comptime {{}}` context", directive_name),
95                location: Some(self.span_to_source_location(span)),
96            });
97        }
98        Ok(())
99    }
100
101    fn emit_comptime_extend_directive(
102        &mut self,
103        extend: &shape_ast::ast::ExtendStatement,
104        span: Span,
105    ) -> Result<()> {
106        let payload = self.serialize_directive_payload(extend, "extend", span)?;
107        self.emit_comptime_internal_call(
108            "__emit_extend",
109            vec![Expr::Literal(Literal::String(payload), span)],
110            span,
111        )
112    }
113
114    fn emit_comptime_remove_directive(&mut self, span: Span) -> Result<()> {
115        self.emit_comptime_internal_call("__emit_remove", Vec::new(), span)
116    }
117
118    fn emit_comptime_set_param_value_directive(
119        &mut self,
120        param_name: &str,
121        expression: &Expr,
122        span: Span,
123    ) -> Result<()> {
124        self.emit_comptime_internal_call(
125            "__emit_set_param_value",
126            vec![
127                Expr::Literal(Literal::String(param_name.to_string()), span),
128                expression.clone(),
129            ],
130            span,
131        )
132    }
133
134    fn emit_comptime_set_param_type_directive(
135        &mut self,
136        param_name: &str,
137        type_annotation: &TypeAnnotation,
138        span: Span,
139    ) -> Result<()> {
140        let payload = self.serialize_directive_payload(type_annotation, "param type", span)?;
141        self.emit_comptime_internal_call(
142            "__emit_set_param_type",
143            vec![
144                Expr::Literal(Literal::String(param_name.to_string()), span),
145                Expr::Literal(Literal::String(payload), span),
146            ],
147            span,
148        )
149    }
150
151    fn emit_comptime_set_return_type_directive(
152        &mut self,
153        type_annotation: &TypeAnnotation,
154        span: Span,
155    ) -> Result<()> {
156        let payload = self.serialize_directive_payload(type_annotation, "return type", span)?;
157        self.emit_comptime_internal_call(
158            "__emit_set_return_type",
159            vec![Expr::Literal(Literal::String(payload), span)],
160            span,
161        )
162    }
163
164    fn emit_comptime_set_return_expr_directive(
165        &mut self,
166        expression: &Expr,
167        span: Span,
168    ) -> Result<()> {
169        self.emit_comptime_internal_call("__emit_set_return_type", vec![expression.clone()], span)
170    }
171
172    fn emit_comptime_replace_body_directive(
173        &mut self,
174        body: &[Statement],
175        span: Span,
176    ) -> Result<()> {
177        let payload = self.serialize_directive_payload(body, "replace-body", span)?;
178        self.emit_comptime_internal_call(
179            "__emit_replace_body",
180            vec![Expr::Literal(Literal::String(payload), span)],
181            span,
182        )
183    }
184
185    fn emit_comptime_replace_body_expr_directive(
186        &mut self,
187        expression: &Expr,
188        span: Span,
189    ) -> Result<()> {
190        self.emit_comptime_internal_call("__emit_replace_body", vec![expression.clone()], span)
191    }
192
193    fn emit_comptime_replace_module_expr_directive(
194        &mut self,
195        expression: &Expr,
196        span: Span,
197    ) -> Result<()> {
198        self.emit_comptime_internal_call("__emit_replace_module", vec![expression.clone()], span)
199    }
200
201    pub(super) fn register_item_functions(&mut self, item: &Item) -> Result<()> {
202        match item {
203            Item::Function(func_def, _) => self.register_function(func_def),
204            Item::BuiltinFunctionDecl(def, _) => self.register_builtin_function_decl(def),
205            Item::Module(module_def, _) => {
206                let module_path = self.current_module_path_for(module_def.name.as_str());
207                self.module_scope_stack.push(module_path.clone());
208                let register_result = (|| -> Result<()> {
209                    for inner in &module_def.items {
210                        let qualified = self.qualify_module_item(inner, &module_path)?;
211                        self.register_item_functions(&qualified)?;
212                    }
213                    Ok(())
214                })();
215                self.module_scope_stack.pop();
216                register_result
217            }
218            Item::Trait(trait_def, _) => {
219                self.known_traits.insert(trait_def.name.clone());
220                self.trait_defs
221                    .insert(trait_def.name.clone(), trait_def.clone());
222                // Register in type inference environment so supertrait checking works
223                self.type_inference.env.define_trait(trait_def);
224                Ok(())
225            }
226            Item::ForeignFunction(def, _) => {
227                // Register as a normal function so call sites resolve the name.
228                // Caller-visible arity excludes `out` params.
229                let caller_visible = def.params.iter().filter(|p| !p.is_out).count();
230                self.function_arity_bounds
231                    .insert(def.name.clone(), (caller_visible, caller_visible));
232                self.function_const_params
233                    .insert(def.name.clone(), Vec::new());
234                let (ref_params, ref_mutates) = Self::native_param_reference_contract(def);
235                let (vis_ref_params, vis_ref_mutates) = if def.params.iter().any(|p| p.is_out) {
236                    let mut vrp = Vec::new();
237                    let mut vrm = Vec::new();
238                    for (i, p) in def.params.iter().enumerate() {
239                        if !p.is_out {
240                            vrp.push(ref_params.get(i).copied().unwrap_or(false));
241                            vrm.push(ref_mutates.get(i).copied().unwrap_or(false));
242                        }
243                    }
244                    (vrp, vrm)
245                } else {
246                    (ref_params, ref_mutates)
247                };
248
249                let func = crate::bytecode::Function {
250                    name: def.name.clone(),
251                    arity: caller_visible as u16,
252                    param_names: def
253                        .params
254                        .iter()
255                        .filter(|p| !p.is_out)
256                        .flat_map(|p| p.get_identifiers())
257                        .collect(),
258                    locals_count: 0,
259                    entry_point: 0,
260                    body_length: 0,
261                    is_closure: false,
262                    captures_count: 0,
263                    is_async: def.is_async,
264                    ref_params: vis_ref_params,
265                    ref_mutates: vis_ref_mutates,
266                    mutable_captures: Vec::new(),
267                    frame_descriptor: None,
268                    osr_entry_points: Vec::new(),
269                    mir_data: None,
270                };
271                self.program.functions.push(func);
272
273                // Store the foreign function def so call sites can resolve
274                // the declared return type (must be Result<T> for dynamic languages).
275                self.foreign_function_defs
276                    .insert(def.name.clone(), def.clone());
277
278                Ok(())
279            }
280            Item::Export(export, _) => match &export.item {
281                ExportItem::Function(func_def) => self.register_function(func_def),
282                ExportItem::BuiltinFunction(def) => self.register_builtin_function_decl(def),
283                ExportItem::Trait(trait_def) => {
284                    self.known_traits.insert(trait_def.name.clone());
285                    self.trait_defs
286                        .insert(trait_def.name.clone(), trait_def.clone());
287                    // Register in type inference environment so supertrait checking works
288                    self.type_inference.env.define_trait(trait_def);
289                    Ok(())
290                }
291                ExportItem::Annotation(annotation_def) => {
292                    self.compile_annotation_def(annotation_def)
293                }
294                ExportItem::ForeignFunction(def) => {
295                    // Same registration as Item::ForeignFunction
296                    let caller_visible = def.params.iter().filter(|p| !p.is_out).count();
297                    self.function_arity_bounds
298                        .insert(def.name.clone(), (caller_visible, caller_visible));
299                    self.function_const_params
300                        .insert(def.name.clone(), Vec::new());
301                    let (ref_params, ref_mutates) = Self::native_param_reference_contract(def);
302                    let (vis_ref_params, vis_ref_mutates) = if def.params.iter().any(|p| p.is_out) {
303                        let mut vrp = Vec::new();
304                        let mut vrm = Vec::new();
305                        for (i, p) in def.params.iter().enumerate() {
306                            if !p.is_out {
307                                vrp.push(ref_params.get(i).copied().unwrap_or(false));
308                                vrm.push(ref_mutates.get(i).copied().unwrap_or(false));
309                            }
310                        }
311                        (vrp, vrm)
312                    } else {
313                        (ref_params, ref_mutates)
314                    };
315
316                    let func = crate::bytecode::Function {
317                        name: def.name.clone(),
318                        arity: caller_visible as u16,
319                        param_names: def
320                            .params
321                            .iter()
322                            .filter(|p| !p.is_out)
323                            .flat_map(|p| p.get_identifiers())
324                            .collect(),
325                        locals_count: 0,
326                        entry_point: 0,
327                        body_length: 0,
328                        is_closure: false,
329                        captures_count: 0,
330                        is_async: def.is_async,
331                        ref_params: vis_ref_params,
332                        ref_mutates: vis_ref_mutates,
333                        mutable_captures: Vec::new(),
334                        frame_descriptor: None,
335                        osr_entry_points: Vec::new(),
336                        mir_data: None,
337                    };
338                    self.program.functions.push(func);
339
340                    self.foreign_function_defs
341                        .insert(def.name.clone(), def.clone());
342
343                    Ok(())
344                }
345                _ => Ok(()),
346            },
347            Item::Extend(extend, _) => {
348                // Desugar extend methods to functions with implicit `self` receiver param.
349                for method in &extend.methods {
350                    let func_def = self.desugar_extend_method(method, &extend.type_name)?;
351                    self.register_function(&func_def)?;
352                }
353                Ok(())
354            }
355            Item::Impl(impl_block, _) => {
356                // J-CT.2 (2026-05-23) — comptime impl blocks are deferred
357                // for in-mini-VM registration. The outer compiler does not
358                // desugar/register/compile their methods into the runtime
359                // program. They are stored on `comptime_impl_blocks` so the
360                // comptime evaluator (`compiler/comptime.rs::execute_comptime`)
361                // can prepend them as `Item::Impl` items into the mini-VM
362                // program, where the in-comptime-mode compiler then
363                // processes them through this same arm normally. Audit
364                // §2.D carve-out: no new dispatch shape — comptime-trait
365                // methods reuse the standard UFCS / `Type::method`
366                // resolution path; the difference is *when* they're
367                // available (only inside `comptime { }`), not *how*.
368                if impl_block.is_comptime {
369                    self.comptime_impl_blocks.push(impl_block.clone());
370                    return Ok(());
371                }
372                // Impl blocks use scoped UFCS names.
373                // - default impl: "Type::method" (legacy compatibility)
374                // - named impl: "Trait::Type::ImplName::method"
375                // This prevents conflicts when multiple named impls exist.
376                let raw_trait_name = match &impl_block.trait_name {
377                    shape_ast::ast::types::TypeName::Simple(n) => n.as_str(),
378                    shape_ast::ast::types::TypeName::Generic { name, .. } => name.as_str(),
379                };
380                let type_name = match &impl_block.target_type {
381                    shape_ast::ast::types::TypeName::Simple(n) => n.as_str(),
382                    shape_ast::ast::types::TypeName::Generic { name, .. } => name.as_str(),
383                };
384                let impl_name = impl_block.impl_name.as_deref();
385
386                // Resolve trait name: canonical for def lookup, basename for dispatch
387                let (canonical_trait, trait_basename) = self.resolve_trait_name(raw_trait_name);
388
389                // From/TryFrom impls use reverse-conversion desugaring:
390                // the method takes an explicit `value` param (no implicit self),
391                // and we auto-derive Into/TryInto trait symbols on the source type.
392                if trait_basename == "From" || trait_basename == "TryFrom" {
393                    return self.compile_from_impl(impl_block, &trait_basename, type_name);
394                }
395
396                // Collect names of methods explicitly provided in the impl block
397                let overridden: std::collections::HashSet<&str> =
398                    impl_block.methods.iter().map(|m| m.name.as_str()).collect();
399
400                for method in &impl_block.methods {
401                    let func_def = self.desugar_impl_method(
402                        method,
403                        &trait_basename,
404                        type_name,
405                        impl_name,
406                        &impl_block.target_type,
407                    )?;
408                    self.program.register_trait_method_symbol(
409                        &trait_basename,
410                        type_name,
411                        impl_name,
412                        &method.name,
413                        &func_def.name,
414                    );
415                    self.register_function(&func_def)?;
416
417                    // Track drop kind per type (sync, async, or both)
418                    if trait_basename == "Drop" && method.name == "drop" {
419                        let type_key = type_name.to_string();
420                        let existing = self.drop_type_info.get(&type_key).copied();
421                        let new_kind = if method.is_async {
422                            match existing {
423                                Some(DropKind::SyncOnly) | Some(DropKind::Both) => DropKind::Both,
424                                _ => DropKind::AsyncOnly,
425                            }
426                        } else {
427                            match existing {
428                                Some(DropKind::AsyncOnly) | Some(DropKind::Both) => DropKind::Both,
429                                _ => DropKind::SyncOnly,
430                            }
431                        };
432                        self.drop_type_info.insert(type_key, new_kind);
433                    }
434                }
435
436                // Install default methods from the trait definition that were not overridden
437                if let Some(trait_def) = self.trait_defs.get(&canonical_trait).cloned() {
438                    for member in &trait_def.members {
439                        if let shape_ast::ast::types::TraitMember::Default(default_method) = member
440                        {
441                            if !overridden.contains(default_method.name.as_str()) {
442                                let func_def = self.desugar_impl_method(
443                                    default_method,
444                                    &trait_basename,
445                                    type_name,
446                                    impl_name,
447                                    &impl_block.target_type,
448                                )?;
449                                self.program.register_trait_method_symbol(
450                                    &trait_basename,
451                                    type_name,
452                                    impl_name,
453                                    &default_method.name,
454                                    &func_def.name,
455                                );
456                                self.register_function(&func_def)?;
457                            }
458                        }
459                    }
460                }
461
462                // ADR-006 §2.7.24 Q25.C: build a VTable for this
463                // `(impl Trait for Type)` pair. The VTable is the
464                // runtime artifact `op_box_trait_object` consults to
465                // allocate `Arc<TraitObjectStorage>` at coerce-to-dyn
466                // sites, and the artifact `op_dyn_method_call` consults
467                // to dispatch methods through `dyn T`. Built once per
468                // impl, shared via `Arc<VTable>` (the vtable half of
469                // the fat pointer; see §Q25.C row 1).
470                //
471                // Trait method shapes handled at this round (Wave 2.6
472                // round-2):
473                //  - `Direct`: return type does not name `Self`, no
474                //    Self-typed args, no method generics. Plain
475                //    function-id dispatch.
476                //  - `BoxedReturn`: return type is `Self` (path=[]).
477                //    The auto-boxing thunk is generated by reusing the
478                //    impl's function and post-processing its return at
479                //    `op_dyn_method_call` time.
480                //
481                // Defection-attractor-safe surface for unhandled shapes:
482                // `SelfArg` (§Q25.C.2), `Generic` (§Q25.C.3), `Compound`
483                // (§Q25.C.5), and `BoxedReturn` with nested `Self`
484                // (`Result<Self, E>`, `(Self, Self)`, etc.) surface as
485                // `VMError::NotImplemented(SURFACE: ...)` at dispatch
486                // time — see `op_dyn_method_call`. No silent default,
487                // no Bool-default fallback (CLAUDE.md "Renames to refuse
488                // on sight"; phase-2d-hardening item (a)).
489                self.build_and_register_vtable(
490                    &trait_basename,
491                    type_name,
492                    impl_block,
493                )?;
494
495                // BUG-4.6 fix: Register the trait impl in the type inference
496                // environment so that `implements()` can see it at comptime.
497                let all_method_names: Vec<String> =
498                    impl_block.methods.iter().map(|m| m.name.clone()).collect();
499                if let Some(selector) = impl_name {
500                    let _ = self.type_inference.env.register_trait_impl_named(
501                        &trait_basename,
502                        type_name,
503                        selector,
504                        all_method_names,
505                    );
506                } else {
507                    let _ = self.type_inference.env.register_trait_impl(
508                        &trait_basename,
509                        type_name,
510                        all_method_names,
511                    );
512                }
513
514                // C3: Verify supertrait constraints.
515                // If the trait has supertraits (e.g. `trait Foo: Bar + Baz`),
516                // check that the target type also implements each supertrait.
517                if let Some(trait_def) = self.trait_defs.get(&canonical_trait).cloned() {
518                    for super_ann in &trait_def.super_traits {
519                        let super_name = match super_ann {
520                            TypeAnnotation::Basic(name) => name.clone(),
521                            TypeAnnotation::Reference(name) => name.to_string(),
522                            TypeAnnotation::Generic { name, .. } => name.to_string(),
523                            _ => continue,
524                        };
525                        let (_canonical_super, super_basename) = self.resolve_trait_name(&super_name);
526                        if !self
527                            .type_inference
528                            .env
529                            .type_implements_trait(type_name, &super_basename)
530                        {
531                            return Err(ShapeError::SemanticError {
532                                message: format!(
533                                    "impl {} for {} requires supertrait '{}' to be implemented first",
534                                    trait_basename, type_name, super_basename
535                                ),
536                                location: None,
537                            });
538                        }
539                    }
540                }
541
542                Ok(())
543            }
544            _ => Ok(()),
545        }
546    }
547
548    /// WS-9b pass-1 prepass: pre-register the runtime `TypeSchema` of every
549    /// struct type declared anywhere in the program (including inside
550    /// modules), so struct schemas are available before any function body
551    /// compiles. Mirrors `register_item_functions`'s module recursion and
552    /// name qualification so a module-scoped `type` registers under its
553    /// fully-qualified name.
554    ///
555    /// Schema registration only — comptime-annotation handlers and the
556    /// annotation lifecycle stay in pass 2's `register_struct_type`
557    /// (`predeclare_struct_schema`'s `is_some()` guard makes that pass-2
558    /// schema registration a no-op once the prepass has run).
559    pub(super) fn predeclare_item_struct_schemas(&mut self, item: &Item) {
560        match item {
561            Item::StructType(struct_def, _) => {
562                self.predeclare_struct_schema(struct_def);
563            }
564            Item::Export(export, _) => {
565                if let ExportItem::Struct(struct_def) = &export.item {
566                    self.predeclare_struct_schema(struct_def);
567                }
568            }
569            Item::Module(module_def, _) => {
570                let module_path = self.current_module_path_for(module_def.name.as_str());
571                self.module_scope_stack.push(module_path.clone());
572                for inner in &module_def.items {
573                    if let Ok(qualified) = self.qualify_module_item(inner, &module_path) {
574                        self.predeclare_item_struct_schemas(&qualified);
575                    }
576                }
577                self.module_scope_stack.pop();
578            }
579            _ => {}
580        }
581    }
582
583    /// Register a function definition
584    pub(super) fn register_function(&mut self, func_def: &FunctionDef) -> Result<()> {
585        // Detect duplicate function definitions (Shape does not support overloading).
586        // Skip names containing "::" (trait impl methods) or "." (extend methods)
587        // — those are type-qualified and live in separate namespaces.
588        if !func_def.name.contains("::") && !func_def.name.contains('.') {
589            if let Some(existing) = self
590                .program
591                .functions
592                .iter()
593                .find(|f| f.name == func_def.name)
594            {
595                // Allow idempotent re-registration from module inlining: when the
596                // prelude and an explicitly imported module both define the same helper
597                // function (e.g., `percentile`), silently keep the first definition
598                // if arities match. Different arities indicate a genuine conflict.
599                if existing.arity == func_def.params.len() as u16 {
600                    return Ok(());
601                }
602                return Err(ShapeError::SemanticError {
603                    message: format!(
604                        "Duplicate function definition: '{}' is already defined",
605                        func_def.name
606                    ),
607                    location: Some(self.span_to_source_location(func_def.name_span)),
608                });
609            }
610        }
611
612        self.function_defs
613            .insert(func_def.name.clone(), func_def.clone());
614
615        let total_params = func_def.params.len();
616        let mut required_params = total_params;
617        let mut saw_default = false;
618        let mut const_params = Vec::new();
619        for (idx, param) in func_def.params.iter().enumerate() {
620            if param.is_const {
621                const_params.push(idx);
622            }
623            if param.default_value.is_some() {
624                if !saw_default {
625                    required_params = idx;
626                    saw_default = true;
627                }
628            } else if saw_default {
629                return Err(ShapeError::SemanticError {
630                    message: "Required parameter cannot follow a parameter with a default value"
631                        .to_string(),
632                    location: Some(self.span_to_source_location(param.span())),
633                });
634            }
635        }
636
637        self.function_arity_bounds
638            .insert(func_def.name.clone(), (required_params, total_params));
639        self.function_const_params
640            .insert(func_def.name.clone(), const_params);
641
642        let inferred_param_modes = self
643            .inferred_param_pass_modes
644            .get(&func_def.name)
645            .cloned()
646            .unwrap_or_default();
647        let mut ref_params: Vec<bool> = Vec::with_capacity(func_def.params.len());
648        let mut ref_mutates: Vec<bool> = Vec::with_capacity(func_def.params.len());
649        for (idx, param) in func_def.params.iter().enumerate() {
650            let fallback = if param.is_reference {
651                ParamPassMode::ByRefShared
652            } else {
653                ParamPassMode::ByValue
654            };
655            let mode = inferred_param_modes.get(idx).copied().unwrap_or(fallback);
656            ref_params.push(mode.is_reference());
657            ref_mutates.push(mode.is_exclusive());
658        }
659
660        let func = Function {
661            name: func_def.name.clone(),
662            arity: func_def.params.len() as u16,
663            param_names: func_def
664                .params
665                .iter()
666                .flat_map(|p| p.get_identifiers())
667                .collect(),
668            locals_count: 0, // Will be updated during compilation
669            entry_point: 0,  // Will be updated during compilation
670            body_length: 0,  // Will be updated during compilation
671            is_closure: false,
672            captures_count: 0,
673            is_async: func_def.is_async,
674            ref_params,
675            ref_mutates,
676            mutable_captures: Vec::new(),
677            frame_descriptor: None,
678            osr_entry_points: Vec::new(),
679            mir_data: None,
680        };
681
682        self.program.functions.push(func);
683
684        // Register function return type for typed opcode emission.
685        // When a function has an explicit return type annotation (e.g., `: int`),
686        // record it so that call sites can propagate NumericType through expressions
687        // like `fib(n-1) + fib(n-2)` and emit AddInt instead of generic Add.
688        if let Some(ref return_type) = func_def.return_type {
689            if let Some(type_name) = return_type.as_simple_name() {
690                self.type_tracker
691                    .register_function_return_type(&func_def.name, type_name);
692            }
693        }
694
695        Ok(())
696    }
697
698    /// Compile a top-level item with context about whether it's the last item
699    /// If is_last is true and the item is an expression, keep the result on the stack
700    pub(super) fn compile_item_with_context(&mut self, item: &Item, is_last: bool) -> Result<()> {
701        match item {
702            Item::Function(func_def, _) => self.compile_function(func_def)?,
703            Item::Module(module_def, span) => {
704                self.compile_module_decl(module_def, *span)?;
705            }
706            Item::VariableDecl(var_decl, span) => {
707                // R8 W8 Cluster A (2026-05-24): module-level `const`
708                // initializers must be comptime-evaluable. Reject
709                // runtime-only initializers (function calls, identifier
710                // references to non-const bindings, etc.) with a clean
711                // compile error. ADR-006 §2.7.5 stamp-at-compile-time
712                // invariant: the const's value must be known at compile
713                // time so the bytecode emits `PushConst(<value>)` instead
714                // of a deferred runtime computation.
715                if var_decl.kind == shape_ast::ast::VarKind::Const {
716                    if let Some(ref init_expr) = var_decl.value {
717                        if !Self::const_initializer_is_comptime_evaluable(init_expr) {
718                            return Err(ShapeError::SemanticError {
719                                message: format!(
720                                    "module-level `const` initializer must be comptime-evaluable \
721                                     (literal, or unary `-`/`!` on a literal). \
722                                     Function calls and other runtime-dependent expressions are \
723                                     rejected per R8 W8 Cluster A (2026-05-24). \
724                                     Extending the comptime evaluator is v0.4-concurrency-design-pass \
725                                     territory per docs/v0.3-close-summary.md \u{a7}5.15."
726                                ),
727                                location: Some(self.span_to_source_location(*span)),
728                            });
729                        }
730                    }
731                }
732                // ModuleBinding variable — register the variable even if the initializer fails,
733                // to prevent cascading "Undefined variable" errors on later references.
734                let mut ref_borrow = None;
735                let init_err = if let Some(init_expr) = &var_decl.value {
736                    let saved_pending_variable_name = self.pending_variable_name.clone();
737                    let saved_pending_variable_typed_array_kind =
738                        self.pending_variable_typed_array_kind;
739                    self.pending_variable_name = var_decl
740                        .pattern
741                        .as_identifier()
742                        .map(|name| name.to_string());
743                    // v2 Phase 3.1 (Agent 3): when the binding has an
744                    // explicit `Array<T>` annotation whose element type
745                    // maps to a typed-array kind, signal it to
746                    // `compile_expr_array` so the literal is lowered to
747                    // the v2 typed-array path.
748                    //
749                    // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element
750                    // (2026-05-18): route through the compiler-aware
751                    // `resolve_typed_array_kind_from_annotation` so `Array<B>`
752                    // for a registered user struct B also maps to
753                    // `TypedArrayKind::TypedObject` per audit §2.1 + §3.A row 1.
754                    self.pending_variable_typed_array_kind = var_decl
755                        .type_annotation
756                        .as_ref()
757                        .and_then(|ann| self.resolve_typed_array_kind_from_annotation(ann));
758                    match self.compile_expr_for_reference_binding(init_expr) {
759                        Ok(tracked_borrow) => {
760                            ref_borrow = tracked_borrow;
761                            self.pending_variable_name = saved_pending_variable_name;
762                            self.pending_variable_typed_array_kind =
763                                saved_pending_variable_typed_array_kind;
764                            None
765                        }
766                        Err(e) => {
767                            self.pending_variable_name = saved_pending_variable_name;
768                            self.pending_variable_typed_array_kind =
769                                saved_pending_variable_typed_array_kind;
770                            // Push null as placeholder so the variable still gets registered
771                            self.emit(Instruction::simple(OpCode::PushNull));
772                            Some(e)
773                        }
774                    }
775                } else {
776                    self.emit(Instruction::simple(OpCode::PushNull));
777                    None
778                };
779                // Phase 4b Round 6 WS-1b W16.2-C residual: capture the bare
780                // empty-array-accumulator placeholder index now, before any
781                // downstream emission can reset it.
782                let captured_empty_array_alloc_idx =
783                    self.pending_empty_array_alloc_idx.take();
784
785                if let Some(name) = var_decl.pattern.as_identifier() {
786                    // R8 W9 B9: removed categorical ban on module-scope `ref_borrow`.
787                    // The MIR borrow solver is the documented sole authority for
788                    // escape analysis (per audit
789                    // docs/cluster-audits/v0.3-r8w9-borrow-b0003-audit.md §3).
790                    // Local-scope `let r = &x` already accepts the same shape;
791                    // module-scope must match.
792                    let binding_idx = self.get_or_create_module_binding(name);
793                    self.emit(Instruction::new(
794                        OpCode::StoreModuleBinding,
795                        Some(Operand::ModuleBinding(binding_idx)),
796                    ));
797                    // v2 Phase 3.1 (Agent 3): record that this binding holds
798                    // a v2 typed array if the annotation drove the typed
799                    // path during initializer compilation. The kind was
800                    // captured in `pending_variable_typed_array_kind` BEFORE
801                    // the initializer compiled and is still set if the
802                    // typed path was taken.
803                    if let Some(kind) = self.pending_variable_typed_array_kind {
804                        self.v2_typed_array_module_bindings.insert(binding_idx, kind);
805                    }
806                    // Phase 4b Round 6 WS-1b W16.2-C residual: re-key a bare
807                    // empty-array-accumulator placeholder against this module
808                    // binding so the first downstream `.push()` can resolve
809                    // its element kind and patch the allocator.
810                    self.register_empty_array_accumulator(
811                        crate::compiler::EmptyArrayAccumulatorKey::ModuleBinding(binding_idx),
812                        var_decl.value.as_ref(),
813                        captured_empty_array_alloc_idx,
814                        name,
815                        var_decl.value.as_ref().map(|v| v.span()),
816                    );
817                    if let Some(value) = &var_decl.value {
818                        self.finish_reference_binding_from_expr(
819                            binding_idx,
820                            false,
821                            name,
822                            value,
823                            ref_borrow,
824                        );
825                        self.update_callable_binding_from_expr(binding_idx, false, value);
826                    } else {
827                        self.clear_reference_binding(binding_idx, false);
828                        self.clear_callable_binding(binding_idx, false);
829                    }
830
831                    // Propagate type info from annotation or initializer expression
832                    if let Some(ref type_ann) = var_decl.type_annotation {
833                        if let Some(type_name) = Self::tracked_type_name_from_annotation(type_ann) {
834                            self.set_module_binding_type_info(binding_idx, &type_name);
835                        }
836                    } else {
837                        let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
838                        self.propagate_initializer_type_to_slot(binding_idx, false, is_mutable);
839                    }
840
841                    // Track for auto-drop at program exit
842                    let binding_type_name = self
843                        .type_tracker
844                        .get_binding_type(binding_idx)
845                        .and_then(|info| info.type_name.clone());
846                    let drop_kind = binding_type_name
847                        .as_ref()
848                        .and_then(|tn| self.drop_type_info.get(tn).copied())
849                        .or_else(|| {
850                            var_decl
851                                .type_annotation
852                                .as_ref()
853                                .and_then(|ann| self.annotation_drop_kind(ann))
854                        });
855                    if drop_kind.is_some() {
856                        let is_async = match drop_kind {
857                            Some(DropKind::AsyncOnly) => true,
858                            Some(DropKind::Both) => false,
859                            Some(DropKind::SyncOnly) | None => false,
860                        };
861                        self.track_drop_module_binding(binding_idx, is_async);
862                    }
863                } else {
864                    self.compile_destructure_pattern_global(&var_decl.pattern)?;
865                }
866
867                if let Some(e) = init_err {
868                    return Err(e);
869                }
870            }
871            Item::Assignment(assign, _) => {
872                self.compile_statement(&Statement::Assignment(assign.clone(), Span::DUMMY))?;
873            }
874            Item::Expression(expr, _) => {
875                self.compile_expr(expr)?;
876                // Only pop if not the last item - keep last expression result on stack
877                if !is_last {
878                    self.emit(Instruction::simple(OpCode::Pop));
879                }
880            }
881            Item::Statement(stmt, stmt_item_span) => {
882                // R8 W8 Cluster A (2026-05-24): reject runtime-only `const`
883                // initializers at the top-level script-item path. Module-
884                // scoped `const`s reach `Item::VariableDecl` via the
885                // qualify pass; script-level `const`s reach
886                // `Item::Statement(Statement::VariableDecl)` per the
887                // grammar (`item_core → statement → variable_decl`).
888                if let Statement::VariableDecl(var_decl, decl_span) = stmt {
889                    if var_decl.kind == shape_ast::ast::VarKind::Const {
890                        if let Some(ref init_expr) = var_decl.value {
891                            if !Self::const_initializer_is_comptime_evaluable(init_expr) {
892                                return Err(ShapeError::SemanticError {
893                                    message: format!(
894                                        "`const` initializer must be comptime-evaluable \
895                                         (literal, or unary `-`/`!` on a literal). \
896                                         Function calls and other runtime-dependent expressions \
897                                         are rejected per R8 W8 Cluster A (2026-05-24). \
898                                         Extending the comptime evaluator is v0.4-concurrency-\
899                                         design-pass territory per docs/v0.3-close-summary.md \u{a7}5.15."
900                                    ),
901                                    location: Some(self.span_to_source_location(*decl_span)),
902                                });
903                            }
904                        }
905                    }
906                }
907                let _ = stmt_item_span;
908                // For expression statements that are the last item, keep result on stack
909                if is_last {
910                    if let Statement::Expression(expr, _) = stmt {
911                        self.compile_expr(expr)?;
912                        // Don't emit Pop - keep result on stack
913                        return Ok(());
914                    }
915                }
916                self.compile_statement(stmt)?;
917            }
918            Item::Export(export, export_span) => {
919                // If the export has a source variable declaration (pub let/const/var),
920                // compile it so the initialization is actually executed.
921                if let Some(ref var_decl) = export.source_decl {
922                    let mut ref_borrow = None;
923                    if let Some(init_expr) = &var_decl.value {
924                        let saved_pending_variable_name = self.pending_variable_name.clone();
925                        let saved_pending_variable_typed_array_kind =
926                            self.pending_variable_typed_array_kind;
927                        self.pending_variable_name = var_decl
928                            .pattern
929                            .as_identifier()
930                            .map(|name| name.to_string());
931                        // v2 Phase 3.1 (Agent 3): see ModuleBinding case above.
932                        // Phase 4b Round 4 W16.2-A (2026-05-18): user-struct
933                        // annotation support via `resolve_typed_array_kind_from_annotation`.
934                        self.pending_variable_typed_array_kind = var_decl
935                            .type_annotation
936                            .as_ref()
937                            .and_then(|ann| self.resolve_typed_array_kind_from_annotation(ann));
938                        let compile_result = self.compile_expr_for_reference_binding(init_expr);
939                        self.pending_variable_name = saved_pending_variable_name;
940                        self.pending_variable_typed_array_kind =
941                            saved_pending_variable_typed_array_kind;
942                        ref_borrow = compile_result?;
943                        if ref_borrow.is_some() {
944                            return Err(ShapeError::SemanticError {
945                                message:
946                                    "[B0003] cannot return or store a reference that outlives its owner"
947                                        .to_string(),
948                                location: Some(self.span_to_source_location(init_expr.span())),
949                            });
950                        }
951                    } else {
952                        self.emit(Instruction::simple(OpCode::PushNull));
953                    }
954                    if let Some(name) = var_decl.pattern.as_identifier() {
955                        let binding_idx = self.get_or_create_module_binding(name);
956                        self.emit(Instruction::new(
957                            OpCode::StoreModuleBinding,
958                            Some(Operand::ModuleBinding(binding_idx)),
959                        ));
960                        if let Some(value) = &var_decl.value {
961                            self.finish_reference_binding_from_expr(
962                                binding_idx,
963                                false,
964                                name,
965                                value,
966                                ref_borrow,
967                            );
968                            self.update_callable_binding_from_expr(binding_idx, false, value);
969                        } else {
970                            self.clear_reference_binding(binding_idx, false);
971                            self.clear_callable_binding(binding_idx, false);
972                        }
973                    }
974                }
975                match &export.item {
976                    ExportItem::Function(func_def) => self.compile_function(func_def)?,
977                    ExportItem::Annotation(annotation_def) => {
978                        self.compile_annotation_def(annotation_def)?;
979                    }
980                    ExportItem::Enum(enum_def) => self.register_enum(enum_def)?,
981                    ExportItem::Struct(struct_def) => {
982                        self.register_struct_type(struct_def, *export_span)?;
983                        if self.struct_types.contains_key(&struct_def.name) {
984                            self.emit_annotation_lifecycle_calls_for_type(
985                                &struct_def.name,
986                                &struct_def.annotations,
987                            )?;
988                        }
989                    }
990                    ExportItem::Trait(_) => {} // no-op for now (trait registration happens in type system)
991                    ExportItem::ForeignFunction(def) => self.compile_foreign_function(def)?,
992                    _ => {}
993                }
994            }
995            Item::Stream(_stream, _) => {
996                return Err(ShapeError::StreamError {
997                    message: "Streaming functionality has been removed".to_string(),
998                    stream_name: None,
999                });
1000            }
1001            Item::TypeAlias(type_alias, _) => {
1002                // Track type alias for meta validation
1003                let base_type_name = match &type_alias.type_annotation {
1004                    TypeAnnotation::Basic(name) => Some(name.clone()),
1005                    TypeAnnotation::Reference(name) => Some(name.to_string()),
1006                    _ => None,
1007                };
1008                self.type_aliases.insert(
1009                    type_alias.name.clone(),
1010                    base_type_name
1011                        .clone()
1012                        .unwrap_or_else(|| format!("{:?}", type_alias.type_annotation)),
1013                );
1014                // Register in type inference environment so lookup_type_alias works
1015                self.type_inference.env.define_type_alias(
1016                    &type_alias.name,
1017                    &type_alias.type_annotation,
1018                    type_alias.meta_param_overrides.clone(),
1019                );
1020
1021                // Apply comptime field overrides from type alias
1022                // (e.g., `type EUR = Currency { symbol: "€" }` overrides
1023                // Currency's comptime symbol).
1024                //
1025                // **Phase-2c rebuild pending — see ADR-006 §2.4.** The
1026                // previous body materialized the override RHS literals into
1027                // a `shape_value::ValueMap` (alias for `HashMap<String,
1028                // ValueWord>`), inserting per-FieldType `ValueWord::from_*`
1029                // constructions keyed by override field name. After the
1030                // strict-typing bulldozer:
1031                //
1032                // - `ValueWord` and the `ValueMap` typedef are deleted from
1033                //   `shape-value` (per CLAUDE.md "Renames to refuse on
1034                //   sight" and ADR-006 §2.4 / Q6).
1035                // - `comptime_fields: HashMap<String, ValueMap>` on the
1036                //   compiler struct (`compiler/mod.rs:906`) is itself the
1037                //   forbidden carrier — its rebuild lives in the cluster
1038                //   that owns `mod.rs`.
1039                // - The kinded replacement is `HashMap<String,
1040                //   HashMap<String, KindedSlot>>` per ADR-006 §2.7.1.3
1041                //   (vector storage with parallel kind tracks): each
1042                //   override RHS becomes a `KindedSlot` constructed by
1043                //   per-Literal arm (`Literal::Int(n) => KindedSlot::from_int(n)`,
1044                //   etc.), preserving the kind without round-tripping
1045                //   through a tagged dynamic word.
1046                //
1047                // Until Phase 2c lands, this branch is a structural no-op:
1048                // type-alias comptime field overrides are silently dropped
1049                // rather than panicking, so the compile succeeds for the
1050                // 99% case (no overrides). Property-access reads against
1051                // an aliased comptime field will fall through to the base
1052                // type's value (also a phase-2c surface). Suppressing the
1053                // override is the correct boundary per playbook §7 #4: we
1054                // do not synthesize a placeholder ValueMap that would
1055                // poison the comptime_fields registry.
1056                let _ = (&base_type_name, &type_alias.meta_param_overrides);
1057            }
1058            Item::StructType(struct_def, span) => {
1059                self.register_struct_type(struct_def, *span)?;
1060                if self.struct_types.contains_key(&struct_def.name) {
1061                    self.emit_annotation_lifecycle_calls_for_type(
1062                        &struct_def.name,
1063                        &struct_def.annotations,
1064                    )?;
1065                }
1066            }
1067            Item::Enum(enum_def, _) => {
1068                self.register_enum(enum_def)?;
1069            }
1070            // Meta/Format definitions removed — formatting now uses Display trait
1071            Item::Import(import_stmt, _) => {
1072                // Import resolution is handled by the module graph pipeline
1073                // before compilation. At this point imports should already
1074                // have been resolved via `register_graph_imports_for_module`.
1075                // If we reach here, the import is either:
1076                // 1. Being compiled standalone (no module context) - skip for now
1077                // 2. A future extension point for runtime imports
1078                //
1079                // For now, we register the imported names as known functions
1080                // that can be resolved later.
1081                self.register_import_names(import_stmt)?;
1082            }
1083            Item::Extend(extend, _) => {
1084                // Compile desugared extend methods
1085                for method in &extend.methods {
1086                    let func_def = self.desugar_extend_method(method, &extend.type_name)?;
1087                    self.compile_function(&func_def)?;
1088                }
1089            }
1090            Item::Impl(impl_block, _) => {
1091                // J-CT.2 — comptime impl blocks are deferred; the
1092                // first-pass arm captured them in `comptime_impl_blocks`
1093                // and skipped runtime processing, so the second pass also
1094                // skips. Their bodies are compiled inside the comptime
1095                // mini-VM (`execute_comptime`).
1096                if impl_block.is_comptime {
1097                    return Ok(());
1098                }
1099                // Compile impl block methods with scoped names
1100                let raw_trait_name = match &impl_block.trait_name {
1101                    shape_ast::ast::types::TypeName::Simple(n) => n.as_str(),
1102                    shape_ast::ast::types::TypeName::Generic { name, .. } => name.as_str(),
1103                };
1104                let type_name = match &impl_block.target_type {
1105                    shape_ast::ast::types::TypeName::Simple(n) => n.as_str(),
1106                    shape_ast::ast::types::TypeName::Generic { name, .. } => name.as_str(),
1107                };
1108                let impl_name = impl_block.impl_name.as_deref();
1109
1110                // Resolve trait name: canonical for def lookup, basename for dispatch
1111                let (canonical_trait, trait_basename) = self.resolve_trait_name(raw_trait_name);
1112
1113                // From/TryFrom: compile the from/tryFrom method + synthetic wrapper
1114                if trait_basename == "From" || trait_basename == "TryFrom" {
1115                    return self.compile_from_impl_bodies(impl_block, &trait_basename, type_name);
1116                }
1117
1118                // Collect names of methods explicitly provided in the impl block
1119                let overridden: std::collections::HashSet<&str> =
1120                    impl_block.methods.iter().map(|m| m.name.as_str()).collect();
1121
1122                for method in &impl_block.methods {
1123                    let func_def = self.desugar_impl_method(
1124                        method,
1125                        &trait_basename,
1126                        type_name,
1127                        impl_name,
1128                        &impl_block.target_type,
1129                    )?;
1130                    self.compile_function(&func_def)?;
1131                }
1132
1133                // Compile default methods from the trait definition that were not overridden
1134                if let Some(trait_def) = self.trait_defs.get(&canonical_trait).cloned() {
1135                    for member in &trait_def.members {
1136                        if let shape_ast::ast::types::TraitMember::Default(default_method) = member
1137                        {
1138                            if !overridden.contains(default_method.name.as_str()) {
1139                                let func_def = self.desugar_impl_method(
1140                                    default_method,
1141                                    &trait_basename,
1142                                    type_name,
1143                                    impl_name,
1144                                    &impl_block.target_type,
1145                                )?;
1146                                self.compile_function(&func_def)?;
1147                            }
1148                        }
1149                    }
1150                }
1151            }
1152            Item::AnnotationDef(ann_def, _) => {
1153                self.compile_annotation_def(ann_def)?;
1154            }
1155            Item::Comptime(stmts, span) => {
1156                // Execute comptime block at compile time (side-effects only; result discarded)
1157                let extensions: Vec<_> = self
1158                    .extension_registry
1159                    .as_ref()
1160                    .map(|r| r.as_ref().clone())
1161                    .unwrap_or_default();
1162                let trait_impls = self.type_inference.env.trait_impl_keys();
1163                let known_type_symbols: std::collections::HashSet<String> = self
1164                    .struct_types
1165                    .keys()
1166                    .chain(self.type_aliases.keys())
1167                    .cloned()
1168                    .collect();
1169                let comptime_helpers = self.collect_comptime_helpers();
1170                // W7 (2026-05-17): build the TypeReflectionSnapshot for
1171                // `type_info(T)` resolution. Top-level comptime block has
1172                // no enclosing generic-type-param scope.
1173                let type_snapshot = super::comptime_builtins::build_type_reflection_snapshot(
1174                    self,
1175                    &[],
1176                );
1177                // J-CT.2 — see `expressions/mod.rs::Expr::Comptime` for
1178                // rationale on comptime-context items.
1179                let comptime_impl_blocks = self.comptime_impl_blocks.clone();
1180                let comptime_context_trait_defs: Vec<_> =
1181                    self.trait_defs.values().cloned().collect();
1182                let comptime_context_struct_defs: Vec<_> = self
1183                    .comptime_context_struct_defs
1184                    .values()
1185                    .cloned()
1186                    .collect();
1187                let execution = super::comptime::execute_comptime_with_context(
1188                    stmts,
1189                    &comptime_helpers,
1190                    &comptime_impl_blocks,
1191                    &comptime_context_trait_defs,
1192                    &comptime_context_struct_defs,
1193                    &extensions,
1194                    trait_impls,
1195                    known_type_symbols,
1196                    type_snapshot,
1197                )
1198                .map_err(|e| ShapeError::RuntimeError {
1199                    message: format!(
1200                        "Comptime block evaluation failed: {}",
1201                        super::helpers::strip_error_prefix(&e)
1202                    ),
1203                    location: Some(self.span_to_source_location(*span)),
1204                })?;
1205                self.process_comptime_directives(execution.directives, "")
1206                    .map_err(|e| ShapeError::RuntimeError {
1207                        message: format!("Comptime block directive processing failed: {}", e),
1208                        location: Some(self.span_to_source_location(*span)),
1209                    })?;
1210            }
1211            Item::Query(query, _span) => {
1212                self.compile_query(query)?;
1213                // Pop the query result unless self is the last item
1214                if !is_last {
1215                    self.emit(Instruction::simple(OpCode::Pop));
1216                }
1217            }
1218            Item::ForeignFunction(def, _) => self.compile_foreign_function(def)?,
1219            _ => {} // Skip other items for now
1220        }
1221        Ok(())
1222    }
1223
1224    /// Register imported names for symbol resolution
1225    ///
1226    /// This allows the compiler to recognize imported functions when
1227    /// they are called later in the code.
1228    fn register_import_names(&mut self, import_stmt: &shape_ast::ast::ImportStmt) -> Result<()> {
1229        use shape_ast::ast::ImportItems;
1230
1231        // Check permissions before registering imports.
1232        // Clone to avoid borrow conflict with &mut self in check_import_permissions.
1233        if let Some(pset) = self.permission_set.clone() {
1234            self.check_import_permissions(import_stmt, &pset)?;
1235        }
1236
1237        match &import_stmt.items {
1238            ImportItems::Named(specs) => {
1239                for spec in specs {
1240                    if spec.is_annotation {
1241                        // W9: register against the canonical module path so the
1242                        // use-site lookup matches `compiled_annotations`'s
1243                        // qualified key produced by the dep module's qualify pass.
1244                        self.module_scope_sources
1245                            .entry(import_stmt.from.clone())
1246                            .or_insert_with(|| import_stmt.from.clone());
1247                        self.imported_annotations.insert(
1248                            spec.name.clone(),
1249                            ImportedAnnotationSymbol {
1250                                original_name: spec.name.clone(),
1251                                _module_path: import_stmt.from.clone(),
1252                                hidden_module_name: import_stmt.from.clone(),
1253                            },
1254                        );
1255                        continue;
1256                    }
1257                    let local_name = spec.alias.as_ref().unwrap_or(&spec.name);
1258                    // Register as a known import - actual function resolution
1259                    // happens when the imported module's bytecode is merged
1260                    self.imported_names.insert(
1261                        local_name.clone(),
1262                        ImportedSymbol {
1263                            original_name: spec.name.clone(),
1264                            module_path: import_stmt.from.clone(),
1265                            kind: None, // legacy path
1266                        },
1267                    );
1268                }
1269            }
1270            ImportItems::Namespace { name, alias } => {
1271                // `use module.path` or `use module.path as alias`
1272                // Register the local namespace binding as a module_binding.
1273                let local_name = alias.as_ref().unwrap_or(name);
1274                let binding_idx = self.get_or_create_module_binding(local_name);
1275                self.module_namespace_bindings.insert(local_name.clone());
1276                self.module_scope_sources
1277                    .entry(local_name.clone())
1278                    .or_insert_with(|| import_stmt.from.clone());
1279                let module_path = if import_stmt.from.is_empty() {
1280                    name.as_str()
1281                } else {
1282                    import_stmt.from.as_str()
1283                };
1284                // Predeclare module object schema so runtime can instantiate
1285                // module module_bindings without synthesizing schemas dynamically.
1286                self.register_extension_module_schema(module_path);
1287                let module_schema_name = format!("__mod_{}", module_path);
1288                if self
1289                    .type_tracker
1290                    .schema_registry()
1291                    .get(&module_schema_name)
1292                    .is_some()
1293                {
1294                    self.set_module_binding_type_info(binding_idx, &module_schema_name);
1295                }
1296                // The module object will be provided at runtime by the VM
1297                let _ = binding_idx;
1298            }
1299        }
1300        Ok(())
1301    }
1302
1303    /// Check whether the imported symbols are allowed by the active permission set.
1304    ///
1305    /// For named imports (`from std::core::file use { read_text }`), checks each function
1306    /// individually. For namespace imports (`use std::core::http`), checks the whole module.
1307    fn check_import_permissions(
1308        &mut self,
1309        import_stmt: &shape_ast::ast::ImportStmt,
1310        pset: &shape_abi_v1::PermissionSet,
1311    ) -> Result<()> {
1312        use shape_ast::ast::ImportItems;
1313        use shape_runtime::stdlib::capability_tags;
1314
1315        // Pass the full canonical path (e.g. "std::core::file") to capability tags.
1316        let module_name = &import_stmt.from as &str;
1317
1318        match &import_stmt.items {
1319            ImportItems::Named(specs) => {
1320                for spec in specs {
1321                    let required = capability_tags::required_permissions(module_name, &spec.name);
1322                    if !required.is_empty() && !required.is_subset(pset) {
1323                        let missing = required.difference(pset);
1324                        let missing_names: Vec<&str> = missing.iter().map(|p| p.name()).collect();
1325                        return Err(ShapeError::SemanticError {
1326                            message: format!(
1327                                "Permission denied: {module_name}::{} requires {} capability, \
1328                                 but the active permission set does not include it. \
1329                                 Add the permission to [permissions] in shape.toml or use a less \
1330                                 restrictive preset.",
1331                                spec.name,
1332                                missing_names.join(", "),
1333                            ),
1334                            location: None,
1335                        });
1336                    }
1337                    self.record_blob_permissions(module_name, &spec.name);
1338                }
1339            }
1340            ImportItems::Namespace { .. } => {
1341                // For namespace imports, check the entire module's permission envelope.
1342                // If the module requires any permissions not granted, deny the import.
1343                let required = capability_tags::module_permissions(module_name);
1344                if !required.is_empty() && !required.is_subset(pset) {
1345                    let missing = required.difference(pset);
1346                    let missing_names: Vec<&str> = missing.iter().map(|p| p.name()).collect();
1347                    return Err(ShapeError::SemanticError {
1348                        message: format!(
1349                            "Permission denied: module '{module_name}' requires {} capabilities, \
1350                             but the active permission set does not include them. \
1351                             Add the permissions to [permissions] in shape.toml or use a less \
1352                             restrictive preset.",
1353                            missing_names.join(", "),
1354                        ),
1355                        location: None,
1356                    });
1357                }
1358                // Record module-level permissions for namespace imports in the current blob
1359                if let Some(ref mut blob) = self.current_blob_builder {
1360                    let module_perms = capability_tags::module_permissions(module_name);
1361                    blob.record_permissions(&module_perms);
1362                }
1363            }
1364        }
1365        Ok(())
1366    }
1367
1368    /// Register imports for a module from the module graph.
1369    ///
1370    /// This is the graph-driven replacement for `register_import_names`.
1371    /// For each `ResolvedImport` on the node:
1372    /// - Namespace: creates canonical + alias bindings, registers schemas
1373    /// - Named: populates `imported_names`, `imported_annotations`, `module_builtin_functions`
1374    pub(super) fn register_graph_imports_for_module(
1375        &mut self,
1376        module_id: crate::module_graph::ModuleId,
1377        graph: &crate::module_graph::ModuleGraph,
1378    ) -> Result<()> {
1379        use crate::module_graph::{ModuleSourceKind, ResolvedImport};
1380
1381        let node = graph.node(module_id);
1382        let resolved_imports = node.resolved_imports.clone();
1383
1384        for ri in &resolved_imports {
1385            match ri {
1386                ResolvedImport::Namespace {
1387                    local_name,
1388                    canonical_path,
1389                    module_id: dep_id,
1390                } => {
1391                    let dep_node = graph.node(*dep_id);
1392
1393                    // 1. Ensure canonical binding exists
1394                    let canonical_idx = self.get_or_create_module_binding(canonical_path);
1395
1396                    // Register native schema on canonical binding for NativeModule/Hybrid
1397                    if matches!(
1398                        dep_node.source_kind,
1399                        ModuleSourceKind::NativeModule | ModuleSourceKind::Hybrid
1400                    ) {
1401                        self.register_extension_module_schema(canonical_path);
1402                        let module_schema_name = format!("__mod_{}", canonical_path);
1403                        if self
1404                            .type_tracker
1405                            .schema_registry()
1406                            .get(&module_schema_name)
1407                            .is_some()
1408                        {
1409                            self.set_module_binding_type_info(canonical_idx, &module_schema_name);
1410                        }
1411                    }
1412
1413                    // 2. Create alias binding if local_name != canonical_path
1414                    if local_name != canonical_path {
1415                        let alias_idx = self.get_or_create_module_binding(local_name);
1416
1417                        // Copy type info from canonical to alias
1418                        let module_schema_name = format!("__mod_{}", canonical_path);
1419                        if self
1420                            .type_tracker
1421                            .schema_registry()
1422                            .get(&module_schema_name)
1423                            .is_some()
1424                        {
1425                            self.set_module_binding_type_info(alias_idx, &module_schema_name);
1426                        }
1427
1428                        // Emit runtime binding copy: alias = canonical
1429                        self.emit(Instruction::new(
1430                            OpCode::LoadModuleBinding,
1431                            Some(Operand::ModuleBinding(canonical_idx)),
1432                        ));
1433                        self.emit(Instruction::new(
1434                            OpCode::StoreModuleBinding,
1435                            Some(Operand::ModuleBinding(alias_idx)),
1436                        ));
1437                    }
1438
1439                    // 3. Register namespace
1440                    self.module_namespace_bindings.insert(local_name.clone());
1441                    self.graph_namespace_map
1442                        .insert(local_name.clone(), canonical_path.clone());
1443
1444                    // 4. W9: Register annotation defs from imported module so
1445                    // bare `@ann` and qualified `@local::ann` resolve at use-site.
1446                    // The compiled annotation lives in `compiled_annotations` under
1447                    // its qualified name `canonical_path::ann_name` (set during the
1448                    // dep module's own compile via `qualify_module_item`).
1449                    for (export_name, exp) in &dep_node.interface.exports {
1450                        if matches!(
1451                            exp.kind,
1452                            shape_ast::module_utils::ModuleExportKind::Annotation
1453                        ) {
1454                            self.imported_annotations
1455                                .entry(export_name.clone())
1456                                .or_insert_with(|| ImportedAnnotationSymbol {
1457                                    original_name: export_name.clone(),
1458                                    _module_path: canonical_path.clone(),
1459                                    hidden_module_name: canonical_path.clone(),
1460                                });
1461                        }
1462                    }
1463                }
1464                ResolvedImport::Named {
1465                    canonical_path,
1466                    module_id: dep_id,
1467                    symbols,
1468                } => {
1469                    let dep_node = graph.node(*dep_id);
1470
1471                    for sym in symbols {
1472                        if sym.is_annotation {
1473                            // W9: register annotation symbol against the canonical
1474                            // module path. The compiled annotation is stored in
1475                            // `compiled_annotations` under `canonical_path::name`
1476                            // (the dep module's own qualify_module_item pass
1477                            // produces that qualified key), so use-site resolution
1478                            // just looks up `canonical_path::original_name`.
1479                            self.module_scope_sources
1480                                .entry(canonical_path.clone())
1481                                .or_insert_with(|| canonical_path.clone());
1482                            // Vacant-only: explicit imports win over prelude
1483                            self.imported_annotations
1484                                .entry(sym.local_name.clone())
1485                                .or_insert_with(|| ImportedAnnotationSymbol {
1486                                    original_name: sym.original_name.clone(),
1487                                    _module_path: canonical_path.clone(),
1488                                    hidden_module_name: canonical_path.clone(),
1489                                });
1490                            continue;
1491                        }
1492
1493                        // Register as imported name (vacant-only: explicit imports
1494                        // are processed first and win over prelude entries)
1495                        self.imported_names
1496                            .entry(sym.local_name.clone())
1497                            .or_insert_with(|| ImportedSymbol {
1498                                original_name: sym.original_name.clone(),
1499                                module_path: canonical_path.clone(),
1500                                kind: Some(sym.kind),
1501                            });
1502
1503                        // For native exports, register as module builtin function
1504                        if matches!(
1505                            dep_node.source_kind,
1506                            ModuleSourceKind::NativeModule | ModuleSourceKind::Hybrid
1507                        ) && matches!(
1508                            sym.kind,
1509                            shape_ast::module_utils::ModuleExportKind::Function
1510                                | shape_ast::module_utils::ModuleExportKind::BuiltinFunction
1511                        ) {
1512                            self.module_builtin_functions
1513                                .entry(sym.local_name.clone())
1514                                .or_insert_with(|| ModuleBuiltinFunction {
1515                                    export_name: sym.original_name.clone(),
1516                                    source_module_path: canonical_path.clone(),
1517                                });
1518                        }
1519
1520                        // R8 W8 Cluster A: imported `pub const NAME = expr` —
1521                        // capture the initializer expression so the consumer-
1522                        // side identifier-load path can emit it inline as
1523                        // `PushConst(<comptime-value>)`. ADR-006 §2.7.5
1524                        // stamp-at-compile-time invariant preserved: the
1525                        // constant's kind is stamped from the literal at
1526                        // compile time when the compiler reaches the
1527                        // identifier reference.
1528                        if matches!(
1529                            sym.kind,
1530                            shape_ast::module_utils::ModuleExportKind::Value
1531                        ) {
1532                            if let Some(ref dep_ast) = dep_node.ast {
1533                                for item in &dep_ast.items {
1534                                    if let shape_ast::ast::Item::Export(export, _) = item {
1535                                        if let Some(ref decl) = export.source_decl {
1536                                            if decl.kind == shape_ast::ast::VarKind::Const {
1537                                                if let Some(decl_name) = decl.pattern.as_identifier() {
1538                                                    if decl_name == sym.original_name {
1539                                                        if let Some(ref init) = decl.value {
1540                                                            self.imported_consts
1541                                                                .entry(sym.local_name.clone())
1542                                                                .or_insert_with(|| init.clone());
1543                                                        }
1544                                                    }
1545                                                }
1546                                            }
1547                                        }
1548                                    }
1549                                }
1550                            }
1551                        }
1552                    }
1553                }
1554            }
1555        }
1556
1557        Ok(())
1558    }
1559
1560    pub(super) fn register_extension_module_schema(&mut self, module_path: &str) {
1561        let Some(registry) = self.extension_registry.as_ref() else {
1562            return;
1563        };
1564        let Some(module) = registry
1565            .iter()
1566            .rev()
1567            .find(|m| m.name == module_path)
1568        else {
1569            return;
1570        };
1571
1572        // Mirror extension type schemas into the per-bytecode registry,
1573        // preserving their pre-allocated IDs. To prevent the synthetic
1574        // `__mod_*` schemas (allocated below from the registry's per-instance
1575        // counter) from colliding with any extension schema's pre-allocated
1576        // ID, bump the per-instance counter past the highest ID observed
1577        // across *all* registered extensions. Scoping the bump to a single
1578        // module is insufficient because later `register_extension_module_schema`
1579        // calls can still introduce extension schemas with IDs <= the counter
1580        // we already advanced past.
1581        let mut global_max_ext_id: Option<shape_runtime::type_schema::SchemaId> = None;
1582        for ext_module in registry.iter() {
1583            for schema in &ext_module.type_schemas {
1584                global_max_ext_id = Some(match global_max_ext_id {
1585                    Some(prev) => prev.max(schema.id),
1586                    None => schema.id,
1587                });
1588            }
1589        }
1590        for schema in &module.type_schemas {
1591            if self
1592                .type_tracker
1593                .schema_registry()
1594                .get(&schema.name)
1595                .is_none()
1596            {
1597                self.type_tracker
1598                    .schema_registry_mut()
1599                    .register(schema.clone());
1600            }
1601        }
1602        if let Some(max_id) = global_max_ext_id {
1603            self.type_tracker
1604                .schema_registry()
1605                .ensure_next_id_above(max_id);
1606        }
1607
1608        let schema_name = format!("__mod_{}", module_path);
1609        if self
1610            .type_tracker
1611            .schema_registry()
1612            .get(&schema_name)
1613            .is_some()
1614        {
1615            return;
1616        }
1617
1618        let mut export_names: Vec<String> = module
1619            .export_names_available(self.comptime_mode)
1620            .into_iter()
1621            .map(|name| name.to_string())
1622            .collect();
1623
1624        for artifact in &module.module_artifacts {
1625            if artifact.module_path != module_path {
1626                continue;
1627            }
1628            let Some(source) = artifact.source.as_deref() else {
1629                continue;
1630            };
1631            if let Ok(names) =
1632                shape_runtime::module_loader::collect_exported_function_names_from_source(
1633                    &artifact.module_path,
1634                    source,
1635                )
1636            {
1637                export_names.extend(names);
1638            }
1639        }
1640
1641        export_names.sort();
1642        export_names.dedup();
1643
1644        let fields: Vec<(String, FieldType)> = export_names
1645            .into_iter()
1646            .map(|name| (name, FieldType::Any))
1647            .collect();
1648        // Allocate the synthetic `__mod_*` schema ID from the per-bytecode
1649        // registry's own counter, not the ambient (process-wide / per-Runtime)
1650        // counter that `register_type` consults via `TypeSchema::new`. The
1651        // ambient counter is shared with `state_builtins::create_state_module`
1652        // and other extension-schema constructors; sharing it here lets a
1653        // synthetic `__mod_<name>` schema receive the same ID as a previously
1654        // baked extension schema (e.g. `ModuleState`), causing the
1655        // per-bytecode `by_id` map to overwrite one with the other and
1656        // surfacing as "module 'X' has no export 'Y'" at compile time.
1657        self.type_tracker
1658            .schema_registry_mut()
1659            .register_type_scoped(schema_name, fields);
1660    }
1661
1662    /// Register an enum definition in the TypeSchemaRegistry
1663    fn register_enum(&mut self, enum_def: &EnumDef) -> Result<()> {
1664        let variants: Vec<EnumVariantInfo> = enum_def
1665            .members
1666            .iter()
1667            .enumerate()
1668            .map(|(id, member)| {
1669                // W18.0 (User 2026-05-23 Item 1): carry variant payload
1670                // shape into the runtime EnumVariantInfo so print() can
1671                // render `Red` / `Blue(42)` / `Point { x: 1, y: 2 }` per
1672                // the source-syntax form. The runtime TypedObject layout
1673                // is unchanged (`__payload_N` slots at offset 8/16/...);
1674                // the kind here only descriptively shapes the print form.
1675                match &member.kind {
1676                    EnumMemberKind::Unit { .. } => {
1677                        EnumVariantInfo::new(&member.name, id as u16, 0)
1678                    }
1679                    EnumMemberKind::Tuple(types) => {
1680                        EnumVariantInfo::new(&member.name, id as u16, types.len() as u16)
1681                    }
1682                    EnumMemberKind::Struct(fields) => {
1683                        let names: Vec<String> =
1684                            fields.iter().map(|f| f.name.clone()).collect();
1685                        EnumVariantInfo::new_struct(&member.name, id as u16, names)
1686                    }
1687                }
1688            })
1689            .collect();
1690
1691        // Sweep phase 3c.x: cache the struct-variant fields so match
1692        // patterns like `m::E::V { x, y }` can recover x and y's types
1693        // for strict-typing binop dispatch (`x + y`). The runtime schema
1694        // collapses per-variant struct fields into `__payload_N: Any`, so
1695        // we keep the named-field annotations on the side. Register
1696        // under both the qualified name (e.g. `m::E`) and the bare name
1697        // to mirror the schema-registration aliasing below.
1698        for member in &enum_def.members {
1699            if let EnumMemberKind::Struct(fields) = &member.kind {
1700                let kv: Vec<(String, shape_ast::ast::TypeAnnotation)> = fields
1701                    .iter()
1702                    .map(|f| (f.name.clone(), f.type_annotation.clone()))
1703                    .collect();
1704                self.enum_struct_variant_fields
1705                    .insert((enum_def.name.clone(), member.name.clone()), kv.clone());
1706                if let Some(basename) = enum_def.name.rsplit("::").next() {
1707                    if basename != enum_def.name {
1708                        self.enum_struct_variant_fields
1709                            .insert((basename.to_string(), member.name.clone()), kv);
1710                    }
1711                }
1712            }
1713            // R8 W7: parallel cache for tuple variants so positional
1714            // payload types are recoverable at pattern-compile time
1715            // (the schema collapses them into `__payload_N: Any`).
1716            if let EnumMemberKind::Tuple(types) = &member.kind {
1717                let tv = types.clone();
1718                self.enum_tuple_variant_fields
1719                    .insert((enum_def.name.clone(), member.name.clone()), tv.clone());
1720                if let Some(basename) = enum_def.name.rsplit("::").next() {
1721                    if basename != enum_def.name {
1722                        self.enum_tuple_variant_fields
1723                            .insert((basename.to_string(), member.name.clone()), tv);
1724                    }
1725                }
1726            }
1727        }
1728
1729        let schema = shape_runtime::type_schema::TypeSchema::new_enum(&enum_def.name, variants.clone());
1730        self.type_tracker.schema_registry_mut().register(schema);
1731
1732        // Also register under bare name if the qualified name contains "::"
1733        // so runtime code that uses bare enum names (e.g., "Snapshot") can find the schema.
1734        if let Some(basename) = enum_def.name.rsplit("::").next() {
1735            if basename != enum_def.name
1736                && self
1737                    .type_tracker
1738                    .schema_registry()
1739                    .get(basename)
1740                    .is_none()
1741            {
1742                let alias_schema =
1743                    shape_runtime::type_schema::TypeSchema::new_enum(basename, variants);
1744                self.type_tracker.schema_registry_mut().register(alias_schema);
1745            }
1746        }
1747        Ok(())
1748    }
1749
1750    /// Pre-register items from an imported module (enums, struct types, functions).
1751    ///
1752    /// Called by the LSP before compilation to make imported enums/types known
1753    /// to the compiler's type tracker. Reuses `register_enum` as single source of truth.
1754    pub fn register_imported_items(&mut self, items: &[Item]) {
1755        for item in items {
1756            match item {
1757                Item::Export(export, _) => {
1758                    match &export.item {
1759                        ExportItem::Enum(enum_def) => {
1760                            let _ = self.register_enum(enum_def);
1761                        }
1762                        ExportItem::Struct(struct_def) => {
1763                            // Register struct type fields so the compiler knows about them
1764                            let _ = self.register_struct_type(struct_def, Span::DUMMY);
1765                        }
1766                        ExportItem::Function(func_def) => {
1767                            // Register function so it's known during compilation
1768                            let _ = self.register_function(func_def);
1769                        }
1770                        _ => {}
1771                    }
1772                }
1773                Item::Enum(enum_def, _) => {
1774                    let _ = self.register_enum(enum_def);
1775                }
1776                _ => {}
1777            }
1778        }
1779    }
1780
1781    /// Register a meta definition in the format registry
1782    ///
1783    // Meta compilation methods removed — formatting now uses Display trait
1784
1785    /// Desugar an extend method to a FunctionDef with implicit `self` first param.
1786    ///
1787    /// `extend Number { method double() { self * 2 } }`
1788    /// becomes: `function double(self) { self * 2 }`
1789    ///
1790    /// UFCS handles the rest: `(5).double()` → `double(5)` → self = 5
1791    pub(super) fn desugar_extend_method(
1792        &self,
1793        method: &shape_ast::ast::types::MethodDef,
1794        target_type: &shape_ast::ast::TypeName,
1795    ) -> Result<FunctionDef> {
1796        let receiver_type = Some(Self::type_name_to_annotation(target_type));
1797        let (params, body) = self.desugar_method_signature_and_body(method, receiver_type)?;
1798
1799        // Extend methods use qualified "Type.method" names to avoid collisions
1800        // with free functions (e.g., prelude's `sum` vs extend Point { method sum() }).
1801        let type_str = match target_type {
1802            shape_ast::ast::TypeName::Simple(n) => n.clone(),
1803            shape_ast::ast::TypeName::Generic { name, .. } => name.clone(),
1804        };
1805
1806        // Propagate type params from the extend block's generic target type.
1807        // `extend Vec<T> { method indexOf(value: T) { ... } }` produces a
1808        // FunctionDef with type_params = [T]. This enables monomorphization
1809        // at call sites (e.g., `[1,2,3].indexOf(2)` → T=int).
1810        //
1811        // Heuristic: a type arg is a type PARAMETER (not a concrete type) if
1812        // it's a Basic annotation whose name is a single uppercase letter
1813        // (the standard convention for type variables: T, U, K, V).
1814        let extend_type_params: Vec<shape_ast::ast::TypeParam> = match target_type {
1815            shape_ast::ast::TypeName::Generic { type_args, .. } => type_args
1816                .iter()
1817                .filter_map(|ta| match ta {
1818                    shape_ast::ast::TypeAnnotation::Basic(name)
1819                        if name.len() == 1
1820                            && name.chars().next().is_some_and(|c| c.is_ascii_uppercase()) =>
1821                    {
1822                        Some(shape_ast::ast::TypeParam::Type {
1823                            name: name.clone(),
1824                            span: Span::DUMMY,
1825                            doc_comment: None,
1826                            default_type: None,
1827                            trait_bounds: Vec::new(),
1828                        })
1829                    }
1830                    _ => None,
1831                })
1832                .collect(),
1833            _ => Vec::new(),
1834        };
1835
1836        // V3-S6a resolver-extension follow-up: merge method-level type
1837        // params (`method map<U>(...)` — the `<U>`) with the extend-block
1838        // type params (`extend Vec<T> { ... }` — the `<T>`). The previous
1839        // shape silently dropped method-level generics, leaving the
1840        // monomorphizer unable to resolve generics like `U` in
1841        // `Vec.map<U>(f: (T) => U) -> Vec<U>` and forcing the call site
1842        // to the generic-template (un-monomorphized) path. The Smoke 2
1843        // regression `[1,2,3,4,5].map(|x|x*2).sum()` surfaced at the
1844        // empty-array `let mut result = []` in the un-specialized
1845        // Vec.map body.
1846        //
1847        // Order: extend params first (the receiver-positional generic
1848        // `T` is conceptually the outer generic), then method-level
1849        // params (`U` is inner / nested). The substitution pass walks
1850        // bindings by name, so positional order matters only for the
1851        // `mono_key`'s stable ordering — extend-first matches the
1852        // user-visible declaration order `Vec<T>.map<U>`.
1853        let mut merged_type_params: Vec<shape_ast::ast::TypeParam> = extend_type_params;
1854        if let Some(method_tps) = method.type_params.as_ref() {
1855            for tp in method_tps {
1856                // Skip duplicates (defensive — if a method redeclares a
1857                // type param of the extend block, prefer the outer).
1858                let name = tp.name();
1859                if !merged_type_params.iter().any(|m| m.name() == name) {
1860                    merged_type_params.push(tp.clone());
1861                }
1862            }
1863        }
1864
1865        Ok(FunctionDef {
1866            name: format!("{}.{}", type_str, method.name),
1867            name_span: Span::DUMMY,
1868            declaring_module_path: method.declaring_module_path.clone(),
1869            doc_comment: None,
1870            params,
1871            return_type: method.return_type.clone(),
1872            body,
1873            type_params: Some(merged_type_params),
1874            annotations: method.annotations.clone(),
1875            is_async: method.is_async,
1876            is_comptime: false,
1877            where_clause: None,
1878        })
1879    }
1880
1881    /// Desugar an impl method to a scoped FunctionDef.
1882    ///
1883    /// - Default impl:
1884    ///   `impl Queryable for DbTable { method filter(pred) { ... } }`
1885    ///   becomes: `function DbTable::filter(self, pred) { ... }`
1886    /// - Named impl:
1887    ///   `impl Display for User as JsonDisplay { method display() { ... } }`
1888    ///   becomes: `function Display::User::JsonDisplay::display(self) { ... }`
1889    ///
1890    /// Named impls use trait/type/impl prefixes to avoid collisions.
1891    ///
1892    /// # Trait declaration return-type substitution (Phase 3 cluster-0 Round 13 T1', gap 3)
1893    ///
1894    /// When the impl source omits the return-type annotation
1895    /// (`impl T for X { method name() { "x" } }` — the impl doesn't repeat
1896    /// the trait's `: string`), `method.return_type` is `None`. Without
1897    /// substitution, the synthesized `FunctionDef.return_type` is also
1898    /// `None` and the Round 6A `function_return_concrete_types[X::name]`
1899    /// side-table holds `ConcreteType::Void`, leaving the JIT MIR
1900    /// conduit's destination-stamp pass unable to classify
1901    /// `t.name() → NativeKind::String` (Smoke 3 surface, T1 close
1902    /// `76b01cf8`).
1903    ///
1904    /// Closing this surface: when the impl's `return_type` is `None`, look
1905    /// up the trait's declared return type for the matching method via
1906    /// `self.trait_defs` (with `resolve_trait_name`-shaped lookup since
1907    /// `trait_name` is the basename) and substitute it into the
1908    /// synthesized `FunctionDef.return_type`. The 6A populator at
1909    /// `compile_post_assembly` (`compiler_impl_reference_model.rs:1474`)
1910    /// then runs `concrete_type_from_annotation` on the substituted
1911    /// annotation, populating `function_return_concrete_types[fn_idx]`
1912    /// with the trait's declared `ConcreteType` (`String` for Smoke 3).
1913    ///
1914    /// This is in-compiler source-side completion of contract information
1915    /// that already exists in source code — trait declarations carry the
1916    /// return type, the compiler just wasn't propagating it.
1917    fn desugar_impl_method(
1918        &self,
1919        method: &shape_ast::ast::types::MethodDef,
1920        trait_name: &str,
1921        type_name: &str,
1922        impl_name: Option<&str>,
1923        target_type: &shape_ast::ast::TypeName,
1924    ) -> Result<FunctionDef> {
1925        // When the target type is a known generic container (Array, HashMap, etc.),
1926        // synthesize type parameters (T, K, V) and enrich the receiver annotation
1927        // to `Array<T>` (etc.). This enables the monomorphization pipeline to
1928        // resolve T from the receiver's concrete element type at call sites,
1929        // producing specialized functions that use typed opcodes.
1930        //
1931        // All methods in `impl Trait for Array` benefit from this because their
1932        // body operates on `self` (the generic receiver) — even methods with no
1933        // explicit parameters like `flatten()`.
1934        let (impl_type_params, receiver_type) =
1935            Self::synthesize_impl_type_params(target_type);
1936
1937        let (params, body) = self.desugar_method_signature_and_body(method, receiver_type)?;
1938
1939        // Async drop methods are named "drop_async" so both sync and async
1940        // variants can coexist in the function name index.
1941        let method_name = if trait_name == "Drop" && method.name == "drop" && method.is_async {
1942            "drop_async".to_string()
1943        } else {
1944            method.name.clone()
1945        };
1946        let fn_name = if let Some(name) = impl_name {
1947            format!("{}::{}::{}::{}", trait_name, type_name, name, method_name)
1948        } else {
1949            format!("{}::{}", type_name, method_name)
1950        };
1951
1952        // ADR-006 §2.7.5 — Phase 3 cluster-0 Round 13 T1' gap 3 closure.
1953        //
1954        // If the impl method omits its return type, look up the trait
1955        // declaration and substitute the trait's declared return type.
1956        // The trait's required-method signature (`TraitMemberSignature::Method
1957        // { return_type, .. }`) is the contract; if the impl chose not
1958        // to repeat it, the contract still applies.
1959        //
1960        // `resolve_trait_name` returns the canonical key into `self.trait_defs`;
1961        // we accept the bare `trait_name` here (callers pass `trait_basename`)
1962        // and walk the resolver. When the trait isn't registered (e.g. built-in
1963        // traits without an entry in `self.trait_defs`), the original `None`
1964        // is preserved per §2.7.7 #9 — no fabricated default.
1965        let return_type = method.return_type.clone().or_else(|| {
1966            let (canonical_trait, _) = self.resolve_trait_name(trait_name);
1967            self.trait_defs
1968                .get(&canonical_trait)
1969                .and_then(|trait_def| {
1970                    // Match on method name. Both Required and Default trait
1971                    // members carry the return type — Required via
1972                    // `TraitMemberSignature::Method { return_type, .. }` (always
1973                    // present), Default via `MethodDef.return_type:
1974                    // Option<TypeAnnotation>` (may itself be None — in which
1975                    // case there's nothing to backfill).
1976                    for member in &trait_def.members {
1977                        match member {
1978                            shape_ast::ast::types::TraitMember::Required(
1979                                shape_ast::ast::TraitMemberSignature::Method {
1980                                    name,
1981                                    return_type,
1982                                    ..
1983                                },
1984                            ) if name == &method.name => {
1985                                return Some(return_type.clone());
1986                            }
1987                            shape_ast::ast::types::TraitMember::Default(default_method)
1988                                if default_method.name == method.name =>
1989                            {
1990                                return default_method.return_type.clone();
1991                            }
1992                            _ => {}
1993                        }
1994                    }
1995                    None
1996                })
1997        });
1998
1999        // V3-S6a resolver-extension follow-up: merge method-level type
2000        // params with impl-block type params (mirrors the parallel fix in
2001        // `desugar_extend_method`). See that function for rationale.
2002        let mut merged_impl_type_params: Vec<shape_ast::ast::TypeParam> = impl_type_params;
2003        if let Some(method_tps) = method.type_params.as_ref() {
2004            for tp in method_tps {
2005                let name = tp.name();
2006                if !merged_impl_type_params.iter().any(|m| m.name() == name) {
2007                    merged_impl_type_params.push(tp.clone());
2008                }
2009            }
2010        }
2011
2012        Ok(FunctionDef {
2013            name: fn_name,
2014            name_span: Span::DUMMY,
2015            declaring_module_path: method.declaring_module_path.clone(),
2016            doc_comment: None,
2017            params,
2018            return_type,
2019            body,
2020            type_params: Some(merged_impl_type_params),
2021            annotations: method.annotations.clone(),
2022            is_async: method.is_async,
2023            is_comptime: false,
2024            where_clause: None,
2025        })
2026    }
2027
2028    /// Synthesize type parameters and a receiver annotation for impl methods
2029    /// on known generic container types.
2030    ///
2031    /// For `impl Iterable for Array`, the target type `Simple("Array")` becomes
2032    /// receiver `Array<T>` with a synthetic type param `T`. This mirrors how
2033    /// `extend Vec<T>` propagates type params, enabling monomorphization at
2034    /// call sites (e.g. `[1,2,3].findIndex(...)` → T=int).
2035    ///
2036    /// Returns `(type_params, receiver_annotation)`.
2037    fn synthesize_impl_type_params(
2038        target_type: &shape_ast::ast::TypeName,
2039    ) -> (Vec<shape_ast::ast::TypeParam>, Option<shape_ast::ast::TypeAnnotation>) {
2040        let type_base = match target_type {
2041            shape_ast::ast::TypeName::Simple(n) => n.as_str(),
2042            shape_ast::ast::TypeName::Generic { name, .. } => name.as_str(),
2043        };
2044
2045        // Known single-element generic containers: Array/Vec → T
2046        let is_single_param_generic = matches!(type_base, "Array" | "Vec");
2047        // Known dual-element generic containers: HashMap/Map → K, V
2048        let is_dual_param_generic = matches!(type_base, "HashMap" | "Map");
2049
2050        if is_single_param_generic {
2051            let type_params = vec![shape_ast::ast::TypeParam::Type {
2052                name: "T".to_string(),
2053                span: Span::DUMMY,
2054                doc_comment: None,
2055                default_type: None,
2056                trait_bounds: Vec::new(),
2057            }];
2058            let receiver_ann = shape_ast::ast::TypeAnnotation::Generic {
2059                name: shape_ast::ast::type_path::TypePath::simple(type_base),
2060                args: vec![shape_ast::ast::TypeAnnotation::Basic("T".to_string())],
2061            };
2062            (type_params, Some(receiver_ann))
2063        } else if is_dual_param_generic {
2064            let type_params = vec![
2065                shape_ast::ast::TypeParam::Type {
2066                    name: "K".to_string(),
2067                    span: Span::DUMMY,
2068                    doc_comment: None,
2069                    default_type: None,
2070                    trait_bounds: Vec::new(),
2071                },
2072                shape_ast::ast::TypeParam::Type {
2073                    name: "V".to_string(),
2074                    span: Span::DUMMY,
2075                    doc_comment: None,
2076                    default_type: None,
2077                    trait_bounds: Vec::new(),
2078                },
2079            ];
2080            let receiver_ann = shape_ast::ast::TypeAnnotation::Generic {
2081                name: shape_ast::ast::type_path::TypePath::simple(type_base),
2082                args: vec![
2083                    shape_ast::ast::TypeAnnotation::Basic("K".to_string()),
2084                    shape_ast::ast::TypeAnnotation::Basic("V".to_string()),
2085                ],
2086            };
2087            (type_params, Some(receiver_ann))
2088        } else {
2089            // Unknown type — no synthetic params, plain receiver.
2090            (Vec::new(), Some(Self::type_name_to_annotation(target_type)))
2091        }
2092    }
2093
2094    /// Build desugared method params/body with implicit receiver handling.
2095    ///
2096    /// Canonical receiver is `self`.
2097    fn desugar_method_signature_and_body(
2098        &self,
2099        method: &shape_ast::ast::types::MethodDef,
2100        receiver_type: Option<shape_ast::ast::TypeAnnotation>,
2101    ) -> Result<(Vec<FunctionParameter>, Vec<Statement>)> {
2102        if let Some(receiver) = method
2103            .params
2104            .first()
2105            .and_then(|p| p.pattern.as_identifier())
2106        {
2107            if receiver == "self" {
2108                let location = method
2109                    .params
2110                    .first()
2111                    .map(|p| self.span_to_source_location(p.span()));
2112                return Err(ShapeError::SemanticError {
2113                    message: format!(
2114                        "Method '{}' has an explicit `self` parameter, but method receivers are implicit. Use `method {}(...)` without `self`.",
2115                        method.name, method.name
2116                    ),
2117                    location,
2118                });
2119            }
2120        }
2121
2122        let mut params = vec![FunctionParameter {
2123            pattern: shape_ast::ast::DestructurePattern::Identifier(
2124                "self".to_string(),
2125                Span::DUMMY,
2126            ),
2127            is_const: false,
2128            is_reference: false,
2129            is_mut_reference: false,
2130            is_out: false,
2131            type_annotation: receiver_type,
2132            default_value: None,
2133        }];
2134        params.extend(method.params.clone());
2135
2136        Ok((params, method.body.clone()))
2137    }
2138
2139    /// Compile a `From` or `TryFrom` impl block.
2140    ///
2141    /// Unlike normal impl methods (which inject implicit `self`), From/TryFrom
2142    /// methods are constructors: `from(value: Source) -> Target`. The value
2143    /// parameter sits at local slot 0 with no receiver.
2144    ///
2145    /// Auto-derives:
2146    /// - `impl From<S> for T`  → `Into<T>::into` on S (direct alias)
2147    ///                          + `TryInto<T>::tryInto` on S (wrapper → Ok())
2148    /// - `impl TryFrom<S> for T` → `TryInto<T>::tryInto` on S (direct alias)
2149    fn compile_from_impl(
2150        &mut self,
2151        impl_block: &shape_ast::ast::types::ImplBlock,
2152        trait_name: &str,
2153        target_type: &str,
2154    ) -> Result<()> {
2155        // Extract source type from generic args: From<Source> → Source
2156        let source_type = match &impl_block.trait_name {
2157            shape_ast::ast::types::TypeName::Generic { type_args, .. } if !type_args.is_empty() => {
2158                match &type_args[0] {
2159                    TypeAnnotation::Basic(name) => name.clone(),
2160                    TypeAnnotation::Reference(name) => name.to_string(),
2161                    other => {
2162                        return Err(ShapeError::SemanticError {
2163                            message: format!(
2164                                "{} impl requires a simple source type, found {:?}",
2165                                trait_name, other
2166                            ),
2167                            location: None,
2168                        });
2169                    }
2170                }
2171            }
2172            _ => {
2173                return Err(ShapeError::SemanticError {
2174                    message: format!(
2175                        "{} impl requires a generic type argument, e.g., {}<string>",
2176                        trait_name, trait_name
2177                    ),
2178                    location: None,
2179                });
2180            }
2181        };
2182
2183        // Named impl selector defaults to the target type name so that
2184        // `as TargetType` / `as TargetType?` dispatch finds the right symbol.
2185        let selector = impl_block.impl_name.as_deref().unwrap_or(target_type);
2186
2187        for method in &impl_block.methods {
2188            let func_def =
2189                self.desugar_from_method(method, trait_name, target_type, &source_type)?;
2190            let from_fn_name = func_def.name.clone();
2191
2192            // Register From/TryFrom trait method symbol on the target type
2193            self.program.register_trait_method_symbol(
2194                trait_name,
2195                target_type,
2196                Some(&source_type),
2197                &method.name,
2198                &from_fn_name,
2199            );
2200            self.register_function(&func_def)?;
2201
2202            // Auto-derive Into/TryInto on the source type
2203            if trait_name == "From" {
2204                // From<S> for T → Into<T>::into on S = direct alias (same fn)
2205                self.program.register_trait_method_symbol(
2206                    "Into",
2207                    &source_type,
2208                    Some(selector),
2209                    "into",
2210                    &from_fn_name,
2211                );
2212
2213                // From<S> for T → TryInto<T>::tryInto on S = wrapper (from + Ok)
2214                let wrapper_name =
2215                    self.emit_from_to_tryinto_wrapper(&from_fn_name, &source_type, target_type)?;
2216                self.program.register_trait_method_symbol(
2217                    "TryInto",
2218                    &source_type,
2219                    Some(selector),
2220                    "tryInto",
2221                    &wrapper_name,
2222                );
2223
2224                // Register trait impls in type inference environment
2225                let _ = self.type_inference.env.register_trait_impl_named(
2226                    "Into",
2227                    &source_type,
2228                    selector,
2229                    vec!["into".to_string()],
2230                );
2231                let _ = self.type_inference.env.register_trait_impl_named(
2232                    "TryInto",
2233                    &source_type,
2234                    selector,
2235                    vec!["tryInto".to_string()],
2236                );
2237            } else {
2238                // TryFrom<S> for T → TryInto<T>::tryInto on S = direct alias
2239                self.program.register_trait_method_symbol(
2240                    "TryInto",
2241                    &source_type,
2242                    Some(selector),
2243                    "tryInto",
2244                    &from_fn_name,
2245                );
2246
2247                // Register TryInto trait impl in type inference environment
2248                let _ = self.type_inference.env.register_trait_impl_named(
2249                    "TryInto",
2250                    &source_type,
2251                    selector,
2252                    vec!["tryInto".to_string()],
2253                );
2254            }
2255        }
2256
2257        // Register From/TryFrom trait impl on target type
2258        let all_method_names: Vec<String> =
2259            impl_block.methods.iter().map(|m| m.name.clone()).collect();
2260        let _ = self.type_inference.env.register_trait_impl_named(
2261            trait_name,
2262            target_type,
2263            &source_type,
2264            all_method_names,
2265        );
2266
2267        Ok(())
2268    }
2269
2270    /// Compile From/TryFrom impl method bodies (and the synthetic TryInto wrapper).
2271    ///
2272    /// Called from `compile_item_with_context` — the registration pass already
2273    /// happened in `compile_from_impl` / `register_item_functions`.
2274    fn compile_from_impl_bodies(
2275        &mut self,
2276        impl_block: &shape_ast::ast::types::ImplBlock,
2277        trait_name: &str,
2278        target_type: &str,
2279    ) -> Result<()> {
2280        let source_type = match &impl_block.trait_name {
2281            shape_ast::ast::types::TypeName::Generic { type_args, .. } if !type_args.is_empty() => {
2282                match &type_args[0] {
2283                    TypeAnnotation::Basic(name) => name.clone(),
2284                    TypeAnnotation::Reference(name) => name.to_string(),
2285                    _ => return Ok(()), // error already reported in registration
2286                }
2287            }
2288            _ => return Ok(()),
2289        };
2290
2291        for method in &impl_block.methods {
2292            let func_def =
2293                self.desugar_from_method(method, trait_name, target_type, &source_type)?;
2294            self.compile_function(&func_def)?;
2295        }
2296
2297        // Also compile the synthetic TryInto wrapper for From impls
2298        if trait_name == "From" {
2299            for method in &impl_block.methods {
2300                let from_fn_name = format!(
2301                    "{}::{}::{}::{}",
2302                    trait_name, target_type, source_type, method.name
2303                );
2304                let wrapper_name = format!("__from_tryinto_{}_{}", source_type, target_type);
2305                // The wrapper was already registered; now compile its body
2306                if let Some(func_def) = self.function_defs.get(&wrapper_name).cloned() {
2307                    let _ = self.compile_function(&func_def);
2308                    // Suppress errors: if Ok() or the from fn is not yet available, it
2309                    // will be resolved at link time.
2310                    let _ = from_fn_name; // used above in the format
2311                }
2312            }
2313        }
2314
2315        Ok(())
2316    }
2317
2318    /// Desugar a From/TryFrom method WITHOUT implicit self injection.
2319    ///
2320    /// `From::from(value: S)` is a constructor — `value` sits at local slot 0.
2321    /// Function name: `"From::TargetType::SourceType::method_name"`
2322    fn desugar_from_method(
2323        &self,
2324        method: &shape_ast::ast::types::MethodDef,
2325        trait_name: &str,
2326        target_type: &str,
2327        source_type: &str,
2328    ) -> Result<FunctionDef> {
2329        // Verify no explicit `self` parameter
2330        if let Some(first) = method
2331            .params
2332            .first()
2333            .and_then(|p| p.pattern.as_identifier())
2334        {
2335            if first == "self" {
2336                return Err(ShapeError::SemanticError {
2337                    message: format!(
2338                        "{}::{} methods are constructors and must not have a `self` parameter",
2339                        trait_name, method.name
2340                    ),
2341                    location: None,
2342                });
2343            }
2344        }
2345
2346        let fn_name = format!(
2347            "{}::{}::{}::{}",
2348            trait_name, target_type, source_type, method.name
2349        );
2350
2351        Ok(FunctionDef {
2352            name: fn_name,
2353            name_span: Span::DUMMY,
2354            declaring_module_path: method.declaring_module_path.clone(),
2355            doc_comment: None,
2356            params: method.params.clone(),
2357            return_type: method.return_type.clone(),
2358            body: method.body.clone(),
2359            type_params: Some(Vec::new()),
2360            annotations: Vec::new(),
2361            is_async: method.is_async,
2362            is_comptime: false,
2363            where_clause: None,
2364        })
2365    }
2366
2367    /// Emit a synthetic wrapper function that calls a From::from function
2368    /// and wraps its result in Ok() for TryInto compatibility.
2369    ///
2370    /// Generated function: `__from_tryinto_{source}_{target}(value) -> Ok(from(value))`
2371    fn emit_from_to_tryinto_wrapper(
2372        &mut self,
2373        from_fn_name: &str,
2374        source_type: &str,
2375        target_type: &str,
2376    ) -> Result<String> {
2377        let wrapper_name = format!("__from_tryinto_{}_{}", source_type, target_type);
2378
2379        // Create a synthetic FunctionDef whose body calls from() and wraps in Ok()
2380        let span = Span::DUMMY;
2381        let body = vec![Statement::Return(
2382            Some(Expr::FunctionCall {
2383                name: "Ok".to_string(),
2384                args: vec![Expr::FunctionCall {
2385                    name: from_fn_name.to_string(),
2386                    args: vec![Expr::Identifier("value".to_string(), span)],
2387                    named_args: Vec::new(),
2388                    span,
2389                }],
2390                named_args: Vec::new(),
2391                span,
2392            }),
2393            span,
2394        )];
2395
2396        let func_def = FunctionDef {
2397            name: wrapper_name.clone(),
2398            name_span: span,
2399            declaring_module_path: None,
2400            doc_comment: None,
2401            params: vec![FunctionParameter {
2402                pattern: DestructurePattern::Identifier("value".to_string(), span),
2403                is_const: false,
2404                is_reference: false,
2405                is_mut_reference: false,
2406                is_out: false,
2407                type_annotation: None,
2408                default_value: None,
2409            }],
2410            return_type: None,
2411            body,
2412            type_params: Some(Vec::new()),
2413            annotations: Vec::new(),
2414            is_async: false,
2415            is_comptime: false,
2416            where_clause: None,
2417        };
2418
2419        self.register_function(&func_def)?;
2420
2421        Ok(wrapper_name)
2422    }
2423
2424    fn type_name_to_annotation(
2425        type_name: &shape_ast::ast::TypeName,
2426    ) -> shape_ast::ast::TypeAnnotation {
2427        match type_name {
2428            shape_ast::ast::TypeName::Simple(name) => {
2429                shape_ast::ast::TypeAnnotation::Basic(name.to_string())
2430            }
2431            shape_ast::ast::TypeName::Generic { name, type_args } => {
2432                shape_ast::ast::TypeAnnotation::Generic {
2433                    name: name.clone(),
2434                    args: type_args.clone(),
2435                }
2436            }
2437        }
2438    }
2439
2440    /// Compile an annotation definition.
2441    ///
2442    /// Each handler is compiled as an internal function:
2443    /// - before(args, ctx) → `{name}___before(self, period, args, ctx)`
2444    /// - after(args, result, ctx) → `{name}___after(self, period, args, result, ctx)`
2445    ///
2446    /// `self` is the annotated item (function/method/property).
2447    /// Annotation params (e.g., `period`) are prepended after `self`.
2448    fn compile_annotation_def(&mut self, ann_def: &shape_ast::ast::AnnotationDef) -> Result<()> {
2449        use crate::bytecode::CompiledAnnotation;
2450        use shape_ast::ast::AnnotationHandlerType;
2451
2452        let mut compiled = CompiledAnnotation {
2453            name: ann_def.name.clone(),
2454            param_names: ann_def
2455                .params
2456                .iter()
2457                .flat_map(|p| p.get_identifiers())
2458                .collect(),
2459            before_handler: None,
2460            after_handler: None,
2461            on_define_handler: None,
2462            metadata_handler: None,
2463            comptime_pre_handler: None,
2464            comptime_post_handler: None,
2465            allowed_targets: Vec::new(),
2466        };
2467
2468        for handler in &ann_def.handlers {
2469            // Comptime handlers are stored as AST (not compiled to bytecode).
2470            // They are executed at compile time when the annotation is applied.
2471            match handler.handler_type {
2472                AnnotationHandlerType::ComptimePre => {
2473                    compiled.comptime_pre_handler = Some(handler.clone());
2474                    continue;
2475                }
2476                AnnotationHandlerType::ComptimePost => {
2477                    compiled.comptime_post_handler = Some(handler.clone());
2478                    continue;
2479                }
2480                _ => {}
2481            }
2482
2483            if handler.params.iter().any(|p| p.is_variadic) {
2484                return Err(ShapeError::SemanticError {
2485                    message:
2486                        "Variadic annotation handler params (`...args`) are only supported on comptime handlers"
2487                            .to_string(),
2488                    location: Some(self.span_to_source_location(handler.span)),
2489                });
2490            }
2491
2492            let handler_type_str = match handler.handler_type {
2493                AnnotationHandlerType::Before => "before",
2494                AnnotationHandlerType::After => "after",
2495                AnnotationHandlerType::OnDefine => "on_define",
2496                AnnotationHandlerType::Metadata => "metadata",
2497                AnnotationHandlerType::ComptimePre => unreachable!(),
2498                AnnotationHandlerType::ComptimePost => unreachable!(),
2499            };
2500
2501            let func_name = format!("{}___{}", ann_def.name, handler_type_str);
2502
2503            // Build function params: self + annotation_params + handler_params
2504            let mut params = vec![FunctionParameter {
2505                pattern: shape_ast::ast::DestructurePattern::Identifier(
2506                    "self".to_string(),
2507                    Span::DUMMY,
2508                ),
2509                is_const: false,
2510                is_reference: false,
2511                is_mut_reference: false,
2512                is_out: false,
2513                type_annotation: None,
2514                default_value: None,
2515            }];
2516            // Add annotation params (e.g., period)
2517            for ann_param in &ann_def.params {
2518                params.push(ann_param.clone());
2519            }
2520            // Add handler params (e.g., args, ctx)
2521            for param in &handler.params {
2522                let inferred_type = if param.name == "ctx" {
2523                    Some(TypeAnnotation::Object(vec![
2524                        shape_ast::ast::ObjectTypeField {
2525                            name: "state".to_string(),
2526                            optional: false,
2527                            type_annotation: TypeAnnotation::Basic("unknown".to_string()),
2528                            annotations: vec![],
2529                        },
2530                        shape_ast::ast::ObjectTypeField {
2531                            name: "event_log".to_string(),
2532                            optional: false,
2533                            type_annotation: TypeAnnotation::Array(Box::new(
2534                                TypeAnnotation::Basic("unknown".to_string()),
2535                            )),
2536                            annotations: vec![],
2537                        },
2538                    ]))
2539                } else if matches!(
2540                    handler.handler_type,
2541                    AnnotationHandlerType::OnDefine | AnnotationHandlerType::Metadata
2542                ) && (param.name == "fn" || param.name == "target")
2543                {
2544                    Some(TypeAnnotation::Object(vec![
2545                        shape_ast::ast::ObjectTypeField {
2546                            name: "name".to_string(),
2547                            optional: false,
2548                            type_annotation: TypeAnnotation::Basic("string".to_string()),
2549                            annotations: vec![],
2550                        },
2551                        shape_ast::ast::ObjectTypeField {
2552                            name: "kind".to_string(),
2553                            optional: false,
2554                            type_annotation: TypeAnnotation::Basic("string".to_string()),
2555                            annotations: vec![],
2556                        },
2557                        shape_ast::ast::ObjectTypeField {
2558                            name: "id".to_string(),
2559                            optional: false,
2560                            type_annotation: TypeAnnotation::Basic("int".to_string()),
2561                            annotations: vec![],
2562                        },
2563                    ]))
2564                } else {
2565                    None
2566                };
2567
2568                params.push(FunctionParameter {
2569                    pattern: shape_ast::ast::DestructurePattern::Identifier(
2570                        param.name.clone(),
2571                        Span::DUMMY,
2572                    ),
2573                    is_const: false,
2574                    is_reference: false,
2575                    is_mut_reference: false,
2576                    is_out: false,
2577                    type_annotation: inferred_type,
2578                    default_value: None,
2579                });
2580            }
2581
2582            // Convert handler body (Expr) to function body (Vec<Statement>)
2583            let body = vec![Statement::Return(Some(handler.body.clone()), Span::DUMMY)];
2584
2585            let func_def = FunctionDef {
2586                name: func_name,
2587                name_span: Span::DUMMY,
2588                declaring_module_path: None,
2589                doc_comment: None,
2590                params,
2591                return_type: handler.return_type.clone(),
2592                body,
2593                type_params: Some(Vec::new()),
2594                annotations: Vec::new(),
2595                is_async: false,
2596                is_comptime: false,
2597                where_clause: None,
2598            };
2599
2600            self.register_function(&func_def)?;
2601            self.compile_function(&func_def)?;
2602
2603            // Capture frame descriptor for the annotation handler function.
2604            // Mirrors functions.rs:1750 — required for v2 typed opcode verification
2605            // (ADR-006 §2.7.5.1) and JIT deoptimization map construction. Without
2606            // this, v2 typed opcodes emitted in the handler body (e.g. NewTypedArrayI64,
2607            // TypedArrayPushI64 from CoW array mutations) fail verification and the
2608            // JIT SEGFAULTs on deopt paths.
2609            let func_idx = self.program.functions.len() - 1;
2610            self.program.functions[func_idx].locals_count = self.next_local;
2611            self.capture_function_local_storage_hints(func_idx);
2612
2613            let func_id = (self.program.functions.len() - 1) as u16;
2614
2615            match handler.handler_type {
2616                AnnotationHandlerType::Before => compiled.before_handler = Some(func_id),
2617                AnnotationHandlerType::After => compiled.after_handler = Some(func_id),
2618                AnnotationHandlerType::OnDefine => compiled.on_define_handler = Some(func_id),
2619                AnnotationHandlerType::Metadata => compiled.metadata_handler = Some(func_id),
2620                AnnotationHandlerType::ComptimePre => {} // handled above
2621                AnnotationHandlerType::ComptimePost => {} // handled above
2622            }
2623        }
2624
2625        // Resolve allowed target kinds.
2626        // Explicit `targets: [...]` in the annotation definition has priority.
2627        // Otherwise infer from handlers:
2628        // before/after handlers only make sense on functions (they wrap calls),
2629        // lifecycle handlers (on_define/metadata) are definition-time only.
2630        if let Some(explicit) = &ann_def.allowed_targets {
2631            compiled.allowed_targets = explicit.clone();
2632        } else if compiled.before_handler.is_some()
2633            || compiled.after_handler.is_some()
2634            || compiled.comptime_pre_handler.is_some()
2635            || compiled.comptime_post_handler.is_some()
2636        {
2637            compiled.allowed_targets =
2638                vec![shape_ast::ast::functions::AnnotationTargetKind::Function];
2639        } else if compiled.on_define_handler.is_some() || compiled.metadata_handler.is_some() {
2640            compiled.allowed_targets = vec![
2641                shape_ast::ast::functions::AnnotationTargetKind::Function,
2642                shape_ast::ast::functions::AnnotationTargetKind::Type,
2643                shape_ast::ast::functions::AnnotationTargetKind::Module,
2644            ];
2645        }
2646
2647        // Enforce that definition-time lifecycle hooks only target definition
2648        // sites (`function` / `type`).
2649        if compiled.on_define_handler.is_some() || compiled.metadata_handler.is_some() {
2650            if compiled.allowed_targets.is_empty() {
2651                return Err(ShapeError::SemanticError {
2652                    message: format!(
2653                        "Annotation '{}' uses `on_define`/`metadata` and cannot have unrestricted targets. Allowed targets are: function, type, module",
2654                        ann_def.name
2655                    ),
2656                    location: Some(self.span_to_source_location(ann_def.span)),
2657                });
2658            }
2659            if let Some(invalid) = compiled
2660                .allowed_targets
2661                .iter()
2662                .find(|kind| !Self::is_definition_annotation_target(**kind))
2663            {
2664                let invalid_label = format!("{:?}", invalid).to_lowercase();
2665                return Err(ShapeError::SemanticError {
2666                    message: format!(
2667                        "Annotation '{}' uses `on_define`/`metadata`, but target '{}' is not a definition target. Allowed targets are: function, type, module",
2668                        ann_def.name, invalid_label
2669                    ),
2670                    location: Some(self.span_to_source_location(ann_def.span)),
2671                });
2672            }
2673        }
2674
2675        self.program
2676            .compiled_annotations
2677            .insert(ann_def.name.clone(), compiled);
2678        Ok(())
2679    }
2680
2681    /// Register ONLY a struct's runtime `TypeSchema` into the compiler's
2682    /// schema registry — no comptime-handler execution, no annotation
2683    /// lifecycle, no native-layout / generic-info side effects.
2684    ///
2685    /// WS-9b: this is the single source of truth for "the struct's runtime
2686    /// fields, indexed by name, are known to the compiler". It is called
2687    /// from two places:
2688    ///
2689    /// * the pass-1 prepass (`predeclare_item_struct_schemas`), so a
2690    ///   struct's schema is available *before any function body compiles* —
2691    ///   making `type` definitions order-independent the same way function
2692    ///   definitions already are (pass-1 `register_item_functions`). Without
2693    ///   this, a function declared *before* the `type` it accepts as a
2694    ///   parameter could not resolve `param.field`: `tracker_schema_id_for_
2695    ///   expr` would miss the not-yet-registered schema, so `a.lo` in
2696    ///   `fn ov(a, b) { a.lo <= b.hi }` (with `type Box` declared after `ov`)
2697    ///   typed as `unknown` and the binop was spuriously rejected.
2698    ///
2699    /// * `register_struct_type` (pass 2), guarded by the same `is_none()`
2700    ///   check — when the prepass already registered the schema this is a
2701    ///   no-op, so pass 2 only runs the comptime handlers / lifecycle once.
2702    fn predeclare_struct_schema(&mut self, struct_def: &shape_ast::ast::StructTypeDef) {
2703        use shape_ast::ast::Literal;
2704        use shape_runtime::type_schema::FieldAnnotation;
2705
2706        if self
2707            .type_tracker
2708            .schema_registry()
2709            .get(&struct_def.name)
2710            .is_some()
2711        {
2712            return;
2713        }
2714        let runtime_fields: Vec<(String, shape_runtime::type_schema::FieldType)> = struct_def
2715            .fields
2716            .iter()
2717            .filter(|f| !f.is_comptime)
2718            .map(|f| {
2719                (
2720                    f.name.clone(),
2721                    Self::type_annotation_to_field_type(&f.type_annotation),
2722                )
2723            })
2724            .collect();
2725        // Collect field annotations (e.g. @alias) so that JSON
2726        // deserialization can map wire names to field names.
2727        let field_annotations: Vec<Vec<FieldAnnotation>> = struct_def
2728            .fields
2729            .iter()
2730            .filter(|f| !f.is_comptime)
2731            .map(|f| {
2732                f.annotations
2733                    .iter()
2734                    .map(|ann| FieldAnnotation {
2735                        name: ann.name.clone(),
2736                        args: ann
2737                            .args
2738                            .iter()
2739                            .filter_map(|arg| match arg {
2740                                Expr::Literal(Literal::String(s), _) => Some(s.clone()),
2741                                _ => None,
2742                            })
2743                            .collect(),
2744                    })
2745                    .collect()
2746            })
2747            .collect();
2748        self.type_tracker
2749            .schema_registry_mut()
2750            .register_type_with_annotations(
2751                struct_def.name.clone(),
2752                runtime_fields,
2753                field_annotations,
2754            );
2755    }
2756
2757    /// Register a struct type definition.
2758    ///
2759    /// Comptime fields are baked at compile time and excluded from the runtime TypeSchema.
2760    /// Their values are stored in `self.comptime_fields` for constant-folded access.
2761    fn register_struct_type(
2762        &mut self,
2763        struct_def: &shape_ast::ast::StructTypeDef,
2764        span: shape_ast::ast::Span,
2765    ) -> Result<()> {
2766        use shape_runtime::type_schema::{FieldAnnotation, TypeSchemaBuilder};
2767
2768        // Validate annotation target kinds before type registration.
2769        for ann in &struct_def.annotations {
2770            self.validate_annotation_target_usage(
2771                ann,
2772                shape_ast::ast::functions::AnnotationTargetKind::Type,
2773                span,
2774            )?;
2775        }
2776
2777        if struct_def.native_layout.is_some() {
2778            self.native_layout_types.insert(struct_def.name.clone());
2779        } else {
2780            self.native_layout_types.remove(&struct_def.name);
2781        }
2782
2783        // Pre-register runtime field layout so comptime-generated methods on
2784        // `extend target { ... }` can resolve `self.field` statically.
2785        // If the target is later removed by comptime directives, these
2786        // placeholders are rolled back below.
2787        let runtime_field_names: Vec<String> = struct_def
2788            .fields
2789            .iter()
2790            .filter(|f| !f.is_comptime)
2791            .map(|f| f.name.clone())
2792            .collect();
2793        let runtime_field_types = struct_def
2794            .fields
2795            .iter()
2796            .filter(|f| !f.is_comptime)
2797            .map(|f| (f.name.clone(), f.type_annotation.clone()))
2798            .collect::<std::collections::HashMap<_, _>>();
2799        self.struct_types
2800            .insert(struct_def.name.clone(), (runtime_field_names, span));
2801        self.struct_generic_info.insert(
2802            struct_def.name.clone(),
2803            StructGenericInfo {
2804                type_params: struct_def.type_params.clone().unwrap_or_default(),
2805                runtime_field_types,
2806            },
2807        );
2808        // J-CT.2 (2026-05-23) — snapshot full struct AST for the comptime
2809        // mini-VM. `comptime_impl_blocks` referencing this type need the
2810        // original AST (field annotations, generic info, default values) to
2811        // compile struct-literal constructions + field access inside
2812        // `comptime { }` blocks. `struct_types` retains only field NAMES;
2813        // the mini-VM gets the full def via `comptime_context_struct_defs`.
2814        // Stored here in the canonical `register_struct_type` site so both
2815        // `Item::StructType` and `Item::Export(ExportItem::Struct)` paths
2816        // populate it uniformly.
2817        self.comptime_context_struct_defs
2818            .insert(struct_def.name.clone(), struct_def.clone());
2819        self.predeclare_struct_schema(struct_def);
2820
2821        // Execute comptime annotation handlers before registration so
2822        // `remove target` can suppress type emission entirely.
2823        if self.execute_struct_comptime_handlers(struct_def)? {
2824            self.struct_types.remove(&struct_def.name);
2825            self.struct_generic_info.remove(&struct_def.name);
2826            self.comptime_context_struct_defs.remove(&struct_def.name);
2827            return Ok(());
2828        }
2829
2830        if struct_def.native_layout.is_some() {
2831            self.register_native_struct_layout(struct_def, span)?;
2832        }
2833
2834        // Build TypeSchema for runtime fields only
2835        if self
2836            .type_tracker
2837            .schema_registry()
2838            .get(&struct_def.name)
2839            .is_none()
2840        {
2841            let mut builder = TypeSchemaBuilder::new(struct_def.name.clone());
2842            for field in &struct_def.fields {
2843                if field.is_comptime {
2844                    continue;
2845                }
2846                let field_type = Self::type_annotation_to_field_type(&field.type_annotation);
2847                let mut annotations = Vec::new();
2848                for ann in &field.annotations {
2849                    let args: Vec<String> = ann
2850                        .args
2851                        .iter()
2852                        .filter_map(Self::eval_annotation_arg)
2853                        .collect();
2854                    annotations.push(FieldAnnotation {
2855                        name: ann.name.clone(),
2856                        args,
2857                    });
2858                }
2859                builder = builder.field_with_meta(field.name.clone(), field_type, annotations);
2860            }
2861            builder.register(self.type_tracker.schema_registry_mut());
2862        }
2863
2864        // Bake comptime field values into the `comptime_fields` registry
2865        // for constant-folded property access.
2866        //
2867        // **Phase-2c rebuild pending — see ADR-006 §2.4.** The previous
2868        // body iterated `struct_def.fields`, materialized each comptime
2869        // field's literal default value into a per-FieldType `ValueWord`
2870        // (`from_f64`, `from_i64`, `from_string`, `from_bool`, `none`),
2871        // and stored the result in a `shape_value::ValueMap`. After the
2872        // strict-typing bulldozer:
2873        //
2874        // - `ValueMap` (typedef alias for `HashMap<String, ValueWord>`)
2875        //   and `ValueWord` are deleted from `shape-value` (per CLAUDE.md
2876        //   "Renames to refuse on sight").
2877        // - The `comptime_fields: HashMap<String, ValueMap>` field on the
2878        //   compiler struct itself uses the deleted carrier; its
2879        //   rebuild lives in the cluster that owns `compiler/mod.rs`.
2880        // - The kinded replacement is per-FieldType `KindedSlot::from_int`
2881        //   / `from_number` / `from_string_arc(Arc<String>)` / `from_bool`
2882        //   / `none()` per ADR-006 §2.4 / Q6 — preserving the source
2883        //   literal's kind by construction without ever round-tripping
2884        //   through a tagged dynamic word. The literal-arm validation
2885        //   (rejecting non-literal default values with the
2886        //   "Comptime field … must have a literal default value" error)
2887        //   stays — that's an AST-level invariant unaffected by the
2888        //   value-tier rewrite.
2889        //
2890        // Until Phase 2c lands, this branch is a structural no-op:
2891        // comptime field defaults are silently dropped, so subsequent
2892        // property-access reads against `Currency.symbol` (cluster-level
2893        // out-of-territory consumer at `expressions/property_access.rs:194`)
2894        // fall through to the runtime field-access path. Suppressing the
2895        // bake is the correct boundary per playbook §7 #4: we do not
2896        // synthesize a placeholder ValueMap that would poison the
2897        // comptime_fields registry.
2898        for field in &struct_def.fields {
2899            if !field.is_comptime {
2900                continue;
2901            }
2902            if let Some(ref default_expr) = field.default_value {
2903                match default_expr {
2904                    Expr::Literal(Literal::Number(_), _)
2905                    | Expr::Literal(Literal::Int(_), _)
2906                    | Expr::Literal(Literal::String(_), _)
2907                    | Expr::Literal(Literal::Bool(_), _)
2908                    | Expr::Literal(Literal::None, _) => {
2909                        // Recognised literal — rebuild surface; drop the
2910                        // bake until the kinded comptime_fields registry
2911                        // lands.
2912                    }
2913                    _ => {
2914                        return Err(ShapeError::SemanticError {
2915                            message: format!(
2916                                "Comptime field '{}' on type '{}' must have a literal default value",
2917                                field.name, struct_def.name
2918                            ),
2919                            location: None,
2920                        });
2921                    }
2922                }
2923            }
2924            // Comptime fields without a default are allowed — they must be
2925            // provided via type alias overrides (e.g., type EUR = Currency { symbol: "€" })
2926        }
2927
2928        self.maybe_generate_native_type_conversions(&struct_def.name, span)?;
2929
2930        Ok(())
2931    }
2932
2933    fn register_native_struct_layout(
2934        &mut self,
2935        struct_def: &shape_ast::ast::StructTypeDef,
2936        span: shape_ast::ast::Span,
2937    ) -> Result<()> {
2938        if struct_def.type_params.is_some() {
2939            return Err(ShapeError::SemanticError {
2940                message: format!(
2941                    "type C '{}' cannot be generic in this version",
2942                    struct_def.name
2943                ),
2944                location: Some(self.span_to_source_location(span)),
2945            });
2946        }
2947
2948        if struct_def.fields.iter().any(|f| f.is_comptime) {
2949            return Err(ShapeError::SemanticError {
2950                message: format!(
2951                    "type C '{}' cannot contain comptime fields",
2952                    struct_def.name
2953                ),
2954                location: Some(self.span_to_source_location(span)),
2955            });
2956        }
2957
2958        let abi = struct_def
2959            .native_layout
2960            .as_ref()
2961            .map(|b| b.abi.clone())
2962            .unwrap_or_else(|| "C".to_string());
2963        if abi != "C" {
2964            return Err(ShapeError::SemanticError {
2965                message: format!(
2966                    "type '{}' uses unsupported native ABI '{}'; only C is supported",
2967                    struct_def.name, abi
2968                ),
2969                location: Some(self.span_to_source_location(span)),
2970            });
2971        }
2972
2973        let mut struct_align: u64 = 1;
2974        let mut offset: u64 = 0;
2975        let mut field_layouts = Vec::with_capacity(struct_def.fields.len());
2976
2977        for field in &struct_def.fields {
2978            let field_spec =
2979                self.native_field_layout_spec(&field.type_annotation, span, &struct_def.name)?;
2980            struct_align = struct_align.max(field_spec.align);
2981            offset = Self::align_to(offset, field_spec.align);
2982            if offset > u32::MAX as u64
2983                || field_spec.size > u32::MAX as u64
2984                || field_spec.align > u32::MAX as u64
2985            {
2986                return Err(ShapeError::SemanticError {
2987                    message: format!(
2988                        "type C '{}' layout exceeds supported size/alignment limits",
2989                        struct_def.name
2990                    ),
2991                    location: Some(self.span_to_source_location(span)),
2992                });
2993            }
2994            field_layouts.push(crate::bytecode::NativeStructFieldLayout {
2995                name: field.name.clone(),
2996                c_type: field_spec.c_type,
2997                offset: offset as u32,
2998                size: field_spec.size as u32,
2999                align: field_spec.align as u32,
3000            });
3001            offset = offset.saturating_add(field_spec.size);
3002        }
3003
3004        let size = Self::align_to(offset, struct_align);
3005        if size > u32::MAX as u64 || struct_align > u32::MAX as u64 {
3006            return Err(ShapeError::SemanticError {
3007                message: format!(
3008                    "type C '{}' layout exceeds supported size/alignment limits",
3009                    struct_def.name
3010                ),
3011                location: Some(self.span_to_source_location(span)),
3012            });
3013        }
3014
3015        let entry = crate::bytecode::NativeStructLayoutEntry {
3016            name: struct_def.name.clone(),
3017            abi,
3018            size: size as u32,
3019            align: struct_align as u32,
3020            fields: field_layouts,
3021        };
3022
3023        if let Some(existing) = self
3024            .program
3025            .native_struct_layouts
3026            .iter_mut()
3027            .find(|existing| existing.name == entry.name)
3028        {
3029            *existing = entry;
3030        } else {
3031            self.program.native_struct_layouts.push(entry);
3032        }
3033
3034        Ok(())
3035    }
3036
3037    fn align_to(value: u64, align: u64) -> u64 {
3038        debug_assert!(align > 0);
3039        let mask = align - 1;
3040        (value + mask) & !mask
3041    }
3042
3043    fn native_field_layout_spec(
3044        &self,
3045        ann: &shape_ast::ast::TypeAnnotation,
3046        span: shape_ast::ast::Span,
3047        struct_name: &str,
3048    ) -> Result<NativeFieldLayoutSpec> {
3049        use shape_ast::ast::TypeAnnotation;
3050
3051        let pointer = std::mem::size_of::<usize>() as u64;
3052
3053        let fail = || -> Result<NativeFieldLayoutSpec> {
3054            Err(ShapeError::SemanticError {
3055                message: format!(
3056                    "unsupported type C field type '{}' in '{}'",
3057                    ann.to_type_string(),
3058                    struct_name
3059                ),
3060                location: Some(self.span_to_source_location(span)),
3061            })
3062        };
3063
3064        if let Some(name) = ann.as_type_name_str() {
3065                if let Some(existing) = self
3066                    .program
3067                    .native_struct_layouts
3068                    .iter()
3069                    .find(|layout| layout.name == name)
3070                {
3071                    return Ok(NativeFieldLayoutSpec {
3072                        c_type: name.to_string(),
3073                        size: existing.size as u64,
3074                        align: existing.align as u64,
3075                    });
3076                }
3077
3078                let spec = match name {
3079                    "f64" | "number" | "Number" | "float" => ("f64", 8, 8),
3080                    "f32" => ("f32", 4, 4),
3081                    "i64" | "int" | "integer" | "Int" | "Integer" => ("i64", 8, 8),
3082                    "i32" => ("i32", 4, 4),
3083                    "i16" => ("i16", 2, 2),
3084                    "i8" | "char" => ("i8", 1, 1),
3085                    "u64" => ("u64", 8, 8),
3086                    "u32" => ("u32", 4, 4),
3087                    "u16" => ("u16", 2, 2),
3088                    "u8" | "byte" => ("u8", 1, 1),
3089                    "bool" | "boolean" => ("bool", 1, 1),
3090                    "isize" => ("isize", pointer, pointer),
3091                    "usize" | "ptr" | "pointer" => ("ptr", pointer, pointer),
3092                    "string" | "str" | "cstring" => ("cstring", pointer, pointer),
3093                    _ => return fail(),
3094                };
3095                return Ok(NativeFieldLayoutSpec {
3096                    c_type: spec.0.to_string(),
3097                    size: spec.1,
3098                    align: spec.2,
3099                });
3100        }
3101        match ann {
3102            TypeAnnotation::Generic { name, args } if name == "Option" && args.len() == 1 => {
3103                let inner = self.native_field_layout_spec(&args[0], span, struct_name)?;
3104                if inner.c_type == "cstring" {
3105                    Ok(NativeFieldLayoutSpec {
3106                        c_type: "cstring?".to_string(),
3107                        size: pointer,
3108                        align: pointer,
3109                    })
3110                } else {
3111                    fail()
3112                }
3113            }
3114            _ => fail(),
3115        }
3116    }
3117
3118    fn maybe_generate_native_type_conversions(
3119        &mut self,
3120        type_name: &str,
3121        span: shape_ast::ast::Span,
3122    ) -> Result<()> {
3123        let pair = if self.native_layout_types.contains(type_name) {
3124            let Some(object_type) = Self::object_type_name_for_native_layout(type_name) else {
3125                return Ok(());
3126            };
3127            if !self.struct_types.contains_key(&object_type)
3128                || self.native_layout_types.contains(&object_type)
3129            {
3130                return Ok(());
3131            }
3132            (type_name.to_string(), object_type)
3133        } else {
3134            let candidates: Vec<String> = Self::native_layout_name_candidates_for_object(type_name)
3135                .into_iter()
3136                .filter(|candidate| self.native_layout_types.contains(candidate))
3137                .collect();
3138            if candidates.is_empty() {
3139                return Ok(());
3140            }
3141            if candidates.len() > 1 {
3142                return Err(ShapeError::SemanticError {
3143                    message: format!(
3144                        "type '{}' matches multiple `type C` companions ({}) - use one canonical name",
3145                        type_name,
3146                        candidates.join(", ")
3147                    ),
3148                    location: Some(self.span_to_source_location(span)),
3149                });
3150            }
3151            (candidates[0].clone(), type_name.to_string())
3152        };
3153
3154        let pair_key = format!("{}::{}", pair.0, pair.1);
3155        if self.generated_native_conversion_pairs.contains(&pair_key) {
3156            return Ok(());
3157        }
3158
3159        self.validate_native_conversion_pair(&pair.0, &pair.1, span)?;
3160        self.generate_native_conversion_direction(&pair.0, &pair.1, span)?;
3161        self.generate_native_conversion_direction(&pair.1, &pair.0, span)?;
3162        self.generated_native_conversion_pairs.insert(pair_key);
3163        Ok(())
3164    }
3165
3166    fn object_type_name_for_native_layout(name: &str) -> Option<String> {
3167        if let Some(base) = name.strip_suffix("Layout")
3168            && !base.is_empty()
3169        {
3170            return Some(base.to_string());
3171        }
3172        if let Some(base) = name.strip_suffix('C')
3173            && !base.is_empty()
3174        {
3175            return Some(base.to_string());
3176        }
3177        if let Some(base) = name.strip_prefix('C')
3178            && !base.is_empty()
3179            && base
3180                .chars()
3181                .next()
3182                .map(|ch| ch.is_ascii_uppercase())
3183                .unwrap_or(false)
3184        {
3185            return Some(base.to_string());
3186        }
3187        None
3188    }
3189
3190    fn native_layout_name_candidates_for_object(name: &str) -> Vec<String> {
3191        vec![
3192            format!("{}Layout", name),
3193            format!("{}C", name),
3194            format!("C{}", name),
3195        ]
3196    }
3197
3198    fn validate_native_conversion_pair(
3199        &self,
3200        c_type: &str,
3201        object_type: &str,
3202        span: shape_ast::ast::Span,
3203    ) -> Result<()> {
3204        if !self.native_layout_types.contains(c_type) {
3205            return Err(ShapeError::SemanticError {
3206                message: format!("'{}' is not declared as `type C`", c_type),
3207                location: Some(self.span_to_source_location(span)),
3208            });
3209        }
3210        if self.native_layout_types.contains(object_type) {
3211            return Err(ShapeError::SemanticError {
3212                message: format!(
3213                    "auto conversion target '{}' cannot also be declared as `type C`",
3214                    object_type
3215                ),
3216                location: Some(self.span_to_source_location(span)),
3217            });
3218        }
3219
3220        let c_type_info =
3221            self.struct_generic_info
3222                .get(c_type)
3223                .ok_or_else(|| ShapeError::SemanticError {
3224                    message: format!("missing compiler metadata for `type C {}`", c_type),
3225                    location: Some(self.span_to_source_location(span)),
3226                })?;
3227        let object_type_info =
3228            self.struct_generic_info
3229                .get(object_type)
3230                .ok_or_else(|| ShapeError::SemanticError {
3231                    message: format!(
3232                        "missing compiler metadata for companion type '{}'",
3233                        object_type
3234                    ),
3235                    location: Some(self.span_to_source_location(span)),
3236                })?;
3237
3238        if !c_type_info.type_params.is_empty() || !object_type_info.type_params.is_empty() {
3239            return Err(ShapeError::SemanticError {
3240                message: format!(
3241                    "auto `type C` conversions currently require non-generic types (`{}` <-> `{}`)",
3242                    c_type, object_type
3243                ),
3244                location: Some(self.span_to_source_location(span)),
3245            });
3246        }
3247
3248        let c_fields = self
3249            .struct_types
3250            .get(c_type)
3251            .map(|(fields, _)| fields)
3252            .ok_or_else(|| ShapeError::SemanticError {
3253                message: format!("missing field metadata for `type C {}`", c_type),
3254                location: Some(self.span_to_source_location(span)),
3255            })?;
3256        let object_fields = self
3257            .struct_types
3258            .get(object_type)
3259            .map(|(fields, _)| fields)
3260            .ok_or_else(|| ShapeError::SemanticError {
3261                message: format!(
3262                    "missing field metadata for companion type '{}'",
3263                    object_type
3264                ),
3265                location: Some(self.span_to_source_location(span)),
3266            })?;
3267
3268        let c_field_set: std::collections::HashSet<&str> =
3269            c_fields.iter().map(String::as_str).collect();
3270        let object_field_set: std::collections::HashSet<&str> =
3271            object_fields.iter().map(String::as_str).collect();
3272        if c_field_set != object_field_set {
3273            return Err(ShapeError::SemanticError {
3274                message: format!(
3275                    "auto conversion pair '{}' <-> '{}' must have identical runtime fields",
3276                    c_type, object_type
3277                ),
3278                location: Some(self.span_to_source_location(span)),
3279            });
3280        }
3281
3282        for field_name in c_field_set {
3283            let c_ann = c_type_info
3284                .runtime_field_types
3285                .get(field_name)
3286                .ok_or_else(|| ShapeError::SemanticError {
3287                    message: format!(
3288                        "missing type metadata for field '{}.{}'",
3289                        c_type, field_name
3290                    ),
3291                    location: Some(self.span_to_source_location(span)),
3292                })?;
3293            let object_ann = object_type_info
3294                .runtime_field_types
3295                .get(field_name)
3296                .ok_or_else(|| ShapeError::SemanticError {
3297                    message: format!(
3298                        "missing type metadata for field '{}.{}'",
3299                        object_type, field_name
3300                    ),
3301                    location: Some(self.span_to_source_location(span)),
3302                })?;
3303            if c_ann != object_ann {
3304                return Err(ShapeError::SemanticError {
3305                    message: format!(
3306                        "field type mismatch for auto conversion '{}.{}' (`{}`) vs '{}.{}' (`{}`)",
3307                        c_type,
3308                        field_name,
3309                        c_ann.to_type_string(),
3310                        object_type,
3311                        field_name,
3312                        object_ann.to_type_string()
3313                    ),
3314                    location: Some(self.span_to_source_location(span)),
3315                });
3316            }
3317        }
3318
3319        Ok(())
3320    }
3321
3322    fn generate_native_conversion_direction(
3323        &mut self,
3324        source_type: &str,
3325        target_type: &str,
3326        span: shape_ast::ast::Span,
3327    ) -> Result<()> {
3328        let fn_name = format!(
3329            "__auto_native_from_{}_to_{}",
3330            Self::sanitize_auto_symbol(source_type),
3331            Self::sanitize_auto_symbol(target_type)
3332        );
3333        if self.function_defs.contains_key(&fn_name) {
3334            return Ok(());
3335        }
3336
3337        let target_fields = self
3338            .struct_types
3339            .get(target_type)
3340            .map(|(fields, _)| fields.clone())
3341            .ok_or_else(|| ShapeError::SemanticError {
3342                message: format!(
3343                    "missing target type metadata for auto conversion '{}'",
3344                    target_type
3345                ),
3346                location: Some(self.span_to_source_location(span)),
3347            })?;
3348
3349        let source_expr = Expr::Identifier("value".to_string(), span);
3350        let struct_fields = target_fields
3351            .iter()
3352            .map(|field| {
3353                (
3354                    field.clone(),
3355                    Expr::PropertyAccess {
3356                        object: Box::new(source_expr.clone()),
3357                        property: field.clone(),
3358                        optional: false,
3359                        span,
3360                    },
3361                )
3362            })
3363            .collect::<Vec<_>>();
3364        let body = vec![Statement::Return(
3365            Some(Expr::StructLiteral {
3366                type_name: target_type.into(),
3367                fields: struct_fields,
3368                span,
3369            }),
3370            span,
3371        )];
3372        let fn_def = FunctionDef {
3373            name: fn_name.clone(),
3374            name_span: span,
3375            declaring_module_path: None,
3376            doc_comment: None,
3377            params: vec![FunctionParameter {
3378                pattern: DestructurePattern::Identifier("value".to_string(), span),
3379                is_const: false,
3380                is_reference: false,
3381                is_mut_reference: false,
3382                is_out: false,
3383                type_annotation: Some(TypeAnnotation::Reference(source_type.into())),
3384                default_value: None,
3385            }],
3386            return_type: Some(TypeAnnotation::Reference(target_type.into())),
3387            body,
3388            type_params: Some(Vec::new()),
3389            annotations: Vec::new(),
3390            is_async: false,
3391            is_comptime: false,
3392            where_clause: None,
3393        };
3394        self.register_function(&fn_def)?;
3395        self.compile_function(&fn_def)?;
3396
3397        self.program.register_trait_method_symbol(
3398            "From",
3399            target_type,
3400            Some(source_type),
3401            "from",
3402            &fn_name,
3403        );
3404        self.program.register_trait_method_symbol(
3405            "Into",
3406            source_type,
3407            Some(target_type),
3408            "into",
3409            &fn_name,
3410        );
3411        let _ = self.type_inference.env.register_trait_impl_named(
3412            "From",
3413            target_type,
3414            source_type,
3415            vec!["from".to_string()],
3416        );
3417        let _ = self.type_inference.env.register_trait_impl_named(
3418            "Into",
3419            source_type,
3420            target_type,
3421            vec!["into".to_string()],
3422        );
3423        Ok(())
3424    }
3425
3426    fn sanitize_auto_symbol(name: &str) -> String {
3427        let mut out = String::with_capacity(name.len());
3428        for ch in name.chars() {
3429            if ch.is_ascii_alphanumeric() {
3430                out.push(ch);
3431            } else {
3432                out.push('_');
3433            }
3434        }
3435        out
3436    }
3437
3438    /// Execute comptime annotation handlers for a struct type definition.
3439    ///
3440    /// Mirrors `execute_comptime_handlers` in functions.rs but uses
3441    /// `ComptimeTarget::from_type()` to build the target from struct fields.
3442    fn execute_struct_comptime_handlers(
3443        &mut self,
3444        struct_def: &shape_ast::ast::StructTypeDef,
3445    ) -> Result<bool> {
3446        let mut removed = false;
3447        for ann in &struct_def.annotations {
3448            if let Some((_, compiled)) = self.lookup_compiled_annotation(ann) {
3449                let handlers = [
3450                    compiled.comptime_pre_handler,
3451                    compiled.comptime_post_handler,
3452                ];
3453                for handler in handlers.into_iter().flatten() {
3454                    // Build field info for ComptimeTarget::from_type()
3455                    // Include per-field annotations so comptime handlers can inspect them.
3456                    let fields: Vec<(
3457                        String,
3458                        Option<shape_ast::ast::TypeAnnotation>,
3459                        Vec<shape_ast::ast::functions::Annotation>,
3460                    )> = struct_def
3461                        .fields
3462                        .iter()
3463                        .map(|f| {
3464                            (
3465                                f.name.clone(),
3466                                Some(f.type_annotation.clone()),
3467                                f.annotations.clone(),
3468                            )
3469                        })
3470                        .collect();
3471
3472                    let target = super::comptime_target::ComptimeTarget::from_type(
3473                        &struct_def.name,
3474                        &fields,
3475                    );
3476                    // R8 W9 G.2 Step 2 Bucket 7: to_nanboxed now returns
3477                    // Result; surface the V3-S5 ckpt-5 SURFACE through the
3478                    // caller's Result chain instead of panicking.
3479                    let target_value = target.to_nanboxed()?;
3480                    let target_name = struct_def.name.clone();
3481                    let handler_span = handler.span;
3482                    let execution = self.execute_comptime_annotation_handler(
3483                        ann,
3484                        &handler,
3485                        target_value,
3486                        &compiled.param_names,
3487                        &[],
3488                    )?;
3489
3490                    if self
3491                        .process_comptime_directives(execution.directives, &target_name)
3492                        .map_err(|e| ShapeError::RuntimeError {
3493                            message: format!(
3494                                "Comptime handler '{}' directive processing failed: {}",
3495                                ann.name, e
3496                            ),
3497                            location: Some(self.span_to_source_location(handler_span)),
3498                        })?
3499                    {
3500                        removed = true;
3501                        break;
3502                    }
3503                }
3504            }
3505            if removed {
3506                break;
3507            }
3508        }
3509        Ok(removed)
3510    }
3511
3512    fn current_module_path_for(&self, module_name: &str) -> String {
3513        if let Some(parent) = self.module_scope_stack.last() {
3514            format!("{}::{}", parent, module_name)
3515        } else {
3516            module_name.to_string()
3517        }
3518    }
3519
3520    pub(super) fn qualify_module_symbol(module_path: &str, name: &str) -> String {
3521        format!("{}::{}", module_path, name)
3522    }
3523
3524    /// R8 W8 Cluster A (2026-05-24): predicate for the module-level
3525    /// `const` reject-runtime-init validation. Returns `true` when the
3526    /// initializer expression can be evaluated entirely at compile time
3527    /// without invoking the VM:
3528    ///   - literals (any kind)
3529    ///   - unary `-` / `!` / `~` applied to a comptime-evaluable operand
3530    /// Function calls, identifiers, binary ops, etc. return `false` and
3531    /// surface a clean compile error per the dispatch's reject test.
3532    /// ADR-006 §2.7.5 stamp-at-compile-time alignment: the predicate
3533    /// matches the loader-side `comptime_eval_const_initializer` shape
3534    /// in `crates/shape-runtime/src/module_loader/loading.rs`.
3535    fn const_initializer_is_comptime_evaluable(expr: &shape_ast::ast::Expr) -> bool {
3536        use shape_ast::ast::Expr;
3537        match expr {
3538            Expr::Literal(_, _) => true,
3539            Expr::UnaryOp { operand, .. } => {
3540                Self::const_initializer_is_comptime_evaluable(operand)
3541            }
3542            _ => false,
3543        }
3544    }
3545
3546    /// Returns true if a name refers to a builtin/primitive type that should
3547    /// not be module-qualified.
3548    fn is_builtin_type_name(name: &str) -> bool {
3549        matches!(
3550            name,
3551            "int" | "number" | "string" | "bool" | "decimal" | "bigint"
3552                | "Array" | "HashMap" | "Option" | "Result" | "DateTime"
3553                | "Content" | "Table" | "DataTable" | "Mat"
3554                // W18.5 per-type content builders (supervisor D4,
3555                // R8 W3 2026-05-24): `Code::new()` / `KeyValue::new()`
3556                // namespaces ride the same builtin-type-name path as
3557                // `Table` / `Content` so module-qualification doesn't
3558                // wrap them. The runtime ctors are wired in
3559                // `function_calls.rs::compile_type_namespace_builtin_call`.
3560                | "Code" | "KeyValue"
3561                | "Json" | "Duration" | "Regex"
3562                | "Vec"
3563                | "int8" | "int16" | "int32" | "int64"
3564                | "uint8" | "uint16" | "uint32" | "uint64"
3565                | "float32" | "float64"
3566                | "IoHandle"
3567        )
3568    }
3569
3570    fn qualify_type_name(
3571        type_name: &shape_ast::ast::TypeName,
3572        module_path: &str,
3573    ) -> shape_ast::ast::TypeName {
3574        match type_name {
3575            shape_ast::ast::TypeName::Simple(path)
3576                if !path.is_qualified() && !Self::is_builtin_type_name(path.as_str()) =>
3577            {
3578                shape_ast::ast::TypeName::Simple(
3579                    Self::qualify_module_symbol(module_path, path.as_str()).into(),
3580                )
3581            }
3582            shape_ast::ast::TypeName::Generic { name, type_args }
3583                if !name.is_qualified() && !Self::is_builtin_type_name(name.as_str()) =>
3584            {
3585                shape_ast::ast::TypeName::Generic {
3586                    name: Self::qualify_module_symbol(module_path, name.as_str()).into(),
3587                    type_args: type_args.clone(),
3588                }
3589            }
3590            _ => type_name.clone(),
3591        }
3592    }
3593
3594    pub(super) fn qualify_module_item(&self, item: &Item, module_path: &str) -> Result<Item> {
3595        match item {
3596            Item::Function(func, span) => {
3597                let mut qualified = func.clone();
3598                qualified.name = Self::qualify_module_symbol(module_path, &func.name);
3599                Ok(Item::Function(qualified, *span))
3600            }
3601            Item::Export(export, span) if export.source_decl.is_none() => {
3602                let mut qualified = export.clone();
3603                match &mut qualified.item {
3604                    ExportItem::Function(func) => {
3605                        func.name = Self::qualify_module_symbol(module_path, &func.name);
3606                    }
3607                    ExportItem::BuiltinFunction(func) => {
3608                        func.name = Self::qualify_module_symbol(module_path, &func.name);
3609                    }
3610                    ExportItem::ForeignFunction(func) => {
3611                        func.name = Self::qualify_module_symbol(module_path, &func.name);
3612                    }
3613                    ExportItem::Annotation(annotation) => {
3614                        annotation.name =
3615                            Self::qualify_module_symbol(module_path, &annotation.name);
3616                    }
3617                    ExportItem::Struct(def) => {
3618                        def.name = Self::qualify_module_symbol(module_path, &def.name);
3619                    }
3620                    ExportItem::Enum(def) => {
3621                        def.name = Self::qualify_module_symbol(module_path, &def.name);
3622                    }
3623                    ExportItem::TypeAlias(def) => {
3624                        def.name = Self::qualify_module_symbol(module_path, &def.name);
3625                    }
3626                    ExportItem::Trait(def) => {
3627                        def.name = Self::qualify_module_symbol(module_path, &def.name);
3628                    }
3629                    _ => {}
3630                }
3631                Ok(Item::Export(qualified, *span))
3632            }
3633            Item::BuiltinFunctionDecl(def, span) => {
3634                let mut qualified = def.clone();
3635                qualified.name = Self::qualify_module_symbol(module_path, &def.name);
3636                Ok(Item::BuiltinFunctionDecl(qualified, *span))
3637            }
3638            Item::AnnotationDef(def, span) => {
3639                let mut qualified = def.clone();
3640                qualified.name = Self::qualify_module_symbol(module_path, &def.name);
3641                Ok(Item::AnnotationDef(qualified, *span))
3642            }
3643            Item::VariableDecl(decl, span) => {
3644                if decl.kind != VarKind::Const {
3645                    return Err(ShapeError::SemanticError {
3646                        message: "module-level variable declarations currently require `const`"
3647                            .to_string(),
3648                        location: Some(self.span_to_source_location(*span)),
3649                    });
3650                }
3651                let mut qualified = decl.clone();
3652                let Some(name) = decl.pattern.as_identifier() else {
3653                    return Err(ShapeError::SemanticError {
3654                        message:
3655                            "module-level constants currently require a simple identifier binding"
3656                                .to_string(),
3657                        location: Some(self.span_to_source_location(*span)),
3658                    });
3659                };
3660                qualified.pattern = DestructurePattern::Identifier(
3661                    Self::qualify_module_symbol(module_path, name),
3662                    *span,
3663                );
3664                Ok(Item::VariableDecl(qualified, *span))
3665            }
3666            Item::Statement(Statement::VariableDecl(decl, stmt_span), item_span) => {
3667                if decl.kind != VarKind::Const {
3668                    return Err(ShapeError::SemanticError {
3669                        message: "module-level variable declarations currently require `const`"
3670                            .to_string(),
3671                        location: Some(self.span_to_source_location(*stmt_span)),
3672                    });
3673                }
3674                let mut qualified = decl.clone();
3675                let Some(name) = decl.pattern.as_identifier() else {
3676                    return Err(ShapeError::SemanticError {
3677                        message:
3678                            "module-level constants currently require a simple identifier binding"
3679                                .to_string(),
3680                        location: Some(self.span_to_source_location(*stmt_span)),
3681                    });
3682                };
3683                qualified.pattern = DestructurePattern::Identifier(
3684                    Self::qualify_module_symbol(module_path, name),
3685                    *stmt_span,
3686                );
3687                Ok(Item::Statement(
3688                    Statement::VariableDecl(qualified, *stmt_span),
3689                    *item_span,
3690                ))
3691            }
3692            Item::Statement(Statement::Assignment(assign, stmt_span), item_span) => {
3693                let mut qualified = assign.clone();
3694                if let Some(name) = assign.pattern.as_identifier() {
3695                    qualified.pattern = DestructurePattern::Identifier(
3696                        Self::qualify_module_symbol(module_path, name),
3697                        *stmt_span,
3698                    );
3699                }
3700                Ok(Item::Statement(
3701                    Statement::Assignment(qualified, *stmt_span),
3702                    *item_span,
3703                ))
3704            }
3705            Item::Export(export, span) if export.source_decl.is_some() => {
3706                // pub const/let/var: unwrap the source_decl and qualify it as a VariableDecl
3707                let decl = export.source_decl.as_ref().unwrap();
3708                if decl.kind != VarKind::Const {
3709                    return Err(ShapeError::SemanticError {
3710                        message: "module-level variable declarations currently require `const`"
3711                            .to_string(),
3712                        location: Some(self.span_to_source_location(*span)),
3713                    });
3714                }
3715                let mut qualified = decl.clone();
3716                let Some(name) = decl.pattern.as_identifier() else {
3717                    return Err(ShapeError::SemanticError {
3718                        message:
3719                            "module-level constants currently require a simple identifier binding"
3720                                .to_string(),
3721                        location: Some(self.span_to_source_location(*span)),
3722                    });
3723                };
3724                qualified.pattern = DestructurePattern::Identifier(
3725                    Self::qualify_module_symbol(module_path, name),
3726                    *span,
3727                );
3728                Ok(Item::VariableDecl(qualified, *span))
3729            }
3730            Item::StructType(def, span) => {
3731                let mut q = def.clone();
3732                q.name = Self::qualify_module_symbol(module_path, &def.name);
3733                Ok(Item::StructType(q, *span))
3734            }
3735            Item::Enum(def, span) => {
3736                let mut q = def.clone();
3737                q.name = Self::qualify_module_symbol(module_path, &def.name);
3738                Ok(Item::Enum(q, *span))
3739            }
3740            Item::TypeAlias(def, span) => {
3741                let mut q = def.clone();
3742                q.name = Self::qualify_module_symbol(module_path, &def.name);
3743                Ok(Item::TypeAlias(q, *span))
3744            }
3745            Item::Trait(def, span) => {
3746                let mut q = def.clone();
3747                q.name = Self::qualify_module_symbol(module_path, &def.name);
3748                Ok(Item::Trait(q, *span))
3749            }
3750            Item::Extend(extend, span) => {
3751                let mut q = extend.clone();
3752                q.type_name = Self::qualify_type_name(&extend.type_name, module_path);
3753                Ok(Item::Extend(q, *span))
3754            }
3755            Item::Impl(impl_block, span) => {
3756                let mut q = impl_block.clone();
3757                q.target_type = Self::qualify_type_name(&impl_block.target_type, module_path);
3758                // Do NOT qualify trait_name — traits may be imported from other scopes
3759                Ok(Item::Impl(q, *span))
3760            }
3761            _ => Ok(item.clone()),
3762        }
3763    }
3764
3765    pub(super) fn collect_module_runtime_exports(
3766        &self,
3767        items: &[Item],
3768        module_path: &str,
3769    ) -> Vec<(String, String)> {
3770        let mut exports = Vec::new();
3771        let has_explicit_exports = items.iter().any(|item| matches!(item, Item::Export(..)));
3772
3773        if has_explicit_exports {
3774            for item in items {
3775                let Item::Export(export, _) = item else {
3776                    continue;
3777                };
3778                if let Some(ref decl) = export.source_decl {
3779                    if let Some(name) = decl.pattern.as_identifier() {
3780                        exports.push((
3781                            name.to_string(),
3782                            Self::qualify_module_symbol(module_path, name),
3783                        ));
3784                    }
3785                }
3786                match &export.item {
3787                    ExportItem::Function(func) => {
3788                        let exported_name = func
3789                            .name
3790                            .rsplit("::")
3791                            .next()
3792                            .unwrap_or(func.name.as_str())
3793                            .to_string();
3794                        exports.push((
3795                            exported_name.clone(),
3796                            Self::qualify_module_symbol(module_path, &exported_name),
3797                        ));
3798                    }
3799                    ExportItem::ForeignFunction(func) => {
3800                        let exported_name = func
3801                            .name
3802                            .rsplit("::")
3803                            .next()
3804                            .unwrap_or(func.name.as_str())
3805                            .to_string();
3806                        exports.push((
3807                            exported_name.clone(),
3808                            Self::qualify_module_symbol(module_path, &exported_name),
3809                        ));
3810                    }
3811                    ExportItem::Named(specs) => {
3812                        for spec in specs {
3813                            let exported_name =
3814                                spec.alias.clone().unwrap_or_else(|| spec.name.clone());
3815                            exports.push((
3816                                exported_name,
3817                                Self::qualify_module_symbol(module_path, &spec.name),
3818                            ));
3819                        }
3820                    }
3821                    // W9: Annotations are compile-time only — they live in
3822                    // `compiled_annotations`, not as runtime values. Including
3823                    // them as runtime exports would synthesize a module-object
3824                    // entry like `{ remote: std::core::remote::remote }` whose
3825                    // RHS has no runtime variable binding. Annotation imports
3826                    // are resolved through `imported_annotations` + use-site
3827                    // lookup; the module object only carries runtime values.
3828                    ExportItem::Annotation(_) => {}
3829                    _ => {}
3830                }
3831            }
3832            exports.sort_by(|a, b| a.0.cmp(&b.0));
3833            exports.dedup_by(|a, b| a.0 == b.0);
3834            return exports;
3835        }
3836
3837        for item in items {
3838            match item {
3839                Item::Function(func, _) => {
3840                    exports.push((
3841                        func.name.clone(),
3842                        Self::qualify_module_symbol(module_path, &func.name),
3843                    ));
3844                }
3845                Item::VariableDecl(decl, _) => {
3846                    if decl.kind == VarKind::Const
3847                        && let Some(name) = decl.pattern.as_identifier()
3848                    {
3849                        exports.push((
3850                            name.to_string(),
3851                            Self::qualify_module_symbol(module_path, name),
3852                        ));
3853                    }
3854                }
3855                Item::Statement(Statement::VariableDecl(decl, _), _) => {
3856                    if decl.kind == VarKind::Const
3857                        && let Some(name) = decl.pattern.as_identifier()
3858                    {
3859                        exports.push((
3860                            name.to_string(),
3861                            Self::qualify_module_symbol(module_path, name),
3862                        ));
3863                    }
3864                }
3865                Item::Export(export, _) => {
3866                    if let Some(ref decl) = export.source_decl {
3867                        if let Some(name) = decl.pattern.as_identifier() {
3868                            exports.push((
3869                                name.to_string(),
3870                                Self::qualify_module_symbol(module_path, name),
3871                            ));
3872                        }
3873                    }
3874                }
3875                Item::Module(module, _) => {
3876                    exports.push((
3877                        module.name.clone(),
3878                        Self::qualify_module_symbol(module_path, &module.name),
3879                    ));
3880                }
3881                // W9: Annotations are compile-time only — see the explicit-
3882                // export arm above. Skipped here for the same reason.
3883                Item::AnnotationDef(_, _) => {}
3884                // Note: Type items (StructType, Enum, TypeAlias, Trait, Interface) are NOT
3885                // included as runtime exports. They are resolved through the type system
3886                // (struct_types, schema_registry, type_aliases) via resolve_type_name(),
3887                // not through runtime module bindings.
3888                _ => {}
3889            }
3890        }
3891        exports.sort_by(|a, b| a.0.cmp(&b.0));
3892        exports.dedup_by(|a, b| a.0 == b.0);
3893        exports
3894    }
3895
3896    fn module_target_fields(items: &[Item]) -> Vec<(String, String)> {
3897        let mut fields = Vec::new();
3898        for item in items {
3899            match item {
3900                Item::Function(func, _) => fields.push((func.name.clone(), "function".to_string())),
3901                Item::VariableDecl(decl, _) => {
3902                    if let Some(name) = decl.pattern.as_identifier() {
3903                        let type_name = decl
3904                            .type_annotation
3905                            .as_ref()
3906                            .and_then(TypeAnnotation::as_simple_name)
3907                            .unwrap_or("any")
3908                            .to_string();
3909                        fields.push((name.to_string(), type_name));
3910                    }
3911                }
3912                Item::Statement(Statement::VariableDecl(decl, _), _) => {
3913                    if let Some(name) = decl.pattern.as_identifier() {
3914                        let type_name = decl
3915                            .type_annotation
3916                            .as_ref()
3917                            .and_then(TypeAnnotation::as_simple_name)
3918                            .unwrap_or("any")
3919                            .to_string();
3920                        fields.push((name.to_string(), type_name));
3921                    }
3922                }
3923                Item::Export(export, _) => {
3924                    if let Some(ref decl) = export.source_decl {
3925                        if let Some(name) = decl.pattern.as_identifier() {
3926                            let type_name = decl
3927                                .type_annotation
3928                                .as_ref()
3929                                .and_then(TypeAnnotation::as_simple_name)
3930                                .unwrap_or("any")
3931                                .to_string();
3932                            fields.push((name.to_string(), type_name));
3933                        }
3934                    }
3935                }
3936                Item::StructType(def, _) => fields.push((def.name.clone(), "type".to_string())),
3937                Item::Enum(def, _) => fields.push((def.name.clone(), "type".to_string())),
3938                Item::TypeAlias(def, _) => fields.push((def.name.clone(), "type".to_string())),
3939                Item::Module(def, _) => fields.push((def.name.clone(), "module".to_string())),
3940                // H4: Include annotation definitions in module target fields
3941                Item::AnnotationDef(def, _) => {
3942                    fields.push((def.name.clone(), "annotation".to_string()))
3943                }
3944                _ => {}
3945            }
3946        }
3947        fields
3948    }
3949
3950    fn process_comptime_directives_for_module(
3951        &mut self,
3952        directives: Vec<super::comptime_builtins::ComptimeDirective>,
3953        module_name: &str,
3954        module_items: &mut Vec<Item>,
3955    ) -> std::result::Result<bool, String> {
3956        let mut removed = false;
3957        for directive in directives {
3958            match directive {
3959                super::comptime_builtins::ComptimeDirective::Extend(extend) => {
3960                    self.apply_comptime_extend(extend, module_name)
3961                        .map_err(|e| e.to_string())?;
3962                }
3963                super::comptime_builtins::ComptimeDirective::RemoveTarget => {
3964                    removed = true;
3965                    break;
3966                }
3967                super::comptime_builtins::ComptimeDirective::ReplaceModule { items } => {
3968                    *module_items = items;
3969                }
3970                super::comptime_builtins::ComptimeDirective::SetParamType { .. }
3971                | super::comptime_builtins::ComptimeDirective::SetParamValue { .. } => {
3972                    return Err(
3973                        "`set param` directives are only valid when compiling function targets"
3974                            .to_string(),
3975                    );
3976                }
3977                super::comptime_builtins::ComptimeDirective::SetReturnType { .. } => {
3978                    return Err(
3979                        "`set return` directives are only valid when compiling function targets"
3980                            .to_string(),
3981                    );
3982                }
3983                super::comptime_builtins::ComptimeDirective::ReplaceBody { .. } => {
3984                    return Err(
3985                        "`replace body` directives are only valid when compiling function targets"
3986                            .to_string(),
3987                    );
3988                }
3989            }
3990        }
3991        Ok(removed)
3992    }
3993
3994    fn execute_module_comptime_handlers(
3995        &mut self,
3996        module_def: &ModuleDecl,
3997        module_path: &str,
3998        module_items: &mut Vec<Item>,
3999    ) -> Result<bool> {
4000        let mut removed = false;
4001        for ann in &module_def.annotations {
4002            if let Some((_, compiled)) = self.lookup_compiled_annotation(ann) {
4003                let handlers = [
4004                    compiled.comptime_pre_handler,
4005                    compiled.comptime_post_handler,
4006                ];
4007                for handler in handlers.into_iter().flatten() {
4008                    let target = super::comptime_target::ComptimeTarget::from_module(
4009                        module_path,
4010                        &Self::module_target_fields(module_items),
4011                    );
4012                    // R8 W9 G.2 Step 2 Bucket 7: to_nanboxed now returns
4013                    // Result; surface the V3-S5 ckpt-5 SURFACE through the
4014                    // caller's Result chain instead of panicking.
4015                    let target_value = target.to_nanboxed()?;
4016                    let handler_span = handler.span;
4017                    let execution = self.execute_comptime_annotation_handler(
4018                        ann,
4019                        &handler,
4020                        target_value,
4021                        &compiled.param_names,
4022                        &[],
4023                    )?;
4024                    if self
4025                        .process_comptime_directives_for_module(
4026                            execution.directives,
4027                            module_path,
4028                            module_items,
4029                        )
4030                        .map_err(|e| ShapeError::RuntimeError {
4031                            message: format!(
4032                                "Comptime handler '{}' directive processing failed: {}",
4033                                ann.name, e
4034                            ),
4035                            location: Some(self.span_to_source_location(handler_span)),
4036                        })?
4037                    {
4038                        removed = true;
4039                        break;
4040                    }
4041                }
4042            }
4043            if removed {
4044                break;
4045            }
4046        }
4047        Ok(removed)
4048    }
4049
4050    fn inject_module_local_comptime_helper_aliases(
4051        &self,
4052        module_path: &str,
4053        helpers: &mut Vec<FunctionDef>,
4054    ) {
4055        let module_prefix = format!("{}::", module_path);
4056        let mut seen: std::collections::HashSet<String> =
4057            helpers.iter().map(|h| h.name.clone()).collect();
4058        let mut aliases = Vec::new();
4059
4060        for helper in helpers.iter() {
4061            let Some(local_name) = helper.name.strip_prefix(&module_prefix) else {
4062                continue;
4063            };
4064            if local_name.contains("::") || !seen.insert(local_name.to_string()) {
4065                continue;
4066            }
4067            let mut alias = helper.clone();
4068            alias.name = local_name.to_string();
4069            aliases.push(alias);
4070        }
4071
4072        helpers.extend(aliases);
4073    }
4074
4075    fn execute_module_inline_comptime_blocks(
4076        &mut self,
4077        module_path: &str,
4078        module_items: &mut Vec<Item>,
4079    ) -> Result<bool> {
4080        loop {
4081            let Some(idx) = module_items
4082                .iter()
4083                .position(|item| matches!(item, Item::Comptime(_, _)))
4084            else {
4085                break;
4086            };
4087
4088            let (stmts, span) = match module_items[idx].clone() {
4089                Item::Comptime(stmts, span) => (stmts, span),
4090                _ => unreachable!("index is guarded by position() matcher"),
4091            };
4092
4093            let extensions: Vec<_> = self
4094                .extension_registry
4095                .as_ref()
4096                .map(|r| r.as_ref().clone())
4097                .unwrap_or_default();
4098            let trait_impls = self.type_inference.env.trait_impl_keys();
4099            let known_type_symbols: std::collections::HashSet<String> = self
4100                .struct_types
4101                .keys()
4102                .chain(self.type_aliases.keys())
4103                .cloned()
4104                .collect();
4105            let mut comptime_helpers = self.collect_comptime_helpers();
4106            self.inject_module_local_comptime_helper_aliases(module_path, &mut comptime_helpers);
4107
4108            // W7 (2026-05-17): TypeReflectionSnapshot for `type_info(T)`
4109            // resolution from a module-scoped comptime block.
4110            let type_snapshot = super::comptime_builtins::build_type_reflection_snapshot(
4111                self,
4112                &[],
4113            );
4114            // J-CT.2 — see `expressions/mod.rs::Expr::Comptime` for
4115            // rationale on comptime-context items.
4116            let comptime_impl_blocks = self.comptime_impl_blocks.clone();
4117            let comptime_context_trait_defs: Vec<_> =
4118                self.trait_defs.values().cloned().collect();
4119            let comptime_context_struct_defs: Vec<_> = self
4120                .comptime_context_struct_defs
4121                .values()
4122                .cloned()
4123                .collect();
4124            let execution = super::comptime::execute_comptime_with_context(
4125                &stmts,
4126                &comptime_helpers,
4127                &comptime_impl_blocks,
4128                &comptime_context_trait_defs,
4129                &comptime_context_struct_defs,
4130                &extensions,
4131                trait_impls,
4132                known_type_symbols,
4133                type_snapshot,
4134            )
4135            .map_err(|e| ShapeError::RuntimeError {
4136                message: format!(
4137                    "Comptime block evaluation failed: {}",
4138                    super::helpers::strip_error_prefix(&e)
4139                ),
4140                location: Some(self.span_to_source_location(span)),
4141            })?;
4142
4143            if self
4144                .process_comptime_directives_for_module(
4145                    execution.directives,
4146                    module_path,
4147                    module_items,
4148                )
4149                .map_err(|e| ShapeError::RuntimeError {
4150                    message: format!("Comptime block directive processing failed: {}", e),
4151                    location: Some(self.span_to_source_location(span)),
4152                })?
4153            {
4154                return Ok(true);
4155            }
4156
4157            if idx < module_items.len() && matches!(module_items[idx], Item::Comptime(_, _)) {
4158                module_items.remove(idx);
4159            }
4160        }
4161
4162        Ok(false)
4163    }
4164
4165    pub(super) fn register_missing_module_items(&mut self, item: &Item) -> Result<()> {
4166        match item {
4167            Item::Function(func, _) => {
4168                if !self.function_defs.contains_key(&func.name) {
4169                    self.register_function(func)?;
4170                }
4171                Ok(())
4172            }
4173            Item::Trait(trait_def, _) => {
4174                if !self.trait_defs.contains_key(&trait_def.name) {
4175                    self.known_traits.insert(trait_def.name.clone());
4176                    self.trait_defs
4177                        .insert(trait_def.name.clone(), trait_def.clone());
4178                    self.type_inference.env.define_trait(trait_def);
4179                }
4180                Ok(())
4181            }
4182            Item::Enum(enum_def, _) => {
4183                self.register_enum(enum_def)?;
4184                Ok(())
4185            }
4186            Item::StructType(struct_def, span) => {
4187                // Pre-declare struct type layout without running full
4188                // register_struct_type (which does annotation validation,
4189                // comptime handlers, native layout, and schema registration).
4190                // This makes the type name resolvable for forward references
4191                // during first-pass registration.
4192                //
4193                // J-CT.2 — also overwrite empty-field placeholders inserted
4194                // by the comptime mini-VM bootstrapping via
4195                // `compile_and_execute_comptime_program`'s `known_type_symbols`
4196                // pre-population (which inserts `Vec::new()` field lists to
4197                // mark types as "known" for downstream resolution). If the
4198                // existing entry is empty and we have real fields, replace
4199                // it. Without this, the `contains_key` guard short-circuits
4200                // and the real fields never land — struct literals inside
4201                // `comptime { }` blocks then fail with "Unknown field 'x'".
4202                let existing_is_empty = self
4203                    .struct_types
4204                    .get(&struct_def.name)
4205                    .map(|(names, _)| names.is_empty())
4206                    .unwrap_or(false);
4207                let has_real_fields = struct_def
4208                    .fields
4209                    .iter()
4210                    .any(|f| !f.is_comptime);
4211                if !self.struct_types.contains_key(&struct_def.name)
4212                    || (existing_is_empty && has_real_fields)
4213                {
4214                    let runtime_field_names: Vec<String> = struct_def
4215                        .fields
4216                        .iter()
4217                        .filter(|f| !f.is_comptime)
4218                        .map(|f| f.name.clone())
4219                        .collect();
4220                    let runtime_field_types = struct_def
4221                        .fields
4222                        .iter()
4223                        .filter(|f| !f.is_comptime)
4224                        .map(|f| (f.name.clone(), f.type_annotation.clone()))
4225                        .collect::<std::collections::HashMap<_, _>>();
4226                    self.struct_types.insert(
4227                        struct_def.name.clone(),
4228                        (runtime_field_names, *span),
4229                    );
4230                    self.struct_generic_info.insert(
4231                        struct_def.name.clone(),
4232                        StructGenericInfo {
4233                            type_params: struct_def.type_params.clone().unwrap_or_default(),
4234                            runtime_field_types,
4235                        },
4236                    );
4237                    // J-CT.2 — snapshot full struct AST for the comptime
4238                    // mini-VM. `comptime_impl_blocks` referencing this
4239                    // type need the original AST (field annotations,
4240                    // generic info, default values) to compile
4241                    // struct-literal constructions + field access inside
4242                    // `comptime { }` blocks. `struct_types` retains only
4243                    // field NAMES; the mini-VM gets the full def via
4244                    // `comptime_context_struct_defs`.
4245                    self.comptime_context_struct_defs
4246                        .insert(struct_def.name.clone(), struct_def.clone());
4247                }
4248                Ok(())
4249            }
4250            Item::TypeAlias(type_alias, _) => {
4251                if !self.type_aliases.contains_key(&type_alias.name) {
4252                    let base_type_name = match &type_alias.type_annotation {
4253                        TypeAnnotation::Basic(name) => Some(name.clone()),
4254                        TypeAnnotation::Reference(name) => Some(name.to_string()),
4255                        _ => None,
4256                    };
4257                    self.type_aliases.insert(
4258                        type_alias.name.clone(),
4259                        base_type_name.unwrap_or_else(|| {
4260                            format!("{:?}", type_alias.type_annotation)
4261                        }),
4262                    );
4263                    self.type_inference.env.define_type_alias(
4264                        &type_alias.name,
4265                        &type_alias.type_annotation,
4266                        type_alias.meta_param_overrides.clone(),
4267                    );
4268                }
4269                Ok(())
4270            }
4271            Item::BuiltinFunctionDecl(def, _) => {
4272                self.register_builtin_function_decl(def)
4273            }
4274            Item::ForeignFunction(def, _) => {
4275                if !self.function_defs.contains_key(&def.name) {
4276                    // Register arity + foreign def (same as register_item_functions)
4277                    let caller_visible = def.params.iter().filter(|p| !p.is_out).count();
4278                    self.function_arity_bounds
4279                        .insert(def.name.clone(), (caller_visible, caller_visible));
4280                    self.function_const_params
4281                        .insert(def.name.clone(), Vec::new());
4282                    self.foreign_function_defs
4283                        .insert(def.name.clone(), def.clone());
4284                }
4285                Ok(())
4286            }
4287            Item::Export(export, _) => match &export.item {
4288                ExportItem::Function(func) => {
4289                    if !self.function_defs.contains_key(&func.name) {
4290                        self.register_function(func)?;
4291                    }
4292                    Ok(())
4293                }
4294                ExportItem::Trait(trait_def) => {
4295                    if !self.trait_defs.contains_key(&trait_def.name) {
4296                        self.known_traits.insert(trait_def.name.clone());
4297                        self.trait_defs
4298                            .insert(trait_def.name.clone(), trait_def.clone());
4299                        self.type_inference.env.define_trait(trait_def);
4300                    }
4301                    Ok(())
4302                }
4303                ExportItem::Enum(enum_def) => {
4304                    self.register_enum(enum_def)?;
4305                    Ok(())
4306                }
4307                ExportItem::Struct(struct_def) => {
4308                    // Pre-declare only — full registration happens in second pass
4309                    if !self.struct_types.contains_key(&struct_def.name) {
4310                        let runtime_field_names: Vec<String> = struct_def
4311                            .fields
4312                            .iter()
4313                            .filter(|f| !f.is_comptime)
4314                            .map(|f| f.name.clone())
4315                            .collect();
4316                        let runtime_field_types = struct_def
4317                            .fields
4318                            .iter()
4319                            .filter(|f| !f.is_comptime)
4320                            .map(|f| (f.name.clone(), f.type_annotation.clone()))
4321                            .collect::<std::collections::HashMap<_, _>>();
4322                        self.struct_types.insert(
4323                            struct_def.name.clone(),
4324                            (runtime_field_names, Span::DUMMY),
4325                        );
4326                        self.struct_generic_info.insert(
4327                            struct_def.name.clone(),
4328                            StructGenericInfo {
4329                                type_params: struct_def.type_params.clone().unwrap_or_default(),
4330                                runtime_field_types,
4331                            },
4332                        );
4333                        // J-CT.2 — see Item::StructType arm above for
4334                        // rationale; mirror the snapshot for exported structs
4335                        // so `comptime { }` blocks in the same compilation
4336                        // unit can resolve them.
4337                        self.comptime_context_struct_defs
4338                            .insert(struct_def.name.clone(), struct_def.clone());
4339                    }
4340                    Ok(())
4341                }
4342                ExportItem::TypeAlias(type_alias) => {
4343                    if !self.type_aliases.contains_key(&type_alias.name) {
4344                        let base_type_name = match &type_alias.type_annotation {
4345                            TypeAnnotation::Basic(name) => Some(name.clone()),
4346                            TypeAnnotation::Reference(name) => Some(name.to_string()),
4347                            _ => None,
4348                        };
4349                        self.type_aliases.insert(
4350                            type_alias.name.clone(),
4351                            base_type_name.unwrap_or_else(|| {
4352                                format!("{:?}", type_alias.type_annotation)
4353                            }),
4354                        );
4355                        self.type_inference.env.define_type_alias(
4356                            &type_alias.name,
4357                            &type_alias.type_annotation,
4358                            type_alias.meta_param_overrides.clone(),
4359                        );
4360                    }
4361                    Ok(())
4362                }
4363                ExportItem::BuiltinFunction(def) => {
4364                    self.register_builtin_function_decl(def)
4365                }
4366                ExportItem::ForeignFunction(def) => {
4367                    if !self.function_defs.contains_key(&def.name) {
4368                        let caller_visible = def.params.iter().filter(|p| !p.is_out).count();
4369                        self.function_arity_bounds
4370                            .insert(def.name.clone(), (caller_visible, caller_visible));
4371                        self.function_const_params
4372                            .insert(def.name.clone(), Vec::new());
4373                        self.foreign_function_defs
4374                            .insert(def.name.clone(), def.clone());
4375                    }
4376                    Ok(())
4377                }
4378                _ => Ok(()),
4379            },
4380            // Impl and Extend blocks: delegate to register_item_functions
4381            // which handles the full registration (desugar methods, trait symbols,
4382            // type inference impls, drop tracking, etc.)
4383            Item::Impl(..) | Item::Extend(..) => {
4384                self.register_item_functions(item)
4385            }
4386            Item::Module(module, _) => {
4387                let module_path = self.current_module_path_for(module.name.as_str());
4388                self.module_scope_stack.push(module_path.clone());
4389                let register_result = (|| -> Result<()> {
4390                    for inner in &module.items {
4391                        let qualified = self.qualify_module_item(inner, &module_path)?;
4392                        self.register_missing_module_items(&qualified)?;
4393                    }
4394                    Ok(())
4395                })();
4396                self.module_scope_stack.pop();
4397                register_result
4398            }
4399            _ => Ok(()),
4400        }
4401    }
4402
4403    fn compile_module_decl(&mut self, module_def: &ModuleDecl, span: Span) -> Result<()> {
4404        for ann in &module_def.annotations {
4405            self.validate_annotation_target_usage(ann, AnnotationTargetKind::Module, span)?;
4406        }
4407
4408        let module_path = self.current_module_path_for(&module_def.name);
4409        if let Some(parent_path) = self.module_scope_stack.last().cloned()
4410            && let Some(parent_source) = self.resolve_canonical_module_path(&parent_path)
4411        {
4412            self.module_scope_sources
4413                .entry(module_path.clone())
4414                .or_insert_with(|| format!("{}::{}", parent_source, module_def.name));
4415        }
4416        self.module_scope_stack.push(module_path.clone());
4417        self.push_module_reference_scope();
4418
4419        let mut module_items = module_def.items.clone();
4420        if self.execute_module_comptime_handlers(module_def, &module_path, &mut module_items)? {
4421            self.pop_module_reference_scope();
4422            self.module_scope_stack.pop();
4423            return Ok(());
4424        }
4425        if self.execute_module_inline_comptime_blocks(&module_path, &mut module_items)? {
4426            self.pop_module_reference_scope();
4427            self.module_scope_stack.pop();
4428            return Ok(());
4429        }
4430
4431        let mut qualified_items = Vec::with_capacity(module_items.len());
4432        for inner in &module_items {
4433            qualified_items.push(self.qualify_module_item(inner, &module_path)?);
4434        }
4435
4436        for qualified in &qualified_items {
4437            self.register_missing_module_items(qualified)?;
4438        }
4439
4440        self.non_function_mir_context_stack
4441            .push(module_path.clone());
4442        let compile_result = (|| -> Result<()> {
4443            for (idx, qualified) in qualified_items.iter().enumerate() {
4444                let future_names = self
4445                    .future_reference_use_names_for_remaining_items(&qualified_items[idx + 1..]);
4446                self.push_future_reference_use_names(future_names);
4447                let compile_result = self.compile_item_with_context(qualified, false);
4448                self.pop_future_reference_use_names();
4449                compile_result?;
4450                self.release_unused_module_reference_borrows_for_remaining_items(
4451                    &qualified_items[idx + 1..],
4452                );
4453            }
4454            Ok(())
4455        })();
4456        self.non_function_mir_context_stack.pop();
4457        compile_result?;
4458
4459        let exports = self.collect_module_runtime_exports(&module_items, &module_path);
4460        let entries: Vec<ObjectEntry> = exports
4461            .into_iter()
4462            .map(|(name, value_ident)| ObjectEntry::Field {
4463                key: name,
4464                value: Expr::Identifier(value_ident, span),
4465                type_annotation: None,
4466            })
4467            .collect();
4468        let module_object = Expr::Object(entries, span);
4469        self.compile_expr(&module_object)?;
4470
4471        let binding_idx = self.get_or_create_module_binding(&module_path);
4472        self.emit(Instruction::new(
4473            OpCode::StoreModuleBinding,
4474            Some(Operand::ModuleBinding(binding_idx)),
4475        ));
4476        self.propagate_initializer_type_to_slot(binding_idx, false, false);
4477
4478        if self.module_scope_stack.len() == 1 {
4479            self.module_namespace_bindings
4480                .insert(module_def.name.clone());
4481        }
4482
4483        self.emit_annotation_lifecycle_calls_for_module(
4484            &module_path,
4485            &module_def.annotations,
4486            Some(binding_idx),
4487        )?;
4488
4489        self.pop_module_reference_scope();
4490        self.module_scope_stack.pop();
4491        Ok(())
4492    }
4493
4494    /// Compile a query (Backtest, Alert, or With/CTE).
4495    ///
4496    /// For CTE (WITH) queries:
4497    /// 1. Compile each CTE subquery and store the result in a named module_binding variable.
4498    /// 2. Compile the main query (which can reference CTEs by name as variables).
4499    ///
4500    /// For Backtest and Alert queries, emit a stub for now.
4501    fn compile_query(&mut self, query: &Query) -> Result<()> {
4502        match query {
4503            Query::With(with_query) => {
4504                // Compile each CTE: evaluate its subquery and store as a named variable
4505                for cte in &with_query.ctes {
4506                    // Recursively compile the CTE's subquery
4507                    self.compile_query(&cte.query)?;
4508
4509                    // Store the result in a module_binding variable with the CTE's name
4510                    let binding_idx = self.get_or_create_module_binding(&cte.name);
4511                    self.emit(Instruction::new(
4512                        OpCode::StoreModuleBinding,
4513                        Some(Operand::ModuleBinding(binding_idx)),
4514                    ));
4515                }
4516
4517                // Compile the main query
4518                self.compile_query(&with_query.query)?;
4519            }
4520            Query::Backtest(_backtest) => {
4521                // Backtest queries require runtime context to evaluate.
4522                // Push null as placeholder — the runtime executor handles backtest
4523                // execution when given a full ExecutionContext.
4524                self.emit(Instruction::simple(OpCode::PushNull));
4525            }
4526            Query::Alert(alert) => {
4527                // Compile alert condition
4528                self.compile_expr(&alert.condition)?;
4529                // Push null as placeholder (alert evaluation requires runtime context)
4530                self.emit(Instruction::simple(OpCode::Pop));
4531                self.emit(Instruction::simple(OpCode::PushNull));
4532            }
4533        }
4534        Ok(())
4535    }
4536
4537    pub(super) fn propagate_initializer_type_to_slot(
4538        &mut self,
4539        slot: u16,
4540        is_local: bool,
4541        _is_mutable: bool,
4542    ) {
4543        self.propagate_assignment_type_to_slot(slot, is_local, true);
4544    }
4545
4546    /// Compile a statement
4547    pub(super) fn compile_statement(&mut self, stmt: &Statement) -> Result<()> {
4548        match stmt {
4549            Statement::Return(expr_opt, _span) => {
4550                if let Some(expr) = expr_opt {
4551                    self.plan_flexible_binding_escape_from_expr(expr);
4552                    // Phase F: when the returned expression is a closure
4553                    // literal, the closure escapes by definition (it is
4554                    // about to cross the return boundary). Flag the next
4555                    // closure emission to use the heap ABI opcode so the
4556                    // JIT and future Phase H cleanup can rely on a stable
4557                    // signal. Matches the escape vector in
4558                    // `docs/v2-closure-specialization.md` §2.1 row 1.
4559                    if matches!(expr, Expr::FunctionExpr { .. }) {
4560                        self.emit_make_closure_heap_next = true;
4561                    }
4562                    if self.current_function_return_reference_summary.is_some() {
4563                        self.compile_expr_preserving_refs(expr)?;
4564                    } else {
4565                        self.compile_expr(expr)?;
4566                    }
4567                } else {
4568                    self.emit(Instruction::simple(OpCode::PushNull));
4569                }
4570                // Emit drops for all active drop scopes before returning
4571                let total_scopes = self.drop_locals.len();
4572                if total_scopes > 0 {
4573                    self.emit_drops_for_early_exit(total_scopes)?;
4574                }
4575                self.emit_return_value_with_ownership();
4576            }
4577
4578            Statement::Break(_) => {
4579                let in_loop = !self.loop_stack.is_empty();
4580                if in_loop {
4581                    // Emit drops for drop scopes inside the loop before breaking
4582                    let scopes_to_exit = self
4583                        .loop_stack
4584                        .last()
4585                        .map(|ctx| self.drop_locals.len().saturating_sub(ctx.drop_scope_depth))
4586                        .unwrap_or(0);
4587                    if scopes_to_exit > 0 {
4588                        self.emit_drops_for_early_exit(scopes_to_exit)?;
4589                    }
4590                    let jump_idx = self.emit_jump(OpCode::Jump, 0);
4591                    if let Some(loop_ctx) = self.loop_stack.last_mut() {
4592                        loop_ctx.break_jumps.push(jump_idx);
4593                    }
4594                } else {
4595                    return Err(ShapeError::RuntimeError {
4596                        message: "break statement outside of loop".to_string(),
4597                        location: None,
4598                    });
4599                }
4600            }
4601
4602            Statement::Continue(_) => {
4603                if let Some(loop_ctx) = self.loop_stack.last() {
4604                    // Copy values we need before mutable borrow
4605                    let scopes_to_exit = self
4606                        .drop_locals
4607                        .len()
4608                        .saturating_sub(loop_ctx.drop_scope_depth);
4609                    let continue_target = loop_ctx.continue_target;
4610                    // Emit drops for drop scopes inside the loop before continuing
4611                    if scopes_to_exit > 0 {
4612                        self.emit_drops_for_early_exit(scopes_to_exit)?;
4613                    }
4614                    if continue_target == usize::MAX {
4615                        // Deferred continue: emit placeholder forward jump
4616                        let jump_idx = self.emit_jump(OpCode::Jump, 0);
4617                        if let Some(loop_ctx) = self.loop_stack.last_mut() {
4618                            loop_ctx.continue_jumps.push(jump_idx);
4619                        }
4620                    } else {
4621                        let offset =
4622                            continue_target as i32 - self.program.current_offset() as i32 - 1;
4623                        self.emit(Instruction::new(
4624                            OpCode::Jump,
4625                            Some(Operand::Offset(offset)),
4626                        ));
4627                    }
4628                } else {
4629                    return Err(ShapeError::RuntimeError {
4630                        message: "continue statement outside of loop".to_string(),
4631                        location: None,
4632                    });
4633                }
4634            }
4635
4636            Statement::VariableDecl(var_decl, _) => {
4637                // Set pending variable name for hoisting integration.
4638                // compile_typed_object_literal uses self to include hoisted fields in the schema.
4639                self.pending_variable_name =
4640                    var_decl.pattern.as_identifier().map(|s| s.to_string());
4641                // v2 Phase 3.1 (Agent 3): when the binding has an explicit
4642                // `Array<T>` annotation whose element type maps to a
4643                // typed-array kind, signal it to `compile_expr_array` so
4644                // the literal lowers to the v2 typed-array path.
4645                //
4646                // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element
4647                // (2026-05-18): route through the compiler-aware
4648                // `resolve_typed_array_kind_from_annotation` so `Array<B>`
4649                // for a registered user struct B also maps to
4650                // `TypedArrayKind::TypedObject` per audit §2.1 + §3.A row 1.
4651                self.pending_variable_typed_array_kind = var_decl
4652                    .type_annotation
4653                    .as_ref()
4654                    .and_then(|ann| self.resolve_typed_array_kind_from_annotation(ann));
4655                // v2 Phase 3.2: when the binding has an explicit
4656                // `HashMap<K, V>` annotation whose key/value pair maps to a
4657                // typed-map kind, signal it to `compile_expr_function_call`
4658                // (HashMap ctor path) so the constructor lowers to the v2
4659                // typed-map opcode.
4660                self.pending_variable_typed_map_kind = var_decl
4661                    .type_annotation
4662                    .as_ref()
4663                    .and_then(|ann| {
4664                        crate::compiler::v2_map_emission::map_key_value_from_annotation(ann)
4665                    })
4666                    .and_then(|(k, v)| {
4667                        crate::compiler::v2_typed_map_emission::should_use_typed_map(&k, &v)
4668                    });
4669
4670                // Compile-time range check: if the type annotation is a width type
4671                // (i8, u8, i16, etc.) and the initializer is a constant expression,
4672                // verify the value fits in the declared width.
4673                if let (Some(type_ann), Some(init_expr)) =
4674                    (&var_decl.type_annotation, &var_decl.value)
4675                {
4676                    if let shape_ast::ast::TypeAnnotation::Basic(type_name) = type_ann {
4677                        if let Some(w) = shape_ast::IntWidth::from_name(type_name) {
4678                            // Const-fold path is dormant per ADR-006 §2.4
4679                            // — `ConstFoldValue` is an uninhabited
4680                            // placeholder until the phase-2c carrier
4681                            // shape lands. `eval_const_expr_to_nanboxed`
4682                            // returns `Option<ConstFoldValue>` whose
4683                            // `Some` arm is statically unreachable;
4684                            // matching on the empty enum here keeps the
4685                            // caller compile-clean while preserving the
4686                            // surface for the phase-2c rebuild.
4687                            let _ = (w, type_name);
4688                            if let Some(const_val) =
4689                                crate::compiler::expressions::function_calls::eval_const_expr_to_nanboxed(init_expr)
4690                            {
4691                                match const_val {}
4692                            }
4693                        }
4694                    }
4695                }
4696
4697                // Compile initializer — register the variable even if the initializer fails,
4698                // to prevent cascading "Undefined variable" errors on later references.
4699                let mut ref_borrow = None;
4700                let init_err = if let Some(init_expr) = &var_decl.value {
4701                    // Special handling: Table row literal syntax
4702                    // `let t: Table<T> = [a, b], [c, d]` → compile as table construction
4703                    if let Expr::TableRows(rows, tr_span) = init_expr {
4704                        match self.compile_table_rows(rows, &var_decl.type_annotation, *tr_span) {
4705                            Ok(()) => None,
4706                            Err(e) => {
4707                                self.emit(Instruction::simple(OpCode::PushNull));
4708                                Some(e)
4709                            }
4710                        }
4711                    } else if let Expr::Array(items, arr_span) = init_expr {
4712                        // Single-row table literal: `let t: Table<T> = [a, b, c]`
4713                        // When the annotation is Table<T>, treat the array as a single row.
4714                        let is_table_annotated = matches!(
4715                            &var_decl.type_annotation,
4716                            Some(shape_ast::ast::TypeAnnotation::Generic { name, args })
4717                                if name == "Table" && args.len() == 1
4718                        );
4719                        if is_table_annotated {
4720                            let single_row = vec![items.clone()];
4721                            match self.compile_table_rows(
4722                                &single_row,
4723                                &var_decl.type_annotation,
4724                                *arr_span,
4725                            ) {
4726                                Ok(()) => None,
4727                                Err(e) => {
4728                                    self.emit(Instruction::simple(OpCode::PushNull));
4729                                    Some(e)
4730                                }
4731                            }
4732                        } else {
4733                            match self.compile_expr_for_reference_binding(init_expr) {
4734                                Ok(tracked_borrow) => {
4735                                    ref_borrow = tracked_borrow;
4736                                    None
4737                                }
4738                                Err(e) => {
4739                                    self.emit(Instruction::simple(OpCode::PushNull));
4740                                    Some(e)
4741                                }
4742                            }
4743                        }
4744                    } else {
4745                        match self.compile_expr_for_reference_binding(init_expr) {
4746                            Ok(tracked_borrow) => {
4747                                ref_borrow = tracked_borrow;
4748                                None
4749                            }
4750                            Err(e) => {
4751                                self.emit(Instruction::simple(OpCode::PushNull));
4752                                Some(e)
4753                            }
4754                        }
4755                    }
4756                } else {
4757                    self.emit(Instruction::simple(OpCode::PushNull));
4758                    None
4759                };
4760
4761                // Capture (then clear) pending variable name and v2 typed
4762                // array kind after the init expression is compiled. The
4763                // captured kind is recorded against the binding slot below
4764                // so subsequent typed Get/Set/Push opcode emission can
4765                // verify the receiver is actually a v2 typed array.
4766                self.pending_variable_name = None;
4767                let captured_typed_array_kind = self.pending_variable_typed_array_kind;
4768                self.pending_variable_typed_array_kind = None;
4769                let captured_typed_map_kind = self.pending_variable_typed_map_kind;
4770                self.pending_variable_typed_map_kind = None;
4771                // Phase 4b Round 6 WS-1b W16.2-C residual: capture the bare
4772                // empty-array-accumulator placeholder index alongside the
4773                // other initializer-derived signals.
4774                let captured_empty_array_alloc_idx =
4775                    self.pending_empty_array_alloc_idx.take();
4776
4777                // ADR-006 §2.7.24 Q25.C: coerce-to-dyn emission. When the
4778                // binding's annotation is `TypeAnnotation::Dyn(traits)`,
4779                // emit `OpCode::BoxTraitObject` after the RHS has been
4780                // pushed onto the stack. The opcode pops the concrete
4781                // value, looks up `(concrete_type, trait_name)` in the
4782                // program's `trait_vtables` registry, allocates a
4783                // `TraitObjectStorage`, and pushes back a kinded
4784                // `Ptr(HeapKind::TraitObject)` slot.
4785                //
4786                // The operand is the trait name as a `Operand::Name(StringId)`
4787                // — the executor resolves the trait name → vtable lookup
4788                // via the receiver's concrete type. Multi-trait dyn
4789                // (`dyn A + B + C`) uses the FIRST trait as the boxing
4790                // discriminator per §Q25.C.5 `trait_names` field;
4791                // wider dispatch through additional traits is a future
4792                // amendment.
4793                let is_dyn_coerce = init_err.is_none()
4794                    && var_decl
4795                        .type_annotation
4796                        .as_ref()
4797                        .map(|ann| {
4798                            crate::compiler::trait_object_emission::trait_name_from_annotation(ann)
4799                                .is_some()
4800                        })
4801                        .unwrap_or(false);
4802                if is_dyn_coerce {
4803                    if let Some(trait_name) = var_decl
4804                        .type_annotation
4805                        .as_ref()
4806                        .and_then(crate::compiler::trait_object_emission::trait_name_from_annotation)
4807                    {
4808                        let sid = self.program.add_string(trait_name.to_string());
4809                        self.emit(Instruction::new(
4810                            OpCode::BoxTraitObject,
4811                            Some(Operand::Name(shape_value::StringId(sid as u32))),
4812                        ));
4813                    }
4814                }
4815
4816                // Emit BindSchema for Table<T> annotations (runtime safety net)
4817                if let Some(ref type_ann) = var_decl.type_annotation {
4818                    if let Some(schema_id) = self.get_table_schema_id(type_ann) {
4819                        self.emit(Instruction::new(
4820                            OpCode::BindSchema,
4821                            Some(Operand::Count(schema_id)),
4822                        ));
4823                    }
4824                }
4825
4826                // At top-level (no current function), create module_bindings; otherwise create locals
4827                if self.current_function.is_none() {
4828                    // Top-level: create module_binding variable
4829                    if let Some(name) = var_decl.pattern.as_identifier() {
4830                        // R8 W9 B9: removed categorical ban on module-scope
4831                        // `ref_borrow`. The MIR borrow solver is the documented
4832                        // sole authority for escape analysis (per audit
4833                        // docs/cluster-audits/v0.3-r8w9-borrow-b0003-audit.md §3).
4834                        // Local-scope `let r = &x` already accepts the same shape;
4835                        // module-scope must match.
4836                        let binding_idx = self.get_or_create_module_binding(name);
4837
4838                        // Emit StoreModuleBindingTyped for width-typed bindings,
4839                        // otherwise emit regular StoreModuleBinding.
4840                        let used_typed_store = if let Some(TypeAnnotation::Basic(type_name)) =
4841                            var_decl.type_annotation.as_ref()
4842                        {
4843                            if let Some(w) = shape_ast::IntWidth::from_name(type_name) {
4844                                self.emit(Instruction::new(
4845                                    OpCode::StoreModuleBindingTyped,
4846                                    Some(Operand::TypedModuleBinding(
4847                                        binding_idx,
4848                                        crate::bytecode::NumericWidth::from_int_width(w),
4849                                    )),
4850                                ));
4851                                true
4852                            } else {
4853                                false
4854                            }
4855                        } else {
4856                            false
4857                        };
4858                        if !used_typed_store {
4859                            self.emit(Instruction::new(
4860                                OpCode::StoreModuleBinding,
4861                                Some(Operand::ModuleBinding(binding_idx)),
4862                            ));
4863                        }
4864
4865                        // v2 Phase 3.1 (Agent 3): record v2 typed array kind for this binding
4866                        if let Some(kind) = captured_typed_array_kind {
4867                            self.v2_typed_array_module_bindings.insert(binding_idx, kind);
4868                        }
4869                        // Phase 4b Round 6 WS-1b W16.2-C residual: re-key a
4870                        // bare empty-array-accumulator placeholder against
4871                        // this module binding (top-level `Statement::VarDecl`).
4872                        if let Some(name) = var_decl.pattern.as_identifier() {
4873                            self.register_empty_array_accumulator(
4874                                crate::compiler::EmptyArrayAccumulatorKey::ModuleBinding(
4875                                    binding_idx,
4876                                ),
4877                                var_decl.value.as_ref(),
4878                                captured_empty_array_alloc_idx,
4879                                name,
4880                                var_decl.value.as_ref().map(|v| v.span()),
4881                            );
4882                        }
4883                        // v2 Phase 3.2: record v2 typed map kind for this binding
4884                        if let Some(kind) = captured_typed_map_kind {
4885                            self.v2_typed_map_module_bindings.insert(binding_idx, kind);
4886                        }
4887                        // ADR-006 §2.7.27 / Item 4 ruling: transfer the
4888                        // pending container-kind signal to the module
4889                        // binding for write-back-aware method dispatch.
4890                        if let Some(ckind) = self.pending_variable_container_kind.take() {
4891                            self.mut_self_container_bindings.insert(binding_idx, ckind);
4892                        }
4893                        // ADR-006 §2.7.24 Q25.C: record dyn-typed module
4894                        // binding so subsequent `a.method()` calls emit
4895                        // `OpCode::DynMethodCall` instead of the standard
4896                        // `OpCode::CallMethod` path.
4897                        if let Some(trait_name) = var_decl
4898                            .type_annotation
4899                            .as_ref()
4900                            .and_then(crate::compiler::trait_object_emission::trait_name_from_annotation)
4901                        {
4902                            self.dyn_module_bindings
4903                                .insert(binding_idx, trait_name.to_string());
4904                        }
4905
4906                        // Track type annotation if present (for type checker)
4907                        if let Some(ref type_ann) = var_decl.type_annotation {
4908                            if let Some(type_name) =
4909                                Self::tracked_type_name_from_annotation(type_ann)
4910                            {
4911                                self.set_module_binding_type_info(binding_idx, &type_name);
4912                            }
4913                            // v0.3 WS-6: record the binding's concrete type
4914                            // from its explicit annotation so a later generic
4915                            // call site `id(n)` can resolve the argument's
4916                            // type. The type-tracker only retains a lossy
4917                            // head-name string (e.g. "option" with no inner
4918                            // type); this table carries the full ConcreteType.
4919                            if let Some(ct) = crate::compiler::monomorphization::type_resolution::declared_annotation_concrete_type(self, type_ann) {
4920                                self.module_binding_concrete_types.insert(binding_idx, ct);
4921                            }
4922                            // Handle Table<T> generic annotation
4923                            self.try_track_datatable_type(type_ann, binding_idx, false)?;
4924                        } else {
4925                            let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
4926                            self.propagate_initializer_type_to_slot(binding_idx, false, is_mutable);
4927                            // v0.3 WS-6b GAP A: an *inferred-type* `let p =
4928                            // <expr>` carries no annotation, so the WS-6
4929                            // annotated-only recording above never runs. Resolve
4930                            // the binding's ConcreteType structurally from the
4931                            // initializer expression (struct literal, enum
4932                            // constructor, `Some`/`Ok`/`Err`, …) so a later
4933                            // generic call site `id(p)` can bind its type
4934                            // argument. `concrete_type_for_expr` returns `None`
4935                            // for a genuinely type-ambiguous initializer (e.g.
4936                            // `let n = None`), in which case nothing is recorded
4937                            // and `id(n)` stays a clean compile error.
4938                            if let Some(init_expr) = var_decl.value.as_ref() {
4939                                if let Some(ct) = crate::compiler::monomorphization::type_resolution::concrete_type_for_expr(self, init_expr) {
4940                                    self.module_binding_concrete_types.insert(binding_idx, ct);
4941                                }
4942                            }
4943                        }
4944
4945                        // cluster-2-cw-IC-class-c (Phase 3 cluster-2 Round 3,
4946                        // 2026-05-16): Class C method-chain intermediate
4947                        // binding coverage at module-binding (top-level)
4948                        // path. The canonical Class C fixture
4949                        // (`/tmp/cw-B-class-c-method-chain.shape`) is
4950                        // top-level (`let doubled = xs.map(...)` at module
4951                        // scope), so the side-table to populate is
4952                        // `module_binding_array_element_types` (consumed by
4953                        // `identifier_concrete_type`'s module-binding
4954                        // fallback arm at
4955                        // `monomorphization/type_resolution.rs:1462`).
4956                        //
4957                        // Must run AFTER `propagate_initializer_type_to_slot`
4958                        // because that path's fallback writes
4959                        // `VariableTypeInfo::unknown()` when no type info is
4960                        // available from the expression's compile-time
4961                        // metadata (`last_expr_type_info` /
4962                        // `last_expr_numeric_type` / `last_expr_schema`),
4963                        // which would wipe a prior type_tracker entry. The
4964                        // method call's `last_expr_type_info` is cleared at
4965                        // `compile_expr_method_call` line 2197/2287/2438 per
4966                        // the method-result clearing pattern, so the
4967                        // initializer-propagate fallback unconditionally
4968                        // hits the unknown-write arm for method-call RHS.
4969                        //
4970                        // Per ADR-006 §2.7.5 stamp-at-compile-time: the
4971                        // specialized callee's substituted return-type
4972                        // annotation IS the proof — no runtime decode, no
4973                        // inference fabrication, no Bool-default. The
4974                        // monomorphization site-table was populated by
4975                        // `try_monomorphize_method_call` /
4976                        // `_with_closures` per the V3-S6b conduit; this
4977                        // walks the same side-tables used by the link-time
4978                        // MIR resolver, but at bytecode-emission time so
4979                        // subsequent statements see the type before the
4980                        // resolver runs. Per §2.7.7 #9: when any chain link
4981                        // is absent the helper returns None and the slot
4982                        // stays unstamped (surface-and-stop preserved).
4983                        //
4984                        // The mirror set_module_binding_type_info call is
4985                        // required because `resolve_receiver_extend_type`
4986                        // at `helpers.rs:3826` consults the type_tracker
4987                        // (not the side-table) to gate the UFCS extend-
4988                        // function lookup for `Vec.map` at
4989                        // `compile_expr_method_call:2116`. Without the
4990                        // type_tracker mirror, the second `.map(...)` falls
4991                        // through to the generic `CallMethod` path without
4992                        // specializing, and the bytecode runtime hits the
4993                        // ckpt-2 surface in `array_transform.rs::map`.
4994                        if let Some(init_expr) = var_decl.value.as_ref() {
4995                            if let Some(shape_value::v2::ConcreteType::Array(elem)) =
4996                                crate::compiler::monomorphization::type_resolution::specialized_call_return_concrete_type(
4997                                    self, init_expr,
4998                                )
4999                            {
5000                                self.module_binding_array_element_types
5001                                    .insert(binding_idx, (*elem).clone());
5002                                if let Some(elem_ann) = crate::compiler::expressions::closures::concrete_type_to_type_annotation(&elem) {
5003                                    let vec_ann = shape_ast::ast::TypeAnnotation::Generic {
5004                                        name: shape_ast::ast::TypePath::simple("Vec"),
5005                                        args: vec![elem_ann],
5006                                    };
5007                                    if let Some(type_name) = Self::tracked_type_name_from_annotation(&vec_ann) {
5008                                        self.set_module_binding_type_info(binding_idx, &type_name);
5009                                    }
5010                                }
5011                            }
5012                        }
5013
5014                        // Track for auto-drop at program exit
5015                        let binding_type_name = self
5016                            .type_tracker
5017                            .get_binding_type(binding_idx)
5018                            .and_then(|info| info.type_name.clone());
5019                        let drop_kind = binding_type_name
5020                            .as_ref()
5021                            .and_then(|tn| self.drop_type_info.get(tn).copied())
5022                            .or_else(|| {
5023                                var_decl
5024                                    .type_annotation
5025                                    .as_ref()
5026                                    .and_then(|ann| self.annotation_drop_kind(ann))
5027                            });
5028                        if drop_kind.is_some() {
5029                            let is_async = match drop_kind {
5030                                Some(DropKind::AsyncOnly) => true,
5031                                Some(DropKind::Both) => false,
5032                                Some(DropKind::SyncOnly) | None => false,
5033                            };
5034                            self.track_drop_module_binding(binding_idx, is_async);
5035                        }
5036                        if let Some(value) = &var_decl.value {
5037                            self.finish_reference_binding_from_expr(
5038                                binding_idx,
5039                                false,
5040                                name,
5041                                value,
5042                                ref_borrow,
5043                            );
5044                            self.update_callable_binding_from_expr(binding_idx, false, value);
5045                        } else {
5046                            self.clear_reference_binding(binding_idx, false);
5047                            self.clear_callable_binding(binding_idx, false);
5048                        }
5049                    } else {
5050                        self.compile_destructure_pattern_global(&var_decl.pattern)?;
5051                    }
5052
5053                    for (binding_name, _) in var_decl.pattern.get_bindings() {
5054                        let scoped_name = self
5055                            .resolve_scoped_module_binding_name(&binding_name)
5056                            .unwrap_or(binding_name);
5057                        if let Some(&binding_idx) = self.module_bindings.get(&scoped_name) {
5058                            if var_decl.kind == VarKind::Const {
5059                                self.const_module_bindings.insert(binding_idx);
5060                            }
5061                            if var_decl.kind == VarKind::Let && !var_decl.is_mut {
5062                                self.immutable_module_bindings.insert(binding_idx);
5063                            }
5064                        }
5065                    }
5066                    self.apply_binding_semantics_to_pattern_bindings(
5067                        &var_decl.pattern,
5068                        false,
5069                        Self::binding_semantics_for_var_decl(var_decl),
5070                    );
5071                    self.plan_flexible_binding_storage_for_pattern_initializer(
5072                        &var_decl.pattern,
5073                        false,
5074                        var_decl.value.as_ref(),
5075                    );
5076                } else {
5077                    // Inside function: create local variable
5078                    self.compile_destructure_pattern(&var_decl.pattern)?;
5079
5080                    // Patch StoreLocal → StoreLocalTyped for width-typed simple bindings.
5081                    // compile_destructure_pattern emits StoreLocal(idx) for Identifier patterns;
5082                    // we upgrade it here when the type annotation is a width type.
5083                    if let (Some(name), Some(TypeAnnotation::Basic(type_name))) = (
5084                        var_decl.pattern.as_identifier(),
5085                        var_decl.type_annotation.as_ref(),
5086                    ) {
5087                        if let Some(w) = shape_ast::IntWidth::from_name(type_name) {
5088                            if let Some(local_idx) = self.resolve_local(name) {
5089                                if let Some(last) = self.program.instructions.last_mut() {
5090                                    if last.opcode == OpCode::StoreLocal {
5091                                        last.opcode = OpCode::StoreLocalTyped;
5092                                        last.operand = Some(Operand::TypedLocal(
5093                                            local_idx,
5094                                            crate::bytecode::NumericWidth::from_int_width(w),
5095                                        ));
5096                                    }
5097                                }
5098                            }
5099                        }
5100                    }
5101
5102                    // Phase 3/4: Emit PromoteToOwned before StoreLocal for uniquely-owned
5103                    // bindings. This converts freshly-allocated Arc<HeapValue> (refcount 1)
5104                    // to Box<HeapValue>, eliminating atomic refcount overhead for the lifetime
5105                    // of the binding. Applies to:
5106                    //   - `let` (immutable) with Direct storage
5107                    //   - `let mut` (owned mutable) with Direct storage
5108                    //   - `const` with Direct storage
5109                    // Does NOT apply to `var` bindings, which stay Arc for shared mutability.
5110                    //
5111                    // Phase V1.3 (flag `SHAPE_V2_BOX_BY_DEFAULT`, default on): the
5112                    // predicate is extended to also cover `UniqueHeap` storage — the
5113                    // class `storage_planning.rs` rule 2 assigns when a `let`/`const`
5114                    // is mutably captured by a closure. Pre-V1.3 those bindings
5115                    // allocated as `Arc<HeapValue>` despite being uniquely owned by
5116                    // construction; the V1.2D `PromoteToShared` emission covers the
5117                    // escape vectors (Site A = escape into closure, Site B = SharedCow
5118                    // var write), so the non-escape case can safely switch to Box.
5119                    // When the flag is off the predicate reverts to Direct-only and
5120                    // emission is byte-identical to pre-V1.3.
5121                    //
5122                    // Phase 5.C: When the initializer was a call to a function whose
5123                    // return-ownership mode is `NewlyOwned`, the callee already emitted
5124                    // `ReturnOwned` and handed us a Box-backed value on the stack. In
5125                    // that case we skip the caller-side `PromoteToOwned` — inserting it
5126                    // would be a harmless no-op (the owned bit is already set), but
5127                    // emitting one extra opcode per binding across an entire pipeline
5128                    // is measurable, and the hint is free to consult.
5129                    if let Some(name) = var_decl.pattern.as_identifier() {
5130                        let is_owned_binding = var_decl.kind == VarKind::Let
5131                            || var_decl.kind == VarKind::Const;
5132                        if is_owned_binding {
5133                            if let Some(local_idx) = self.resolve_local(name) {
5134                                let box_by_default =
5135                                    super::helpers::box_by_default_enabled();
5136                                let should_promote = self
5137                                    .mir_storage_class_for_slot(local_idx)
5138                                    .map_or(false, |sc| {
5139                                        matches!(
5140                                            sc,
5141                                            crate::type_tracking::BindingStorageClass::Direct
5142                                        ) || (box_by_default
5143                                            && matches!(
5144                                                sc,
5145                                                crate::type_tracking::BindingStorageClass::UniqueHeap
5146                                            ))
5147                                    });
5148                                // Compute the Phase 5.B hint directly from the
5149                                // initializer AST — the binding semantics aren't
5150                                // populated yet at this point in the let-statement
5151                                // pipeline (that happens a few lines below via
5152                                // `apply_binding_semantics_to_pattern_bindings`),
5153                                // so we can't read `return_ownership_hint` off the
5154                                // slot's semantics here.
5155                                let callee_already_owned = var_decl
5156                                    .value
5157                                    .as_ref()
5158                                    .and_then(|init| {
5159                                        self.return_ownership_hint_for_initializer(init)
5160                                    })
5161                                    .map_or(false, |hint| {
5162                                        hint == crate::mir::ReturnOwnershipMode::NewlyOwned
5163                                    });
5164                                if should_promote && !callee_already_owned {
5165                                    // The last instruction should be StoreLocal(local_idx).
5166                                    // Insert PromoteToOwned just before it.
5167                                    let instr_count = self.program.instructions.len();
5168                                    if instr_count > 0 {
5169                                        let last = self.program.instructions[instr_count - 1];
5170                                        if last.opcode == OpCode::StoreLocal {
5171                                            // Remove the StoreLocal, emit PromoteToOwned, re-emit StoreLocal.
5172                                            self.program.instructions.pop();
5173                                            self.emit(Instruction::simple(OpCode::PromoteToOwned));
5174                                            self.emit(last);
5175                                        }
5176                                    }
5177                                }
5178                            }
5179                        }
5180                    }
5181
5182                    for (binding_name, _) in var_decl.pattern.get_bindings() {
5183                        if let Some(local_idx) = self.resolve_local(&binding_name) {
5184                            if var_decl.kind == VarKind::Const {
5185                                self.const_locals.insert(local_idx);
5186                            }
5187                            if var_decl.kind == VarKind::Let && !var_decl.is_mut {
5188                                self.immutable_locals.insert(local_idx);
5189                            }
5190                            // Track A.1C.3: record `let mut` locals so
5191                            // later closure capture classification has
5192                            // a persistent witness when the type-
5193                            // tracker local semantics get wiped by a
5194                            // sibling closure's `compile_function`.
5195                            if var_decl.kind == VarKind::Let && var_decl.is_mut {
5196                                self.owned_mutable_locals.insert(binding_name.clone());
5197                            }
5198                        }
5199                    }
5200                    self.apply_binding_semantics_to_pattern_bindings(
5201                        &var_decl.pattern,
5202                        true,
5203                        Self::binding_semantics_for_var_decl(var_decl),
5204                    );
5205                    // Phase 5.B: If the initializer is a call to a function whose
5206                    // return-ownership mode is known, record the hint on each
5207                    // pattern binding so Phase 5.C codegen can skip the Arc→Box
5208                    // PromoteToOwned round-trip.
5209                    if let Some(init) = var_decl.value.as_ref() {
5210                        if let Some(hint) = self.return_ownership_hint_for_initializer(init) {
5211                            self.apply_return_ownership_hint_to_pattern_bindings(
5212                                &var_decl.pattern,
5213                                true,
5214                                hint,
5215                            );
5216                        }
5217                    }
5218                    self.plan_flexible_binding_storage_for_pattern_initializer(
5219                        &var_decl.pattern,
5220                        true,
5221                        var_decl.value.as_ref(),
5222                    );
5223
5224                    // Track type annotation first (so drop tracking can resolve the type)
5225                    if let Some(name) = var_decl.pattern.as_identifier() {
5226                        if let Some(ref type_ann) = var_decl.type_annotation {
5227                            if let Some(type_name) =
5228                                Self::tracked_type_name_from_annotation(type_ann)
5229                            {
5230                                // Get the local index for self variable
5231                                if let Some(local_idx) = self.resolve_local(name) {
5232                                    self.set_local_type_info(local_idx, &type_name);
5233                                }
5234                            }
5235                            // v0.3 WS-6: record the local's concrete type from
5236                            // its explicit annotation so a later generic call
5237                            // site `id(n)` can resolve the argument's type.
5238                            // See the mirror module-binding path above.
5239                            if let Some(local_idx) = self.resolve_local(name) {
5240                                if let Some(ct) = crate::compiler::monomorphization::type_resolution::declared_annotation_concrete_type(self, type_ann) {
5241                                    self.current_function_local_concrete_types.insert(local_idx, ct);
5242                                }
5243                            }
5244                            // Handle Table<T> generic annotation
5245                            if let Some(local_idx) = self.resolve_local(name) {
5246                                self.try_track_datatable_type(type_ann, local_idx, true)?;
5247                            }
5248                            // ADR-006 §2.7.24 Q25.C: record dyn-typed
5249                            // local so subsequent `a.method()` calls
5250                            // route through `OpCode::DynMethodCall`.
5251                            if let Some(trait_name) =
5252                                crate::compiler::trait_object_emission::trait_name_from_annotation(type_ann)
5253                            {
5254                                if let Some(local_idx) = self.resolve_local(name) {
5255                                    self.dyn_locals
5256                                        .insert(local_idx, trait_name.to_string());
5257                                }
5258                            }
5259                        } else if let Some(local_idx) = self.resolve_local(name) {
5260                            let is_mutable = var_decl.kind == shape_ast::ast::VarKind::Var;
5261                            self.propagate_initializer_type_to_slot(local_idx, true, is_mutable);
5262                            // v0.3 WS-6b GAP A: mirror of the inferred-type
5263                            // module-binding path above. An inferred `let p =
5264                            // <expr>` local carries no annotation, so the WS-6
5265                            // annotated-only `current_function_local_concrete_types`
5266                            // recording never fires. Resolve the local's
5267                            // ConcreteType structurally from the initializer so a
5268                            // later generic call site `id(p)` (inside the same
5269                            // function) can bind its type argument. `None` for a
5270                            // type-ambiguous initializer records nothing — the
5271                            // clean-compile-error contract is preserved.
5272                            if let Some(init_expr) = var_decl.value.as_ref() {
5273                                if let Some(ct) = crate::compiler::monomorphization::type_resolution::concrete_type_for_expr(self, init_expr) {
5274                                    self.current_function_local_concrete_types.insert(local_idx, ct);
5275                                }
5276                            }
5277                        }
5278                    }
5279
5280                    // v2 Phase 3.1 (Agent 3): record v2 typed array kind for the local
5281                    if let Some(kind) = captured_typed_array_kind {
5282                        if let Some(name) = var_decl.pattern.as_identifier() {
5283                            if let Some(local_idx) = self.resolve_local(name) {
5284                                self.v2_typed_array_locals.insert(local_idx, kind);
5285                            }
5286                        }
5287                    }
5288                    // Phase 4b Round 6 WS-1b W16.2-C residual: re-key a bare
5289                    // empty-array-accumulator placeholder against this local
5290                    // slot so the first downstream `.push()` resolves its
5291                    // element kind and patches the allocator.
5292                    if let Some(name) = var_decl.pattern.as_identifier() {
5293                        if let Some(local_idx) = self.resolve_local(name) {
5294                            self.register_empty_array_accumulator(
5295                                crate::compiler::EmptyArrayAccumulatorKey::Local(local_idx),
5296                                var_decl.value.as_ref(),
5297                                captured_empty_array_alloc_idx,
5298                                name,
5299                                var_decl.value.as_ref().map(|v| v.span()),
5300                            );
5301                        }
5302                    }
5303                    // v0.3 WS-6b GAP B: record v2 typed-map kind for the
5304                    // local. The module-binding path above stamps
5305                    // `v2_typed_map_module_bindings`, but the function-local
5306                    // path had no mirror — so a function-scoped
5307                    // `let m: HashMap<K,V> = HashMap()` produced a
5308                    // `NewTypedMap*` carrier whose slot was never registered
5309                    // as a typed map. `is_typed_map_receiver` /
5310                    // `try_compile_typed_slot_method` then both missed it,
5311                    // method dispatch fell through to the generic
5312                    // `CallMethod` path, and the runtime surfaced
5313                    // `no method 'set'/'get' on receiver kind UInt64`.
5314                    // Mirrors the `v2_typed_array_locals` arm directly above.
5315                    if let Some(kind) = captured_typed_map_kind {
5316                        if let Some(name) = var_decl.pattern.as_identifier() {
5317                            if let Some(local_idx) = self.resolve_local(name) {
5318                                self.v2_typed_map_locals.insert(local_idx, kind);
5319                            }
5320                        }
5321                    }
5322
5323                    // cluster-2-cw-IC-class-c (Phase 3 cluster-2 Round 3,
5324                    // 2026-05-16): Class C method-chain intermediate slot
5325                    // coverage. When the RHS is a method call that just
5326                    // monomorphized to a specialization whose return type
5327                    // is `Array<C>`, populate `local_array_element_types`
5328                    // so the next statement's
5329                    // `concrete_type_for_expr(receiver_identifier)` chain
5330                    // can reach the `Array<C>` annotation through
5331                    // `identifier_concrete_type`'s side-table arm
5332                    // (`monomorphization/type_resolution.rs:1443`). This
5333                    // closes the canonical chain
5334                    // `let doubled = xs.map(|x|x*2); let trebled =
5335                    // doubled.map(|y|y+1); print(trebled.sum())` whose
5336                    // pre-fix failure was that the SECOND `.map(...)`
5337                    // could not specialize because `doubled`'s slot had
5338                    // no entry in any concrete-type side-table.
5339                    //
5340                    // Per ADR-006 §2.7.5 stamp-at-compile-time: the
5341                    // specialized callee's substituted return-type
5342                    // annotation IS the proof — no runtime decode, no
5343                    // inference fabrication, no Bool-default. The
5344                    // monomorphization site-table was populated by
5345                    // `try_monomorphize_method_call` /
5346                    // `_with_closures` per the V3-S6b conduit; this
5347                    // walks the same side-tables used by the link-time
5348                    // MIR resolver, but at bytecode-emission time so
5349                    // subsequent statements see the type before the
5350                    // resolver runs. Per §2.7.7 #9: when any chain link
5351                    // is absent the helper returns None and the slot
5352                    // stays unstamped (surface-and-stop preserved).
5353                    if let Some(init_expr) = var_decl.value.as_ref() {
5354                        if let Some(shape_value::v2::ConcreteType::Array(elem)) =
5355                            crate::compiler::monomorphization::type_resolution::specialized_call_return_concrete_type(
5356                                self, init_expr,
5357                            )
5358                        {
5359                            if let Some(name) = var_decl.pattern.as_identifier() {
5360                                if let Some(local_idx) = self.resolve_local(name) {
5361                                    self.local_array_element_types
5362                                        .insert(local_idx, (*elem).clone());
5363                                    // Mirror `set_local_type_info` for the
5364                                    // corresponding `Vec<elem>` type name
5365                                    // — see the module-binding site above
5366                                    // for the full rationale on why both
5367                                    // the side-table and the type_tracker
5368                                    // entry are required to close the
5369                                    // `let intermediate = recv.map(...)`
5370                                    // → `intermediate.map(...)` chain.
5371                                    if let Some(elem_ann) = crate::compiler::expressions::closures::concrete_type_to_type_annotation(&elem) {
5372                                        let vec_ann = shape_ast::ast::TypeAnnotation::Generic {
5373                                            name: shape_ast::ast::TypePath::simple("Vec"),
5374                                            args: vec![elem_ann],
5375                                        };
5376                                        if let Some(type_name) = Self::tracked_type_name_from_annotation(&vec_ann) {
5377                                            self.set_local_type_info(local_idx, &type_name);
5378                                        }
5379                                    }
5380                                }
5381                            }
5382                        }
5383                    }
5384
5385                    // ADR-006 §2.7.27 / Item 4 ruling: transfer the
5386                    // pending container-kind signal from the
5387                    // initializer ctor (`Set()` / `HashMap()` /
5388                    // `Deque()` / `PriorityQueue()`) onto the target
5389                    // local-slot so method-call dispatch can decide
5390                    // whether to emit `Dup; StoreLocal` write-back. The
5391                    // signal is consumed (taken) here so a later
5392                    // statement doesn't accidentally inherit it.
5393                    let captured_container_kind = self.pending_variable_container_kind.take();
5394                    if let Some(kind) = captured_container_kind {
5395                        if let Some(name) = var_decl.pattern.as_identifier() {
5396                            if let Some(local_idx) = self.resolve_local(name) {
5397                                self.mut_self_container_locals.insert(local_idx, kind);
5398                            }
5399                        }
5400                    }
5401
5402                    // Track for auto-drop at scope exit (DropCall silently skips non-Drop types).
5403                    // Select sync vs async opcode based on the type's DropKind.
5404                    if let Some(name) = var_decl.pattern.as_identifier() {
5405                        if let Some(local_idx) = self.resolve_local(name) {
5406                            let drop_kind = self.local_drop_kind(local_idx).or_else(|| {
5407                                var_decl
5408                                    .type_annotation
5409                                    .as_ref()
5410                                    .and_then(|ann| self.annotation_drop_kind(ann))
5411                            });
5412
5413                            let is_async = match drop_kind {
5414                                Some(DropKind::AsyncOnly) => {
5415                                    if !self.current_function_is_async {
5416                                        let tn = self
5417                                            .type_tracker
5418                                            .get_local_type(local_idx)
5419                                            .and_then(|info| info.type_name.clone())
5420                                            .unwrap_or_else(|| name.to_string());
5421                                        return Err(ShapeError::SemanticError {
5422                                            message: format!(
5423                                                "type '{}' has only an async drop() and cannot be used in a sync context; \
5424                                                 add a sync method drop(self) or use it inside an async function",
5425                                                tn
5426                                            ),
5427                                            location: None,
5428                                        });
5429                                    }
5430                                    true
5431                                }
5432                                Some(DropKind::Both) => self.current_function_is_async,
5433                                Some(DropKind::SyncOnly) | None => false,
5434                            };
5435                            self.track_drop_local(local_idx, is_async);
5436                            // Phase V1.1C: also track the slot for an
5437                            // ownership-aware `DropLocal` at scope exit
5438                            // when the binding is heap-backed (so releasing
5439                            // the Arc/Box actually does work). The
5440                            // `pop_drop_scope` emission is gated on
5441                            // `SHAPE_V2_OWNERSHIP_MOVES`, so this tracking
5442                            // is a no-op at the bytecode level when the
5443                            // flag is off.
5444                            //
5445                            // Heap-backed means one of:
5446                            //   * `UniqueHeap` storage class — owned Box
5447                            //     allocation per the Phase 4 spec;
5448                            //   * `Direct` storage class *for a let/const
5449                            //     of a heap type* — the `PromoteToOwned`
5450                            //     emission a few lines above boxed a heap
5451                            //     value into the slot (inline scalars
5452                            //     round-trip as a no-op and don't need
5453                            //     drops).
5454                            //
5455                            // We skip `SharedCow` (its own refcount path
5456                            // handles release), `Reference` (borrow, no
5457                            // ownership), and `LocalMutablePtr`
5458                            // (stack-resident).
5459                            if self.binding_slot_needs_ownership_drop(local_idx, var_decl.kind) {
5460                                self.track_ownership_drop_local(local_idx);
5461                            }
5462                            if let Some(value) = &var_decl.value {
5463                                self.finish_reference_binding_from_expr(
5464                                    local_idx, true, name, value, ref_borrow,
5465                                );
5466                                self.update_callable_binding_from_expr(local_idx, true, value);
5467                            } else {
5468                                self.clear_reference_binding(local_idx, true);
5469                                self.clear_callable_binding(local_idx, true);
5470                            }
5471                        }
5472                    }
5473                }
5474
5475                if let Some(e) = init_err {
5476                    return Err(e);
5477                }
5478            }
5479
5480            Statement::Assignment(assign, _) => 'assign: {
5481                // Check for const reassignment
5482                if let Some(name) = assign.pattern.as_identifier() {
5483                    if let Some(local_idx) = self.resolve_local(name) {
5484                        if !self.current_binding_uses_mir_write_authority(true)
5485                            && self.const_locals.contains(&local_idx)
5486                        {
5487                            return Err(ShapeError::SemanticError {
5488                                message: format!("Cannot reassign const variable '{}'", name),
5489                                location: None,
5490                            });
5491                        }
5492                        // Check for immutable `let` reassignment
5493                        if !self.current_binding_uses_mir_write_authority(true)
5494                            && self.immutable_locals.contains(&local_idx)
5495                        {
5496                            return Err(ShapeError::SemanticError {
5497                                message: format!(
5498                                    "Cannot reassign immutable variable '{}'. Use `let mut` or `var` for mutable bindings",
5499                                    name
5500                                ),
5501                                location: None,
5502                            });
5503                        }
5504                        self.check_write_allowed_in_current_context(
5505                            Self::borrow_key_for_local(local_idx),
5506                            None,
5507                        )
5508                        .map_err(|e| match e {
5509                            ShapeError::SemanticError { message, location } => {
5510                                let user_msg = message.replace(
5511                                    &format!("(slot {})", local_idx),
5512                                    &format!("'{}'", name),
5513                                );
5514                                ShapeError::SemanticError {
5515                                    message: user_msg,
5516                                    location,
5517                                }
5518                            }
5519                            other => other,
5520                        })?;
5521                    } else {
5522                        let scoped_name = self
5523                            .resolve_scoped_module_binding_name(name)
5524                            .unwrap_or_else(|| name.to_string());
5525                        if let Some(&binding_idx) = self.module_bindings.get(&scoped_name) {
5526                            if !self.current_binding_uses_mir_write_authority(false)
5527                                && self.const_module_bindings.contains(&binding_idx)
5528                            {
5529                                return Err(ShapeError::SemanticError {
5530                                    message: format!("Cannot reassign const variable '{}'", name),
5531                                    location: None,
5532                                });
5533                            }
5534                            // Check for immutable `let` reassignment at module level
5535                            if !self.current_binding_uses_mir_write_authority(false)
5536                                && self.immutable_module_bindings.contains(&binding_idx)
5537                            {
5538                                return Err(ShapeError::SemanticError {
5539                                    message: format!(
5540                                        "Cannot reassign immutable variable '{}'. Use `let mut` or `var` for mutable bindings",
5541                                        name
5542                                    ),
5543                                    location: None,
5544                                });
5545                            }
5546                            self.check_write_allowed_in_current_context(
5547                                Self::borrow_key_for_module_binding(binding_idx),
5548                                None,
5549                            )
5550                            .map_err(|e| match e {
5551                                ShapeError::SemanticError { message, location } => {
5552                                    let user_msg = message.replace(
5553                                        &format!(
5554                                            "(slot {})",
5555                                            Self::borrow_key_for_module_binding(binding_idx)
5556                                        ),
5557                                        &format!("'{}'", name),
5558                                    );
5559                                    ShapeError::SemanticError {
5560                                        message: user_msg,
5561                                        location,
5562                                    }
5563                                }
5564                                other => other,
5565                            })?;
5566                        }
5567                    }
5568                }
5569
5570                // Optimization: x = x.push(val) → ArrayPushLocal (O(1) in-place mutation)
5571                if let Some(name) = assign.pattern.as_identifier() {
5572                    if let Expr::MethodCall {
5573                        receiver,
5574                        method,
5575                        args,
5576                        ..
5577                    } = &assign.value
5578                    {
5579                        if method == "push" && args.len() == 1 {
5580                            if let Expr::Identifier(recv_name, _) = receiver.as_ref() {
5581                                if recv_name == name {
5582                                    if let Some(local_idx) = self.resolve_local(name) {
5583                                        self.compile_expr(&args[0])?;
5584                                        let pushed_numeric = self.last_expr_numeric_type;
5585                                        self.emit(Instruction::new(
5586                                            OpCode::ArrayPushLocal,
5587                                            Some(Operand::Local(local_idx)),
5588                                        ));
5589                                        if let Some(numeric_type) = pushed_numeric {
5590                                            self.mark_slot_as_numeric_array(
5591                                                local_idx,
5592                                                true,
5593                                                numeric_type,
5594                                            );
5595                                        }
5596                                        self.plan_flexible_binding_storage_from_expr(
5597                                            local_idx,
5598                                            true,
5599                                            &assign.value,
5600                                        );
5601                                        break 'assign;
5602                                    } else {
5603                                        let binding_idx = self.get_or_create_module_binding(name);
5604                                        self.compile_expr(&args[0])?;
5605                                        let pushed_numeric = self.last_expr_numeric_type;
5606                                        self.emit(Instruction::new(
5607                                            OpCode::ArrayPushLocal,
5608                                            Some(Operand::ModuleBinding(binding_idx)),
5609                                        ));
5610                                        if let Some(numeric_type) = pushed_numeric {
5611                                            self.mark_slot_as_numeric_array(
5612                                                binding_idx,
5613                                                false,
5614                                                numeric_type,
5615                                            );
5616                                        }
5617                                        self.plan_flexible_binding_storage_from_expr(
5618                                            binding_idx,
5619                                            false,
5620                                            &assign.value,
5621                                        );
5622                                        break 'assign;
5623                                    }
5624                                }
5625                            }
5626                        }
5627                    }
5628                }
5629
5630                // Compile value
5631                let saved_pending_variable_name = self.pending_variable_name.clone();
5632                self.pending_variable_name =
5633                    assign.pattern.as_identifier().map(|name| name.to_string());
5634                let compile_result = self.compile_expr_for_reference_binding(&assign.value);
5635                self.pending_variable_name = saved_pending_variable_name;
5636                let ref_borrow = compile_result?;
5637                let assigned_ident = assign.pattern.as_identifier().map(str::to_string);
5638
5639                // Store in variable
5640                self.compile_destructure_assignment(&assign.pattern)?;
5641                if let Some(name) = assigned_ident.as_deref() {
5642                    if let Some(local_idx) = self.resolve_local(name) {
5643                        if !self.local_binding_is_reference_value(local_idx) {
5644                            self.finish_reference_binding_from_expr(
5645                                local_idx,
5646                                true,
5647                                name,
5648                                &assign.value,
5649                                ref_borrow,
5650                            );
5651                            self.update_callable_binding_from_expr(local_idx, true, &assign.value);
5652                        }
5653                        self.plan_flexible_binding_storage_from_expr(
5654                            local_idx,
5655                            true,
5656                            &assign.value,
5657                        );
5658                    } else if let Some(scoped_name) = self.resolve_scoped_module_binding_name(name)
5659                    {
5660                        if let Some(&binding_idx) = self.module_bindings.get(&scoped_name) {
5661                            self.finish_reference_binding_from_expr(
5662                                binding_idx,
5663                                false,
5664                                name,
5665                                &assign.value,
5666                                ref_borrow,
5667                            );
5668                            self.update_callable_binding_from_expr(
5669                                binding_idx,
5670                                false,
5671                                &assign.value,
5672                            );
5673                            self.plan_flexible_binding_storage_from_expr(
5674                                binding_idx,
5675                                false,
5676                                &assign.value,
5677                            );
5678                        }
5679                    }
5680                    self.propagate_assignment_type_to_identifier(name);
5681                }
5682            }
5683
5684            Statement::Expression(expr, _) => {
5685                // Fast path: arr.push(val) as standalone statement → in-place mutation
5686                // (avoids the LoadLocal+Pop overhead from the expression-level optimization)
5687                //
5688                // ADR-006 §2.7.27 / Item 4 ruling (W17-mutation-writeback):
5689                // gate the fast path so it does NOT fire when the receiver
5690                // is a non-Array container (Deque / PriorityQueue / HashMap
5691                // / HashSet). Those containers have their own `push`
5692                // handlers in `method_registry`; `ArrayPushLocal` would
5693                // error on a non-Array slot kind. Falls through to the
5694                // generic compile_expr path, which dispatches via
5695                // `CallMethod` and emits the writeback per
5696                // `resolve_mut_self_writeback_target`.
5697                if let Expr::MethodCall {
5698                    receiver,
5699                    method,
5700                    args,
5701                    ..
5702                } = expr
5703                {
5704                    let bespoke_push_blocked = if method == "push"
5705                        && args.len() == 1
5706                        && let Expr::Identifier(recv_name, _) = receiver.as_ref()
5707                    {
5708                        let local_kind = self
5709                            .resolve_local(recv_name)
5710                            .and_then(|idx| self.mut_self_container_locals.get(&idx).copied());
5711                        let module_kind = if local_kind.is_none() {
5712                            let scoped = self
5713                                .resolve_scoped_module_binding_name(recv_name)
5714                                .unwrap_or_else(|| recv_name.to_string());
5715                            self.module_bindings
5716                                .get(&scoped)
5717                                .copied()
5718                                .and_then(|idx| {
5719                                    self.mut_self_container_bindings.get(&idx).copied()
5720                                })
5721                        } else {
5722                            None
5723                        };
5724                        local_kind
5725                            .or(module_kind)
5726                            .map(|kind| {
5727                                !matches!(
5728                                    kind,
5729                                    crate::compiler::mutation_writeback::ContainerKind::Array
5730                                )
5731                            })
5732                            .unwrap_or(false)
5733                    } else {
5734                        false
5735                    };
5736                    if method == "push" && args.len() == 1 && !bespoke_push_blocked {
5737                        if let Expr::Identifier(recv_name, _) = receiver.as_ref() {
5738                            let source_loc = self.span_to_source_location(receiver.as_ref().span());
5739                            // Phase 4b Round 6 WS-1b W16.2-C residual
5740                            // (2026-05-21): a bare empty-array accumulator's
5741                            // FIRST `.push()` resolves its element kind,
5742                            // patches the placeholder allocator, and promotes
5743                            // the binding. The method leaves the array on the
5744                            // stack — pop it (statement context discards the
5745                            // result).
5746                            if self.compile_first_push_to_empty_accumulator(
5747                                recv_name,
5748                                &args[0],
5749                                Some(source_loc.clone()),
5750                            )? {
5751                                self.emit(Instruction::simple(OpCode::Pop));
5752                                return Ok(());
5753                            }
5754                            // Resolve the receiver's `TypedArrayKind`. A
5755                            // receiver that is ALREADY a v2 typed array
5756                            // (annotated `Array<T>`, inferred typed literal,
5757                            // a promoted accumulator from an earlier push)
5758                            // must emit the typed `TypedArrayPush*` opcode.
5759                            // The legacy `ArrayPushLocal` below is the v1
5760                            // NaN-boxed carrier path: emitting it against a
5761                            // v2-raw `TypedArray<T>` receiver is a
5762                            // kind-mismatch (the V3-S5 `op_array_push`
5763                            // strict-kind check).
5764                            let typed_kind = self
5765                                .resolve_receiver_typed_array_kind(receiver.as_ref());
5766                            if let Some(local_idx) = self.resolve_local(recv_name) {
5767                                if !self.ref_locals.contains(&local_idx) {
5768                                    self.check_named_binding_write_allowed(
5769                                        recv_name,
5770                                        Some(source_loc.clone()),
5771                                    )?;
5772                                }
5773                                if let Some(kind) = typed_kind {
5774                                    // v2 typed array push: `TypedArrayPush*`
5775                                    // pops (arr_ptr, value).
5776                                    self.emit(Instruction::new(
5777                                        OpCode::LoadLocal,
5778                                        Some(Operand::Local(local_idx)),
5779                                    ));
5780                                    self.compile_typed_array_element_value(kind, &args[0])?;
5781                                    self.emit(Instruction::simple(kind.push_opcode()));
5782                                    return Ok(());
5783                                }
5784                                self.compile_expr(&args[0])?;
5785                                let pushed_numeric = self.last_expr_numeric_type;
5786                                self.emit(Instruction::new(
5787                                    OpCode::ArrayPushLocal,
5788                                    Some(Operand::Local(local_idx)),
5789                                ));
5790                                if let Some(numeric_type) = pushed_numeric {
5791                                    self.mark_slot_as_numeric_array(local_idx, true, numeric_type);
5792                                }
5793                                return Ok(());
5794                            } else if !self
5795                                .mutable_closure_captures
5796                                .contains_key(recv_name.as_str())
5797                            {
5798                                self.check_named_binding_write_allowed(
5799                                    recv_name,
5800                                    Some(source_loc),
5801                                )?;
5802                                let binding_idx = self.get_or_create_module_binding(recv_name);
5803                                if let Some(kind) = typed_kind {
5804                                    self.emit(Instruction::new(
5805                                        OpCode::LoadModuleBinding,
5806                                        Some(Operand::ModuleBinding(binding_idx)),
5807                                    ));
5808                                    self.compile_typed_array_element_value(kind, &args[0])?;
5809                                    self.emit(Instruction::simple(kind.push_opcode()));
5810                                    return Ok(());
5811                                }
5812                                self.compile_expr(&args[0])?;
5813                                self.emit(Instruction::new(
5814                                    OpCode::ArrayPushLocal,
5815                                    Some(Operand::ModuleBinding(binding_idx)),
5816                                ));
5817                                return Ok(());
5818                            }
5819                        }
5820                    }
5821                }
5822                self.compile_expr(expr)?;
5823                self.emit(Instruction::simple(OpCode::Pop));
5824            }
5825
5826            Statement::For(for_loop, _) => {
5827                self.compile_for_loop(for_loop)?;
5828            }
5829
5830            Statement::While(while_loop, _) => {
5831                self.compile_while_loop(while_loop)?;
5832            }
5833
5834            Statement::If(if_stmt, _) => {
5835                self.compile_if_statement(if_stmt)?;
5836            }
5837            Statement::Extend(extend, span) => {
5838                self.require_comptime_mode("extend", *span)?;
5839                self.emit_comptime_extend_directive(extend, *span)?;
5840            }
5841            Statement::RemoveTarget(span) => {
5842                self.require_comptime_mode("remove target", *span)?;
5843                self.emit_comptime_remove_directive(*span)?;
5844            }
5845            Statement::SetParamType {
5846                param_name,
5847                type_annotation,
5848                span,
5849            } => {
5850                self.require_comptime_mode("set param", *span)?;
5851                self.emit_comptime_set_param_type_directive(param_name, type_annotation, *span)?;
5852            }
5853            Statement::SetParamValue {
5854                param_name,
5855                expression,
5856                span,
5857            } => {
5858                self.require_comptime_mode("set param", *span)?;
5859                self.emit_comptime_set_param_value_directive(param_name, expression, *span)?;
5860            }
5861            Statement::SetReturnType {
5862                type_annotation,
5863                span,
5864            } => {
5865                self.require_comptime_mode("set return", *span)?;
5866                self.emit_comptime_set_return_type_directive(type_annotation, *span)?;
5867            }
5868            Statement::SetReturnExpr { expression, span } => {
5869                self.require_comptime_mode("set return", *span)?;
5870                self.emit_comptime_set_return_expr_directive(expression, *span)?;
5871            }
5872            Statement::ReplaceBody { body, span } => {
5873                self.require_comptime_mode("replace body", *span)?;
5874                self.emit_comptime_replace_body_directive(body, *span)?;
5875            }
5876            Statement::ReplaceBodyExpr { expression, span } => {
5877                self.require_comptime_mode("replace body", *span)?;
5878                self.emit_comptime_replace_body_expr_directive(expression, *span)?;
5879            }
5880            Statement::ReplaceModuleExpr { expression, span } => {
5881                self.require_comptime_mode("replace module", *span)?;
5882                self.emit_comptime_replace_module_expr_directive(expression, *span)?;
5883            }
5884        }
5885        Ok(())
5886    }
5887}
5888
5889#[cfg(test)]
5890mod tests {
5891    use crate::compiler::BytecodeCompiler;
5892    use crate::executor::{VMConfig, VirtualMachine};
5893    use shape_ast::ast::{Item, Span, Statement};
5894    use shape_ast::parser::parse_program;
5895
5896    // The four `test_module_*` / `test_module_inline_comptime_*` tests
5897    // below assert against the `vm.execute(None)` return shape's deleted
5898    // `as_number_coerce()` helper. Both the synthesis surface
5899    // (`synthesize_value_word_from_raw`, playbook §1) and the carrier
5900    // (`ValueWord` / `ValueWordExt::as_number_coerce`, CLAUDE.md "Renames
5901    // to refuse on sight") are deleted in the strict-typing bulldozer.
5902    // Each test is gated `#[cfg(any())]` (always-false) to keep the
5903    // assertion shape as documentation while preventing the deleted
5904    // accessor from re-entering compile. Re-enable when the kinded
5905    // `vm.execute_raw -> (bits, kind)` boundary lands and the tests can
5906    // read `f64::from_bits(bits)` directly. Phase-2c rebuild surface —
5907    // see ADR-006 §2.4.
5908    #[cfg(any())]
5909    #[test]
5910    fn test_module_decl_function_resolves_module_const() {
5911        let code = r#"
5912            mod math {
5913                const BASE = 21
5914                fn twice() {
5915                    BASE * 2
5916                }
5917            }
5918            math::twice()
5919        "#;
5920
5921        let program = parse_program(code).expect("Failed to parse");
5922        let bytecode = BytecodeCompiler::new()
5923            .compile(&program)
5924            .expect("Failed to compile");
5925
5926        let mut vm = VirtualMachine::new(VMConfig::default());
5927        vm.load_program(bytecode);
5928        vm.populate_module_objects();
5929        let result = vm.execute(None).expect("Failed to execute");
5930        assert_eq!(
5931            result
5932                .as_number_coerce()
5933                .expect("module call should return number"),
5934            42.0
5935        );
5936    }
5937
5938    #[cfg(any())]
5939    #[test]
5940    fn test_module_annotation_can_replace_module_items() {
5941        let code = r#"
5942            annotation synth_module() {
5943                targets: [module]
5944                comptime post(target, ctx) {
5945                    replace module ("const ANSWER = 40; fn plus_two() { ANSWER + 2 }")
5946                }
5947            }
5948
5949            @synth_module()
5950            mod demo {}
5951
5952            demo::plus_two()
5953        "#;
5954
5955        let program = parse_program(code).expect("Failed to parse");
5956        let bytecode = BytecodeCompiler::new()
5957            .compile(&program)
5958            .expect("Failed to compile");
5959
5960        let mut vm = VirtualMachine::new(VMConfig::default());
5961        vm.load_program(bytecode);
5962        vm.populate_module_objects();
5963        let result = vm.execute(None).expect("Failed to execute");
5964        assert_eq!(
5965            result
5966                .as_number_coerce()
5967                .expect("module call should return number"),
5968            42.0
5969        );
5970    }
5971
5972    #[cfg(any())]
5973    #[test]
5974    fn test_module_inline_comptime_can_replace_module_items() {
5975        let code = r#"
5976            mod demo {
5977                comptime {
5978                    replace module ("const ANSWER = 40; fn plus_two() { ANSWER + 2 }")
5979                }
5980            }
5981
5982            demo::plus_two()
5983        "#;
5984
5985        let program = parse_program(code).expect("Failed to parse");
5986        let bytecode = BytecodeCompiler::new()
5987            .compile(&program)
5988            .expect("Failed to compile");
5989
5990        let mut vm = VirtualMachine::new(VMConfig::default());
5991        vm.load_program(bytecode);
5992        vm.populate_module_objects();
5993        let result = vm.execute(None).expect("Failed to execute");
5994        assert_eq!(
5995            result
5996                .as_number_coerce()
5997                .expect("module call should return number"),
5998            42.0
5999        );
6000    }
6001
6002    #[cfg(any())]
6003    #[test]
6004    fn test_module_inline_comptime_can_use_module_local_comptime_helper() {
6005        let code = r#"
6006            mod demo {
6007                comptime fn synth() {
6008                    "const ANSWER = 40; fn plus_two() { ANSWER + 2 }"
6009                }
6010
6011                comptime {
6012                    replace module (synth())
6013                }
6014            }
6015
6016            demo::plus_two()
6017        "#;
6018
6019        let program = parse_program(code).expect("Failed to parse");
6020        let bytecode = BytecodeCompiler::new()
6021            .compile(&program)
6022            .expect("Failed to compile");
6023
6024        let mut vm = VirtualMachine::new(VMConfig::default());
6025        vm.load_program(bytecode);
6026        vm.populate_module_objects();
6027        let result = vm.execute(None).expect("Failed to execute");
6028        assert_eq!(
6029            result
6030                .as_number_coerce()
6031                .expect("module call should return number"),
6032            42.0
6033        );
6034    }
6035
6036    #[test]
6037    fn test_type_annotated_variable_no_wrapping() {
6038        // BUG-1/BUG-2 fix: variable declarations must NOT emit WrapTypeAnnotation
6039        // (the wrapper broke arithmetic and comparisons)
6040        let code = r#"
6041            type Currency = Number
6042            let x: Currency = 123
6043        "#;
6044        let program = parse_program(code).expect("Failed to parse");
6045        let bytecode = BytecodeCompiler::new()
6046            .compile(&program)
6047            .expect("Failed to compile");
6048
6049        // WrapTypeAnnotation should NOT be emitted for variable declarations
6050        let has_wrap_instruction = bytecode
6051            .instructions
6052            .iter()
6053            .any(|instr| instr.opcode == crate::bytecode::OpCode::WrapTypeAnnotation);
6054        assert!(
6055            !has_wrap_instruction,
6056            "Should NOT emit WrapTypeAnnotation for type-annotated variable"
6057        );
6058    }
6059
6060    #[test]
6061    fn test_untyped_variable_no_wrapping() {
6062        // Variables without type annotations should NOT emit WrapTypeAnnotation
6063        let code = r#"
6064            let x = 123
6065        "#;
6066        let program = parse_program(code).expect("Failed to parse");
6067        let bytecode = BytecodeCompiler::new()
6068            .compile(&program)
6069            .expect("Failed to compile");
6070
6071        // Check that WrapTypeAnnotation instruction was NOT emitted
6072        let has_wrap_instruction = bytecode
6073            .instructions
6074            .iter()
6075            .any(|instr| instr.opcode == crate::bytecode::OpCode::WrapTypeAnnotation);
6076        assert!(
6077            !has_wrap_instruction,
6078            "Should NOT emit WrapTypeAnnotation for untyped variable"
6079        );
6080    }
6081
6082    // ===== Phase 2: Extend Block Compilation Tests =====
6083
6084    #[test]
6085    fn test_extend_block_compiles() {
6086        let code = r#"
6087            extend Number {
6088                method double() {
6089                    return self * 2
6090                }
6091            }
6092        "#;
6093        let program = parse_program(code).expect("Failed to parse extend block");
6094        let bytecode = BytecodeCompiler::new().compile(&program);
6095        assert!(
6096            bytecode.is_ok(),
6097            "Extend block should compile: {:?}",
6098            bytecode.err()
6099        );
6100
6101        // Verify a function named "Number.double" was generated (qualified extend name).
6102        let bytecode = bytecode.unwrap();
6103        let has_double = bytecode.functions.iter().any(|f| f.name == "Number.double");
6104        assert!(
6105            has_double,
6106            "Should generate 'Number.double' function from extend block"
6107        );
6108    }
6109
6110    #[test]
6111    fn test_extend_method_has_self_param() {
6112        let code = r#"
6113            extend Number {
6114                method add(n) {
6115                    return self + n
6116                }
6117            }
6118        "#;
6119        let program = parse_program(code).expect("Failed to parse");
6120        let bytecode = BytecodeCompiler::new()
6121            .compile(&program)
6122            .expect("Failed to compile");
6123
6124        let func = bytecode.functions.iter().find(|f| f.name == "Number.add");
6125        assert!(func.is_some(), "Should have 'Number.add' function");
6126        // The function should have 2 params: self + n
6127        assert_eq!(
6128            func.unwrap().arity,
6129            2,
6130            "add() should have arity 2 (self + n)"
6131        );
6132    }
6133
6134    #[test]
6135    fn test_extend_method_rejects_explicit_self_param() {
6136        let code = r#"
6137            extend Number {
6138                method add(self, n) {
6139                    return self + n
6140                }
6141            }
6142        "#;
6143        let program = parse_program(code).expect("Failed to parse");
6144        let err = BytecodeCompiler::new()
6145            .compile(&program)
6146            .expect_err("Compiler should reject explicit self receiver param in methods");
6147        let msg = format!("{err}");
6148        assert!(
6149            msg.contains("explicit `self` parameter"),
6150            "Expected explicit self error, got: {msg}"
6151        );
6152    }
6153
6154    // ===== Phase 3: Annotation Handler Compilation Tests =====
6155
6156    #[test]
6157    fn test_annotation_def_compiles_handlers() {
6158        let code = r#"
6159            annotation warmup(period) {
6160                before(args, ctx) {
6161                    args
6162                }
6163                after(args, result, ctx) {
6164                    result
6165                }
6166            }
6167            function test() { return 42; }
6168        "#;
6169        let program = parse_program(code).expect("Failed to parse annotation def");
6170        let bytecode = BytecodeCompiler::new().compile(&program);
6171        assert!(
6172            bytecode.is_ok(),
6173            "Annotation def should compile: {:?}",
6174            bytecode.err()
6175        );
6176
6177        let bytecode = bytecode.unwrap();
6178        // Verify CompiledAnnotation was registered
6179        assert!(
6180            bytecode.compiled_annotations.contains_key("warmup"),
6181            "Should have compiled 'warmup' annotation"
6182        );
6183
6184        let compiled = bytecode.compiled_annotations.get("warmup").unwrap();
6185        assert!(
6186            compiled.before_handler.is_some(),
6187            "Should have before handler"
6188        );
6189        assert!(
6190            compiled.after_handler.is_some(),
6191            "Should have after handler"
6192        );
6193    }
6194
6195    #[test]
6196    fn test_exported_annotation_def_compiles_handlers() {
6197        let code = r#"
6198            pub annotation warmup(period) {
6199                before(args, ctx) {
6200                    args
6201                }
6202            }
6203
6204            @warmup(5)
6205            fn test() { 42 }
6206        "#;
6207        let program = parse_program(code).expect("Failed to parse exported annotation def");
6208        let bytecode = BytecodeCompiler::new().compile(&program);
6209        assert!(
6210            bytecode.is_ok(),
6211            "Exported annotation def should compile: {:?}",
6212            bytecode.err()
6213        );
6214
6215        let bytecode = bytecode.unwrap();
6216        assert!(
6217            bytecode.compiled_annotations.contains_key("warmup"),
6218            "Should have compiled exported 'warmup' annotation"
6219        );
6220    }
6221
6222    #[test]
6223    fn test_annotation_handler_function_names() {
6224        let code = r#"
6225            annotation my_ann(x) {
6226                before(args, ctx) {
6227                    args
6228                }
6229            }
6230            function test() { return 1; }
6231        "#;
6232        let program = parse_program(code).expect("Failed to parse");
6233        let bytecode = BytecodeCompiler::new()
6234            .compile(&program)
6235            .expect("Failed to compile");
6236
6237        // Handler should be compiled as an internal function
6238        let compiled = bytecode.compiled_annotations.get("my_ann").unwrap();
6239        let handler_id = compiled.before_handler.unwrap() as usize;
6240        assert!(
6241            handler_id < bytecode.functions.len(),
6242            "Handler function ID should be valid"
6243        );
6244
6245        let handler_fn = &bytecode.functions[handler_id];
6246        assert_eq!(
6247            handler_fn.name, "my_ann___before",
6248            "Handler function should be named my_ann___before"
6249        );
6250    }
6251
6252    // ===== Phase 4: Compile-Time Function Wrapping Tests =====
6253
6254    #[test]
6255    fn test_annotated_function_generates_wrapper() {
6256        let code = r#"
6257            annotation tracked(label) {
6258                before(args, ctx) {
6259                    args
6260                }
6261            }
6262            @tracked("my_func")
6263            function compute(x) {
6264                return x * 2
6265            }
6266            function test() { return 1; }
6267        "#;
6268        let program = parse_program(code).expect("Failed to parse");
6269        let bytecode = BytecodeCompiler::new().compile(&program);
6270        assert!(
6271            bytecode.is_ok(),
6272            "Annotated function should compile: {:?}",
6273            bytecode.err()
6274        );
6275
6276        let bytecode = bytecode.unwrap();
6277        // Should have the original function (wrapper) and the impl
6278        let has_impl = bytecode
6279            .functions
6280            .iter()
6281            .any(|f| f.name == "compute___impl");
6282        assert!(has_impl, "Should generate compute___impl function");
6283
6284        let has_wrapper = bytecode.functions.iter().any(|f| f.name == "compute");
6285        assert!(has_wrapper, "Should keep compute as wrapper");
6286    }
6287
6288    #[test]
6289    fn test_unannotated_function_no_wrapper() {
6290        let code = r#"
6291            function plain(x) {
6292                return x + 1
6293            }
6294        "#;
6295        let program = parse_program(code).expect("Failed to parse");
6296        let bytecode = BytecodeCompiler::new()
6297            .compile(&program)
6298            .expect("Failed to compile");
6299
6300        // Should NOT have an ___impl function
6301        let has_impl = bytecode
6302            .functions
6303            .iter()
6304            .any(|f| f.name.ends_with("___impl"));
6305        assert!(
6306            !has_impl,
6307            "Non-annotated function should not generate ___impl"
6308        );
6309    }
6310
6311    // ===== Sprint 10: Annotation chaining and target validation =====
6312
6313    #[test]
6314    fn test_annotation_chaining_generates_chain() {
6315        // Two annotations on the same function should generate chained wrappers
6316        let code = r#"
6317            annotation first() {
6318                before(args, ctx) {
6319                    return args
6320                }
6321            }
6322
6323            annotation second() {
6324                before(args, ctx) {
6325                    return args
6326                }
6327            }
6328
6329            @first
6330            @second
6331            function compute(x) {
6332                return x * 2
6333            }
6334        "#;
6335        let program = parse_program(code).expect("Failed to parse");
6336        let bytecode = BytecodeCompiler::new().compile(&program);
6337        assert!(
6338            bytecode.is_ok(),
6339            "Chained annotations should compile: {:?}",
6340            bytecode.err()
6341        );
6342        let bytecode = bytecode.unwrap();
6343
6344        // Should have: compute (outermost wrapper), compute___impl (body), compute___second (intermediate)
6345        let has_impl = bytecode
6346            .functions
6347            .iter()
6348            .any(|f| f.name == "compute___impl");
6349        assert!(has_impl, "Should generate compute___impl function");
6350        let has_wrapper = bytecode.functions.iter().any(|f| f.name == "compute");
6351        assert!(has_wrapper, "Should keep compute as outermost wrapper");
6352        let has_intermediate = bytecode
6353            .functions
6354            .iter()
6355            .any(|f| f.name == "compute___second");
6356        assert!(
6357            has_intermediate,
6358            "Should generate compute___second intermediate wrapper"
6359        );
6360    }
6361
6362    #[test]
6363    fn test_annotation_allowed_targets_inferred() {
6364        // An annotation with before/after should have allowed_targets = [Function]
6365        let code = r#"
6366            annotation traced() {
6367                before(args, ctx) {
6368                    return args
6369                }
6370            }
6371        "#;
6372        let program = parse_program(code).expect("Failed to parse");
6373        let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
6374        let ann = bytecode
6375            .compiled_annotations
6376            .get("traced")
6377            .expect("traced annotation");
6378        assert!(
6379            !ann.allowed_targets.is_empty(),
6380            "before handler should restrict targets"
6381        );
6382        assert!(
6383            ann.allowed_targets
6384                .contains(&shape_ast::ast::functions::AnnotationTargetKind::Function),
6385            "before handler should allow Function target"
6386        );
6387    }
6388
6389    #[test]
6390    fn test_annotation_allowed_targets_explicit_override() {
6391        // Explicit `targets: [...]` should override inferred defaults.
6392        let code = r#"
6393            annotation traced() {
6394                targets: [type]
6395                before(args, ctx) {
6396                    return args
6397                }
6398            }
6399        "#;
6400        let program = parse_program(code).expect("Failed to parse");
6401        let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
6402        let ann = bytecode
6403            .compiled_annotations
6404            .get("traced")
6405            .expect("traced annotation");
6406        assert_eq!(
6407            ann.allowed_targets,
6408            vec![shape_ast::ast::functions::AnnotationTargetKind::Type]
6409        );
6410    }
6411
6412    #[test]
6413    fn test_metadata_only_annotation_defaults_to_definition_targets() {
6414        // An annotation with only metadata handler should default to definition targets.
6415        let code = r#"
6416            annotation info() {
6417                metadata() {
6418                    return { version: 1 }
6419                }
6420            }
6421        "#;
6422        let program = parse_program(code).expect("Failed to parse");
6423        let bytecode = BytecodeCompiler::new().compile(&program).expect("compile");
6424        let ann = bytecode
6425            .compiled_annotations
6426            .get("info")
6427            .expect("info annotation");
6428        assert_eq!(
6429            ann.allowed_targets,
6430            vec![
6431                shape_ast::ast::functions::AnnotationTargetKind::Function,
6432                shape_ast::ast::functions::AnnotationTargetKind::Type,
6433                shape_ast::ast::functions::AnnotationTargetKind::Module
6434            ],
6435            "metadata-only annotation should default to definition targets"
6436        );
6437    }
6438
6439    #[test]
6440    fn test_definition_lifecycle_targets_reject_expression_target() {
6441        let code = r#"
6442            annotation info() {
6443                targets: [expression]
6444                metadata(target, ctx) {
6445                    target.name
6446                }
6447            }
6448        "#;
6449        let program = parse_program(code).expect("Failed to parse");
6450        let err = BytecodeCompiler::new()
6451            .compile(&program)
6452            .expect_err("metadata hooks on expression targets should fail");
6453        let msg = format!("{}", err);
6454        assert!(
6455            msg.contains("not a definition target"),
6456            "expected definition-target restriction error, got: {}",
6457            msg
6458        );
6459    }
6460
6461    #[test]
6462    fn test_annotation_target_validation_on_struct_type() {
6463        // Function-only annotation applied to a type should fail.
6464        let code = r#"
6465            annotation traced() {
6466                before(args, ctx) { return args }
6467            }
6468
6469            @traced()
6470            type Point { x: int }
6471        "#;
6472        let program = parse_program(code).expect("Failed to parse");
6473        let err = BytecodeCompiler::new()
6474            .compile(&program)
6475            .expect_err("function-only annotation on type should fail");
6476        let msg = format!("{}", err);
6477        assert!(
6478            msg.contains("cannot be applied to a type"),
6479            "expected type target validation error, got: {}",
6480            msg
6481        );
6482    }
6483
6484    #[test]
6485    fn test_type_c_emits_native_layout_metadata() {
6486        let bytecode = compiles_to(
6487            r#"
6488            type C Pair32 {
6489                left: i32,
6490                right: i32,
6491            }
6492            "#,
6493        );
6494
6495        assert_eq!(bytecode.native_struct_layouts.len(), 1);
6496        let layout = &bytecode.native_struct_layouts[0];
6497        assert_eq!(layout.name, "Pair32");
6498        assert_eq!(layout.abi, "C");
6499        assert_eq!(layout.size, 8);
6500        assert_eq!(layout.align, 4);
6501        assert_eq!(layout.fields.len(), 2);
6502        assert_eq!(layout.fields[0].name, "left");
6503        assert_eq!(layout.fields[0].offset, 0);
6504        assert_eq!(layout.fields[0].size, 4);
6505        assert_eq!(layout.fields[1].name, "right");
6506        assert_eq!(layout.fields[1].offset, 4);
6507        assert_eq!(layout.fields[1].size, 4);
6508    }
6509
6510    #[test]
6511    fn test_type_c_auto_generates_into_from_traits() {
6512        let bytecode = compiles_to(
6513            r#"
6514            type C QuoteC {
6515                bid: i64,
6516                ask: i64,
6517            }
6518
6519            type Quote {
6520                bid: i64,
6521                ask: i64,
6522            }
6523            "#,
6524        );
6525
6526        let c_to_shape =
6527            bytecode.lookup_trait_method_symbol("Into", "QuoteC", Some("Quote"), "into");
6528        let shape_to_c =
6529            bytecode.lookup_trait_method_symbol("Into", "Quote", Some("QuoteC"), "into");
6530        let from_c = bytecode.lookup_trait_method_symbol("From", "Quote", Some("QuoteC"), "from");
6531        let from_shape =
6532            bytecode.lookup_trait_method_symbol("From", "QuoteC", Some("Quote"), "from");
6533
6534        assert!(c_to_shape.is_some(), "expected Into<Quote> for QuoteC");
6535        assert!(shape_to_c.is_some(), "expected Into<QuoteC> for Quote");
6536        assert!(from_c.is_some(), "expected From<QuoteC> for Quote");
6537        assert!(from_shape.is_some(), "expected From<Quote> for QuoteC");
6538    }
6539
6540    #[test]
6541    fn test_type_c_auto_conversion_function_compiles() {
6542        let _ = compiles_to(
6543            r#"
6544            type Quote {
6545                bid: i64,
6546                ask: i64,
6547            }
6548
6549            type C QuoteC {
6550                bid: i64,
6551                ask: i64,
6552            }
6553
6554            fn spread(q: QuoteC) -> i64 {
6555                let q_shape = __auto_native_from_QuoteC_to_Quote(q);
6556                q_shape.ask - q_shape.bid
6557            }
6558
6559            spread(QuoteC { bid: 10, ask: 13 })
6560            "#,
6561        );
6562    }
6563
6564    #[test]
6565    fn test_type_c_auto_conversion_rejects_incompatible_fields() {
6566        let program = parse_program(
6567            r#"
6568            type Price {
6569                value: i64,
6570            }
6571
6572            type C PriceC {
6573                value: u64,
6574            }
6575            "#,
6576        )
6577        .expect("parse failed");
6578        let err = BytecodeCompiler::new()
6579            .compile(&program)
6580            .expect_err("incompatible type C conversion pair should fail");
6581        let msg = format!("{}", err);
6582        assert!(
6583            msg.contains("field type mismatch for auto conversion"),
6584            "expected type mismatch error, got: {}",
6585            msg
6586        );
6587    }
6588
6589    // ===== Task 1: Meta on traits =====
6590
6591    // ===== Drop Track: Sprint 2 Tests =====
6592
6593    fn compiles_to(code: &str) -> crate::bytecode::BytecodeProgram {
6594        let program = parse_program(code).expect("parse failed");
6595        let compiler = BytecodeCompiler::new();
6596        compiler.compile(&program).expect("compile failed")
6597    }
6598
6599    // --- Permission checking tests ---
6600
6601    #[test]
6602    fn test_permission_check_allows_pure_module_imports() {
6603        // json is a pure module — should compile even with empty permissions
6604        let code = "from std::core::json use { parse }";
6605        let program = parse_program(code).expect("parse failed");
6606        let mut compiler = BytecodeCompiler::new();
6607        compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
6608        // Should not fail — json requires no permissions
6609        let _result = compiler.compile(&program);
6610    }
6611
6612    #[test]
6613    fn test_permission_check_blocks_file_import_under_pure() {
6614        let code = "from std::core::file use { read_text }";
6615        let program = parse_program(code).expect("parse failed");
6616        let mut compiler = BytecodeCompiler::new();
6617        compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
6618        let result = compiler.compile(&program);
6619        assert!(
6620            result.is_err(),
6621            "Expected permission error for file::read_text under pure"
6622        );
6623        let err_msg = format!("{}", result.unwrap_err());
6624        assert!(
6625            err_msg.contains("Permission denied"),
6626            "Error should mention permission denied: {err_msg}"
6627        );
6628        assert!(
6629            err_msg.contains("fs.read"),
6630            "Error should mention fs.read: {err_msg}"
6631        );
6632    }
6633
6634    #[test]
6635    fn test_permission_check_allows_file_import_with_fs_read() {
6636        let code = "from std::core::file use { read_text }";
6637        let program = parse_program(code).expect("parse failed");
6638        let mut compiler = BytecodeCompiler::new();
6639        let pset = shape_abi_v1::PermissionSet::from_iter([shape_abi_v1::Permission::FsRead]);
6640        compiler.set_permission_set(Some(pset));
6641        // Should not fail
6642        let _result = compiler.compile(&program);
6643    }
6644
6645    #[test]
6646    fn test_permission_check_no_permission_set_allows_everything() {
6647        // When permission_set is None (default), no checking is done
6648        let code = "from std::core::file use { read_text }";
6649        let program = parse_program(code).expect("parse failed");
6650        let compiler = BytecodeCompiler::new();
6651        // permission_set is None by default — should compile fine
6652        let _result = compiler.compile(&program);
6653    }
6654
6655    #[test]
6656    fn test_permission_check_namespace_import_blocked() {
6657        let code = "use std::core::http";
6658        let program = parse_program(code).expect("parse failed");
6659        let mut compiler = BytecodeCompiler::new();
6660        compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::pure()));
6661        let result = compiler.compile(&program);
6662        assert!(
6663            result.is_err(),
6664            "Expected permission error for `use std::core::http` under pure"
6665        );
6666        let err_msg = format!("{}", result.unwrap_err());
6667        assert!(
6668            err_msg.contains("Permission denied"),
6669            "Error should mention permission denied: {err_msg}"
6670        );
6671    }
6672
6673    #[test]
6674    fn test_permission_check_namespace_import_allowed() {
6675        let code = "use std::core::http";
6676        let program = parse_program(code).expect("parse failed");
6677        let mut compiler = BytecodeCompiler::new();
6678        compiler.set_permission_set(Some(shape_abi_v1::PermissionSet::full()));
6679        // Should not fail
6680        let _result = compiler.compile(&program);
6681    }
6682
6683    fn test_decl(kind: shape_ast::ast::VarKind, is_mut: bool) -> shape_ast::ast::VariableDecl {
6684        shape_ast::ast::VariableDecl {
6685            kind,
6686            is_mut,
6687            pattern: shape_ast::ast::DestructurePattern::Identifier(
6688                "x".to_string(),
6689                shape_ast::ast::Span::DUMMY,
6690            ),
6691            type_annotation: None,
6692            value: None,
6693            ownership: Default::default(),
6694        }
6695    }
6696
6697    #[test]
6698    fn test_binding_semantics_for_decl_maps_let_var_classes() {
6699        let let_semantics = BytecodeCompiler::binding_semantics_for_var_decl(&test_decl(
6700            shape_ast::ast::VarKind::Let,
6701            false,
6702        ));
6703        assert_eq!(
6704            let_semantics.ownership_class,
6705            crate::type_tracking::BindingOwnershipClass::OwnedImmutable
6706        );
6707        assert_eq!(
6708            let_semantics.storage_class,
6709            crate::type_tracking::BindingStorageClass::Direct
6710        );
6711
6712        let let_mut_semantics = BytecodeCompiler::binding_semantics_for_var_decl(&test_decl(
6713            shape_ast::ast::VarKind::Let,
6714            true,
6715        ));
6716        assert_eq!(
6717            let_mut_semantics.ownership_class,
6718            crate::type_tracking::BindingOwnershipClass::OwnedMutable
6719        );
6720        assert_eq!(
6721            let_mut_semantics.storage_class,
6722            crate::type_tracking::BindingStorageClass::Direct
6723        );
6724
6725        let var_semantics = BytecodeCompiler::binding_semantics_for_var_decl(&test_decl(
6726            shape_ast::ast::VarKind::Var,
6727            false,
6728        ));
6729        assert_eq!(
6730            var_semantics.ownership_class,
6731            crate::type_tracking::BindingOwnershipClass::Flexible
6732        );
6733        assert_eq!(
6734            var_semantics.storage_class,
6735            crate::type_tracking::BindingStorageClass::Deferred
6736        );
6737    }
6738
6739    #[test]
6740    fn test_destructured_module_bindings_get_binding_semantics() {
6741        let mut compiler = BytecodeCompiler::new();
6742        let pattern = shape_ast::ast::DestructurePattern::Array(vec![
6743            shape_ast::ast::DestructurePattern::Identifier(
6744                "left".to_string(),
6745                shape_ast::ast::Span::DUMMY,
6746            ),
6747            shape_ast::ast::DestructurePattern::Identifier(
6748                "right".to_string(),
6749                shape_ast::ast::Span::DUMMY,
6750            ),
6751        ]);
6752        compiler
6753            .compile_destructure_pattern_global(&pattern)
6754            .expect("destructure should compile");
6755        compiler.apply_binding_semantics_to_pattern_bindings(
6756            &pattern,
6757            false,
6758            BytecodeCompiler::binding_semantics_for_var_decl(&test_decl(
6759                shape_ast::ast::VarKind::Let,
6760                false,
6761            )),
6762        );
6763
6764        let left_idx = *compiler
6765            .module_bindings
6766            .get("left")
6767            .expect("left binding should exist");
6768        let right_idx = *compiler
6769            .module_bindings
6770            .get("right")
6771            .expect("right binding should exist");
6772
6773        assert_eq!(
6774            compiler
6775                .type_tracker
6776                .get_binding_semantics(left_idx)
6777                .map(|semantics| semantics.ownership_class),
6778            Some(crate::type_tracking::BindingOwnershipClass::OwnedImmutable)
6779        );
6780        assert_eq!(
6781            compiler
6782                .type_tracker
6783                .get_binding_semantics(left_idx)
6784                .map(|semantics| semantics.storage_class),
6785            Some(crate::type_tracking::BindingStorageClass::Direct)
6786        );
6787        assert_eq!(
6788            compiler
6789                .type_tracker
6790                .get_binding_semantics(right_idx)
6791                .map(|semantics| semantics.ownership_class),
6792            Some(crate::type_tracking::BindingOwnershipClass::OwnedImmutable)
6793        );
6794    }
6795
6796    #[test]
6797    fn test_flexible_binding_alias_initializer_marks_shared_storage() {
6798        let mut compiler = BytecodeCompiler::new();
6799        compiler.push_scope();
6800        let source = compiler.declare_local("source").expect("declare source");
6801        let dest = compiler.declare_local("dest").expect("declare dest");
6802        let var_semantics = BytecodeCompiler::binding_semantics_for_var_decl(&test_decl(
6803            shape_ast::ast::VarKind::Var,
6804            false,
6805        ));
6806        compiler
6807            .type_tracker
6808            .set_local_binding_semantics(source, var_semantics);
6809        compiler
6810            .type_tracker
6811            .set_local_binding_semantics(dest, var_semantics);
6812
6813        compiler.plan_flexible_binding_storage_from_expr(
6814            dest,
6815            true,
6816            &shape_ast::ast::Expr::Identifier("source".to_string(), shape_ast::ast::Span::DUMMY),
6817        );
6818
6819        assert_eq!(
6820            compiler
6821                .type_tracker
6822                .get_local_binding_semantics(source)
6823                .map(|semantics| semantics.storage_class),
6824            Some(crate::type_tracking::BindingStorageClass::SharedCow)
6825        );
6826        assert_eq!(
6827            compiler
6828                .type_tracker
6829                .get_local_binding_semantics(dest)
6830                .map(|semantics| semantics.storage_class),
6831            Some(crate::type_tracking::BindingStorageClass::SharedCow)
6832        );
6833    }
6834
6835    #[test]
6836    fn test_flexible_destructure_bindings_finalize_to_direct_storage() {
6837        let mut compiler = BytecodeCompiler::new();
6838        compiler.push_scope();
6839        let left = compiler.declare_local("left").expect("declare left");
6840        let right = compiler.declare_local("right").expect("declare right");
6841        let var_semantics = BytecodeCompiler::binding_semantics_for_var_decl(&test_decl(
6842            shape_ast::ast::VarKind::Var,
6843            false,
6844        ));
6845        compiler
6846            .type_tracker
6847            .set_local_binding_semantics(left, var_semantics);
6848        compiler
6849            .type_tracker
6850            .set_local_binding_semantics(right, var_semantics);
6851
6852        let pattern = shape_ast::ast::DestructurePattern::Array(vec![
6853            shape_ast::ast::DestructurePattern::Identifier(
6854                "left".to_string(),
6855                shape_ast::ast::Span::DUMMY,
6856            ),
6857            shape_ast::ast::DestructurePattern::Identifier(
6858                "right".to_string(),
6859                shape_ast::ast::Span::DUMMY,
6860            ),
6861        ]);
6862        compiler.plan_flexible_binding_storage_for_pattern_initializer(
6863            &pattern,
6864            true,
6865            Some(&shape_ast::ast::Expr::Identifier(
6866                "source".to_string(),
6867                shape_ast::ast::Span::DUMMY,
6868            )),
6869        );
6870
6871        assert_eq!(
6872            compiler
6873                .type_tracker
6874                .get_local_binding_semantics(left)
6875                .map(|semantics| semantics.storage_class),
6876            Some(crate::type_tracking::BindingStorageClass::Direct)
6877        );
6878        assert_eq!(
6879            compiler
6880                .type_tracker
6881                .get_local_binding_semantics(right)
6882                .map(|semantics| semantics.storage_class),
6883            Some(crate::type_tracking::BindingStorageClass::Direct)
6884        );
6885    }
6886
6887    #[test]
6888    fn test_module_var_alias_decl_marks_shared_storage() {
6889        let program = parse_program(
6890            r#"
6891                var source = [1]
6892                var alias = source
6893            "#,
6894        )
6895        .expect("parse failed");
6896        let mut compiler = BytecodeCompiler::new();
6897        let first_decl = match &program.items[0] {
6898            Item::VariableDecl(var_decl, _) => {
6899                Statement::VariableDecl(var_decl.clone(), Span::DUMMY)
6900            }
6901            Item::Statement(stmt, _) => stmt.clone(),
6902            _ => panic!("expected first variable declaration"),
6903        };
6904        let second_decl = match &program.items[1] {
6905            Item::VariableDecl(var_decl, _) => {
6906                Statement::VariableDecl(var_decl.clone(), Span::DUMMY)
6907            }
6908            Item::Statement(stmt, _) => stmt.clone(),
6909            _ => panic!("expected second variable declaration"),
6910        };
6911        compiler
6912            .compile_statement(&first_decl)
6913            .expect("first decl should compile");
6914        compiler
6915            .compile_statement(&second_decl)
6916            .expect("second decl should compile");
6917
6918        let source_idx = *compiler
6919            .module_bindings
6920            .get("source")
6921            .expect("source binding should exist");
6922        let alias_idx = *compiler
6923            .module_bindings
6924            .get("alias")
6925            .expect("alias binding should exist");
6926
6927        assert_eq!(
6928            compiler
6929                .type_tracker
6930                .get_binding_semantics(source_idx)
6931                .map(|semantics| semantics.storage_class),
6932            Some(crate::type_tracking::BindingStorageClass::SharedCow)
6933        );
6934        assert_eq!(
6935            compiler
6936                .type_tracker
6937                .get_binding_semantics(alias_idx)
6938                .map(|semantics| semantics.storage_class),
6939            Some(crate::type_tracking::BindingStorageClass::SharedCow)
6940        );
6941    }
6942
6943    #[test]
6944    fn test_module_var_fresh_decl_marks_direct_storage() {
6945        let program = parse_program("var values = [1, 2, 3]").expect("parse failed");
6946        let mut compiler = BytecodeCompiler::new();
6947        let decl = match &program.items[0] {
6948            Item::VariableDecl(var_decl, _) => {
6949                Statement::VariableDecl(var_decl.clone(), Span::DUMMY)
6950            }
6951            Item::Statement(stmt, _) => stmt.clone(),
6952            _ => panic!("expected variable declaration"),
6953        };
6954        compiler
6955            .compile_statement(&decl)
6956            .expect("decl should compile");
6957
6958        let values_idx = *compiler
6959            .module_bindings
6960            .get("values")
6961            .expect("values binding should exist");
6962
6963        assert_eq!(
6964            compiler
6965                .type_tracker
6966                .get_binding_semantics(values_idx)
6967                .map(|semantics| semantics.storage_class),
6968            Some(crate::type_tracking::BindingStorageClass::Direct)
6969        );
6970    }
6971
6972    #[test]
6973    fn test_module_var_collection_escape_marks_source_unique_heap() {
6974        let program = parse_program(
6975            r#"
6976                var source = [1]
6977                var wrapped = [source]
6978            "#,
6979        )
6980        .expect("parse failed");
6981        let mut compiler = BytecodeCompiler::new();
6982        for item in &program.items {
6983            let stmt = match item {
6984                Item::VariableDecl(var_decl, _) => {
6985                    Statement::VariableDecl(var_decl.clone(), Span::DUMMY)
6986                }
6987                Item::Statement(stmt, _) => stmt.clone(),
6988                _ => continue,
6989            };
6990            compiler
6991                .compile_statement(&stmt)
6992                .expect("item should compile");
6993        }
6994
6995        let source_idx = *compiler
6996            .module_bindings
6997            .get("source")
6998            .expect("source binding should exist");
6999        let wrapped_idx = *compiler
7000            .module_bindings
7001            .get("wrapped")
7002            .expect("wrapped binding should exist");
7003
7004        assert_eq!(
7005            compiler
7006                .type_tracker
7007                .get_binding_semantics(source_idx)
7008                .map(|semantics| semantics.storage_class),
7009            Some(crate::type_tracking::BindingStorageClass::UniqueHeap)
7010        );
7011        assert_eq!(
7012            compiler
7013                .type_tracker
7014                .get_binding_semantics(wrapped_idx)
7015                .map(|semantics| semantics.storage_class),
7016            Some(crate::type_tracking::BindingStorageClass::Direct)
7017        );
7018    }
7019
7020    #[test]
7021    fn test_module_var_assignment_alias_marks_shared_storage() {
7022        let program = parse_program(
7023            r#"
7024                var source = [1]
7025                var alias = []
7026                alias = source
7027            "#,
7028        )
7029        .expect("parse failed");
7030        let mut compiler = BytecodeCompiler::new();
7031        for item in &program.items {
7032            let stmt = match item {
7033                Item::VariableDecl(var_decl, _) => {
7034                    Statement::VariableDecl(var_decl.clone(), Span::DUMMY)
7035                }
7036                Item::Assignment(assign, _) => Statement::Assignment(assign.clone(), Span::DUMMY),
7037                Item::Statement(stmt, _) => stmt.clone(),
7038                _ => continue,
7039            };
7040            compiler
7041                .compile_statement(&stmt)
7042                .expect("item should compile");
7043        }
7044
7045        let source_idx = *compiler
7046            .module_bindings
7047            .get("source")
7048            .expect("source binding should exist");
7049        let alias_idx = *compiler
7050            .module_bindings
7051            .get("alias")
7052            .expect("alias binding should exist");
7053
7054        assert_eq!(
7055            compiler
7056                .type_tracker
7057                .get_binding_semantics(source_idx)
7058                .map(|semantics| semantics.storage_class),
7059            Some(crate::type_tracking::BindingStorageClass::SharedCow)
7060        );
7061        assert_eq!(
7062            compiler
7063                .type_tracker
7064                .get_binding_semantics(alias_idx)
7065                .map(|semantics| semantics.storage_class),
7066            Some(crate::type_tracking::BindingStorageClass::SharedCow)
7067        );
7068    }
7069
7070    // ─── Phase 3 cluster-0 Round 13 T1' gap 3 source-side fix ──────────
7071    //
7072    // `desugar_impl_method` now substitutes the trait's declared return
7073    // type into the synthesized `FunctionDef.return_type` when the impl
7074    // method omits its own. The 6A `function_return_concrete_types`
7075    // populator at `compile_post_assembly` reads from
7076    // `expanded_function_defs` which mirrors `function_defs` — populated
7077    // by `register_function` which clones the synthesized `FunctionDef`
7078    // verbatim. So the substituted return-type annotation flows through
7079    // unchanged, and the trait's declared `ConcreteType` lands in the
7080    // 6A side-table for the impl-method's function entry.
7081
7082    #[test]
7083    fn desugar_impl_method_backfills_return_type_from_trait_declaration() {
7084        // Smoke 3 minimal shape: trait T { method name() -> string }
7085        // type X {} impl T for X { method name() { "x" } }
7086        //
7087        // The impl method body lacks the return-type annotation. Before
7088        // T1' gap 3 closure, `FunctionDef.return_type` is `None` for the
7089        // synthesized `X::name`, and `function_return_concrete_types[X::name]
7090        // = ConcreteType::Void`. After T1' gap 3 closure, the
7091        // synthesized return_type is `Some(TypeAnnotation::Basic("string"))`
7092        // backfilled from the trait's `Required(Method { return_type:
7093        // Basic("string"), .. })` declaration.
7094        let code = r#"
7095            trait T { method name() -> string }
7096            type X {}
7097            impl T for X {
7098                method name() { "x" }
7099            }
7100        "#;
7101        let program = parse_program(code).expect("Failed to parse");
7102        let bytecode = BytecodeCompiler::new()
7103            .compile(&program)
7104            .expect("Failed to compile");
7105
7106        // The impl method desugars to scoped function name `X::name`
7107        // (default-impl naming per desugar_impl_method line ~1678 —
7108        // `format!("{}::{}", type_name, method_name)`).
7109        let func_def = bytecode
7110            .expanded_function_defs
7111            .get("X::name")
7112            .expect("X::name function def should be registered");
7113
7114        assert!(
7115            func_def.return_type.is_some(),
7116            "T1' gap 3: impl method `X::name` return_type should be \
7117             backfilled from trait declaration `T::method name() -> string`, \
7118             got None"
7119        );
7120
7121        // Extract the substituted annotation and verify it is the trait's
7122        // declared `string` (not a fabricated default, not the void
7123        // sentinel).
7124        let return_ann = func_def
7125            .return_type
7126            .as_ref()
7127            .expect("return_type Some after backfill");
7128        match return_ann {
7129            shape_ast::ast::TypeAnnotation::Basic(name) => {
7130                assert_eq!(
7131                    name, "string",
7132                    "T1' gap 3: backfilled return_type must be the \
7133                     trait's declared `string`, got Basic(`{}`)",
7134                    name
7135                );
7136            }
7137            other => panic!(
7138                "T1' gap 3: expected Basic(\"string\") from trait \
7139                 declaration, got: {:?}",
7140                other
7141            ),
7142        }
7143    }
7144
7145    #[test]
7146    fn desugar_impl_method_preserves_explicit_impl_return_type() {
7147        // If the impl explicitly declares a return-type, the backfill must
7148        // not override it (the impl's annotation is the authoritative
7149        // shape — the trait's declared shape is structurally compatible
7150        // but the impl may be more specific). Verify the substitution is
7151        // strictly a fallback for None.
7152        //
7153        // Smoke 3 -- but with explicit `-> string` repeated on the impl
7154        // method (impl methods use the `->` return-type syntax per
7155        // `shape.pest::return_type = { "->" ~ type_annotation }`).
7156        let code = r#"
7157            trait T { method name() -> string }
7158            type X {}
7159            impl T for X {
7160                method name() -> string { "x" }
7161            }
7162        "#;
7163        let program = parse_program(code).expect("Failed to parse");
7164        let bytecode = BytecodeCompiler::new()
7165            .compile(&program)
7166            .expect("Failed to compile");
7167
7168        let func_def = bytecode
7169            .expanded_function_defs
7170            .get("X::name")
7171            .expect("X::name function def should be registered");
7172
7173        let return_ann = func_def
7174            .return_type
7175            .as_ref()
7176            .expect("return_type Some — impl declared explicitly");
7177        match return_ann {
7178            shape_ast::ast::TypeAnnotation::Basic(name) => {
7179                assert_eq!(
7180                    name, "string",
7181                    "T1' gap 3: explicit impl return_type must be \
7182                     preserved verbatim"
7183                );
7184            }
7185            other => panic!(
7186                "T1' gap 3: expected explicit Basic(\"string\") from \
7187                 impl source, got: {:?}",
7188                other
7189            ),
7190        }
7191    }
7192
7193    #[test]
7194    fn desugar_impl_method_leaves_none_when_trait_method_has_no_return_type() {
7195        // A trait method that itself has no return type (e.g., a void
7196        // method) provides no return type to backfill. The substitution
7197        // path returns the impl's `None` unchanged — no fabricated
7198        // default per §2.7.7 #9.
7199        //
7200        // (`Required` trait members always have a return_type per the
7201        // AST — `TraitMemberSignature::Method { return_type: TypeAnnotation,
7202        // .. }` is non-optional. Default trait members carry
7203        // `Option<TypeAnnotation>`, which can be None for `method foo()
7204        // {}` default bodies that elide it. This test verifies the
7205        // default-trait-member arm preserves None when the trait
7206        // default itself has no return_type annotation.)
7207        let code = r#"
7208            trait T {
7209                method greet() { print("hi") }
7210            }
7211            type X {}
7212            impl T for X {}
7213        "#;
7214        let program = parse_program(code).expect("Failed to parse");
7215        let bytecode = BytecodeCompiler::new()
7216            .compile(&program)
7217            .expect("Failed to compile");
7218
7219        // The default trait method's impl-side desugar is `X::greet`
7220        // (default-method path through `desugar_impl_method` invocation
7221        // at line ~426). When the trait default has no return_type, the
7222        // backfill returns None — preserving the impl's None.
7223        if let Some(func_def) = bytecode.expanded_function_defs.get("X::greet") {
7224            assert!(
7225                func_def.return_type.is_none(),
7226                "T1' gap 3: trait default method without return_type \
7227                 must not fabricate a default annotation, got: {:?}",
7228                func_def.return_type
7229            );
7230        }
7231        // The fn may not be registered if default-method inlining doesn't
7232        // emit a synthesized FunctionDef when the impl block is empty;
7233        // that's fine — the test asserts the negative space (no
7234        // fabricated annotation).
7235    }
7236}