Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

memory leak: if($foo++){} and = overloading #6090

Closed
p5pRT opened this issue Nov 21, 2002 · 20 comments
Closed

memory leak: if($foo++){} and = overloading #6090

p5pRT opened this issue Nov 21, 2002 · 20 comments

Comments

@p5pRT
Copy link

@p5pRT p5pRT commented Nov 21, 2002

Migrated from rt.perl.org#18581 (status was 'resolved')

Searchable as RT18581$

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Nov 21, 2002

From khkramer@allafrica.com

From​: kwindla@​allafrica.com

Hi,

There appears to be a scoping problem that certain kinds of operator
overloading can trigger, leading to a memory leak. The bug shows up in
both 5.8.0 and 5.6.1.

The constructions​:

  while ( $foo++ ) {}
  if ( $foo++ ) {}

lead to $foo's reference count being high by one, if $foo is an object
that returns a reference to itself in its copy constructor.

This bug is easily reproduced -- I have attached a test case below. I
have not succeeded in finding a work-around. Using WeakRef, writing XS
code by hand to mortalize the reference, and various attempts to
take/make copies of the object and return those, all run into dead
ends.

Just to clarify, I've carefully read all the information I can find on
operator overloading and the copy constructor (Camel book pages
357-358, perldoc overload), as well as perlguts and perlapi. I believe
that I understand how copy constructor overloading works, and why --
the bug is not (as I see it) in the copy-constructor overloading
itself, but in the interaction between the scoping of while()/if() and
the overload. Other constructions that trigger the copy-constructor,
but that don't have the same form as while(){} and if(){}, work fine
with my test-case, such as​:

  my $copy = $foo; $foo++;

This bug, while admittedly in a very obscure corner of Perl, turns out
to be a big problem if one is writing iterator-style objects that need
to be used inside long-running processes (such as a web
server). That's what I'm doing, and that's how I found this bug.

I apologize for not having a patch. I've gotten lost in the perl 5.8.0
source, and am not having much luck finding my way to the appropriate
sections.

With sincere thanks,
Kwindla


package Toy_Iterator;

use overload
  '""' => sub { $_[0] },
  bool => sub { ! $_[0]->done_p() },
  '++' => \&inc,
  '=' => sub { $_[0] },
;

sub new {
  my ( $class, $arg ) = @​_;
  my $self = {};
  bless $self, $class;
  $self->set ( $arg );
  return $self;
}

sub set { $_[0]->{_i} = $_[1] }
sub get { return $_[0]->{_i} }
sub inc { ++$_[0]->{_i} }
sub done_p { $_[0]->{_i} > 5 }
sub DESTROY { print "Destroying $_[0]\n" }

package main;
use Devel​::Peek;

my $a = Toy_Iterator->new ( 2 );

##
# The while/++ idiom causes there to be a leaked reference to the
# object.
#
while ( $a++ ) {
  print "$a -> " . $a->get() . "\n";
}

##
# On the other hand, it's not a problem with the copy constructor
# *per-se*, because this code does not cause the leak
#
# my $copy = $a;
# $copy++;
# undef $copy;

print "\n"; Dump $a; print "\n";
print "undef'ing \$a...\n";
undef $a;

print "---- script finished (should have already gc'ed) ---\n";


Flags​:
  category=core
  severity=medium


Site configuration information for perl v5.8.0​:

Configured by khkramer at Thu Nov 14 13​:37​:07 EST 2002.

