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

Complex Assignment Operators - behaviour differs from the expanded equivalent #13752

Open
p5pRT opened this issue Apr 22, 2014 · 5 comments
Open

Comments

@p5pRT
Copy link

p5pRT commented Apr 22, 2014

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

Searchable as RT121703$

@p5pRT
Copy link
Author

p5pRT commented Apr 22, 2014

From the.rob.dixon@gmail.com

This is a bug report for perl from the.rob.dixon@​gmail.com,
generated with the help of perlbug 1.39 running under perl 5.16.2.


This issue was raised as a comment by user `mob` on Stack Overflow here

  http​://stackoverflow.com/questions/23176023/reading-line-by-line-does-not-work#comment35447515_23176172

The problem is that the *compound* assignment operators should behave
identically to the expanded equivalent. So, for example

$x .= $y

should be no different from

$x = $x + $y

The documentation has an exemption for this when it says, in `perlop` under
`Assignment Operators`

... `$a += 2` is equivalent to `$a = $a + 2` although without duplicating
any side effects that dereferencing the lvalue might trigger, such as from
tie()

But there is an additional case where this assertion fails​: when the lvalue is a
non-existent hash element. The compound assignment operator has the effect of
autovivifying the hash element before the right-hand-side is evaluated, so any
occurrence of the `exists` operator will evaluate as `true`. Whereas the
"equivalent" simple assignment will not autovivify the element until after
evaluation of the right-hand-side, so `exists` evaluates as `false`.

This example pogram derives from another comment from `mob` referred to above.
I felt it was useful to demonstrate the problem within a single program with
strictures and warnings enabled.

In the simple assignment `exists $hash_a{key}` correctly returns `undef`, and
because `warnings` is enabled we get

Use of uninitialized value in concatenation (.) or string

But in the case of the compound assignment, the element is autovivified
*first*, so `exists $hash_a{key}` returns 1, and the immunity of `undef`
from concatenation warnings prevents a message from being printed.

My gut feeling is that this may have to be relegated to a documentation bug,
as this doesn't fall within "any side effects that dereferencing the lvalue
might trigger", but the problem is clearly a core one so I have reported it as
such.

This seems especially insidious when there is no way of forcing the order of
evaluation, for example

$hash_c{key} .= (exists $hash_c{key})

still evaluates the lvalue first.

use strict;
use warnings;

use Data​::Dumper;
$Data​::Dumper​::Useqq = 1;

my (%hash_b, %hash_a);

$hash_a{key} = $hash_a{key} . exists $hash_a{key};
print Dumper $hash_a{key};

$hash_b{key} .= exists $hash_b{key};
print Dumper $hash_b{key};

__END__

*output*

Use of uninitialized value in concatenation (.) or string at
E​:\Perl\source\problem.pl line 9.
$VAR1 = "";
$VAR1 = 1;



Flags​:
  category=core
  severity=medium


Site configuration information for perl 5.16.2​:

Configured by strawberry-perl at Fri Nov 2 00​:34​:53 2012.

Summary of my perl5 (revision 5 version 16 subversion 2) configuration​:

  Platform​:
  osname=MSWin32, osvers=4.0, archname=MSWin32-x86-multi-thread
  uname='Win32 strawberry-perl 5.16.2.1 #1 Fri Nov 2 00​:33​:54 2012 i386'
  config_args='undef'
  hint=recommended, useposix=true, d_sigaction=undef
  useithreads=define, usemultiplicity=define
  useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
  use64bitint=undef, use64bitall=undef, uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='gcc', ccflags =' -s -O2 -DWIN32 -DPERL_TEXTMODE_SCRIPTS
-DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS -fno-strict-aliasing
-mms-bitfields',
  optimize='-s -O2',
  cppflags='-DWIN32'
  ccversion='', gccversion='4.6.3', gccosandvers=''
  intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
  d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=12
  ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='long
long', lseeksize=8
  alignbytes=8, prototype=define
  Linker and Libraries​:
  ld='g++', ldflags ='-s -L"C​:\strawberry\perl\lib\CORE"
-L"C​:\strawberry\c\lib"'
  libpth=C​:\strawberry\c\lib C​:\strawberry\c\i686-w64-mingw32\lib
  libs=-lmoldname -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32
