develooper Front page | perl.perl5.porters | Postings from April 2012

Re: [perl #24250] "return" required in some anonymous closures

From:
Nicholas Clark
Date:
April 20, 2012 04:38
Subject:
Re: [perl #24250] "return" required in some anonymous closures
Message ID:
20120420113754.GA39608@plum.flirble.org
The question is at the end. It won't make much sense without the backstory.

On Sun, Oct 19, 2003 at 11:01:42PM -0000, martin@suttles.sca.sfbay.sun.com (via RT) wrote:

> [Please enter your report here]
> Consider the following perl program
> 
> my $count = 0;
> my $x = [sub() { $count },
> 	 sub() { return $count },
> 	 sub($) { $count += $_[0] }];
> $x->[2](3);
> printf "%d %d\n", $x->[0](), $x->[1]();
> 
> This prints
> 0 3
> when it should print
> 3 3
> 
> The two anonymous subs $x->[0] and $x->[1] should
> have exactly the same effect because `return' is
> supposed to be redundant when returning the last
> expression in a sub.

And the behaviour in blead remains is the same

On Fri, Mar 30, 2012 at 10:15:03PM -0700, Father Chrysostomos via RT wrote:
> On Fri Mar 30 18:15:13 2012, jkeenan wrote:

> > Discussion in this ticket petered out nine years ago.
> > 
> > Are there any code or documentation changes we should be considering
> > with respect to the issues raised?
> > 
> > If not, can we close the ticket?
> 
> This is still a bug that needs fixing.  See also
> <https://rt.perl.org/rt3/Ticket/Display.html?id=94490#txn-919044>.

What's curious is that I never remember anyone hitting this problem
again. Perhaps it's not as common as we thought.

On Mon, Oct 20, 2003 at 11:53:03PM +0100, Dave Mitchell wrote:
> On Mon, Oct 20, 2003 at 06:20:13PM +0100, Graham Barr wrote:
> > 
> > On 20 Oct 2003, at 17:48, Dave Mitchell wrote:
> > 
> > >On Mon, Oct 20, 2003 at 04:59:20PM +0100, Graham Barr wrote:
> > >>It is only supposed to generate a constant sub if there are no other
> > >>references to the variable. ie there is no way the variable can 
> > >>change.
> > >
> > >But it's easy to create a reference after the event:
> > >
> > >    sub new_constant_sub {
> > >	my $val = shift;
> > >	my $s = sub() {$val};	# only one ref, so $s a const sub
> > 
> > But that is not when its decided that its a constant sub. Or at least 
> > originally it was not.
> > 
> > When constant subs were first introduced it was in the optimizer that 
> > the decision was taken if a sub was constant, not when the sab was 
> > made. The decision was "does the sub being called have a proto of () 
> > and is its body a single scalar with a refcnt of 1", if yes then inline 
> > it.
> > 
> > I am guessing that logic has been pushed back to when the sub is 
> > defined, which has resulted in this bug
> 
> The 5.6.0 behaviour was that subs (named or anon) were not marked as CONST
> at compile time; instead at every compile-time appearance of a sub call,
> the sub was checked for having a constant value or a single lexical with
> refcount<2, and if so the OP_ENTERSUB was replaced with an OP_CONST.
> 
> This meant that C<use constant> cloned a full heavyweight anon sub for
> each defined constant.
> 
> In 5.8.0, sub f(){1}-style subs were detected at compile time as being
> constant, and if so, replaced with CvCONSTs. sub(){$x}'s were detected
> as being CONST *candidates* at compile time if the refcnt of $x was < 2.
> Then each time they were cloned, the current value of $x was copied to
> make a new CvCONST sub. Due to the vagarites of lexical bugs prior to
> bleedperl, the compile-time test for the lexical refcnt being < 2 was not
> always very DWIM.
> 
> In bleedperl, I changed it to a hybrid behaviour. At compile time, the
> anon CV prototype is marked as a CONST candidate if it contains a single
> outer lexical. At clone time, the cloned CV is converted into a CvCONST if
> the lexical has a refcnt < 2.
> 
> Thus, the refcnt<2 test is done:
> 
> 5.6.0: sometime after cloning, when the sub call is being compiled
> 5.6.0: when the anon sub is compiled
> bleed: when the anon sub is cloned
> 
> Both the 5.8.0 and bleedperl behaviours have problems, and are very
> sensitive to their environments, and will generally do things that people
> find unexpected.


I forgot that you thought of this. I knew that I *implemented* it :-)

On Tue, Oct 21, 2003 at 12:53:12AM +0100, Dave Mitchell wrote:

> An idea occurs to me.
> 
> Ilya introduced a patch that allowed stash values to be an IV or a PV
> rather than a full-blown GV+CV when a sub had only been declared rather
> than defined (IV => no protoptye, PV => protoptye with the PVX containing
> the protoptye string).  Only later when the sub was actually defined was
> the IV/PV replaced with a full GV containing a CV. This allowed Exporter
> to whack lots of sub declarations into the caller's namespace without a
> big memory overhead.
>
> I wonder whether this idea could be extended - so for constant subs where
> the package has no other variable of the same name, then rather than
> having a GV pointing to a const CV, the stash just contains a "something"
> (perhaps a PV with the right flags set) that actually holds the const
> value. This PV would be shared by each packahge that imports it.
> 
> We would have to provide mechanisms
> a) for the module to create all the values in its own namespace when
> compiled;
> b) for Exporter to cheaply copy these to the callers namespace.
> 
> Here endeth the 1am vague handwaving. Time for bed (said Zebedee).

On Tue, Oct 21, 2003 at 09:34:24PM +0100, Dave Mitchell wrote:
> In the medium-term, I want to remove the sub(){$x} feature from Perl,
> and also make constant.pm do it another way. Both are relatively easy to
> do (although in the longer term there may be even cleverer ways of doing
> it in constant.pm that would be harder to do but more efficient).

constant.pm now takes advantage of the "proxy constant subroutines"
on all versions from 5.10.0 onwards. It no longer users the fragile
"feature" under discussion here, that

    *foo::bar = sub () { $baz; }

for an unshared $baz of value 42 behaves the same as

    sub foo::bar () {
        42;
    }

and creates a constant subroutine that the parser knows it can inline.

So I think we've done the first part of that - "another way"

> So the only real question for 5.8.2 is whether we want to add a
> deprecation warning for the feature we added (undocumented) in 5.8.0
> and which we intend to remove in 5.10.0 (conscensus permitting).


So, do we want to remove the inlining of (inferred to be)-constant value
closures? And only inline subroutines that trivially return true constants.
And fix this 5.8.0 regression. What is relying on the regressed behaviour?

In the general case, is it impossible to detect all scenarios where the
variable within the closure can not later be modified?

(Proxy constant subroutines remain unchanged)

Nicholas Clark



nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About