Summary of my perl5 (revision 5.0 version 8 subversion 0) configuration​:
  Platform​:
  osname=linux, osvers=2.4.7-10, archname=i686-linux
  uname='linux khk 2.4.7-10 #1 thu sep 6 16​:46​:36 edt 2001 i686 unknown '
  config_args='-de -Dprefix=/usr -Dotherlibdirs=/allafrica/perllib​:/usr/lib/perl5/site_perl/5.6.1/i386-linux​:/usr/lib/perl5/vendor_perl/5.6.1​:/usr/lib/perl5/vendor_perl/5.6.1/i386-linux​:/usr/lib/perl5​:/usr/lib/perl5/5.6.0'
  hint=recommended, useposix=true, d_sigaction=define
  usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef
  useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
  use64bitint=undef use64bitall=undef uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='cc', ccflags ='-fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
  optimize='-O2',
  cppflags='-fno-strict-aliasing -I/usr/local/include -I/usr/include/gdbm'
  ccversion='', gccversion='2.96 20000731 (Red Hat Linux 7.2 2.96-108.7.2)', gccosandvers=''
  intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
  d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
  ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
  alignbytes=4, prototype=define
  Linker and Libraries​:
  ld='cc', ldflags =' -L/usr/local/lib'
  libpth=/usr/local/lib /lib /usr/lib
  libs=-lnsl -lndbm -lgdbm -ldl -lm -lc -lcrypt -lutil
  perllibs=-lnsl -ldl -lm -lc -lcrypt -lutil
  libc=/lib/libc-2.2.4.so, so=so, useshrplib=false, libperl=libperl.a
  gnulibc_version='2.2.4'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-rdynamic'
  cccdlflags='-fpic', lddlflags='-shared -L/usr/local/lib'

Locally applied patches​:
 


@​INC for perl v5.8.0​:
  /usr/lib/perl5/5.8.0/i686-linux
  /usr/lib/perl5/5.8.0
  /usr/lib/perl5/site_perl/5.8.0/i686-linux
  /usr/lib/perl5/site_perl/5.8.0
  /usr/lib/perl5/site_perl/5.6.1
  /usr/lib/perl5/site_perl/5.6.0
  /usr/lib/perl5/site_perl
  /allafrica/perllib
  /usr/lib/perl5/site_perl/5.6.1/i386-linux
  /usr/lib/perl5/vendor_perl/5.6.1
  /usr/lib/perl5/vendor_perl/5.6.1/i386-linux
  /usr/lib/perl5/5.8.0/i686-linux
  /usr/lib/perl5/5.8.0
  /usr/lib/perl5/5.6.1
  /usr/lib/perl5/5.6.0
  /usr/lib/perl5
  /usr/lib/perl5/5.6.0
  .


Environment for perl v5.8.0​:
  HOME=/u/khkramer
  LANG=en_US
  LANGUAGE (unset)
  LC_COLLATE=POSIX
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=/usr/kerberos/bin​:/usr/local/bin​:/bin​:/usr/bin​:/usr/X11R6/bin​:/sbin​:/usr/sbin​:/usr/local/sbin​:/usr/local/bin​:/u/khkramer/bin​:/allafrica/bin
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Nov 22, 2002

From goldbb2@earthlink.net

khkramer@​allafrica.com (via RT) wrote​:
[snip]

package Toy_Iterator;

use overload
'""' => sub { $_[0] },
bool => sub { ! $_[0]->done_p() },
'++' => \&inc,
'=' => sub { $_[0] },
;

Your copy constructor (the '=' operator) doesn't appear to do anything.

Could this be the source of your problems?

(Try replacing that line with​:
  '=' => sub { ref( $_[0] )->new( $_[0]->get ) },
and perhaps your bug will go away. [Untested].)

--
my $n = 2; print +(split //, 'e,4c3H r ktulrnsJ2tPaeh'
...."\n1oa! er")[map $n = ($n * 24 + 30) % 31, (42) x 26]

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Nov 22, 2002

From kwindla@allafrica.com

Benjamin Goldberg (via RT) writes​:

khkramer@​allafrica.com (via RT) wrote​:
[snip]

package Toy_Iterator;

use overload
'""' => sub { $_[0] },
bool => sub { ! $_[0]->done_p() },
'++' => \&inc,
'=' => sub { $_[0] },
;

Your copy constructor (the '=' operator) doesn't appear to do anything.

Could this be the source of your problems?

(Try replacing that line with​:
'=' => sub { ref( $_[0] )->new( $_[0]->get ) },
and perhaps your bug will go away. [Untested].)

Well, returning oneself in the overloaded copy constructor works
flawlessly in all cases except​:

  while ($foo++) {} and if ($foo++)

