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

third part of C-style for has messed up scoping #15161

Open
p5pRT opened this issue Feb 5, 2016 · 7 comments
Open

third part of C-style for has messed up scoping #15161

p5pRT opened this issue Feb 5, 2016 · 7 comments

Comments

@p5pRT
Copy link

p5pRT commented Feb 5, 2016

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

Searchable as RT127463$

@p5pRT
Copy link
Author

p5pRT commented Feb 5, 2016

From @mauke

Created by @mauke

$ perl -we 'for (my $i = 1; $i < 10; $i += my $k) { $k = $i; print "$i\n"; }'
1
2
4
8

I'm not sure what's going on here, but 'my $k' should probably not do that (it
should always evaluate to undef). In fact, I would have expected the third part
of a C-style for to have its own little scope, like

  for (my $i = 1; $i < 10; do { $i += my $k })

But for some reason $k leaks through to the loop body, where it can be set
before officially being created by 'my'. This is probably a variant of the 'my
$x if 0' problem.

Perl Info

Flags:
    category=core
    severity=low

Site configuration information for perl 5.22.1:

Configured by mauke at Tue Dec 29 15:36:05 CET 2015.

Summary of my perl5 (revision 5 version 22 subversion 1) configuration:
   
  Platform:
    osname=linux, osvers=4.2.5-1-arch, archname=i686-linux
    uname='linux simplicio 4.2.5-1-arch #1 smp preempt tue oct 27 08:28:41 cet 2015 i686 gnulinux '
    config_args=''
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=undef, usemultiplicity=undef
    use64bitint=undef, use64bitall=undef, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cc', ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-O2',
    cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
    ccversion='', gccversion='5.3.0', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234, doublekind=3
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12, longdblkind=3
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=4, prototype=define
  Linker and Libraries:
    ld='cc', ldflags =' -fstack-protector-strong -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib/gcc/i686-pc-linux-gnu/5.3.0/include-fixed /usr/lib /lib
    libs=-lpthread -lnsl -lnm -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
    perllibs=-lpthread -lnsl -lnm -ldl -lm -lcrypt -lutil -lc
    libc=libc-2.22.so, so=so, useshrplib=false, libperl=libperl.a
    gnulibc_version='2.22'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
    cccdlflags='-fPIC', lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'



@INC for perl 5.22.1:
    /home/mauke/usr/lib/perl5/site_perl/5.22.1/i686-linux
    /home/mauke/usr/lib/perl5/site_perl/5.22.1
    /home/mauke/usr/lib/perl5/5.22.1/i686-linux
    /home/mauke/usr/lib/perl5/5.22.1
    .


Environment for perl 5.22.1:
    HOME=/home/mauke
    LANG=en_US.UTF-8
    LANGUAGE (unset)
    LC_COLLATE=POSIX
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/mauke/perl5/perlbrew/bin:/home/mauke/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
    PERLBREW_BASHRC_VERSION=0.73
    PERLBREW_HOME=/home/mauke/.perlbrew
    PERLBREW_ROOT=/home/mauke/perl5/perlbrew
    PERL_BADLANG (unset)
    PERL_UNICODE=SAL
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Feb 5, 2016

From @Tux

On Thu, 4 Feb 2016 17​:08​:28 -0800, "l.mai@​web.de (via RT)"
<perlbug-followup@​perl.org> wrote​:

$ perl -we 'for (my $i = 1; $i < 10; $i += my $k) { $k = $i; print "$i\n"; }'
1
2
4
8

I'm not sure what's going on here, but 'my $k' should probably not do that (it
should always evaluate to undef). In fact, I would have expected the third part
of a C-style for to have its own little scope, like

for (my $i = 1; $i < 10; do { $i += my $k })

But for some reason $k leaks through to the loop body, where it can be set
before officially being created by 'my'. This is probably a variant of the 'my
$x if 0' problem.

Normally, the part inside the parens scopes to the block connected, so
this does not surprise me. Code like this I use on a very regular basis​:

$ cat t.pl
use warnings;

if (my @​foo = (1..4)) {
  print "foo​: @​foo\n";
  }
print "$_\n" for @​foo;

$ perl t.pl
Name "main​::foo" used only once​: possible typo at t.pl line 6.
foo​: 1 2 3 4

Which is completely in line with

$ cat t.pl
use warnings;

for (my $i = 1; $i < 5; $i += my $k) {
  $k = $i;
  print "i​: $i\n";
  }
print "i​: $i\n";
print "k​: $k\n";

