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

"my" re-initialization failure #5321

Closed
p5pRT opened this issue Apr 11, 2002 · 6 comments
Closed

"my" re-initialization failure #5321

p5pRT opened this issue Apr 11, 2002 · 6 comments

Comments

@p5pRT
Copy link

p5pRT commented Apr 11, 2002

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

Searchable as RT8930$

@p5pRT
Copy link
Author

p5pRT commented Apr 11, 2002

From ken@nsds.com

This is a bug report for perl from ken@​nsds.com,
generated with the help of perlbug 1.33 running under perl v5.6.1.


I have found that "my" does not re-initialize its variable to undef as
described in the perlsub documentation​:

  The parameter list to my() may be assigned to if desired,
  which allows you to initialize your variables. (If no
  initializer is given for a particular variable, it is cre­
  ated with the undefined value.)

This behavior only occurs under certain circumstances. Specifically,
I can repeat the bug with the following conditions​:

0. The "my" declaration appears in a loop or subroutine.

1. The "my" declaration and initialization is followed by a
  conditional modifier which evaluates to false, for example​:

  my $value = "$initial_value\n" if defined $initial_value;

2. The declared value is "auto-initialized" later in the loop or
  subroutine, for example​:

  $value .= "more text\n";

Here is a short construct to illustrate the bug​:

my $init = undef;
for ( my $i=0; $i<2; $i++ ) {
  my $value = $init if defined $init;
  ++$value;
  if ( not defined $init and $value > 1 ) {
  print "value = $value but should be 1\n";
  }
  else {
  print "value = $value as expected\n";
  }
}

Note that the initializer, $init, is undefined. In the first
iteration of the loop, the value is presumably initialized to undef
and the increment operator effectively re-initializes the value to
zero before incrementing it. In the second iteration of the loop, the
value apparently is NOT re-initialized to undef, because the
incremented value is incorrect (yielding 2 instead of 1).

To work around the bug, I have found that separating the conditional
initialization of the variable from the "my" declaration will suffice​:

  my $value;
  $value = $init if defined $init;

Below is a script that illustrates the bug in other circumstances.

#!/usr/bin/perl -w

################################################################################
# Script to illustrate bug with "my" initialization. When "my" is
# used to declare a variable in a loop or subroutine, and that
# declaration is followed by an "if statement", and the "if statement"
# is false, and the variable is "auto-initialized" later in the loop
# or subroutine (via concatenation, push, addition, hash assignment,
# etc.), then in the second iteration of the loop or on the second
# call to the subroutine the old value of the variable is still used.
################################################################################

use strict;

# Illustrate bug using increment.
my $init = undef;
for ( my $i=0; $i<2; $i++ ) {
  my $value = $init if defined $init;
  ++$value;
  if ( not defined $init and $value > 1 ) {
  print "value = $value but should be 1\n";
  }
  else {
  print "value = $value as expected\n";
  }
}

my $counter = 0;

# Illustrate bug using concatenation.
print "\n";
my $content1 = &content( undef );
print "content one​: '$content1'\n";
my $content2 = &content( undef );
print "content two​: '$content2'\n";

# Illustrate bug using push.
print "\n";
my @​list1 = &list( );
print "list one​: ( " . join( ', ', @​list1 ) . " )\n";
my @​list2 = &list();
print "list two​: ( " . join( ', ', @​list2 ) . " )\n";

# Illustrate bug using hash assignment.
print "\n";
my %hash1 = &hash();
my @​hash1;
while ( my( $key, $value ) = each %hash1 ) {
  push( @​hash1, "$key => $value" );
}
print "hash one​: ( " . join( ', ', @​hash1 ) . " )\n";
my %hash2 = &hash();
my @​hash2;
while ( my( $key, $value ) = each %hash2 ) {
  push( @​hash2, "$key => $value" );
}
print "hash two​: ( " . join( ', ', @​hash2 ) . " )\n";