In particular​:

  while (++$foo) {} # doesn't trigger copy constructor, and

  my $bar = $foo;
  $foo++; # triggers copy constructor, ref counts stay correct

are fine.

The problem -- as far as I can tell -- is not in the copy constructor
that I've written but in the reference counting logic for the above
two cases.

And I'm specifically trying to show what happens in the case of a copy
constructor that shouldn't "do anything" (so to speak). The code you
suggest would be a possible workaround for lots of situations in which
this bug is encountered. But some objects can't (or shouldn't) be
copied. For example, an object might have lots of state, and a copy
operation would be prohibitively slow. Or an object might have open
network or database handles that, because of the architecture on the
other end of the pipe, can't be closed and re-opened.

My understanding of copy constructor overloading is that an actual
"copy" operation is optional -- an important component of DWIM in many
situations, but not something that the Perl interpreter ever forces
you to do.

Kwin

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 23, 2002

From @hvds

"khkramer@​allafrica.com (via RT)" <perlbug@​perl.org> wrote​:
:There appears to be a scoping problem that certain kinds of operator
:overloading can trigger, leading to a memory leak. The bug shows up in
:both 5.8.0 and 5.6.1.
:
:The constructions​:
:
: while ( $foo++ ) {}
: if ( $foo++ ) {}
:
:lead to $foo's reference count being high by one, if $foo is an object
:that returns a reference to itself in its copy constructor.

There is a bug here, but the while (or if) is a red herring; stripping
down the test case some more gets me to​:

  use overload '++' => sub { $_[0] }, '=' => sub { $_[0] };
  sub DESTROY { print "Destroying $_[0]\n" }
  {
  my $a = bless {}, main;
  my $b = $a++;
  }
  print "---- script finished (should have already gc'ed) ---\n";

.. which shows that the object is destroyed late. If the 'my $b = $a++'
is removed, the object is destroyed as expected at the end of the
block. Devel​::Peek shows that after that line, the refcount of the
referent has jumped to 3 rather than 2 as expected.

It goes something like this​:
- pp_postinc first does the assign, via the overloaded '='. The
  refcount goes to 2 as expected.
- pp_postinc then calls sv_inc, which spots overloading and asks
  amagic_call to do the work
- amagic_call works out what it needs to do, and then spots that
  it should copy (RvDEEPCP) the argument. Since the refcount of the
  referent is now 2, this macro drops the refcount to 1 and calls
  the overloaded '=' once more to do the copy
- we're now recursing into amagic_call, which sets up a scope and
  calls the '=' sub; on return from that sub, as you'd expect, the
  refcount has once more gone up to 2
- .. but just before returning, amagic_call ups the refcount
  again​:
  } else if (method==copy_amg) {
  [...]
  return SvREFCNT_inc(SvRV(res));
  .. which makes 3. I've no idea why it does that, either (but
  removing that SvREFCNT_inc causes lib/overload.t to SEGV).

Now I'm not sure how we handle the refcounting of return values;
as I understand it the refcount should be temporarily increased
(to survive the scope cleanup) but I'm not sure when it should be
decreased again. That I think is what is not happening in this
case.

Hugo

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 24, 2002

From @hvds

Tels <perl_dummy@​bloodgate.com> wrote​:
:"khkramer@​allafrica.com (via RT)" <perlbug@​perl.org> wrote​:
:>lead to $foo's reference count being high by one, if $foo is an object
:>that returns a reference to itself in its copy constructor.
:
:Uh, but isn't a copy constructor supposed to copy something, instead of
:merely returning the same thing? How should that supposed to work with a
:"defect" copy constructor?
:
:*puzzled*

I couldn't see anything in the docs to state or imply that returning the
thing itself should be illegal, and I can see no reason for it to be so.
And if it should be illegal, we should make it an error rather than
silently doing the wrong thing.

Hugo

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 27, 2002

From kwindla@allafrica.com

Hugo van der Sanden (via RT) writes​:

Tels <perl_dummy@​bloodgate.com> wrote​:
:"khkramer@​allafrica.com (via RT)" <perlbug@​perl.org> wrote​:
:>lead to $foo's reference count being high by one, if $foo is an object
:>that returns a reference to itself in its copy constructor.
:
:Uh, but isn't a copy constructor supposed to copy something, instead of
:merely returning the same thing? How should that supposed to work with a
:"defect" copy constructor?
:
:*puzzled*

I couldn't see anything in the docs to state or imply that returning the
thing itself should be illegal, and I can see no reason for it to be so.
And if it should be illegal, we should make it an error rather than
silently doing the wrong thing.

In addition, if we "force" the copy constructor to actually copy
something, a whole raft of common and useful approaches become
tortuous and inefficient. I'm thinking particularly of

  while ( $foo++ ) {} # and
  if ( $foo++ ) {}

constructions on iterator-like $foos.

Thanks to Hugo for further stripping down the test-case to demonstrate
that the problem isn't while- and if-centric (and mea culpa for not
doing so myself), but my problems that led to submitting the bug
report were exactly the above. If I have to actually copy my $foos in
order to use the post-increment operator -- which include lots of
stored state and (usually) a live database handle or two -- I'll have
to stop using overloading.

Kwin

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 27, 2002

From goldbb2@earthlink.net

Kwindla Hultman Kramer wrote​:

Hugo van der Sanden (via RT) writes​:

Tels <perl_dummy@​bloodgate.com> wrote​:
:"khkramer@​allafrica.com (via RT)" <perlbug@​perl.org> wrote​:
:>lead to $foo's reference count being high by one, if $foo is an
:>object that returns a reference to itself in its copy
:>constructor.
:
:Uh, but isn't a copy constructor supposed to copy something,
:instead of merely returning the same thing? How should that
:supposed to work with a "defect" copy constructor?
:
:*puzzled*

I couldn't see anything in the docs to state or imply that
returning the thing itself should be illegal, and I can see no
reason for it to be so.
And if it should be illegal, we should make it an error rather than
silently doing the wrong thing.

In addition, if we "force" the copy constructor to actually copy
something, a whole raft of common and useful approaches become
tortuous and inefficient. I'm thinking particularly of

while ( $foo++ ) {} # and
if ( $foo++ ) {}

constructions on iterator-like $foos.

If your copy-constructor doesn't make a copy, but just returns $self,
and your post-increment operator is similar, then aren't the above
(under the old semantics) exactly the same as​:

  while ( ++$foo ) {} # and
  if ( ++$foo ) {}

?

Thanks to Hugo for further stripping down the test-case to demonstrate
that the problem isn't while- and if-centric (and mea culpa for not
doing so myself), but my problems that led to submitting the bug
report were exactly the above. If I have to actually copy my $foos in
order to use the post-increment operator -- which include lots of
stored state and (usually) a live database handle or two -- I'll have
to stop using overloading.

Or else switch from using the post-increment operator to using the
pre-increment operator.

Hmm.. if we *do* make using a defective copy-constructor into a death,
then might I suggest that the error message be something like​:
  'Can't do $val++ with bad copy-constructor, use ++$val instead.'
... assuming that we can easily determine which operation was called
that needed to call the copy-constructor.

--
$..='(?​:(?{local$^C=$^C|'.(1<<$_).'})|)'for+a..4;
$..='(?{print+substr"\n !,$^C,1 if $^C<26})(?!)';
$.=s'!'haktrsreltanPJ,r coeueh"';BEGIN{${"\cH"}
|=(1<<21)}""=
$.;qw(Just another Perl hacker,\n);

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 27, 2002

From kwindla@allafrica.com

Benjamin Goldberg (via RT) writes​:

If your copy-constructor doesn't make a copy, but just returns $self,
and your post-increment operator is similar, then aren't the above
(under the old semantics) exactly the same as​:

while ( ++$foo ) {} # and
if ( ++$foo ) {}

[ snip ]

Or else switch from using the post-increment operator to using the
pre-increment operator.

You're right that there's no operational difference between using pre-
and post-increment operators in my examples.