-ladvapi32 -lshell32 -lole32 -loleaut32 -lnetapi32 -luuid -lws2_32
-lmpr -lwinmm -lversion -lodbc32 -lodbccp32 -lcomctl32
  perllibs=-lmoldname -lkernel32 -luser32 -lgdi32 -lwinspool
-lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -lnetapi32 -luuid
-lws2_32 -lmpr -lwinmm -lversion -lodbc32 -lodbccp32 -lcomctl32
  libc=, so=dll, useshrplib=true, libperl=libperl516.a
  gnulibc_version=''
  Dynamic Linking​:
  dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' '
  cccdlflags=' ', lddlflags='-mdll -s
-L"C​:\strawberry\perl\lib\CORE" -L"C​:\strawberry\c\lib"'

Locally applied patches​:


@​INC for perl 5.16.2​:
  C​:/strawberry/perl/site/lib/MSWin32-x86-multi-thread
  C​:/strawberry/perl/site/lib
  C​:/strawberry/perl/vendor/lib
  C​:/strawberry/perl/lib
  .


Environment for perl 5.16.2​:
  HOME (unset)
  LANG (unset)
  LANGUAGE (unset)
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=C​:\Program Files (x86)\ImageMagick-6.8.3-Q16;C​:\Program
Files\ImageMagick-6.8.3-Q16;C​:\Program Files (x86)\NVIDIA
Corporation\PhysX\Common;C​:\Program Files (x86)\PC Connectivity
Solution\;C​:\Program Files\Common Files\Microsoft Shared\Windows
Live;C​:\Program Files
(x86)\PHP\;C​:\Windows\system32;C​:\Windows;C​:\Windows\System32\Wbem;C​:\Windows\System32\WindowsPowerShell\v1.0\;C​:\Program
Files\jEdit;C​:\usr\local\ppt\bin;C​:\Program Files
(x86)\GnuWin32\bin;C​:\Program Files (x86)\Smart
Projects\IsoBuster;C​:\Program Files (x86)\Oracle\Berkeley DB 11gR2
5.3.15\bin;C​:\Program Files (x86)\Git\cmd;C​:\Program Files
(x86)\Bazaar;C​:\Program Files (x86)\Lua\5.1;C​:\Program Files
(x86)\Lua\5.1\clibs;C​:\strawberry\c\bin;C​:\strawberry\perl\site\bin;C​:\strawberry\perl\bin;C​:\Program
Files\TortoiseSVN\bin;C​:\MediaInfoCLI;C​:\Program Files
(x86)\MKVToolNix;C​:\Program Files (x86)\Subversion\bin;C​:\Program
Files (x86)\Common Files\Ulead Systems\MPEG;C​:\Program Files
(x86)\QuickTime\QTSystem\;C​:\Program Files\Microsoft Network Monitor
3\;C​:\Program Files\Calibre2\;C​:\Program Files (x86)\MySQL\MySQL
Utilities 1.3.4\;C​:\Program Files (x86)\Common
Files\Acronis\SnapAPI\;C​:\Program Files\WinRAR;C​:\Program Files\Common
Files\Microsoft Shared\Windows Live;C​:\Program Files (x86)\Common
Files\Hackety Hack\0.r1529\..;C​:\Program Files (x86)\IDM Computer
Solutions\UltraCompare\;C​:\ffmpeg\bin;C​:\Program Files
(x86)\Serviio\lib;C​:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\bin\x86_amd64;C​:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\bin\amd64;C​:\SWIG;C​:\MinGW\bin;C​:\MinGW\MSYS\1.0\bin;C​:\Python27;C​:\Curl;C​:\pkg-config\bin;C​:\glib\bin;C​:\gettext-runtime\bin;C​:\Program
Files (x86)\IDM Computer
Solutions\UltraEdit\;C​:\LuaRocks\2.0;C​:\Program Files
(x86)\Android\android-sdk\platform-tools;C​:\Ruby193\bin;C​:\Ruby193.DevKit\bin;C​:\Program
Files (x86)\Nmap;C​:\MobiPerl;C​:\Program Files (x86)\EaseUS\Todo
Backup\bin\x64\
  PERL_BADLANG (unset)
  SHELL (unset)

@p5pRT
Copy link
Author

p5pRT commented Jun 12, 2014

From @jkeenan

On Tue Apr 22 16​:50​:24 2014, the.rob.dixon@​gmail.com wrote​:

This is a bug report for perl from the.rob.dixon@​gmail.com,
generated with the help of perlbug 1.39 running under perl 5.16.2.

-----------------------------------------------------------------

[snip]

The problem is that the *compound* assignment operators should behave
identically to the expanded equivalent. So, for example

$x .= $y

should be no different from

$x = $x + $y

The documentation has an exemption for this when it says, in `perlop`
under
`Assignment Operators`

... `$a += 2` is equivalent to `$a = $a + 2` although without
duplicating
any side effects that dereferencing the lvalue might trigger, such as
from
tie()

But there is an additional case where this assertion fails​: when the
lvalue is a
non-existent hash element. The compound assignment operator has the
effect of
autovivifying the hash element before the right-hand-side is
evaluated, so any
occurrence of the `exists` operator will evaluate as `true`. Whereas
the
"equivalent" simple assignment will not autovivify the element until
after
evaluation of the right-hand-side, so `exists` evaluates as `false`.

[snip]

In the simple assignment `exists $hash_a{key}` correctly returns
`undef`, and
because `warnings` is enabled we get

Use of uninitialized value in concatenation (.) or string

But in the case of the compound assignment, the element is
autovivified
*first*, so `exists $hash_a{key}` returns 1, and the immunity of
`undef`
from concatenation warnings prevents a message from being printed.

My gut feeling is that this may have to be relegated to a
documentation bug,
as this doesn't fall within "any side effects that dereferencing the
lvalue
might trigger", but the problem is clearly a core one so I have
reported it as
such.

Would you be able to provide a patch to pod/perlop.pod which addresses this issue?

Thank you very much.
Jim Keenan

@p5pRT
Copy link
Author

p5pRT commented Jun 12, 2014

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

@p5pRT
Copy link
Author

p5pRT commented Jun 12, 2014

From @ikegami

On Wed, Jun 11, 2014 at 8​:31 PM, James E Keenan via RT <
perlbug-followup@​perl.org> wrote​:

Would you be able to provide a patch to pod/perlop.pod which addresses
this issue?

Current​:

`$a += 2` is equivalent to `$a = $a + 2` although without duplicating any
side effects that dereferencing the lvalue might trigger, such as from tie()

Proposed​:

`$a += 2` is equivalent to `$a = $a + 2` except that $a is only evaluated
once. Side effects of dereferencing the lvalue such as those you might get
from tie() will only trigger once.

Overkill​:

`$a += 2` is equivalent to `$a = $a + 2` except that $a is only evaluated
once as an lvalue. Side effects of dereferencing the lvalue such as those
you might get from tie() will only trigger once.

@p5pRT
Copy link
Author

p5pRT commented Jun 12, 2014

From @ikegami

On Wed, Jun 11, 2014 at 9​:24 PM, Eric Brine <ikegami@​adaelis.com> wrote​:

On Wed, Jun 11, 2014 at 8​:31 PM, James E Keenan via RT <
perlbug-followup@​perl.org> wrote​:

Would you be able to provide a patch to pod/perlop.pod which addresses
this issue?

Current​:

`$a += 2` is equivalent to `$a = $a + 2` although without duplicating any
side effects that dereferencing the lvalue might trigger, such as from tie()

Proposed​:

`$a += 2` is equivalent to `$a = $a + 2` except that $a is only evaluated
once. Side effects of dereferencing the lvalue such as those you might get
from tie() will only trigger once.

Overkill​:

`$a += 2` is equivalent to `$a = $a + 2` except that $a is only evaluated
once as an lvalue. Side effects of dereferencing the lvalue such as those
you might get from tie() will only trigger once.

I left the tie bit in without thinking. What difference is there between
C<< $a = $a + 2; >> and C<< $a += 2; >> for tied variables? Both call FETCH
and STORE once.

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

No branches or pull requests

2 participants