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" variable appears to inadvertently act as "state" variable #15764

Open
p5pRT opened this issue Dec 12, 2016 · 11 comments
Open

"my" variable appears to inadvertently act as "state" variable #15764

p5pRT opened this issue Dec 12, 2016 · 11 comments

Comments

@p5pRT
Copy link

p5pRT commented Dec 12, 2016

Migrated from rt.perl.org#130328 (status was 'open')

Searchable as RT130328$

@p5pRT
Copy link
Author

p5pRT commented Dec 12, 2016

From dcmertens.perl@gmail.com

Created by dcmertens.perl@gmail.com

For this script​:

  for (1 .. 6) {
  my $thing = 'foo' if /3/;
  $thing .= ' bar';
  print "$_) thing is $thing\n";
  }
  print "globally-scoped thing is $main​::thing\n";

I would expect $thing to start out each time through the loop as
undefined. Only when $_ is 3 would it be assigned 'foo'. Since
$thing is a lexically-scoped variable, $main​::thing should be
untouched. As such, I think the output should be

  1) thing is bar
  2) thing is bar
  3) thing is foo bar
  4) thing is bar
  5) thing is bar
  6) thing is bar
  globally-scoped thing is

However, I get

  1) thing is bar
  2) thing is bar bar
  3) thing is foo bar
  4) thing is bar
  5) thing is bar bar
  6) thing is bar bar bar
  globally-scoped thing is

Somehow, $thing accumulates data with each iteration of the loop, but
the data is not being stored in the globally scoped $main​::thing. The
problem appears to arise when the declaration "my $thing" is
post-conditionalized with an "if" that evaluates to false. If this is
somehow resolving to a global variable $thing, I would have expected
$main​::thing to contain stuff when we're done, but it does not.

Perl Info

Flags:
    category=core
    severity=medium

Site configuration information for perl 5.20.3:

Configured by dcmertens-perl at Sat May 21 05:52:24 EDT 2016.

Summary of my perl5 (revision 5 version 20 subversion 3) configuration:

  Platform:
    osname=linux, osvers=4.4.0-22-generic,
archname=x86_64-linux-thread-multi
    uname='linux dcmertens-latitude-e6410 4.4.0-22-generic #40-ubuntu smp
thu may 12 22:03:46 utc 2016 x86_64 x86_64 x86_64 gnulinux '
    config_args='-de
-Dprefix=/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug
-Dusemultiplicity -Dusethreads -DDEBUGGING
-Aeval:scriptdir=/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug/bin'
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=define, usemultiplicity=define
    use64bitint=define, use64bitall=define, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -fwrapv -DDEBUGGING
-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include
-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-O2 -g',
    cppflags='-D_REENTRANT -D_GNU_SOURCE -fwrapv -DDEBUGGING
-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
    ccversion='', gccversion='5.3.1 20160413', gccosandvers=''
    intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
    ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t',
lseeksize=8
    alignbytes=8, prototype=define
  Linker and Libraries:
    ld='cc', ldflags =' -fstack-protector -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
/usr/include/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib/../lib
/usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib
    libs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    libc=libc-2.23.so, so=so, useshrplib=false, libperl=libperl.a
    gnulibc_version='2.23'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
    cccdlflags='-fPIC', lddlflags='-shared -O2 -g -L/usr/local/lib
-fstack-protector'

Locally applied patches:
    Devel::PatchPerl 1.38


@INC for perl 5.20.3:

/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug/lib/site_perl/5.20.3/x86_64-linux-thread-multi

/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug/lib/site_perl/5.20.3

/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug/lib/5.20.3/x86_64-linux-thread-multi

/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug/lib/5.20.3
    .


Environment for perl 5.20.3:
    HOME=/home/dcmertens-perl
    LANG=en_US.UTF-8
    LANGUAGE=en_US
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)

PATH=/home/dcmertens-perl/perl5/perlbrew/bin:/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
    PERLBREW_BASHRC_VERSION=0.74
    PERLBREW_HOME=/home/dcmertens-perl/.perlbrew

PERLBREW_MANPATH=/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug/man

PERLBREW_PATH=/home/dcmertens-perl/perl5/perlbrew/bin:/home/dcmertens-perl/perl5/perlbrew/perls/perl-5.20.3-thread-multi-debug/bin
    PERLBREW_PERL=perl-5.20.3-thread-multi-debug
    PERLBREW_ROOT=/home/dcmertens-perl/perl5/perlbrew
    PERLBREW_VERSION=0.74
    PERL_BADLANG (unset)
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2016

From @tonycoz

On Mon, 12 Dec 2016 07​:23​:24 -0800, run4flat wrote​:

For this script​:

for (1 .. 6) {
my $thing = 'foo' if /3/;
$thing .= ' bar';
print "$_) thing is $thing\n";
}
print "globally-scoped thing is $main​::thing\n";