But, I stumbled on this bug after adding operator overloading to an
often-used chunk of a library I maintain, and the memory-leak wasn't
immediately obvious. There's now lots of code "in the wild" that uses
the post-increment version. This doesn't have catastrophic effects --
there's some memory leakage and the garbage collector often has
difficulty doing a graceful global destruction (when a process
exits). But no big deal.

The library documentation now explains what's going on -- $foo++ is
deprecated, yadda yadda -- but I can't completely remove the
post-increment stuff without breaking existing code. And it would be
nice not to have to explain (or remember) that ++$foo is allowed but
$foo++ isn't!

Yours,
Kwin

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 29, 2002

From kwindla@allafrica.com

Tels writes​:

I still think that overloading with a copy operator that does not copy is
"broken" code. But more for the reason that it will bite you later on, like​:

    $x = $y;
    $x \+= 1;

Now you also modified $y, which might come quite unsuspected. So,
preferable you could change you copy constructor :))

But that's only one way of looking at what

  $x = $y;

means. You're right, that often the '=' statement serves to put a
"distinct" copy of the value of $y into the variable $x. But in the
heavily object-oriented code that I spend most of my time writing, the
programmer's intent is usually quite different​: to assign to $x a copy
of the reference that $y holds.

Note that in Perl, refererences are just another kind of value, and
the second "intent" is what Perl *always* does by default if $y holds
any kind of reference. So it seems to me that making copy constructors
that return "self" illegal is actually breaking the language. The
behavior that programmers expect from the core language becomes
impossible to implement for any classes that use (a certain
configuration) of operator overloading.