# Illustrate bug using addition.
print "\n";
my $plus1 = &plus();
print "plus one​: $plus1\n";
my $plus2 = &plus( undef );
print "plus two​: $plus2\n";

################################################################################
sub content {
  my $init = shift;
  ++$counter;
  my $content = "$init\n" if defined $init;
  if ( not defined $init and defined $content ) {
  print "initial content is '$content' but should be undef\n";
  }
  elsif ( defined $content ) {
  print "initial content is '$content' as expected\n";
  }
  else {
  print "initial content is undef as expected\n";
  }
  $content .= $counter;
  $content;
}

################################################################################
sub list {
  my @​init = @​_;
  ++$counter;
  my @​list = @​init if @​init;
  if ( @​list and not @​init ) {
  print "initial list is ( " . join( ', ', @​list ) . " ) but should be ()\n";
  }
  else {
  print "initial list is ( " . join( ', ', @​list ) . " ) as expected\n";
  }
  push( @​list, $counter );
  @​list;
}

################################################################################
sub hash {
  my %init = @​_;
  ++$counter;
  my %hash = %init if %init;
  my @​hash;
  while ( my( $key, $value ) = each %hash ) {
  push( @​hash, "$key => $value" );
  }
  if ( @​hash and not %init ) {
  print "initial hash is ( " . join( ', ', @​hash ) . " ) but should be ()\n";
  }
  else {
  print "initial hash is ( " . join( ', ', @​hash ) . " ) as expected\n";
  }
  $hash{$counter} = $counter;
  %hash;
}

################################################################################
sub plus {
  my $init = shift;
  ++$counter;
  my $plus = $init if defined $init;
  if ( not defined $init and defined $plus ) {
  print "initial plus is '$plus' but should be undef\n";
  }
  elsif ( defined $plus ) {
  print "initial plus is '$plus' as expected\n";
  }
  else {
  print "initial plus is undef as expected\n";
  }
  $plus += $counter;
  $plus;
}

################################################################################
# END OF SCRIPT
################################################################################



Flags​:
  category=core
  severity=medium


Site configuration information for perl v5.6.1​:

Configured by bod at Fri Jan 11 04​:14​:18 EST 2002.

Summary of my perl5 (revision 5.0 version 6 subversion 1) configuration​:
  Platform​:
  osname=linux, osvers=2.4.13, archname=i386-linux
  uname='linux duende 2.4.13 #1 wed oct 31 19​:18​:07 est 2001 i686 unknown '
  config_args='-Dccflags=-DDEBIAN -Dcccdlflags=-fPIC -Darchname=i386-linux -Dprefix=/usr -Dprivlib=/usr/share/perl/5.6.1 -Darchlib=/usr/lib/perl/5.6.1 -Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5 -Dvendorarch=/usr/lib/perl5 -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl/5.6.1 -Dsitearch=/usr/local/lib/perl/5.6.1 -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 -Dman1ext=1 -Dman3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Uusesfio -Duseshrplib -Dlibperl=libperl.so.5.6.1 -Dd_dosuid -des'
  hint=recommended, useposix=true, d_sigaction=define
  usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef
  useperlio=undef d_sfio=undef uselargefiles=define usesocks=undef
  use64bitint=undef use64bitall=undef uselongdouble=undef
  Compiler​:
  cc='cc', ccflags ='-DDEBIAN -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
  optimize='-O2',
  cppflags='-DDEBIAN -fno-strict-aliasing -I/usr/local/include'
  ccversion='', gccversion='2.95.4 (Debian prerelease)', 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, usemymalloc=n, prototype=define
  Linker and Libraries​:
  ld='cc', ldflags =' -L/usr/local/lib'
  libpth=/usr/local/lib /lib /usr/lib
  libs=-lgdbm -ldb -ldl -lm -lc -lcrypt
  perllibs=-ldl -lm -lc -lcrypt
  libc=/lib/libc-2.2.4.so, so=so, useshrplib=true, libperl=libperl.so.5.6.1
  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.6.1​:
  /usr/local/lib/perl/5.6.1
  /usr/local/share/perl/5.6.1
  /usr/lib/perl5
  /usr/share/perl5
  /usr/lib/perl/5.6.1
  /usr/share/perl/5.6.1
  /usr/local/lib/site_perl
  .


