There appears to be a fundamental design problem in Parrot's current implementation of :outer. The short summary is that :outer("sub_name") doesn't provide sufficient specificity to accurately resolve an outer sub.
In particular, given: .namespace ['A'] .sub 'outer' ... .end .namespace ['B'] .sub 'outer' ... .lex '$a', x 'inner'() ... .end .sub 'inner' :outer('outer') $P0 = find_lex '$a' .end Parrot incorrectly uses A::outer as the :outer sub of B::inner. In fact, the :outer flag seems to always use the first sub that it finds having a matching name. At the bottom of this message I've added a fuller description and demonstration of the problem. Jonathan and I discussed this briefly on #parrot. Constraining :outer to subs only in the same namespace isn't a sufficient solution for at least two reasons: 1. Some languages (incl Perl 6) allow inner classes and inner namespaces that can access items in (outer) lexical scopes. 2. Some subs can be :multi -- i.e., multiple subs (each of which may be an independent outer scope) may be referenced by the same global symbol name. The best solution I've come up with thus far is to allow every sub to have a :lexid("...") attribute that uniquely identifies the sub. The :outer() flag would then refer to this lexid instead of the sub's name. A sub that doesn't supply a :lexid("...") attribute would use its normal name as its lexid (thus existing simple cases of :outer continue to work). An alternate approach would be go the other way -- have every sub use a unique name, and use an :export("xyz") flag to cause the sub to be placed in the namespace under its common name. The :anon flag would continue to mean "don't make an entry in the namespace", and omitting :export() would continue to use the sub's name as the exported name. (I choose :export here to parallel the proposal in RT#53302 regarding listing methods in namespaces, but any flag name would work for me.) Yet another approach would be to keep things as they are now, but have :outer only refer to the closest (most recent) version of a sub with that name. We still may have to be careful about dealing with :multi subs, though, and it might be possible to craft some HLL code where it's not possible to make this approach work. (A fourth approach, which I have a strong dislike for, is to have PCT always generate a unique name for every sub and then use a :load :init sub to bind them as their common names in the namespace.) Lexical symbol handling in Parrot is rapidly becoming a huge blocker for progress on Rakudo -- there are a number of cases in the test suite that have nested blocks and subs that can't really be implemented in Rakudo due to problems with Parrot's lexicals. Pm -----fuller description----- Here's a longish test program that demonstrates the problem. The key thing to note is that we have two subs named 'bar', albeit in different namespaces. The Foo::inner sub wants Foo::bar to be its :outer lexical scope. $ cat x.pir .sub 'main' :main 'bar'() $P0 = get_hll_global ['Foo'], 'bar' $P0('hello world') .end .sub 'printf' .param string fmt .param pmc args :slurpy $S0 = sprintf fmt, args print $S0 .return () .end .sub 'bar' $P0 = get_global 'bar' $I0 = get_addr $P0 'printf'("in global 'bar' (0x%x)\n", $I0) .end .namespace ['Foo'] .sub 'bar' .param pmc x .lex '$a', x $P0 = get_global 'bar' $I0 = get_addr $P0 'printf'("in Foo::bar (0x%x)\n", $I0) 'inner'() 'printf'("back in Foo::bar (0x%x)\n", $I0) .end .sub 'inner' :outer('bar') $P0 = get_global 'inner' $I0 = get_addr $P0 'printf'("in Foo::inner (0x%x)\n", $I0) $P0 = getinterp $P1 = $P0['outer'] $I1 = get_addr $P1 'printf'("Foo::inner's :outer is 0x%x\n", $I1) $P0 = find_lex '$a' say $P0 .return () .end When the above is run, we can see that Foo::inner incorrectly receives the global 'bar' sub as its outer scope. As a result, it's unable to find the lexical '$a' that was set by Foo::bar . $ ./parrot x.pir in global 'bar' (0x82484b8) in Foo::bar (0x8248508) in Foo::inner (0x82485b4) Foo::inner's :outer is 0x82484b8 Lexical '$a' not found current instr.: 'parrot;Foo;inner' pc 136 (x.pir:48) called from Sub 'parrot;Foo;bar' pc 83 (x.pir:33) called from Sub 'main' pc 18 (x.pir:5) If we change the name of the global 'bar' to something like 'bar2', then Foo::inner correctly attaches Foo::bar as its outer sub and everything works fine: $ ./parrot y.pir in global 'bar2' (0x8248528) in Foo::bar (0x8248578) in Foo::inner (0x8248624) Foo::inner's :outer is 0x8248578 hello world back in Foo::bar (0x8248578) $