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

Subs ending in { @a } return list, not hashref; can Perl defend against this trap? #12078

Closed
p5pRT opened this issue May 2, 2012 · 8 comments

Comments

@p5pRT
Copy link

p5pRT commented May 2, 2012

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

Searchable as RT112732$

@p5pRT
Copy link
Author

p5pRT commented May 2, 2012

From @jimav

This is a bug report for perl from james_avera@​yahoo.com,
generated with the help of perlbug 1.39 running under perl 5.12.4.


In the following, Perl silently parses the {...} in the last line
of each sub as a BLOCK rather than an anonymous-hash constructor.
The functions return the list (a,1,b,1), not a hahsref
as the programmer obviously intended​:

  use struct; use warnings;
  sub f {
  my @​keys = ("a","b");
  { map{$_ => 1} @​keys }
  }
  sub g { my @​a = (a => 1, b => 2); { @​a } }

This is a trap. Can Perl do something to save programmers from it?

One solution might be to actually recognize when {...} is the last thing
in a sub definition and parse it as an expression in that case.

Another might be to warn about *statements* consisting only of
expressions with no side-effects, which would let the user know that a
dangling "{ map{$_=>1} @​keys }" was not handled by Perl as they expected.



Flags​:
  category=library
  severity=low
  module=warnings


Site configuration information for perl 5.12.4​:

Configured by Debian Project at Tue Sep 6 08​:08​:24 UTC 2011.

Summary of my perl5 (revision 5 version 12 subversion 4) configuration​:

  Platform​:
  osname=linux, osvers=2.6.24-28-server,
archname=x86_64-linux-gnu-thread-multi
  uname='linux allspice 2.6.24-28-server #1 smp wed aug 18 21​:17​:51
utc 2010 x86_64 x86_64 x86_64 gnulinux '
  config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN
-Dcccdlflags=-fPIC -Darchname=x86_64-linux-gnu -Dprefix=/usr
-Dprivlib=/usr/share/perl/5.12 -Darchlib=/usr/lib/perl/5.12
-Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5
-Dvendorarch=/usr/lib/perl5 -Dsiteprefix=/usr/local
-Dsitelib=/usr/local/share/perl/5.12.4
-Dsitearch=/usr/local/lib/perl/5.12.4 -Dman1dir=/usr/share/man/man1
-Dman3dir=/usr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1
-Dsiteman3dir=/usr/local/man/man3 -Duse64bitint -Dman1ext=1
-Dman3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Ud_ualarm
-Uusesfio -Uusenm -Ui_libutil -DDEBUGGING=-g -Doptimize=-O2 -Duseshrplib
-Dlibperl=libperl.so.5.12.4 -des'
  hint=recommended, useposix=true, d_sigaction=define
  useithreads=define, usemultiplicity=define
  useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
  use64bitint=define, use64bitall=define, uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DDEBIAN
-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 -DDEBIAN -fno-strict-aliasing
-pipe -fstack-protector -I/usr/local/include'
  ccversion='', gccversion='4.6.1', 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 /lib/x86_64-linux-gnu /lib/../lib
/usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib /usr/lib
  libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt
  perllibs=-ldl -lm -lpthread -lc -lcrypt
  libc=, so=so, useshrplib=true, libperl=libperl.so.5.12.4
  gnulibc_version='2.13'
  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​:


@​INC for perl 5.12.4​:
  /home/jima/local/share/perl/5.12.4
  /home/jima/local/share/perl
  /etc/perl
  /usr/local/lib/perl/5.12.4
  /usr/local/share/perl/5.12.4
  /usr/lib/perl5
  /usr/share/perl5
  /usr/lib/perl/5.12
  /usr/share/perl/5.12
  /usr/local/lib/site_perl
  .


Environment for perl 5.12.4​:
  HOME=/home/jima
  LANG=en_US.UTF-8
  LANGUAGE (unset)
  LD_LIBRARY_PATH=/home/jima/local/lib
  LOGDIR (unset)

PATH=/home/jima/bin​:/home/jima/local/bin​:/home/jima/jima_tools/linux86_64/bin​:/usr/bin​:/bin​:/usr/sbin​:/sbin​:/usr/bin/X11​:/usr/local/bin​:/opt/openoffice.org3/program​:/usr/lib/lightdm/lightdm​:/usr/local/sbin​:/usr/games​:.
  PERL5LIB=/home/jima/local/share/perl
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented May 3, 2012

From @Tux

On Wed, 02 May 2012 12​:39​:45 -0700, Jim Avera (via RT)
<perlbug-followup@​perl.org> wrote​:

In the following, Perl silently parses the {...} in the last line
of each sub as a BLOCK rather than an anonymous-hash constructor.
The functions return the list (a,1,b,1), not a hahsref
as the programmer obviously intended​:

use struct; use warnings;
sub f {
my @​keys = ("a","b");
{ map{$_ => 1} @​keys }
}
sub g { my @​a = (a => 1, b => 2); { @​a } }

This is a trap. Can Perl do something to save programmers from it?

Yes, either put a + in front of the opening brace or add an explicit
return​:

sub f
{
  my @​keys = ("a", "b");
  +{ map { $_ => 1 } @​keys }
  }

sub g
{
  my @​a = (a => 1, b => 2);
  return { @​a }
  }

One solution might be to actually recognize when {...} is the last thing
in a sub definition and parse it as an expression in that case.

