Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.93k stars 549 forks source link

overload "0+" doesn't handle integer results #9047

Closed p5pRT closed 16 years ago

p5pRT commented 17 years ago

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

Searchable as RT46011$

p5pRT commented 17 years ago

From zefram@fysh.org

Created by zefram@fysh.org

This perl is configured with 64-bit integers and 64-bit floats (1+52 bits of significand).

$ perl -lwe '{ package t0; sub mynum { 36028797018963971 } use overload "0+" => \&mynum; } print int(t0​::mynum); print int(bless({}\, "t0"))' 36028797018963971 36028797018963968 $

36028797018963971 is 2^55+3. Its salient feature here is that it's a native integer that can't be exactly represented as a native float. When it's returned by the "0+" overload function\, it's being unnecessarily and lossily coerced to float. This coercion doesn't happen if the return value of t0​::mynum() goes directly into int() without going via the overloading system.

Perl Info ``` Flags: category=core severity=low Site configuration information for perl v5.8.8: Configured by zefram at Fri Jan 19 20:29:50 GMT 2007. Summary of my perl5 (revision 5 version 8 subversion 8) configuration: Platform: osname=linux, osvers=2.6.8-3-686-smp, archname=i386-linux-thread-multi-64int uname='linux monst.rous.org 2.6.8-3-686-smp #1 smp thu sep 7 04:39:15 utc 2006 i686 gnulinux ' config_args='-des -Darchname=i386-linux -Dcccdlflags=-fPIC -Dccdlflags=-rdynamic -Dprefix=/home/zefram/usr/perl/perl_install/perl-5.8.8-i64-f52 -Dman1ext=1 -Dman3ext=3perl -Duselargefiles -Dusethreads -Uafs -Ud_csh -Uusesfio -Uusenm -Duseshrplib -Duse64bitint' hint=recommended, useposix=true, d_sigaction=define usethreads=define use5005threads=undef useithreads=define usemultiplicity=define useperlio=define d_sfio=undef uselargefiles=define usesocks=undef use64bitint=define use64bitall=undef uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64', optimize='-O2', cppflags='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -fno-strict-aliasing -pipe -I/usr/local/include' ccversion='', gccversion='3.3.5 (Debian 1:3.3.5-13)', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=12345678 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, prototype=define Linker and Libraries: ld='cc', ldflags =' -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lnsl -ldl -lm -lcrypt -lutil -lpthread -lc perllibs=-lnsl -ldl -lm -lcrypt -lutil -lpthread -lc libc=/lib/libc-2.3.2.so, so=so, useshrplib=true, libperl=libperl.so gnulibc_version='2.3.2' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-rdynamic -Wl,-rpath,/home/zefram/usr/perl/perl_install/perl-5.8.8-i64-f52/lib/5.8.8/i386-linux-thread-multi-64int/CORE' cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib' Locally applied patches: @INC for perl v5.8.8: /home/zefram/usr/perl/perl_install/perl-5.8.8-i64-f52/lib/5.8.8/i386-linux-thread-multi-64int /home/zefram/usr/perl/perl_install/perl-5.8.8-i64-f52/lib/5.8.8 /home/zefram/usr/perl/perl_install/perl-5.8.8-i64-f52/lib/site_perl/5.8.8/i386-linux-thread-multi-64int /home/zefram/usr/perl/perl_install/perl-5.8.8-i64-f52/lib/site_perl/5.8.8 /home/zefram/usr/perl/perl_install/perl-5.8.8-i64-f52/lib/site_perl . Environment for perl v5.8.8: HOME=/home/zefram LANG (unset) LANGUAGE (unset) LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/home/zefram/pub/i686-pc-linux-gnu/bin:/home/zefram/pub/common/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/local/bin:/usr/games PERL_BADLANG (unset) SHELL=/usr/bin/zsh ```
p5pRT commented 16 years ago

From rick@bort.ca

On Oct 02 2007\, Zefram wrote​:

This perl is configured with 64-bit integers and 64-bit floats (1+52 bits of significand).

$ perl -lwe '{ package t0; sub mynum { 36028797018963971 } use overload "0+" => \&mynum; } print int(t0​::mynum); print int(bless({}\, "t0"))' 36028797018963971 36028797018963968 $

36028797018963971 is 2^55+3. Its salient feature here is that it's a native integer that can't be exactly represented as a native float. When it's returned by the "0+" overload function\, it's being unnecessarily and lossily coerced to float. This coercion doesn't happen if the return value of t0​::mynum() goes directly into int() without going via the overloading system.

The problem here is that the arg passed to int() is checked if IOK (integer) but because it's a reference\, it's not. So it is treated as a float (NV). And currently it actually calls mynum() twice for the second int() call. The attached patch fixes that and should fix your problem\, although I couldn't test it directly. I added tests for the call-twice problem\, but wasn't sure how to add one for the 64-bit int issue.

-- Rick Delaney rick@​bort.ca

p5pRT commented 16 years ago

From rick@bort.ca