Environment for perl v5.6.1​:
  HOME=/home/ken
  LANG=C
  LANGUAGE (unset)
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=~/bin​:/usr/local/bin​:/usr/bin​:/bin​:/usr/bin/X11​:/usr/games
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Apr 11, 2002

From @schwern

On Thu, Apr 11, 2002 at 05​:09​:23PM -0700, Ken Neighbors wrote​:

This is a bug report for perl from ken@​nsds.com,
generated with the help of perlbug 1.33 running under perl v5.6.1.

-----------------------------------------------------------------
I have found that "my" does not re-initialize its variable to undef as
described in the perlsub documentation​:

I think this is just a case of the surprising​:

  my $foo if $bar;

feature.

Here's another way to think of it...

  $foo = 42;
  use This​::Module if $foo;

won't do what you mean for similar reasons. The module will be loaded
no matter what the value of $foo. The "use" happens at compile time
but the assignment to $foo happens after, at runtime.

Perhaps more illustrative​:

  my $foo = 42;
  BEGIN { print "Something" if $foo == 42 }

A simple fix would be... "don't do that". (This is an old and gnarled
issue).

--

Michael G. Schwern <schwern@​pobox.com> http​://www.pobox.com/~schwern/
Perl Quality Assurance <perl-qa@​perl.org> Kwalitee Is Job One
Not king today, either

@p5pRT
Copy link
Author

p5pRT commented Apr 11, 2002

From @schwern

On Thu, Apr 11, 2002 at 07​:01​:14PM -0700, Ken Neighbors wrote​:

Yes, my fix for this problem is "don't do that" anymore. However, I
remain doubtful that this is a compile-time effect because the
*initialization* of the variable is supposed to happen at run-time
according to the Perl documentation. Here is the relevant excerpt
from the perlsub man page​:

   A "my" has both a compile\-time and a run\-time effect\.  At
   compile time\, the compiler takes notice of it\.  The prin­
   cipal usefulness of this is to quiet "use strict 'vars'"\,
   but it is also essential for generation of closures as
   detailed in the perlref manpage\.  Actual initialization is
   delayed until run time\, though\, so it gets executed at the
   appropriate time\, such as each time through a loop\, for
   example\.

Yes, but the "if defined $init" supresses the run-time effect, just
like any other statement.

  for (1..10) {
  my $foo;
  ...
  }

means​:

  At compile-time, declare $foo.
  At run-time, each iteration through the loop, undef $foo.

so this​:

  for (1..10) {
  my $foo if $bar;
  ...
  }

means​:

  At compile-time, declare $foo
  At run-time, each iteration through the loop, undef $foo only if
  $bar is true.

it's just as if you'd basically said​:

  {
  my $foo;
  for (1..10) {
  $foo = undef if $bar;
  }
  }

Perl is acting as expected. It's not a bug, just a misfeature.

