Based on discussion earlier today in #parrotsketch, here's the detailed
specification for resolving outer subs using :lexid . If someone wants
to fold this into pdd20 somehow (on or about line 233), that would
be especially helpful.

--------

Every Parrot subroutine that serves as a lexical outer scope has a 
I<lexid> attribute, which contains a string value that is (should be)
unique within any given compilation unit.  The lexid can be explicitly 
set at compile time by the C<:lexid()> attribute on a subroutine;
if no C<:lexid()> attribute is provided then the subroutine's name 
serves as its lexid.

A subroutine that is to be lexically nested within an outer subroutine
specifies an C<:outer()> attribute referencing the lexid of the outer 
subroutine.  For example, assuming that subroutine C<&a> is to be 
lexically nested inside of C<&foo>, we would have:

    .sub 'foo' :lexid('lex1234')
        ...
    .end

    .sub 'a' :outer('lex1234')
        ...
    .end

If a given subroutine name is known to be unique within the
compilation unit, then the C<:lexid()> attribute can be omitted
from the outer subroutine, and the nested subroutine simply uses
the name of the outer subroutine as its C<:outer> attribute:

     ##  Note:  'foo' must be unique in the compilation unit!
    .sub 'foo'
        ...
    .end

    .sub 'a' :outer('foo')
        ...
    .end

In either case, an outer subroutine must be compiled I<before>
any of its lexically nested subroutines.  The C<:lexid()> attribute
makes it possible to accurately identify a specific outer subroutine
when its name might not be unique (e.g., as is the case with :multi 
subroutines, or identically-named subroutines in different namespaces).
It is up to code generators (human or automated) to make sure that
the lexids of any outer subroutines are unique within a given
compilation unit -- if an C<:outer()> attribute specifies a
lexid that is shared by multiple subroutines in a compilation unit,
an exception is thrown.

--------

Some alternatives in the above that we may want to consider:

1.  If we want to save the cost of checking for an :outer
attribute referencing a lexid used by multiple subs, we could
just say that Parrot's behavior in such a situation is undefined 
(as exists now).  In fact, I had it this way in my first drafts of
the above, but I've since convinced myself that checking for the
condition and throwing the exception is far better (and worth the
minor additional compile-time cost involved).  Indeed, it's
the lack of such checking that cost me hours of investigation
in the first place. ("Why aren't my lexicals working?  <six hours
of gdb inspection and tracing>  Oh!  It's because some other 
identically named but largely unrelated sub in a totally different
namespace is stealing the outer scope!")

2.  We could require that lexids be globally unique, as opposed to
being unique only within a given compilation unit.  Having such
a restriction in place might facilitate dynamic compilation of
subroutines that are lexically nested within already compiled
subroutines, such as for things like C<eval>.  But doing so would
also make lexid generation and mapping quite a bit more complicated,
and we have other (probably better) mechanisms for dealing with 
C<eval>, so I much prefer to go with the simpler unique-within-
compilation-unit approach for now.

Comments on the above welcomed.  Thanks,

Pm

Reply via email to