I would expect $thing to start out each time through the loop as
undefined. Only when $_ is 3 would it be assigned 'foo'. Since
$thing is a lexically-scoped variable, $main​::thing should be
untouched. As such, I think the output should be

This (mis-)behaviour is well known and documented to be changable,
from perlsyn​:

  B<NOTE​:> The behaviour of a C<my>, C<state>, or
  C<our> modified with a statement modifier conditional
  or loop construct (for example, C<my $x if ...>) is
  B<undefined>. The value of the C<my> variable may be C<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.

It's retained for compatibility with older code which used this to
emulate state variables​:

  my $x if 0;

but this specific usage now generates a warning.

  $ ./perl -e 'my $x if 0'
  Deprecated use of my() in false conditional at -e line 1.

Perhaps the more general case should also produce a warning.

Tony

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2016

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2016

From dcmertens.perl@gmail.com

I came upon this behavior completely unexpectedly. Would it be feasible to
warn for *any* post-conditional on "my"? I suspect that the unpredictable
value for a false post-conditional if-statement is almost never wanted by
the programmer, except for the state emulation that you mention.

David

On Tue, Dec 13, 2016 at 11​:41 PM, Tony Cook via RT <
perlbug-followup@​perl.org> wrote​:

On Mon, 12 Dec 2016 07​:23​:24 -0800, run4flat wrote​:

For this script​:

for (1 .. 6) {
my $thing = 'foo' if /3/;
$thing .= ' bar';
print "$_) thing is $thing\n";
}
print "globally-scoped thing is $main​::thing\n";

I would expect $thing to start out each time through the loop as
undefined. Only when $_ is 3 would it be assigned 'foo'. Since
$thing is a lexically-scoped variable, $main​::thing should be
untouched. As such, I think the output should be

This (mis-)behaviour is well known and documented to be changable,
from perlsyn​:

B<NOTE​:> The behaviour of a C<my>, C<state>, or
C<our> modified with a statement modifier conditional
or loop construct (for example, C<my $x if ...>) is
B<undefined>. The value of the C<my> variable may be C<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.

It's retained for compatibility with older code which used this to
emulate state variables​:

my $x if 0;

but this specific usage now generates a warning.

$ ./perl -e 'my $x if 0'
Deprecated use of my() in false conditional at -e line 1.

Perhaps the more general case should also produce a warning.

Tony

---
via perlbug​: queue​: perl5 status​: new
https://rt-archive.perl.org/perl5/Ticket/Display.html?id=130328

--
"Debugging is twice as hard as writing the code in the first place.
  Therefore, if you write the code as cleverly as possible, you are,
  by definition, not smart enough to debug it." -- Brian Kernighan

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2016

From @wolfsage

On Wed, Dec 14, 2016 at 9​:08 AM, David Mertens <dcmertens.perl@​gmail.com> wrote​:

I came upon this behavior completely unexpectedly. Would it be feasible to
warn for *any* post-conditional on "my"? I suspect that the unpredictable
value for a false post-conditional if-statement is almost never wanted by
the programmer, except for the state emulation that you mention.

This is something I have been working on (slowly). It would go faster
if I just said "for now we will warn for the easy-to-detect general
case" -- maybe that's enough?

But otherwise you can bury variable declarations deep in the op tree
of a single statement and detecting them right gets a little tricky.

I think I've figured out what needs doing but I don't see having it in
time for 5.26 (unless we pick the easy route for now).

-- Matthew Horsfall (alh)

@p5pRT
Copy link
Author

p5pRT commented Dec 15, 2016

From @xsawyerx

On 12/14/2016 06​:05 PM, Matthew Horsfall (alh) wrote​:

On Wed, Dec 14, 2016 at 9​:08 AM, David Mertens <dcmertens.perl@​gmail.com> wrote​:

I came upon this behavior completely unexpectedly. Would it be feasible to
warn for *any* post-conditional on "my"? I suspect that the unpredictable
value for a false post-conditional if-statement is almost never wanted by
the programmer, except for the state emulation that you mention.
This is something I have been working on (slowly). It would go faster
if I just said "for now we will warn for the easy-to-detect general
case" -- maybe that's enough?

But otherwise you can bury variable declarations deep in the op tree
of a single statement and detecting them right gets a little tricky.

I think I've figured out what needs doing but I don't see having it in
time for 5.26 (unless we pick the easy route for now).

Iterations FTW?

Seriously though, nothing wrong with a start that catches some, if not all.

@p5pRT
Copy link
Author

p5pRT commented Mar 27, 2017

From @iabyn

On Thu, Dec 15, 2016 at 01​:21​:52PM +0100, Sawyer X wrote​:

On 12/14/2016 06​:05 PM, Matthew Horsfall (alh) wrote​:

On Wed, Dec 14, 2016 at 9​:08 AM, David Mertens <dcmertens.perl@​gmail.com> wrote​:

I came upon this behavior completely unexpectedly. Would it be feasible to
warn for *any* post-conditional on "my"? I suspect that the unpredictable
value for a false post-conditional if-statement is almost never wanted by
the programmer, except for the state emulation that you mention.
This is something I have been working on (slowly). It would go faster
if I just said "for now we will warn for the easy-to-detect general
case" -- maybe that's enough?

But otherwise you can bury variable declarations deep in the op tree
of a single statement and detecting them right gets a little tricky.

I think I've figured out what needs doing but I don't see having it in
time for 5.26 (unless we pick the easy route for now).

Iterations FTW?

Seriously though, nothing wrong with a start that catches some, if not all.

There's some history to 'my $x if ...' deprecations.

Back in 2004, when I were a lad, there was this thread​:

  http​://nntp.perl.org/group/perl.perl5.porters/88428
  Subject​: Thoughts about my $x = $foo if $bar

which led to these commits of mine​:

  perl-5.8.0-3308-g76df5e8 remove C<my $x if foo> construct from
  core modules
  perl-5.8.0-3313-gdc9aa44 add deprecation warning for C<my $x if
  foo> and C<foo && (my $x)>
  perl-5.8.0-3316-gedd7382 Add tests for the C<my $x if foo>
  deprecation, and change the warning text

Then there was a later thread,

  http​://nntp.perl.org/group/perl.perl5.porters/89114
  Subject​: warning for my $x if 0

which decided that the general deprecation was wrong (and the
implementation was wrong too​: a simple 'my $x if 0' didn't warn),
so the code from the earlier thread was reverted, and I added
these instead​:

  perl-5.8.0-3389-g722969e retract 22328 and 22332​: deprecation
  warning for my $x if foo
  perl-5.8.0-3391-g7921d0f add deprecation warning for my $x if 0

I can't find any discussion as to why the first set of patches was such a
bad idea, but in the first thread, someone did suggest a valid use case​:

  $cond = ...;
  my $only_needed_for_cond = foo() if $cond;
  ....
  if ($cond) {
  ... do something with $only_needed_for_cond ...;
  }

Also, we may need to distinguish between

  my $x if $cond;
  my $x = ... if $cond;

We may want to deprecate one but not the other.

--
In economics, the exam questions are the same every year.
They just change the answers.

@p5pRT
Copy link
Author

p5pRT commented Mar 28, 2017

From @iabyn

On Mon, Mar 27, 2017 at 06​:17​:46PM +0100, Dave Mitchell wrote​:

I can't find any discussion as to why the first set of patches was such a
bad idea

I've now found the thread​:

  http​://nntp.perl.org/group/perl.perl5.porters/88928
  Subject​: CPAN versus bleadperl

Apparently it broke everything.

--
The optimist believes that he lives in the best of all possible worlds.
As does the pessimist.

@p5pRT
Copy link
Author

p5pRT commented Mar 29, 2017

From @iabyn

On Tue, Mar 28, 2017 at 01​:03​:02AM +0100, Dave Mitchell wrote​:

On Mon, Mar 27, 2017 at 06​:17​:46PM +0100, Dave Mitchell wrote​:

I can't find any discussion as to why the first set of patches was such a
bad idea

I've now found the thread​:

http&#8203;://nntp\.perl\.org/group/perl\.perl5\.porters/88928
    Subject&#8203;: CPAN versus bleadperl

Apparently it broke everything.

Also see
  #122567​: [PATCH] Warn when a conditional my() is used with an assignment
from 2014

--
Never work with children, animals, or actors.

@YuriyYev
Copy link

Hi! I know about weird and hard-to-fix Perl behavior with conditional 'my' and similar declarants. But there is at least one more situation when 'my' variable turns into 'state' variable, and without condition for 'my'. I tried to search for a bug report, but failed. This situation is actual for version 5.38 (not tested over current 5.39). It's GOTO which bypasses 'my' declaration. I know that C-compilers always warn or even error when "goto bypasses initialization of local variable", Perl does not. At least with default settings.
Sample code:

use strict;
sub test {
my $cond = shift;
my $iter = shift;
if(!$cond) {
goto ENDIT;
}
my $data;
$data='true'.$iter;
ENDIT:
if(!$data) { $data='false'.$iter; }
print "$iter: $data\n";
}
test(1,1);
test(0,2);
test(0,3);
test(1,4);
test(0,5);
test(0,6);

Outputs:
1: true1
2: false2
3: false2
4: true4
5: false5
6: false5

Expected: some warning or error about bypassing lexical var declaration

So, everytime when $data is assigned inside "if(!$data)" after bypassing "my $data", it becomes a 'state' variable until "my $data" is reached during some next iteration. Situation is completely the same as "my in false condition" and has the same cause, as I understand, but is somewhat more difficult to be found. And, I guess, not documented.
I know all the rules like 'do not use goto....blah blah', there are some in documentation in particular, but this specific situation of using GOTO in a "C-style" within single function... would be great to have a warning or even error if lexical var is used after its declaration/initialization was bypassed by GOTO.

@iabyn
Copy link
Contributor

iabyn commented Mar 17, 2024 via email

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

6 participants