Because there's so much confusion around this construct, 5.8.0's
perlsub man page adds​:

  NOTE​: The behaviour of a "my" statement modified with a
  statement modifier conditional or loop construct (e.g. "my
  $x if ...") is undefined. The value of the "my" variable
  may be "undef", any previously assigned value, or possibly
  anything else. Don't rely on it. Future versions of perl
  might do something different from the version of perl you
  try it out on. Here be dragons.

so the behavior of "my $foo if $bar" is officially undefined.

--

Michael G. Schwern <schwern@​pobox.com> http​://www.pobox.com/~schwern/
Perl Quality Assurance <perl-qa@​perl.org> Kwalitee Is Job One
I represent GOD you fuck!
  http​://www.unamerican.com/

@p5pRT
Copy link
Author

p5pRT commented Apr 12, 2002

From [Unknown Contact. See original ticket]

Michael,

Yes, my fix for this problem is "don't do that" anymore. However, I
remain doubtful that this is a compile-time effect because the
*initialization* of the variable is supposed to happen at run-time
according to the Perl documentation. Here is the relevant excerpt
from the perlsub man page​:

  A "my" has both a compile-time and a run-time effect. At
  compile time, the compiler takes notice of it. The prin­
  cipal usefulness of this is to quiet "use strict 'vars'",
  but it is also essential for generation of closures as
  detailed in the perlref manpage. Actual initialization is
  delayed until run time, though, so it gets executed at the
  appropriate time, such as each time through a loop, for
  example.

As you can see, it says "Actual initialization is delayed until run
time". It also basically says "initialization ... gets executed
.... each time through a loop". But this is not happening for these
cases. The variable is only initialized the *first* time through the
loop. I wonder if closures are getting in the way, or perhaps they
are completely unrelated to the problem.

Note also that the problem only occurs when the variable is
re-initialized using concatenation or something similar. For example,
if you simply comment out the "++$value" statement in the following
code, the problem goes away!

my $init = undef;
for ( my $i=0; $i<2; $i++ ) {
  my $value = $init if defined $init;
  #++$value; # uncomment this line to see problem
  if ( not defined $value ) {
  print "value = undef as expected\n";
  }
  elsif ( not defined $init and $value > 1 ) {
  print "value = $value but should be 1\n";
  }
  else {
  print "value = $value as expected\n";
  }
}

Please run the above snippet and you'll see that the value is
undefined as it should be in both iterations of the loop, but as soon
as you uncomment that "++$value" statement the problem will occur.
This behavior is another reason I doubt it is a compile-time issue.
How can the existence of an increment statement affect a "my"
initialization at compile-time?

Anyway, I have now gone through much of my code and tried to find all
occurrences of where I said something like the following​:

  my $value = $init if defined $init;

In any event, I think that either this problem should be fixed or a
warning placed in the perlsub documentation for the benefit of other
perl coders who might get bit by this same bug--it is very obscure.

BTW, thanks for the quick response!

Ken

Michael G Schwern writes​:

On Thu, Apr 11, 2002 at 05​:09​:23PM -0700, Ken Neighbors wrote​:

This is a bug report for perl from ken@​nsds.com,
generated with the help of perlbug 1.33 running under perl v5.6.1.

-----------------------------------------------------------------
I have found that "my" does not re-initialize its variable to undef as
described in the perlsub documentation​:

I think this is just a case of the surprising​:

my $foo if $bar;

feature.

Here's another way to think of it...

$foo = 42;
use This&#8203;::Module if $foo;

won't do what you mean for similar reasons. The module will be loaded
no matter what the value of $foo. The "use" happens at compile time
but the assignment to $foo happens after, at runtime.

Perhaps more illustrative​:

my $foo = 42;
BEGIN \{ print "Something" if $foo == 42 \}

A simple fix would be... "don't do that". (This is an old and gnarled
issue).

--

Michael G. Schwern <schwern@​pobox.com> http​://www.pobox.com/~schwern/
Perl Quality Assurance <perl-qa@​perl.org> Kwalitee Is Job One
Not king today, either

@p5pRT
Copy link
Author

p5pRT commented Apr 12, 2002

From [Unknown Contact. See original ticket]

Michael G Schwern writes​:

Perl is acting as expected. It's not a bug, just a misfeature.

Thank you for the extended explanation and the excerpt from the new
docs. I now understand what is going on. I appreciate your patience
:-)

Ken

@p5pRT
Copy link
Author

p5pRT commented Jul 13, 2005

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

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

No branches or pull requests

2 participants