$ perl t.pl
Name "main​::k" used only once​: possible typo at t.pl line 8.
Name "main​::i" used only once​: possible typo at t.pl line 7.
i​: 1
i​: 2
i​: 4
Use of uninitialized value $i in concatenation (.) or string at t.pl line 7.
i​:
Use of uninitialized value $k in concatenation (.) or string at t.pl line 8.
k​:

--
H.Merijn Brand http​://tux.nl Perl Monger http​://amsterdam.pm.org/
using perl5.00307 .. 5.23 porting perl5 on HP-UX, AIX, and openSUSE
http​://mirrors.develooper.com/hpux/ http​://www.test-smoke.org/
http​://qa.perl.org http​://www.goldmark.org/jeff/stupid-disclaimers/

@p5pRT
Copy link
Author

p5pRT commented Feb 5, 2016

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

@p5pRT
Copy link
Author

p5pRT commented Feb 5, 2016

From @mauke

Am Do 04. Feb 2016, 23​:29​:24, hmbrand schrieb​:

Normally, the part inside the parens scopes to the block connected, so
this does not surprise me.

The difference is that

  for (A; B; C) { D }

should be equivalent to

  A; while (B) { D } continue { C }

C is executed after A, B, and D. That's why it's surprising that the loop body D can see and access variables declared in C, even though their definitions haven't even been executed yet.

The situation is analogous to

  % perl -wE 'my $r; { say(my $k), last if $r; $k = "what"; $r = 1; redo }'
  what

Here we also manage to make 'my $k' not evaluate to undef by arranging for the control flow not to match the lexical structure of the program, executing 'my $k' after setting $k.

This version is more obviously broken because the behavior of 'my ... if ...' is documented as undefined ("Here be dragons"). But with C-style 'for' you get this effect automatically and implicitly, just by declaring variables in the third part.

@p5pRT
Copy link
Author

p5pRT commented May 27, 2016

From @ap

* l.mai@​web.de via RT <perlbug-followup@​perl.org> [2016-02-05 12​:00]​:

Am Do 04. Feb 2016, 23​:29​:24, hmbrand schrieb​:

Normally, the part inside the parens scopes to the block connected,
so this does not surprise me.

The difference is that

for (A; B; C) { D }

should be equivalent to

A; while (B) { D } continue { C }

C is executed after A, B, and D. That's why it's surprising that the
loop body D can see and access variables declared in C, even though
their definitions haven't even been executed yet.

Variables declared in the A part of for(A;B;C) do not remain declared
past the loop. If you are serious about strict textual equivalence of
these constructs then that must also bother you. Does it?

Regards,
--
Aristotle Pagaltzis // <http​://plasmasturm.org/>

@p5pRT
Copy link
Author

p5pRT commented May 30, 2016

From @mauke

Am Fr 27. Mai 2016, 04​:19​:32, aristotle schrieb​:

* l.mai@​web.de via RT <perlbug-followup@​perl.org> [2016-02-05 12​:00]​:

Am Do 04. Feb 2016, 23​:29​:24, hmbrand schrieb​:

Normally, the part inside the parens scopes to the block connected,
so this does not surprise me.

The difference is that

for (A; B; C) { D }

should be equivalent to

A; while (B) { D } continue { C }

C is executed after A, B, and D. That's why it's surprising that the
loop body D can see and access variables declared in C, even though
their definitions haven't even been executed yet.

Variables declared in the A part of for(A;B;C) do not remain declared
past the loop. If you are serious about strict textual equivalence of
these constructs then that must also bother you. Does it?

I'm not serious about strict equivalence, let alone textual.

What bothers me is the time traveling / backwards scoping of C, which comes last in execution order.

Anyway, the equivalence wrt A can be easily restored by wrapping the whole thing in another scope​:

  do { A; while (B) { D } continue { C } };

It's a bit harder to make 'my $x' not return undef in normal Perl code.

@p5pRT
Copy link
Author

p5pRT commented May 30, 2016

From @iabyn

On Mon, May 30, 2016 at 11​:55​:09AM -0700, l.mai@​web.de via RT wrote​:

I'm not serious about strict equivalence, let alone textual.

What bothers me is the time traveling / backwards scoping of C, which comes last in execution order.

Anyway, the equivalence wrt A can be easily restored by wrapping the whole thing in another scope​:

do { A; while (B) { D } continue { C } };

It's a bit harder to make 'my $x' not return undef in normal Perl code.

Just as a data point, the grammar for C<for(;;)> is currently written
such that in terms of where (lexically speaking) lexical vars are
introduced and go out of scope,

  for (A; B; C) { D }

is equivalent to​:

  {
  A;
  B;
  C;
  {
  D;
  }
  }

--
Hofstadter's Law​: It always takes longer than you expect, even when you
take into account Hofstadter's Law.

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