This issue comes back every now and then, and whatever way the current
parsing is modified will break someone elses expectations. The ability
to unambiguate using a + is easy enough to warrant not to change this

Another might be to warn about *statements* consisting only of
expressions with no side-effects, which would let the user know that a
dangling "{ map{$_=>1} @​keys }" was not handled by Perl as they expected.

No. Many people use this to explicitely create a scope, possibly with
lexical variable, to avoid side effects or force auto-destruction.

--
H.Merijn Brand http​://tux.nl Perl Monger http​://amsterdam.pm.org/
using perl5.00307 .. 5.14 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 May 3, 2012

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

@p5pRT
Copy link
Author

p5pRT commented May 3, 2012

From @nwc10

On Thu, May 03, 2012 at 11​:10​:43AM +0200, H.Merijn Brand wrote​:

On Wed, 02 May 2012 12​:39​:45 -0700, Jim Avera (via RT)
<perlbug-followup@​perl.org> wrote​:

In the following, Perl silently parses the {...} in the last line
of each sub as a BLOCK rather than an anonymous-hash constructor.
The functions return the list (a,1,b,1), not a hahsref
as the programmer obviously intended​:

use struct; use warnings;
sub f {
my @​keys = ("a","b");
{ map{$_ => 1} @​keys }
}
sub g { my @​a = (a => 1, b => 2); { @​a } }

This is a trap. Can Perl do something to save programmers from it?

We could make return statements mandatory :-)

One solution might be to actually recognize when {...} is the last thing
in a sub definition and parse it as an expression in that case.

This issue comes back every now and then, and whatever way the current
parsing is modified will break someone elses expectations. The ability
to unambiguate using a + is easy enough to warrant not to change this

Worse than that, I think it's not possible. Too much lookahead would be needed,
because the parser has to decide early whether the { is a block or a hash
constructor. It's not able to defer that decision until it reaches the }
And, unfortunately, in the example given, the code within the {} is valid
for either interpretation of the {

Another might be to warn about *statements* consisting only of
expressions with no side-effects, which would let the user know that a
dangling "{ map{$_=>1} @​keys }" was not handled by Perl as they expected.

No. Many people use this to explicitely create a scope, possibly with
lexical variable, to avoid side effects or force auto-destruction.

I think the key part of the request was that all code *within* the block
had to have no side effects. The use case that you (Merijn) describe would
(I think) always have some statement with side effects.

However, I'm not sure how easy it is to "know" unambiguously whether a
section of the optree has side effects. (I'm going to discount the hidden
side effects of things lile the values read being tied or overloaded)

Nicholas Clark

@p5pRT
Copy link
Author

p5pRT commented May 3, 2012

From @Abigail

On Wed, May 02, 2012 at 12​:39​:45PM -0700, Jim Avera wrote​:

# New Ticket Created by Jim Avera
# Please include the string​: [perl #112732]
# in the subject line of all future correspondence about this issue.
# <URL​: https://rt-archive.perl.org/perl5/Ticket/Display.html?id=112732 >

This is a bug report for perl from james_avera@​yahoo.com,
generated with the help of perlbug 1.39 running under perl 5.12.4.

-----------------------------------------------------------------
In the following, Perl silently parses the {...} in the last line
of each sub as a BLOCK rather than an anonymous-hash constructor.
The functions return the list (a,1,b,1), not a hahsref
as the programmer obviously intended​:

use struct; use warnings;
sub f {
my @​keys = ("a","b");
{ map{$_ => 1} @​keys }
}
sub g { my @​a = (a => 1, b => 2); { @​a } }

This is a trap. Can Perl do something to save programmers from it?

One solution might be to actually recognize when {...} is the last thing
in a sub definition and parse it as an expression in that case.

Another might be to warn about *statements* consisting only of
expressions with no side-effects, which would let the user know that a
dangling "{ map{$_=>1} @​keys }" was not handled by Perl as they expected.

Subs of the form​:

  sub foo {
  ...
  1;
  }

or

  sub bar {
  ...
  $var;
  }

are as common as dirt; I don't think people appreciate it if this starts
warning.

Abigail

@p5pRT
Copy link
Author

p5pRT commented May 3, 2012

From @ikegami

On Thu, May 3, 2012 at 5​:10 AM, H.Merijn Brand <h.m.brand@​xs4all.nl> wrote​:

One solution might be to actually recognize when {...} is the last thing
in a sub definition and parse it as an expression in that case.

This issue comes back every now and then, and whatever way the current
parsing is modified will break someone elses expectations. The ability
to unambiguate using a + is easy enough to warrant not to change this

I often have code of the form

sub {
  {
  ...
  }
  {
  ...
  }
  {
  ...
  }
}

If that last block becomes a hash, I'd likely get spurious warnings
("Useless use of anonymous hash ({}) in void context", "Odd number of
elements in anonymous hash", "Unitialized value in hash"), and "next" and
such would not work properly (although they're very unlikely to be used
there).

@p5pRT
Copy link
Author

p5pRT commented Dec 12, 2017

From zefram@fysh.org

The documentation of the hash/block disambiguation method was recently
made more prominent in commit 5577141 for
[perl #130958]. That's really all we can do in this area. This ticket
should be closed.

-zefram

@p5pRT p5pRT closed this as completed Dec 12, 2017
@p5pRT
Copy link
Author

p5pRT commented Dec 12, 2017

@iabyn - 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

1 participant