I think that Perl's wonderfully-flexible reference-munging facilities
are causing some confusion here. I'm maintaining a library of around
15,000 lines of rigidly object-oriented code -- all of the OO features
of which are built transparently and gracefully on top of Perl's basic
"bless this reference" capability. Everything just works -- end-users
(programmers, in this case, since we're talking about a library) never
need to think about how the OO stuff is implemented, they just use
it. But when necessary, I can dig underneath the OO abstraction to do
low-level operations on namespaces, classes, syntax, serialization,
etc. The only place this breaks is in the operator overloading, where
the "copy constructor" feature doesn't let you do what the core
language would do if left to its own devices.

To wind back around to $x and $y, in much of my code

  $y->set ( 42 );
  $x = $y;
  $x++;
 
both $x->get() and $y->get() had *better* return 43 after the above
statements, or programmers are going to be very unhappy. This is a
contrived example, and taken out of context I can see why it looks
funny. But think about iterators, or singletons that control access to
shared resources, or objects that require lots of computation to
initialize but are thereafter shared by lots of bits of a system --
all these cases require that the above behavior be the norm.

Hope this makes my perspective little clearer,
Kwin

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 29, 2002

From @ysth

On Sat, 28 Dec 2002 02​:45​:52 +0100 (CET), perl_dummy@​bloodgate.com wrote​:

   if \($foo\+\+\)
     \{
     \.\.\.
     \}

It should do​:

* check that $foo is true, then increment it.

But for some reason it does​:

* copy $foo, increment the original, then check the copy for beeing true,
then throw away the copy (or something similiar)

Note that there is a needless copy here.

I don't think this can easily change, since internally perl only
differentiates between void, scalar, and list context. There is no
easy way for the postinc code to check if it is called in an actual
scalar context (in which case, the copy is needed) or boolean context
(in which case the copy is not needed).

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 30, 2002

From @rgs

Tels <perl_dummy@​bloodgate.com> wrote​:

I don't think this can easily change, since internally perl only
differentiates between void, scalar, and list context. There is no
easy way for the postinc code to check if it is called in an actual
scalar context (in which case, the copy is needed) or boolean context
(in which case the copy is not needed).

You may be right. How hard would it be to introduce a boolean context?

There is no free bit left in op_flags, but in theory, the optimizer could label
ops that semantically provide a boolean context as 'gimme-bool'. Or at least
I imagine it's possible. (see Want.pm for inspiration.)

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Dec 31, 2002

From @hvds

Rafael Garcia-Suarez <rgarciasuarez@​free.fr> wrote​:
:Tels <perl_dummy@​bloodgate.com> wrote​:
:> >
:> > I don't think this can easily change, since internally perl only
:> > differentiates between void, scalar, and list context. There is no
:> > easy way for the postinc code to check if it is called in an actual
:> > scalar context (in which case, the copy is needed) or boolean context
:> > (in which case the copy is not needed).
:>
:> You may be right. How hard would it be to introduce a boolean context?
:
:There is no free bit left in op_flags, but in theory, the optimizer could label
:ops that semantically provide a boolean context as 'gimme-bool'. Or at least
:I imagine it's possible. (see Want.pm for inspiration.)

I think this would be a rather large change, and I'm not convinced this
edge-case is important enough to warrant it. For this particular case,
you can get avoid the copy with a bit of extra effort​:
  if (!$foo) {
  ++$foo;
  } else {
  ++$foo;
  ...
  }

Hugo

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Apr 3, 2010

@chorny - Status changed from 'open' to 'stalled'

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Apr 29, 2011

@cpansprout - Status changed from 'stalled' to 'open'

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Apr 29, 2011

From @cpansprout

I changed this from ‘stalled’ to ‘open’, because I think
‘stalled’ usually means there is not enough information to reproduce the
bug.

And someone may want to revisit this bug some day.

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jan 16, 2018

From zefram@fysh.org

This was fixed by commit 7dcb9b9 in
Perl 5.13.5. This ticket can be closed.

-zefram

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jan 16, 2018

@tonycoz - Status changed from 'open' to 'resolved'

@p5pRT p5pRT closed this as completed Jan 16, 2018
@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jan 17, 2018

From @jkeenan

On Tue, 16 Jan 2018 21​:32​:58 GMT, zefram@​fysh.org wrote​:

This was fixed by commit 7dcb9b9 in
Perl 5.13.5. This ticket can be closed.

-zefram

But I can hear Father C saying, "What about a test?"

Please review the attached. (I'm not very familiar with 'fresh_perl_is' so I suspect both the test and the commit message could be improved.)

Thank you very much.

--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jan 17, 2018

From @jkeenan

0001-Demonstrate-timely-destruction-of-object.patch
From 9422dbd7fd7a3c2aaa1fcb230f0229d70c203eb2 Mon Sep 17 00:00:00 2001
From: James E Keenan <jkeenan@cpan.org>
Date: Tue, 16 Jan 2018 19:21:53 -0500
Subject: [PATCH] Demonstrate timely destruction of object.

For RT #18581
---
 lib/overload.t | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/lib/overload.t b/lib/overload.t
index 2afa6cf..f4ce103 100644
--- a/lib/overload.t
+++ b/lib/overload.t
@@ -48,7 +48,7 @@ package main;
 
 $| = 1;
 BEGIN { require './test.pl'; require './charset_tools.pl' }
-plan tests => 5338;
+plan tests => 5339;
 
 use Scalar::Util qw(tainted);
 
@@ -3047,3 +3047,25 @@ package RT132385 {
     # ditto with a mutator
     ::is($o .= $r1,     "obj-ref1",             "RT #132385 o.=r1");
 }
+
+{
+    no strict;
+    $msg = '---- script finished (should have already garbage-collected) ---';
+    fresh_perl_is(
+	<<'EOF',
+package RT18581;
+use overload
+    '++' => sub { $_[0] },
+    '='  => sub { $_[0] };
+sub DESTROY { print "Destroying $_[0]\n"; }
+{
+    my $a = bless {}, RT18581;
+    my $b = $a++;
+}
+print "---- script finished (should have already garbage-collected) ---\n";
+EOF
+    $msg,
+    {},
+    'RT 18581: timely destruction',
+    );
+}
-- 
2.7.4

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jan 17, 2018

From zefram@fysh.org

James E Keenan via RT wrote​:

But I can hear Father C saying, "What about a test?"

This time the fix was intentional, and the commit that I cited added
a test.

-zefram

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant