1#!./perl -w 2 3BEGIN { 4 chdir 't' if -d 't'; 5 require './test.pl'; 6 set_up_inc('../lib'); 7} 8 9use strict; 10 11use Config; 12 13# Tests of post/pre - increment/decrement operators. 14 15# Verify that addition/subtraction properly upgrade to doubles. 16# These tests are only significant on machines with 32 bit longs, 17# and two's complement negation, but shouldn't fail anywhere. 18 19my $a = 2147483647; 20my $c=$a++; 21cmp_ok($a, '==', 2147483648, "postincrement properly upgrades to double"); 22 23$a = 2147483647; 24$c=++$a; 25cmp_ok($a, '==', 2147483648, "preincrement properly upgrades to double"); 26 27$a = 2147483647; 28$a=$a+1; 29cmp_ok($a, '==', 2147483648, "addition properly upgrades to double"); 30 31$a = -2147483648; 32$c=$a--; 33cmp_ok($a, '==', -2147483649, "postdecrement properly upgrades to double"); 34 35$a = -2147483648; 36$c=--$a; 37cmp_ok($a, '==', -2147483649, "predecrement properly upgrades to double"); 38 39$a = -2147483648; 40$a=$a-1; 41cmp_ok($a, '==', -2147483649, "subtraction properly upgrades to double"); 42 43$a = 2147483648; 44$a = -$a; 45$c=$a--; 46cmp_ok($a, '==', -2147483649, 47 "negation and postdecrement properly upgrade to double"); 48 49$a = 2147483648; 50$a = -$a; 51$c=--$a; 52cmp_ok($a, '==', -2147483649, 53 "negation and predecrement properly upgrade to double"); 54 55$a = 2147483648; 56$a = -$a; 57$a=$a-1; 58cmp_ok($a, '==', -2147483649, 59 "negation and subtraction properly upgrade to double"); 60 61$a = 2147483648; 62$b = -$a; 63$c=$b--; 64cmp_ok($b, '==', -$a-1, "negation, postdecrement and additional negation"); 65 66$a = 2147483648; 67$b = -$a; 68$c=--$b; 69cmp_ok($b, '==', -$a-1, "negation, predecrement and additional negation"); 70 71$a = 2147483648; 72$b = -$a; 73$b=$b-1; 74cmp_ok($b, '==', -(++$a), 75 "negation, subtraction, preincrement and additional negation"); 76 77$a = undef; 78is($a++, '0', "postinc undef returns '0'"); 79 80$a = undef; 81is($a--, undef, "postdec undef returns undef"); 82 83# Verify that shared hash keys become unshared. 84 85sub check_same { 86 my ($orig, $suspect) = @_; 87 my $fail; 88 while (my ($key, $value) = each %$suspect) { 89 if (exists $orig->{$key}) { 90 if ($orig->{$key} ne $value) { 91 print "# key '$key' was '$orig->{$key}' now '$value'\n"; 92 $fail = 1; 93 } 94 } else { 95 print "# key '$key' is '$orig->{$key}', unexpect.\n"; 96 $fail = 1; 97 } 98 } 99 foreach (keys %$orig) { 100 next if (exists $suspect->{$_}); 101 print "# key '$_' was '$orig->{$_}' now missing\n"; 102 $fail = 1; 103 } 104 ok (!$fail, "original hashes unchanged"); 105} 106 107my (%orig) = my (%inc) = my (%dec) = my (%postinc) = my (%postdec) 108 = (1 => 1, ab => "ab"); 109my %up = (1=>2, ab => 'ac'); 110my %down = (1=>0, ab => -1); 111 112foreach (keys %inc) { 113 my $ans = $up{$_}; 114 my $up; 115 eval {$up = ++$_}; 116 is($up, $ans, "key '$_' incremented correctly"); 117 is($@, '', "no error condition"); 118} 119 120check_same (\%orig, \%inc); 121 122foreach (keys %dec) { 123 my $ans = $down{$_}; 124 my $down; 125 eval {$down = --$_}; 126 is($down, $ans, "key '$_' decremented correctly"); 127 is($@, '', "no error condition"); 128} 129 130check_same (\%orig, \%dec); 131 132foreach (keys %postinc) { 133 my $ans = $postinc{$_}; 134 my $up; 135 eval {$up = $_++}; 136 is($up, $ans, "assignment preceded postincrement"); 137 is($@, '', "no error condition"); 138} 139 140check_same (\%orig, \%postinc); 141 142foreach (keys %postdec) { 143 my $ans = $postdec{$_}; 144 my $down; 145 eval {$down = $_--}; 146 is($down, $ans, "assignment preceded postdecrement"); 147 is($@, '', "no error condition"); 148} 149 150check_same (\%orig, \%postdec); 151 152{ 153 no warnings 'uninitialized'; 154 my ($x, $y); 155 eval { 156 $y ="$x\n"; 157 ++$x; 158 }; 159 cmp_ok($x, '==', 1, "preincrement of previously uninitialized variable"); 160 is($@, '', "no error condition"); 161 162 my ($p, $q); 163 eval { 164 $q ="$p\n"; 165 --$p; 166 }; 167 cmp_ok($p, '==', -1, "predecrement of previously uninitialized variable"); 168 is($@, '', "no error condition"); 169} 170 171$a = 2147483648; 172$c=--$a; 173cmp_ok($a, '==', 2147483647, "predecrement properly downgrades from double"); 174 175 176$a = 2147483648; 177$c=$a--; 178cmp_ok($a, '==', 2147483647, "postdecrement properly downgrades from double"); 179 180{ 181 use integer; 182 my $x = 0; 183 $x++; 184 cmp_ok($x, '==', 1, "(void) i_postinc"); 185 $x--; 186 cmp_ok($x, '==', 0, "(void) i_postdec"); 187} 188 189SKIP: { 190 if ($Config{uselongdouble} && 191 ($Config{d_long_double_style_ieee_doubledouble})) { 192 skip "the double-double format is weird", 1; 193 } 194 unless ($Config{d_double_style_ieee}) { 195 skip "the doublekind $Config{doublekind} is not IEEE", 1; 196 } 197 198# I'm sure that there's an IBM format with a 48 bit mantissa 199# IEEE doubles have a 53 bit mantissa 200# 80 bit long doubles have a 64 bit mantissa 201# sparcs have a 112 bit mantissa for their long doubles. Just to be awkward :-) 202 203my $h_uv_max = 1 + (~0 >> 1); 204my $found; 205for my $n (47..113) { 206 my $power_of_2 = 2**$n; 207 my $plus_1 = $power_of_2 + 1; 208 next if $plus_1 != $power_of_2; 209 my ($start_p, $start_n); 210 if ($h_uv_max > $power_of_2 / 2) { 211 my $uv_max = 1 + 2 * (~0 >> 1); 212 # UV_MAX is 2**$something - 1, so subtract 1 to get the start value 213 $start_p = $uv_max - 1; 214 # whereas IV_MIN is -(2**$something), so subtract 2 215 $start_n = -$h_uv_max + 2; 216 print "# Mantissa overflows at 2**$n ($power_of_2)\n"; 217 print "# But max UV ($uv_max) is greater so testing that\n"; 218 } else { 219 print "# Testing 2**$n ($power_of_2) which overflows the mantissa\n"; 220 $start_p = int($power_of_2 - 2); 221 $start_n = -$start_p; 222 my $check = $power_of_2 - 2; 223 die "Something wrong with our rounding assumptions: $check vs $start_p" 224 unless $start_p == $check; 225 } 226 227 foreach ([$start_p, '++$i', 'pre-inc', 'inc'], 228 [$start_p, '$i++', 'post-inc', 'inc'], 229 [$start_n, '--$i', 'pre-dec', 'dec'], 230 [$start_n, '$i--', 'post-dec', 'dec']) { 231 my ($start, $action, $description, $act) = @$_; 232 my $code = eval << "EOC" or die $@; 233sub { 234 no warnings 'imprecision'; 235 my \$i = \$start; 236 for(0 .. 3) { 237 my \$a = $action; 238 } 239} 240EOC 241 242 warning_is($code, undef, "$description under no warnings 'imprecision'"); 243 244 $code = eval << "EOC" or die $@; 245sub { 246 use warnings 'imprecision'; 247 my \$i = \$start; 248 for(0 .. 3) { 249 my \$a = $action; 250 } 251} 252EOC 253 254 warnings_like($code, [(qr/Lost precision when ${act}rementing -?\d+/) x 2], 255 "$description under use warnings 'imprecision'"); 256 } 257 258 # Verify warnings on incrementing/decrementing large values 259 # whose integral part will not fit in NVs. [GH #18333] 260 foreach ([$start_n - 4, '$i++', 'negative large value', 'inc'], 261 [$start_p + 4, '$i--', 'positive large value', 'dec']) { 262 my ($start, $action, $description, $act) = @$_; 263 my $code = eval << "EOC" or die $@; 264sub { 265 use warnings 'imprecision'; 266 my \$i = \$start; 267 $action; 268} 269EOC 270 warning_like($code, qr/Lost precision when ${act}rementing /, 271 "${act}rementing $description under use warnings 'imprecision'"); 272 } 273 274 $found = 1; 275 last; 276} 277 278ok($found, "found a NV value which overflows the mantissa"); 279 280} # SKIP 281 282# these will segfault if they fail 283 284sub PVBM () { 'foo' } 285{ my $dummy = index 'foo', PVBM } 286 287isnt(scalar eval { my $pvbm = PVBM; $pvbm++ }, undef, "postincrement defined"); 288isnt(scalar eval { my $pvbm = PVBM; $pvbm-- }, undef, "postdecrement defined"); 289isnt(scalar eval { my $pvbm = PVBM; ++$pvbm }, undef, "preincrement defined"); 290isnt(scalar eval { my $pvbm = PVBM; --$pvbm }, undef, "predecrement defined"); 291 292# #9466 293 294# don't use pad TARG when the thing you're copying is a ref, or the referent 295# won't get freed. 296{ 297 package P9466; 298 my $x; 299 sub DESTROY { $x = 1 } 300 for (0..1) { 301 $x = 0; 302 my $a = bless {}; 303 my $b = $_ ? $a++ : $a--; 304 undef $a; undef $b; 305 ::is($x, 1, "9466 case $_"); 306 } 307} 308 309# *Do* use pad TARG if it is actually a named variable, even when the thing 310# you’re copying is a ref. The fix for #9466 broke this. 311{ 312 package P9466_2; 313 my $x; 314 sub DESTROY { $x = 1 } 315 for (2..3) { 316 $x = 0; 317 my $a = bless {}; 318 my $b; 319 use integer; 320 if ($_ == 2) { 321 $b = $a--; # sassign optimised away 322 } 323 else { 324 $b = $a++; 325 } 326 ::is(ref $b, __PACKAGE__, 'i_post(in|de)c/TARGMY on ref'); 327 undef $a; undef $b; 328 ::is($x, 1, "9466 case $_"); 329 } 330} 331 332$_ = ${qr //}; 333$_--; 334is($_, -1, 'regexp--'); 335{ 336 no warnings 'numeric'; 337 $_ = ${qr //}; 338 $_++; 339 is($_, 1, 'regexp++'); 340} 341 342if ($::IS_EBCDIC) { 343 $_ = v129; 344 $_++; 345 isnt(ref\$_, 'VSTRING', '++ flattens vstrings'); 346} 347else { 348 $_ = v97; 349 $_++; 350 isnt(ref\$_, 'VSTRING', '++ flattens vstrings'); 351} 352 353sub TIESCALAR {bless\my $x} 354sub STORE { ++$store::called } 355tie my $t, ""; 356{ 357 $t = $_++; 358 $t = $_--; 359 use integer; 360 $t = $_++; 361 $t = $_--; 362} 363is $store::called, 4, 'STORE called on "my" target'; 364 365{ 366 # Temporarily broken between before 5.6.0 (b162f9ea/21f5b33c) and 367 # between 5.21.5 and 5.21.6 (9e319cc4fd) 368 my $x = 7; 369 $x = $x++; 370 is $x, 7, '$lex = $lex++'; 371 $x = 7; 372 # broken in b162f9ea (5.6.0); fixed in 5.21.6 373 use integer; 374 $x = $x++; 375 is $x, 7, '$lex = $lex++ under use integer'; 376} 377 378{ 379 # RT #126637 - it should refuse to modify globs 380 no warnings 'once'; 381 *GLOB126637 = []; 382 383 eval 'my $y = ++$_ for *GLOB126637'; 384 like $@, qr/Modification of a read-only value/, '++*GLOB126637'; 385 eval 'my $y = --$_ for *GLOB126637'; 386 like $@, qr/Modification of a read-only value/, '--*GLOB126637'; 387 eval 'my $y = $_++ for *GLOB126637'; 388 like $@, qr/Modification of a read-only value/, '*GLOB126637++'; 389 eval 'my $y = $_-- for *GLOB126637'; 390 like $@, qr/Modification of a read-only value/, '*GLOB126637--'; 391 392 use integer; 393 394 eval 'my $y = ++$_ for *GLOB126637'; 395 like $@, qr/Modification of a read-only value/, 'use int; ++*GLOB126637'; 396 eval 'my $y = --$_ for *GLOB126637'; 397 like $@, qr/Modification of a read-only value/, 'use int; --*GLOB126637'; 398 eval 'my $y = $_++ for *GLOB126637'; 399 like $@, qr/Modification of a read-only value/, 'use int; *GLOB126637++'; 400 eval 'my $y = $_-- for *GLOB126637'; 401 like $@, qr/Modification of a read-only value/, 'use int; *GLOB126637--'; 402} 403 404# Exercises sv_inc() incrementing UV to UV, UV to NV 405SKIP: { 406 $a = ~1; # assumed to be UV_MAX - 1 407 408 if ($Config{uvsize} eq '4') { 409 cmp_ok(++$a, '==', 4294967295, "preincrement to UV_MAX"); 410 cmp_ok(++$a, '==', 4294967296, "preincrement past UV_MAX"); 411 } 412 elsif ($Config{uvsize} eq '8') { 413 cmp_ok(++$a, '==', 18446744073709551615, "preincrement to UV_MAX"); 414 # assumed that NV can hold 2 ** 64 without rounding. 415 cmp_ok(++$a, '==', 18446744073709551616, "preincrement past UV_MAX"); 416 } 417 else { 418 skip "the uvsize $Config{uvsize} is neither 4 nor 8", 2; 419 } 420} # SKIP 421 422# Incrementing/decrementing Inf/NaN should not trigger 'imprecision' warnings 423# [GH #18333, #18388] 424# Note these tests only check for warnings; t/op/infnan.t has tests that 425# checks the result of incrementing/decrementing Inf/NaN. 426foreach my $infnan ('+Inf', '-Inf', 'NaN') { 427 my $start = $infnan + 0; 428 SKIP: { 429 skip "NV does not have $infnan", 2 430 unless ($infnan eq 'NaN' ? $Config{d_double_has_nan} : $Config{d_double_has_inf}); 431 foreach (['$i++', 'inc'], 432 ['$i--', 'dec']) { 433 my ($action, $act) = @$_; 434 my $code = eval <<"EOC" or die $@; 435sub { 436 use warnings 'imprecision'; 437 my \$i = \$start; 438 $action; 439} 440EOC 441 warning_is($code, undef, "${act}rementing $infnan under use warnings 'imprecision'"); 442 } 443 } # SKIP 444} 445 446done_testing(); 447