diff --git a/crates/plotnik-lib/src/compile/mod.rs b/crates/plotnik-lib/src/compile/mod.rs index 56717ba7..2f648838 100644 --- a/crates/plotnik-lib/src/compile/mod.rs +++ b/crates/plotnik-lib/src/compile/mod.rs @@ -157,13 +157,21 @@ impl<'a> Compiler<'a> { self.instructions .push(Instruction::Return(ReturnIR { label: return_label })); - // Check if definition returns a struct type - if so, wrap in Obj/EndObj - // This ensures recursive calls properly scope their captures + // Check if definition needs Obj/EndObj wrapper. + // A definition needs its own scope when: + // 1. It returns a struct type, AND + // 2. It has direct captures (CapturedExpr not inside a Ref) + // + // When captures come only from Refs (called definitions), those definitions + // already handle their own Obj/EndObj scopes. Adding another wrapper would + // create nested scopes where the inner result gets lost. let def_returns_struct = self .type_ctx .get_def_type(def_id) .and_then(|tid| self.type_ctx.get_type(tid)) .is_some_and(|shape| matches!(shape, TypeShape::Struct(_))); + let has_direct_captures = !Self::collect_captures(body).is_empty(); + let needs_obj_wrapper = def_returns_struct && has_direct_captures; // Definition bodies use StayExact navigation: match at current position only. // The caller (alternation, sequence, quantifier, or VM top-level) owns the search. @@ -171,7 +179,7 @@ impl<'a> Compiler<'a> { // alternation branches should try. let body_nav = Some(Nav::StayExact); - let body_entry = if def_returns_struct { + let body_entry = if needs_obj_wrapper { let type_id = self.type_ctx.get_def_type(def_id).expect("checked above"); // Emit EndObj → Return diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_sequence_in_called_def.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_sequence_in_called_def.snap index 0a272fde..1b670791 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_sequence_in_called_def.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_sequence_in_called_def.snap @@ -56,29 +56,27 @@ Collect: 09 ε 10 10 ε [Obj] 12 12 ε [Arr] 14 - 14 ε 44, 32 + 14 ε 40, 28 Test: 16 ε 17 - 17 ε [Obj] 19 - 19 ! (parent) 20 - 20 ▽ (Collect) 09 : 21 - 21 △ 22 - 22 ε [EndObj] 24 - 24 ▶ - 25 ε [EndObj Push] 27 - 27 ε 50, 32 - 29 ▶ - 30 ε [EndObj] 29 - 32 ε [EndArr Set(M3)] 30 - 34 ε [Set(M2)] 25 - 36 ! (Item) 01 : 34 - 37 ε [Obj] 36 - 39 ▷ 42 - 40 ε 39, 32 - 42 ε 37, 40 - 44 ! 42 - 45 ▷ 48 - 46 ε 45, 32 - 48 ε 37, 46 - 50 ▷ 48 + 17 ! (parent) 18 + 18 ▽ (Collect) 09 : 19 + 19 △ 20 + 20 ▶ + 21 ε [EndObj Push] 23 + 23 ε 46, 28 + 25 ▶ + 26 ε [EndObj] 25 + 28 ε [EndArr Set(M3)] 26 + 30 ε [Set(M2)] 21 + 32 ! (Item) 01 : 30 + 33 ε [Obj] 32 + 35 ▷ 38 + 36 ε 35, 28 + 38 ε 33, 36 + 40 ! 38 + 41 ▷ 44 + 42 ε 41, 28 + 44 ε 33, 42 + 46 ▷ 44