46011.patch ```diff diff -pruN perl-current/lib/overload.t perl-current-dev/lib/overload.t --- perl-current/lib/overload.t 2007-10-03 16:46:52.000000000 -0400 +++ perl-current-dev/lib/overload.t 2007-10-06 20:55:19.000000000 -0400 @@ -47,7 +47,7 @@ sub numify { 0 + "${$_[0]}" } # Not need package main; $| = 1; -use Test::More tests => 528; +use Test::More tests => 535; $a = new Oscalar "087"; @@ -1375,3 +1375,28 @@ foreach my $op (qw(<=> == != < <= > >=)) is("$wham_eth", $string); is ($crunch_eth->Pie("Blackbird"), "$string, Blackbird"); } + +{ + package numify_int; + use overload "0+" => sub { $_[0][0] += 1; 42 }; + package numify_self; + use overload "0+" => sub { $_[0][0]++; $_[0] }; + package numify_other; + use overload "0+" => sub { $_[0][0]++; $_[0][1] = bless [], 'numify_int' }; + + package main; + my $o = bless [], 'numify_int'; + is(int($o), 42, 'numifies to integer'); + is($o->[0], 1, 'int() numifies only once'); + + my $aref = []; + my $num_val = 0 + $aref; + my $r = bless $aref, 'numify_self'; + is(int($r), $num_val, 'numifies to self'); + is($r->[0], 1, 'int() numifies once when returning self'); + + my $s = bless [], 'numify_other'; + is(int($s), 42, 'numifies to numification of other object'); + is($s->[0], 1, 'int() numifies once when returning other object'); + is($s->[1][0], 1, 'returned object numifies too'); +} diff -pruN perl-current/pp.c perl-current-dev/pp.c --- perl-current/pp.c 2007-09-08 16:48:36.000000000 -0400 +++ perl-current-dev/pp.c 2007-10-06 21:48:35.000000000 -0400 @@ -2874,22 +2874,38 @@ PP(pp_int) { dVAR; dSP; dTARGET; tryAMAGICun(int); { - const IV iv = TOPi; /* attempt to convert to IV if possible. */ + SV *sv = TOPs; + IV iv; /* XXX it's arguable that compiler casting to IV might be subtly different from modf (for numbers inside (IV_MIN,UV_MAX)) in which else preferring IV has introduced a subtle behaviour change bug. OTOH relying on floating point to be accurate is a bug. */ - if (!SvOK(TOPs)) + while (SvAMAGIC(sv)) { + SV *tsv = AMG_CALLun(sv,numer); + if (SvROK(tsv) && SvRV(tsv) == SvRV(sv)) { + SETi(PTR2IV(SvRV(sv))); + RETURN; + } + else + sv = tsv; + } + iv = SvIV(sv); /* attempt to convert to IV if possible. */ + + if (!SvOK(sv)) { SETu(0); - else if (SvIOK(TOPs)) { - if (SvIsUV(TOPs)) { - const UV uv = TOPu; - SETu(uv); - } else + } + else if (SvIOK(sv)) { + if (SvIsUV(sv)) + SETu(SvUV(sv)); + else SETi(iv); - } else { - const NV value = TOPn; + } + else if (SvROK(sv)) { + SETi(iv); + } + else { + const NV value = SvNV(sv); if (value >= 0.0) { if (value < (NV)UV_MAX + 0.5) { SETu(U_V(value)); ```
p5pRT commented 16 years ago

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

p5pRT commented 16 years ago

From @rgs

On 07/10/2007\, Rick Delaney \rick@&#8203;bort\.ca wrote​:

The problem here is that the arg passed to int() is checked if IOK (integer) but because it's a reference\, it's not. So it is treated as a float (NV). And currently it actually calls mynum() twice for the second int() call. The attached patch fixes that and should fix your problem\, although I couldn't test it directly. I added tests for the call-twice problem\, but wasn't sure how to add one for the 64-bit int issue.

Thanks\, applied as change #32059\, and I confirmed that fixes the bug on 64-bit int perl.

p5pRT commented 16 years ago

@rgs - Status changed from 'open' to 'resolved'

p5pRT commented 16 years ago

From @jdhedden

Rick Delaney wrote​:

The problem here is that the arg passed to int() is checked if IOK (integer) but because it's a reference\, it's not. So it is treated as a float (NV). And currently it actually calls mynum() twice for the second int() call. The attached patch fixes that and should fix your problem\, although I couldn't test it directly. I added tests for the call-twice problem\, but wasn't sure how to add one for the 64-bit int issue.

Rafael Garcia-Suarez wrote​:

Thanks\, applied as change #32059\, and I confirmed that fixes the bug on 64-bit int perl.

This is the same bug as reported by me exactly 2 years ago​:   http​://rt.perl.org/rt3/Ticket/Display.html?id=37363 Glad to see it's finally fixed.

Rafael\, would you please resolve bug 37363 (or whatever is appropriate in this case)? I don't have permission to do so. Thanks.

p5pRT commented 16 years ago

From zefram@fysh.org

Rafael Garcia-Suarez via RT wrote​:

According to our records\, your request regarding "overload "0+" doesn't handle integer results" has been resolved.

I looked up the patch that was posted to perl5-porters\, and it solves this issue only for int(). There's an equivalent bug still there for other operations​:

$ perl -lwe '{ package t0; sub mynum { 36028797018963971 } use overload "0+" => \&mynum\, fallback => 1; } printf "%d\n"\, 0+t0​::mynum; printf "%d\n"\, 0+bless({}\, "t0")'

It works OK for plain printf "%d"\, but not for addition. Also doesn't work for subtraction\, negation\, multiplication\, division\, remainder. Does work for bit shift and bitwise OR. Perhaps some code factoring is called for.

-zefram

p5pRT commented 16 years ago

From rick@bort.ca

On Oct 07 2007\, Zefram wrote​:

I looked up the patch that was posted to perl5-porters\, and it solves this issue only for int(). There's an equivalent bug still there for other operations​:

$ perl -lwe '{ package t0; sub mynum { 36028797018963971 } use overload "0+" => \&mynum\, fallback => 1; } printf "%d\n"\, 0+t0​::mynum; printf "%d\n"\, 0+bless({}\, "t0")'

I wish you'd mentioned that before.

It works OK for plain printf "%d"\, but not for addition. Also doesn't work for subtraction\, negation\, multiplication\, division\, remainder. Does work for bit shift and bitwise OR. Perhaps some code factoring is called for.

Undoubtedly. In the meantime here is another patch to fix a segfault introduced by the first.

-- Rick Delaney rick@​bort.ca

p5pRT commented 16 years ago

From rick@bort.ca

46011-a.patch ```diff diff -pruN perl-current/lib/overload.t perl-current-dev/lib/overload.t --- perl-current/lib/overload.t 2007-10-07 05:48:30.000000000 -0400 +++ perl-current-dev/lib/overload.t 2007-10-07 22:20:43.000000000 -0400 @@ -47,7 +47,7 @@ sub numify { 0 + "${$_[0]}" } # Not need package main; $| = 1; -use Test::More tests => 535; +use Test::More tests => 536; $a = new Oscalar "087"; @@ -1383,6 +1383,8 @@ foreach my $op (qw(<=> == != < <= > >=)) use overload "0+" => sub { $_[0][0]++; $_[0] }; package numify_other; use overload "0+" => sub { $_[0][0]++; $_[0][1] = bless [], 'numify_int' }; + package numify_by_fallback; + use overload "-" => sub { 1 }, fallback => 1; package main; my $o = bless [], 'numify_int'; @@ -1399,4 +1401,7 @@ foreach my $op (qw(<=> == != < <= > >=)) is(int($s), 42, 'numifies to numification of other object'); is($s->[0], 1, 'int() numifies once when returning other object'); is($s->[1][0], 1, 'returned object numifies too'); + + my $m = bless $aref, 'numify_by_fallback'; + is(int($m), $num_val, 'numifies to usual reference value'); } diff -pruN perl-current/pp.c perl-current-dev/pp.c --- perl-current/pp.c 2007-10-07 05:48:30.000000000 -0400 +++ perl-current-dev/pp.c 2007-10-07 22:20:10.000000000 -0400 @@ -2883,6 +2883,8 @@ PP(pp_int) while (SvAMAGIC(sv)) { SV *tsv = AMG_CALLun(sv,numer); + if (!tsv) + break; if (SvROK(tsv) && SvRV(tsv) == SvRV(sv)) { SETi(PTR2IV(SvRV(sv))); RETURN; ```
p5pRT commented 16 years ago

From @rgs

On 08/10/2007\, Rick Delaney \rick@&#8203;bort\.ca wrote​:

Undoubtedly. In the meantime here is another patch to fix a segfault introduced by the first.

Thanks\, applied.

p5pRT commented 16 years ago

From @rgs

On 08/10/2007\, Rafael Garcia-Suarez \rgarciasuarez@&#8203;gmail\.com wrote​:

On 08/10/2007\, Rick Delaney \rick@&#8203;bort\.ca wrote​:

Undoubtedly. In the meantime here is another patch to fix a segfault introduced by the first.

Thanks\, applied.

From the smoke tests\, it seems that test 531 fails for builds with -Duse64bitall. I can't test with this configuration.

p5pRT commented 16 years ago

From @tux

On Mon\, 8 Oct 2007 14​:01​:02 +0200\, "Rafael Garcia-Suarez" \rgarciasuarez@&#8203;gmail\.com wrote​:

On 08/10/2007\, Rafael Garcia-Suarez \rgarciasuarez@&#8203;gmail\.com wrote​:

On 08/10/2007\, Rick Delaney \rick@&#8203;bort\.ca wrote​:

Undoubtedly. In the meantime here is another patch to fix a segfault introduced by the first.

Thanks\, applied.

From the smoke tests\, it seems that test 531 fails for builds with

Smoke [5.10.0] 32059 FAIL(F) hp-ux B.11.23/64 gcc (ia64/2 cpu) http​://www.test-smoke.org/reports/50698

-Duse64bitall. I can't test with this configuration.

I did two manual runs with -Duse64bitall on 32068

Linux 2.6.18.8-0.5 x86_64 Xeon(R) CPU E5320 @​ 1.86GHz/1596(4) x86_64 3951 Mb

All tests successful\, 70 tests and 758 subtests skipped. Files=1461\, Tests=185010\, 512 wallclock secs (258.50 cusr + 35.60 csys = 294.10 CPU)

HP-UX 11.23/64 U rx1620/64 Itanium 2/1600(2) ia64 2037 Mb

Failed Test Stat Wstat Total Fail List of Failed


../lib/overload.t 2 512 536 2 531 536 (2 subtests UNEXPECTEDLY SUCCEEDED)\, 74 tests and 788 subtests skipped. Failed 1/1460 test scripts. 2/184816 subtests failed. Files=1460\, Tests=184816\, 780 wallclock secs (454.83 cusr + 98.65 csys = 553.48 CPU) Failed 1/1460 test programs. 2/184816 subtests failed.

x1​:/pro/3gl/CPAN/perl-current/t 112 > ./perl -I../lib ../lib/overload.t 1..536 ok 1 ok 2 : : ok 527 ok 528 ok 529 - numifies to integer ok 530 - int() numifies only once not ok 531 - numifies to self # Failed test 'numifies to self' # at ../lib/overload.t line 1397. # got​: '6917529027645582112' # expected​: '6.91752902764558e+18' ok 532 - int() numifies once when returning self ok 533 - numifies to numification of other object ok 534 - int() numifies once when returning other object ok 535 - returned object numifies too not ok 536 - numifies to usual reference value # Failed test 'numifies to usual reference value' # at ../lib/overload.t line 1406. # got​: '6917529027645582112' # expected​: '6.91752902764558e+18' # Looks like you failed 2 tests of 536.

On a side note\, I don't like messages like this​:

../lib/Module/Build/t/ppm........................................ok 2/12Invalid header block at offset unknown at ../lib/Module/Build/t/ppm.t line 122 Couldn't read chunk at offset unknown at ../lib/Module/Build/t/ppm.t line 122 Read error on tarfile (missing data) 'blib/html/bin/hello.html' at offset unknown at ../lib/Module/Build/t/ppm.t line 122 ../lib/Module/Build/t/ppm........................................ok 10/12Invalid header block at offset unknown at ../lib/Module/Build/t/ppm.t line 174 Couldn't read chunk at offset unknown at ../lib/Module/Build/t/ppm.t line 174 Read error on tarfile (missing data) 'blib/html/bin/hello.html' at offset unknown at ../lib/Module/Build/t/ppm.t line 174

Can't those be silenced?

-- H.Merijn Brand Amsterdam Perl Mongers (http​://amsterdam.pm.org/) using & porting perl 5.6.2\, 5.8.x\, 5.9.x on HP-UX 10.20\, 11.00\, 11.11\, & 11.23\, SuSE 10.0 & 10.2\, AIX 4.3 & 5.2\, and Cygwin. http​://qa.perl.org http​://mirrors.develooper.com/hpux/ http​://www.test-smoke.org   http​://www.goldmark.org/jeff/stupid-disclaimers/

p5pRT commented 16 years ago

From jos@dwim.org

On 08 Oct 2007\, at 14​:18\, H.Merijn Brand wrote​:

../lib/Module/Build/t/ppm........................................ok
2/12Invalid header block at offset unknown at ../lib/Module/Build/t/ ppm.t line 122 Couldn't read chunk at offset unknown at ../lib/Module/Build/t/ ppm.t line 122 Read error on tarfile (missing data) 'blib/html/bin/hello.html' at
offset unknown at ../lib/Module/Build/t/ppm.t line 122 ../lib/Module/Build/t/ppm........................................ok
10/12Invalid header block at offset unknown at ../lib/Module/Build/ t/ppm.t line 174 Couldn't read chunk at offset unknown at ../lib/Module/Build/t/ ppm.t line 174 Read error on tarfile (missing data) 'blib/html/bin/hello.html' at
offset unknown at ../lib/Module/Build/t/ppm.t line 174

Can't those be silenced?

They can be by setting C\<$Archive​::Tar​::WARN = 0>\, however you probably don't want to as these are pointing to a real issue; an .html file is being used a tar file\, which is not quite working obviously.

This may point to a more serious issue underneath.

--

  Jos Boumans

  How do I prove I'm not crazy to people who are?

p5pRT commented 16 years ago

From @jdhedden

Zefram wrote​:

It works OK for plain printf "%d"\, but not for addition. Also doesn't work for subtraction\, negation\, multiplication\, division\, remainder. Does work for bit shift and bitwise OR. Perhaps some code factoring is called for.

The attached patch adds tests for these to lib/overload.t. The tests are skipped if not using 64-bit ints. The following tests (which fail for me) are set as TODO​:   0+ (addition)   subtraction   multiplication   division   modulo (%)   exponentiation (**)   abs()

p5pRT commented 16 years ago

From @jdhedden

ol64.patch ```diff --- perl-current/lib/overload.t +++ perl-current/lib/overload.t @@ -47,7 +47,7 @@ package main; $| = 1; -use Test::More tests => 536; +use Test::More tests => 563; $a = new Oscalar "087"; @@ -1405,3 +1405,83 @@ my $m = bless $aref, 'numify_by_fallback'; is(int($m), $num_val, 'numifies to usual reference value'); } + +SKIP: { + skip('64-bit int tests on 32-bit Perl', 12) + if ($Config::Config{'uvsize'} != 8); + + my $ii = 36028797018963971; # 2^55 + 3 + + package Oobj; + use overload '0+' => sub { ${$_[0]} += 1; $ii }, + 'fallback' => 1; + sub new { bless(\do{my $x = 0}, 'Oobj') } + + package main; + my $oo = Oobj->new(); + my $cnt = 1; + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(0+$oo, 0+$ii, '0+ overload on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + is("$oo", "$ii", '0+ overload with stringification on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(0-$oo, 0-$ii, '0+ overload with subtraction on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(2*$oo, 2*$ii, '0+ overload with multiplication on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo/1, $ii/1, '0+ overload with division on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo%100, $ii%100, '0+ overload with modulo on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo**1, $ii**1, '0+ overload with exponentiation on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + is($oo>>3, $ii>>3, '0+ overload with bit shift right on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo<<2, $ii<<2, '0+ overload with bit shift left on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo|0xFF00, $ii|0xFF00, '0+ overload with bitwise or on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo&0xFF03, $ii&0xFF03, '0+ overload with bitwise and on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + ok($oo == $ii, '0+ overload with equality on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(int($oo), $ii, '0+ overload with int() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(abs($oo), $ii, '0+ overload with abs() on 64-bit int'); + } +} + +# EOF ```
p5pRT commented 16 years ago

From @jdhedden

The following tests (which fail for me) are set as TODO​: 0+ (addition) subtraction multiplication division modulo (%) exponentiation (**) abs()

Ticket should be reopened because the above still need fixing.

p5pRT commented 16 years ago

From rick@bort.ca

On Oct 08 2007\, H.Merijn Brand wrote​:

I did two manual runs with -Duse64bitall on 32068

Linux 2.6.18.8-0.5 x86_64 Xeon(R) CPU E5320 @​ 1.86GHz/1596(4) x86_64 3951 Mb

All tests successful\, 70 tests and 758 subtests skipped. Files=1461\, Tests=185010\, 512 wallclock secs (258.50 cusr + 35.60 csys = 294.10 CPU)

HP-UX 11.23/64 U rx1620/64 Itanium 2/1600(2) ia64 2037 Mb ... not ok 531 - numifies to self # Failed test 'numifies to self' # at ../lib/overload.t line 1397. # got​: '6917529027645582112' # expected​: '6.91752902764558e+18'

The expected value is just

  my $aref = [];   my $num_val = 0 + $aref;

So it means that a regular reference in numeric context is returning an NV or that pp_add is not doing integer math when it should here. I would say that either one is a separate bug.

I'm pretty sure changing the line to

  my $num_val = int($aref);

will make the tests pass without changing the semantic of the test too much. But we could probably use some TODO tests to ensure that references in numeric context are always returning IVs. Or is there a platform with pointers bigger than IVs?

-- Rick Delaney rick@​bort.ca

p5pRT commented 16 years ago

From @rgs

On 08/10/2007\, Rick Delaney \rick@&#8203;bort\.ca wrote​:

I'm pretty sure changing the line to

my $num\_val = int\($aref\);

will make the tests pass without changing the semantic of the test too

Thanks\, tweaked as #32071.

much. But we could probably use some TODO tests to ensure that references in numeric context are always returning IVs. Or is there a platform with pointers bigger than IVs?

I think IVs are guaranteed to be able to hold pointers.

p5pRT commented 16 years ago

From @jdhedden

Zefram wrote​:

It works OK for plain printf "%d"\, but not for addition. Also doesn't work for subtraction\, negation\, multiplication\, division\, remainder. Does work for bit shift and bitwise OR. Perhaps some code factoring is called for.

Jerry D. Hedden wrote​:

The attached patch adds tests for these to lib/overload.t. The tests are skipped if not using 64-bit ints. The following tests (which fail for me) are set as TODO​: 0+ (addition) subtraction multiplication division modulo (%) exponentiation (**) abs()

Programming by example\, the attached patch fixes the above problem for abs() overloading. I added more tests as well. (This supercedes my previous patch with lib/overload.t tests.)

p5pRT commented 16 years ago

From @jdhedden

abs.patch ```diff --- perl-current/pp.c +++ perl-current/pp.c @@ -2931,16 +2931,35 @@ { dVAR; dSP; dTARGET; tryAMAGICun(abs); { - /* This will cache the NV value if string isn't actually integer */ - const IV iv = TOPi; + SV *sv = TOPs; + IV iv; - if (!SvOK(TOPs)) + while (SvAMAGIC(sv)) { + SV *tsv = AMG_CALLun(sv,numer); + if (!tsv) + break; + if (SvROK(tsv) && SvRV(tsv) == SvRV(sv)) { + SETi(PTR2IV(SvRV(sv))); + RETURN; + } + else + sv = tsv; + } + iv = SvIV(sv); /* attempt to convert to IV if possible. */ + + if (!SvOK(sv)) { SETu(0); - else if (SvIOK(TOPs)) { + } + else if (SvIOK(sv)) { /* IVX is precise */ - if (SvIsUV(TOPs)) { - SETu(TOPu); /* force it to be numeric only */ + if (SvIsUV(sv)) { + SETu(SvUV(sv)); /* force it to be numeric only */ } else { + goto do_abs; + } + } + else if (SvROK(sv)) { + do_abs: if (iv >= 0) { SETi(iv); } else { @@ -2952,9 +2971,9 @@ SETu(IV_MIN); } } - } - } else{ - const NV value = TOPn; + } + else { + const NV value = SvNV(sv); if (value < 0.0) SETn(-value); else --- perl-current/lib/overload.t +++ perl-current/lib/overload.t @@ -47,7 +47,7 @@ package main; $| = 1; -use Test::More tests => 536; +use Test::More tests => 570; $a = new Oscalar "087"; @@ -1392,7 +1392,7 @@ is($o->[0], 1, 'int() numifies only once'); my $aref = []; - my $num_val = int($aref); + my $num_val = 0 + $aref; my $r = bless $aref, 'numify_self'; is(int($r), $num_val, 'numifies to self'); is($r->[0], 1, 'int() numifies once when returning self'); @@ -1405,3 +1405,112 @@ my $m = bless $aref, 'numify_by_fallback'; is(int($m), $num_val, 'numifies to usual reference value'); } + +SKIP: { + skip('64-bit int tests on 32-bit Perl', 12) + if ($Config::Config{'uvsize'} != 8); + + my $ii = 36028797018963971; # 2^55 + 3 + + package Oobj; + use overload '0+' => sub { ${$_[0]} += 1; $ii }, + 'fallback' => 1; + sub new { bless(\do{my $x = 0}, 'Oobj') } + + package main; + my $oo = Oobj->new(); + my $cnt = 1; + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(0+$oo, 0+$ii, '0+ overload on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + is("$oo", "$ii", '0+ overload with stringification on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(0-$oo, 0-$ii, '0+ overload with subtraction on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(2*$oo, 2*$ii, '0+ overload with multiplication on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo/1, $ii/1, '0+ overload with division on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo%100, $ii%100, '0+ overload with modulo on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo**1, $ii**1, '0+ overload with exponentiation on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + is($oo>>3, $ii>>3, '0+ overload with bit shift right on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo<<2, $ii<<2, '0+ overload with bit shift left on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo|0xFF00, $ii|0xFF00, '0+ overload with bitwise or on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo&0xFF03, $ii&0xFF03, '0+ overload with bitwise and on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + ok($oo == $ii, '0+ overload with equality on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(int($oo), $ii, '0+ overload with int() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(abs($oo), $ii, '0+ overload with abs() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + + ### Tests with large negative int + package Oobj2; + use overload '0+' => sub { ${$_[0]} += 1; 0-$ii }, + 'fallback' => 1; + sub new { bless(\do{my $x = 0}, 'Oobj2') } + + package main; + $oo = Oobj2->new(); + $cnt = 1; + + is(int($oo), 0-$ii, '0+ overload with int() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(abs($oo), $ii, '0+ overload with abs() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + + ### Tests with 'abs' overloading + package Oobj3; + use overload 'abs' => sub { ${$_[0]} += 1; $ii }, + 'fallback' => 1; + sub new { bless(\do{my $x = 0}, 'Oobj3') } + + package main; + $oo = Oobj3->new(); + $cnt = 1; + + is(abs($oo), $ii, '0+ overload with abs() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); +} + +# EOF ```
p5pRT commented 16 years ago

From @jdhedden

Zefram wrote​:

It works OK for plain printf "%d"\, but not for addition. Also doesn't work for subtraction\, negation\, multiplication\, division\, remainder. Does work for bit shift and bitwise OR. Perhaps some code factoring is called for.

Jerry D. Hedden wrote​:

The attached patch adds tests for these to lib/overload.t. The tests are skipped if not using 64-bit ints. The following tests (which fail for me) are set as TODO​: 0+ (addition) subtraction multiplication division modulo (%) exponentiation (**) abs()

The attached patch (which supercedes my previous patches on the matter) fixes the above problem for 'abs' and 'neg' overloading. I added more tests as well.

The patch can be check using the following​:

./perl -Ilib -lwe '{ package t0; sub mynum { -36028797018963971 } use overload "0+" => \&mynum\, fallback => 1; } printf "%d\n"\, abs(t0​::mynum); printf "%d\n"\, abs(bless({}\, "t0"))'

./perl -Ilib -lwe '{ package t0; sub mynum { -36028797018963971 } use overload "0+" => \&mynum\, fallback => 1; } printf "%d\n"\, -&t0​::mynum; printf "%d\n"\, -bless({}\, "t0")'

p5pRT commented 16 years ago

From @jdhedden

overload.patch ```diff --- perl-current/pp.c +++ perl-current/pp.c @@ -2374,7 +2374,21 @@ dVAR; dSP; dTARGET; tryAMAGICun(neg); { dTOPss; - const int flags = SvFLAGS(sv); + int flags; + + while (SvAMAGIC(sv)) { + SV *tsv = AMG_CALLun(sv,numer); + if (!tsv) + break; + if (SvROK(tsv) && SvRV(tsv) == SvRV(sv)) { + SETi(-PTR2IV(SvRV(sv))); + RETURN; + } + else + sv = tsv; + } + + flags = SvFLAGS(sv); SvGETMAGIC(sv); if ((flags & SVf_IOK) || ((flags & (SVp_IOK | SVp_NOK)) == SVp_IOK)) { /* It's publicly an integer, or privately an integer-not-float */ @@ -2874,7 +2888,7 @@ { dVAR; dSP; dTARGET; tryAMAGICun(int); { - SV *sv = TOPs; + dTOPss; IV iv; /* XXX it's arguable that compiler casting to IV might be subtly different from modf (for numbers inside (IV_MIN,UV_MAX)) in which @@ -2931,16 +2945,35 @@ { dVAR; dSP; dTARGET; tryAMAGICun(abs); { - /* This will cache the NV value if string isn't actually integer */ - const IV iv = TOPi; + dTOPss; + IV iv; + + while (SvAMAGIC(sv)) { + SV *tsv = AMG_CALLun(sv,numer); + if (!tsv) + break; + if (SvROK(tsv) && SvRV(tsv) == SvRV(sv)) { + SETi(PTR2IV(SvRV(sv))); + RETURN; + } + else + sv = tsv; + } + iv = SvIV(sv); /* attempt to convert to IV if possible. */ - if (!SvOK(TOPs)) + if (!SvOK(sv)) { SETu(0); - else if (SvIOK(TOPs)) { + } + else if (SvIOK(sv)) { /* IVX is precise */ - if (SvIsUV(TOPs)) { - SETu(TOPu); /* force it to be numeric only */ + if (SvIsUV(sv)) { + SETu(SvUV(sv)); /* force it to be numeric only */ } else { + goto do_abs; + } + } + else if (SvROK(sv)) { + do_abs: if (iv >= 0) { SETi(iv); } else { @@ -2952,9 +2985,9 @@ SETu(IV_MIN); } } - } - } else{ - const NV value = TOPn; + } + else { + const NV value = SvNV(sv); if (value < 0.0) SETn(-value); else --- perl-current/lib/overload.t +++ perl-current/lib/overload.t @@ -47,7 +47,7 @@ package main; $| = 1; -use Test::More tests => 536; +use Test::More tests => 580; $a = new Oscalar "087"; @@ -1392,7 +1392,7 @@ is($o->[0], 1, 'int() numifies only once'); my $aref = []; - my $num_val = int($aref); + my $num_val = 0 + $aref; my $r = bless $aref, 'numify_self'; is(int($r), $num_val, 'numifies to self'); is($r->[0], 1, 'int() numifies once when returning self'); @@ -1405,3 +1405,138 @@ my $m = bless $aref, 'numify_by_fallback'; is(int($m), $num_val, 'numifies to usual reference value'); } + +SKIP: { + skip('64-bit int tests on 32-bit Perl', 12) + if ($Config::Config{'uvsize'} != 8); + + my $ii = 36028797018963971; # 2^55 + 3 + + package Oobj; + use overload '0+' => sub { ${$_[0]} += 1; $ii }, + 'fallback' => 1; + sub new { bless(\do{my $x = 0}, 'Oobj') } + + package main; + my $oo = Oobj->new(); + my $cnt = 1; + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(0+$oo, 0+$ii, '0+ overload on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + is("$oo", "$ii", '0+ overload with stringification on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(0-$oo, 0-$ii, '0+ overload with subtraction on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is(2*$oo, 2*$ii, '0+ overload with multiplication on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo/1, $ii/1, '0+ overload with division on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo%100, $ii%100, '0+ overload with modulo on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + TODO: { + local $TODO = '64-bit int overloading produces floating-point'; + is($oo**1, $ii**1, '0+ overload with exponentiation on 64-bit int'); + } + is($$oo, $cnt++, 'overload called once'); + + is($oo>>3, $ii>>3, '0+ overload with bit shift right on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo<<2, $ii<<2, '0+ overload with bit shift left on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo|0xFF00, $ii|0xFF00, '0+ overload with bitwise or on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is($oo&0xFF03, $ii&0xFF03, '0+ overload with bitwise and on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + ok($oo == $ii, '0+ overload with equality on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(int($oo), $ii, '0+ overload with int() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(abs($oo), $ii, '0+ overload with abs() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(+$oo, $ii, '0+ overload with unary + on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(-$oo, -$ii, '0+ overload with unary - on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + + ### Tests with large negative int + package Oobj2; + use overload '0+' => sub { ${$_[0]} += 1; -$ii }, + 'fallback' => 1; + sub new { bless(\do{my $x = 0}, 'Oobj2') } + + package main; + $oo = Oobj2->new(); + $cnt = 1; + + is(int($oo), -$ii, '0+ overload with int() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(abs($oo), $ii, '0+ overload with abs() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(+$oo, -$ii, '0+ overload with unary + on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + is(-$oo, $ii, '0+ overload with unary - on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + + ### Tests with 'abs' overloading + package Oobj3; + use overload 'abs' => sub { ${$_[0]} += 1; $ii }, + 'fallback' => 1; + sub new { bless(\do{my $x = 0}, 'Oobj3') } + + package main; + $oo = Oobj3->new(); + $cnt = 1; + + is(abs($oo), $ii, '0+ overload with abs() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); + + + ### Tests with 'neg' overloading + package Oobj4; + use overload 'neg' => sub { ${$_[0]} += 1; -$ii }, + 'fallback' => 1; + sub new { bless(\do{my $x = 0}, 'Oobj4') } + + package main; + $oo = Oobj4->new(); + $cnt = 1; + + is(-$oo, -$ii, '0+ overload with neg() on 64-bit int'); + is($$oo, $cnt++, 'overload called once'); +} + +# EOF ```
p5pRT commented 16 years ago

From @jdhedden

I'm really on a roll here. I think I know how to fix all of this stuff\, but there are dozens of fuctions to tackle. Please disregard this patch (and the previous ones\, too). I'll work all this out and submit one big patch that does it all. Sorry for the noise.

Zefram wrote​:

It works OK for plain printf "%d"\, but not for addition. Also doesn't work for subtraction\, negation\, multiplication\, division\, remainder. Does work for bit shift and bitwise OR. Perhaps some code factoring is called for.

Jerry D. Hedden wrote​:

The attached patch adds tests for these to lib/overload.t. The tests are skipped if not using 64-bit ints. The following tests (which fail for me) are set as TODO​: 0+ (addition) subtraction multiplication division modulo (%) exponentiation (**) abs()

The attached patch (which supercedes my previous patches on the matter) fixes the above problem for 'abs' and 'neg' overloading. I added more tests as well.

The patch can be check using the following​:

./perl -Ilib -lwe '{ package t0; sub mynum { -36028797018963971 } use overload "0+" => \&mynum\, fallback => 1; } printf "%d\n"\, abs(t0​::mynum); printf "%d\n"\, abs(bless({}\, "t0"))'

./perl -Ilib -lwe '{ package t0; sub mynum { -36028797018963971 } use overload "0+" => \&mynum\, fallback => 1; } printf "%d\n"\, -&t0​::mynum; printf "%d\n"\, -bless({}\, "t0")'

p5pRT commented 16 years ago

From rick@bort.ca

On Oct 08 2007\, Jerry D. Hedden wrote​:

I'm really on a roll here. I think I know how to fix all of this stuff\, but there are dozens of fuctions to tackle. Please disregard this patch (and the previous ones\, too). I'll work all this out and submit one big patch that does it all. Sorry for the noise.

Thanks for taking this on. I was going to do it but I'm pretty slow with stuff that doesn't affect me directly.

Please refactor the common stuff into a macro or function\, though. And we could use tests for these ops on plain ol' references\, too\, since they are also (I think) returning floats for 64bit when they should return ints. I'm pretty sure the tests will be welcome before the code patch too. If it's not too much trouble. :-)

P.S. I think PTR2IV should be PTR2UV for the reference case.

-- Rick Delaney rick@​bort.ca

p5pRT commented 16 years ago

From @tux

On Mon\, 8 Oct 2007 10​:18​:05 -0400\, Rick Delaney \rick@&#8203;bort\.ca wrote​:

On Oct 08 2007\, H.Merijn Brand wrote​:

I did two manual runs with -Duse64bitall on 32068

Linux 2.6.18.8-0.5 x86_64 Xeon(R) CPU E5320 @​ 1.86GHz/1596(4) x86_64 3951 Mb

All tests successful\, 70 tests and 758 subtests skipped. Files=1461\, Tests=185010\, 512 wallclock secs (258.50 cusr + 35.60 csys = 294.10 CPU)

HP-UX 11.23/64 U rx1620/64 Itanium 2/1600(2) ia64 2037 Mb ... not ok 531 - numifies to self # Failed test 'numifies to self' # at ../lib/overload.t line 1397. # got​: '6917529027645582112' # expected​: '6.91752902764558e+18'

The expected value is just

my $aref = \[\];
my $num\_val = 0 \+ $aref;

So it means that a regular reference in numeric context is returning an NV or that pp_add is not doing integer math when it should here. I would say that either one is a separate bug.

I'm pretty sure changing the line to

my $num\_val = int\($aref\);

will make the tests pass without changing the semantic of the test too much. But we could probably use some TODO tests to ensure that references in numeric context are always returning IVs. Or is there a platform with pointers bigger than IVs?

I don't think Configure allows that currently on the current supported systems

-- H.Merijn Brand Amsterdam Perl Mongers (http​://amsterdam.pm.org/) using & porting perl 5.6.2\, 5.8.x\, 5.9.x on HP-UX 10.20\, 11.00\, 11.11\, & 11.23\, SuSE 10.0 & 10.2\, AIX 4.3 & 5.2\, and Cygwin. http​://qa.perl.org http​://mirrors.develooper.com/hpux/ http​://www.test-smoke.org   http​://www.goldmark.org/jeff/stupid-disclaimers/