perl5 icon indicating copy to clipboard operation
perl5 copied to clipboard

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

Open p5pRT opened this issue 9 years ago • 11 comments

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

Searchable as RT130328$

p5pRT avatar Dec 12 '16 15:12 p5pRT

From [email protected]

Created by [email protected]

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 avatar Dec 12 '16 15:12 p5pRT

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 avatar Dec 14 '16 04:12 p5pRT

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

p5pRT avatar Dec 14 '16 04:12 p5pRT

From [email protected]

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 avatar Dec 14 '16 14:12 p5pRT

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 avatar Dec 14 '16 17:12 p5pRT

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 avatar Dec 15 '16 12:12 p5pRT

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 avatar Mar 27 '17 17:03 p5pRT

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 avatar Mar 28 '17 00:03 p5pRT

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.

p5pRT avatar Mar 29 '17 16:03 p5pRT

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.

YuriyYev avatar Mar 16 '24 01:03 YuriyYev

On Fri, Mar 15, 2024 at 06:27:19PM -0700, Yuriy Yevtukhov wrote:

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.

I'm not opposed to adding such a warning (or even making in it an error within the scope of a suitable 'use v5.xx'), but I suspect adding such a warning at compile time might be tricky in terms of false positives/negatives.

-- "You're so sadly neglected, and often ignored. A poor second to Belgium, When going abroad." -- Monty Python, "Finland"

iabyn avatar Mar 17 '24 10:03 iabyn