Commit ddbb4c9
committed
fix(core): a negative unsigned value overflows instead of failing to parse (#2362)
Byte, UInt16, UInt32 and UInt64's Parse/TryParse now accept a negative-indicating
token grammatically and reject it as a RANGE failure, matching .NET
(Number.Parsing.cs:157, `!TInteger.IsSigned && number.IsNegative`). This ends a
deliberate deviation that had been documented on TryParseUnsignedCore for about a
year.
UInt32::Parse("-1") was FormatException is OverflowException
UInt32::Parse("-0") was FormatException is 0
UInt32::Parse("-0.0") was FormatException is OverflowException
THE OLD ARGUMENT WAS WRONG ON ITS OWN TERMS, and that is the whole ticket. The
deviation was justified on the grounds that the only practical effect was "which
exception TYPE a clearly-invalid input throws". But "-0" is not a clearly-invalid
input. It is a VALID one that returns 0, because Number.Parsing.Common.cs:259-268
clears IsNegative when StateNonZero was never set and no decimal separator was
seen. So the old rule did not substitute one exception for another -- it rejected
an input .NET accepts.
THE TWO ROWS COULD NOT BE SEPARATED. Repairing "-0" alone would have aligned one
spelling and left "-1" throwing the wrong type, which is worse than the old
consistent rule. So the grammar (accept '-' and a closed "(...)"), the all-zero
normalisation (clear the sign when there is no nonzero digit and no decimal
separator) and the negative-is-overflow rejection all landed together.
The asymmetry between "-0" and "-0.0" is .NET's, not a convenience: a decimal
separator sets StateDecimal and the sign is then NOT cleared. That single
`!sawDecimal` guard is the only thing separating them -- and this makes #2356's
mutation M2 OBSERVABLE for the first time. #2356 transcribed the same guard into
the signed core and recorded honestly that its mutation was not caught, because
negating zero is zero either way. It is caught now, in the unsigned core.
Note also that the rejection sits in the SAME disjunction as the digit-count
overflow in .NET, so a caller cannot tell the two apart -- both are
OverflowException -- which is why the order between them here is free, and the
comment says so rather than implying a derivation that does not exist.
Three pins inverted (two of them asserting FormatException for "-1", one asserting
it for "-0"), one case added, and two further pins' comments corrected: they kept
passing across the change because TryParse reports both failures the same way,
which is precisely why the new case pins the exception TYPE that TryParse cannot
show.
Six mutations caught: drop the rejection; drop the sign half of the normalisation;
clear the sign ignoring sawDecimal; leading loop stops accepting '-'; leading loop
stops accepting '('; drop the unclosed-paren check.
A SEVENTH IS A NO-OP AND IS RECORDED AS ONE rather than counted as a pass:
reinstating the old grammar-level rejection immediately before the digit scan
changes nothing, because the leading-token loop has already consumed the '-' by
then. That is the same shape #2138 recorded -- an allow-list and a deny-list are
equivalent once the token is gone.
Downstream, measured per SA-2 condition 5: neither cna nor mobile-eggbert calls
any unsigned Parse/TryParse -- zero sites in both. Neither was modified. TryParse
callers see no change at all; only the exception type moved, plus the one row that
stopped failing.
Gate: 17,302 run, 17,302 passed, 0 failed, 0 skipped across 38 executables, GREEN.
docs/Migration-UnsignedNegativeOverflows.md1 parent c079845 commit ddbb4c9
6 files changed
Lines changed: 268 additions & 50 deletions
File tree
- docs
- modules/core
- include/System/detail
- tests/System
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
Lines changed: 71 additions & 32 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
118 | 118 | | |
119 | 119 | | |
120 | 120 | | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
132 | 143 | | |
133 | 144 | | |
134 | 145 | | |
| |||
443 | 454 | | |
444 | 455 | | |
445 | 456 | | |
446 | | - | |
447 | | - | |
448 | | - | |
449 | | - | |
450 | | - | |
451 | | - | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
452 | 477 | | |
453 | 478 | | |
454 | 479 | | |
| |||
475 | 500 | | |
476 | 501 | | |
477 | 502 | | |
478 | | - | |
| 503 | + | |
479 | 504 | | |
480 | 505 | | |
481 | 506 | | |
482 | 507 | | |
483 | 508 | | |
484 | | - | |
485 | | - | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
486 | 514 | | |
487 | 515 | | |
488 | 516 | | |
489 | 517 | | |
490 | | - | |
491 | 518 | | |
492 | 519 | | |
493 | 520 | | |
| |||
497 | 524 | | |
498 | 525 | | |
499 | 526 | | |
500 | | - | |
501 | | - | |
502 | | - | |
| 527 | + | |
503 | 528 | | |
504 | 529 | | |
505 | 530 | | |
506 | 531 | | |
507 | | - | |
508 | | - | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
509 | 537 | | |
510 | 538 | | |
511 | 539 | | |
512 | 540 | | |
513 | 541 | | |
514 | 542 | | |
515 | 543 | | |
516 | | - | |
517 | | - | |
| 544 | + | |
518 | 545 | | |
519 | 546 | | |
520 | 547 | | |
521 | | - | |
522 | | - | |
523 | | - | |
524 | | - | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
525 | 564 | | |
526 | 565 | | |
527 | 566 | | |
| |||
0 commit comments