Skip to content

fix(libc): match reported wide scanf edge cases - #9331

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
MarcZX10:codex/wide-scan-reported-edge-cases
Oct 22, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
MarcZX10:codex/wide-scan-reported-edge-cases

Conversation

@MarcZX10

Copy link
Copy Markdown
Contributor

What

Implements two of the reported wide-scanner differences in #9321, based on the console scanner behavior reported in merged #8479: an incomplete floating-point exponent such as 1e+ converts its mantissa, and a format literal at end-of-input returns the assignment count (zero when nothing was assigned) instead of EOF. Adds regressions for sized and unsized buffer scans and the stream scanner.

The reported short %c field behavior is not included: #9321 asks to confirm its return count, partial-output extent and precedence first. The libc debt entry records that remaining gap. This pull request does not close #9321.

Tested

Linux x86-64, Debian GCC 12.2, Release. Configured with cmake -S . -B build -G Ninja -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DANYPS5_USE_SYSTEM_DEPS=OFF; built guest_wide_conversion_tests and guest_wide_stream_scan_tests; CTest passed guest_wide_conversion and guest_wide_stream_scan (2/2). No PS5 runtime or hardware test was performed.

python3 tools/check_conventions.py --base upstream/main reports zero errors and one debt-length warning on the existing libc scanner entry. The required local origin/main reference is 10,325 commits behind upstream; checking against it reports unrelated historical findings outside this change. tools/pr_overlap.py reports dependencies listed below.

Checklist

@github-actions github-actions Bot added the fix error Closes an issue (Fixes #N) label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3965 docs/dev/debt/libc.md
PR 5021 docs/dev/debt/libc.md
PR 6469 docs/dev/debt/libc.md
PR 6565 docs/dev/debt/libc.md
PR 6879 core/libs/prx/libc/src/ScanningWide.cpp
docs/dev/debt/libc.md
PR 7081 docs/dev/debt/libc.md
PR 7447 docs/dev/debt/libc.md
PR 7648 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 8965 docs/dev/debt/libc.md
PR 9001 docs/dev/debt/libc.md
PR 9038 docs/dev/debt/libc.md
PR 9133 docs/dev/debt/libc.md
PR 9170 docs/dev/debt/libc.md
PR 9408 docs/dev/debt/libc.md
PR 9447 docs/dev/debt/libc.md
PR 9526 docs/dev/debt/libc.md
PR 9527 docs/dev/debt/libc.md
PR 9669 docs/dev/debt/libc.md

@wachi35676

Copy link
Copy Markdown
Contributor

Windows run, the platform the description doesn't list, plus a wider set of inputs compared with main and with FreeBSD 11.4.

Head c092916666 merged onto main a6dcea927 (clean). Windows 11 Home 10.0.26300, Ryzen 5 9600X, WinLibs GCC 15.2.0 (MinGW-w64 UCRT), Release, BUILD_TESTING=ON.

  • guest_wide_conversion, guest_wide_stream_scan, guest_scan_count, guest_wide_formatting, guest_stdio_variadic, guest_wide_string: 6/6, 3 runs.
  • Negative control: with ScanningWide.cpp from main, guest_wide_conversion fails and guest_wide_stream_scan aborts (0xc0000409).
  • check_conventions.py: one warning, debt-length on docs/dev/debt/libc.md:128.

swscanf with %lf (destination -99), main → PR:

  • 1e+, 1e, 1e-, 1e+x, 1ex, 0x1p, 0x1p+: 0 → 1, value 1. 1.5e: 0 → 1, value 1.5.
  • -, ., e5, in, nan(, 0x: 0 on both. 1e+5, inf: 1 on both.

So the change is not limited to 1e+: every field ending in an exponent marker or sign now converts its mantissa, including 1e, 1ex and hex 0x1p. The updated debt entry says "other incomplete exponent forms are not separately verified", but their behaviour changes too. FreeBSD 11.4's parsefloat also commits to the last valid prefix, so it gives 1 for all of these.

Literals, main → PR:

  • ""/x, ""/ x, ""/x%d, ""/%%: -1 → 0.
  • x/x%d: -1 on both. /x: 0 on both. a/b: 0 on both. 5/%dx: 1 on both.

For a literal that meets end of input, FreeBSD 11.4's vfwscanf goes to input_failure and returns EOF when nothing was converted (vfwscanf.c L490-L495, L783-L784). C11 7.29.2.2 also calls running out of input an input failure. So the new 0 rests only on the firmware result reported in #8479; neither FreeBSD nor the standard supports it. The %% case changes too, so it would help to say whether #8479 covered %%.

AI-assisted: yes (Claude Code); results above were run on this machine.

@xXPBSXx

xXPBSXx commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Linux data point for c092916666 (WSL2, Ubuntu 24.04, kernel 6.18 - a virtualised kernel, not bare-metal Linux; GCC 13.3, Release, BUILD_TESTING=ON), this head merged onto main df2569d2c5 (clean merge). I built guest_wide_conversion_tests and guest_wide_stream_scan_tests and ran the executable directly (exit code, not through ctest):

  • With the PR: exit code 0 for both.
  • Without it (the PR's test sources on top of main, with core/libs/prx/libc/src/ScanningWide.cpp reset to main): guest_wide_conversion_tests exits 1 and guest_wide_stream_scan_tests aborts (SIGABRT, exit code 134).
    Not tested: macOS, Windows, the rest of the test suite.

AI-assisted (Claude Code).

@harisibrar1 harisibrar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked against main 8b3e398 and FreeBSD 11.4 lib/libc/stdio/vfwscanf.c (release/11.4.0), plus the "Remaining differences" in #8479:

  • The debt line seems to read main's "FreeBSD's vfwscanf reports an input failure for the first and converts the second" as being about 0x. On main, "the first" is the literal mismatch: vfwscanf.c L493-495 goes to input_failure when the character differs.
  • For 0x with %x, FreeBSD's parseint pushes back the x and converts the 0 (L405-418, with PFXOK from L565). So 0x/%x returns 1 with 0 there, not an input failure.
  • As written, the entry now says the wrong thing about 0x, and it no longer records that a/b%d returns 0 here but EOF on FreeBSD. Could the literal-mismatch divergence be kept, and the 0x clause say that FreeBSD converts it as 0?
  • Small one: the literal-at-end-of-input sentence could also say it follows #8479 and that FreeBSD returns EOF there, as the exponent sentence does.
  • #8306, #9087 and #6044 are all still open, and this PR merges and passes on main without them. If they are only shared-file overlaps, moving them out of "Depends on" might keep this from being held up.

Reviewed with AI assistance (Claude Code); the checks above were run locally on my machine.

@MarcZX10

Copy link
Copy Markdown
Contributor Author

The new measurements in #9321 resolve the %% edge case too: the bundled libc.prx returned EOF for empty input with %%, while ordinary literals at end of input returned the assignment count. In this diff, Conversion() handles %% by calling the same Literal(u'%') method changed here to return Outcome::Matching on EOF. That means this change appears to make empty-input %% return 0, contrary to the newly reported firmware result.

Could the %% path preserve input-failure behavior while ordinary format literals use the measured assignment-count result, with buffer and stream regressions for both cases? The issue author identifies these as separate outcomes; the existing PR tests cover ordinary x but not %%.

@adriadam10 adriadam10 self-assigned this Oct 22, 2026
@adriadam10
adriadam10 force-pushed the codex/wide-scan-reported-edge-cases branch from c092916 to e8c7362 Compare October 22, 2026 18:14
@adriadam10
adriadam10 merged commit 8580e7e into boykopovar:main Oct 22, 2026
17 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the incomplete-exponent and literal-at-EOF changes against the measurements in #9321 (bundled libc.prx, PPSA03671); tests fail without the change (also confirmed by @wachi35676 on Windows and @xXPBSXx on Linux).

Added on top:

  • %% at end of input returns EOF again, as measured (Conversion() shared the changed Literal); tests for buffer, unsized and stream scans.
  • Debt entry: the 0x clause now says FreeBSD converts the 0, and the literal-mismatch divergence from FreeBSD is kept (@harisibrar1's review).

merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs fix error Closes an issue (Fixes #N) libc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

libc: track reported wide scanf incomplete-field and literal-EOF differences

5 participants