Skip to content

fix: align math/tonumber/format/table.insert-remove with Lua 5.4 semantics - #27

Merged
esrrhs merged 1 commit into
masterfrom
fix/lua54-math-tonumber-format-table-bounds
Oct 9, 2026
Merged

esrrhs merged 1 commit into
masterfrom
fix/lua54-math-tonumber-format-table-bounds

Conversation

@esrrhs

@esrrhs esrrhs commented Oct 8, 2026

Copy link
Copy Markdown
Owner

fix: align math/tonumber/format/table.insert-remove with Lua 5.4 semantics

Found by a third differential-testing pass against Lua 5.4.9 (semantics
verified against the 5.4.6 sources: lmathlib.c, lobject.c l_str2d,
lstrlib.c str_format, ltablib.c).

math library:

  • math.floor/ceil now follow Lua's pushnumint: results that fit in
    int64 are returned as integer; inf/-inf/nan/values >= 2^63 stay
    float instead of always returning float.
  • math.fmod distinguishes integer pairs (integer result, C truncation
    semantics; divisor zero raises, mininteger % -1 is 0) from mixed/
    float pairs (always float, divide-by-zero is NaN, no error).
  • math.modf integer part truncates toward zero and returns integer when
    integral; fractional part is +0.0 when there is no fraction (integer
    values and inf), matching lua 5.4 including modf(inf)=(inf, 0.0).

tonumber:

  • reject 'inf'/'nan' spellings in any case (Lua only accepts constructed
    math.huge/0/0, never literals), while accepting exponent overflow:
    tonumber('1e999') -> inf, '1e-999' -> 0.0, hex floats likewise.
    Unify the three parser paths (boost charconv decimal, strtod fallback,
    JIT inline FlTonumber) on l_str2d rules; ERANGE saturates via strtod.

string.format:

  • %d %i %u %x %X %o %c now require an exact integer via the
    checkinteger rule (3.9, 1e20, inf raise 'number has no integer
    representation') instead of silently truncating; exact-value floats
    like 2.0 still work.
  • unknown conversion specifiers and dangling '%' raise 'invalid
    conversion' instead of being copied through verbatim (covers %z,
    '%1', '%.f'; Lua does not support '' either).

table.insert / table.remove:

  • insert raises 'position out of bounds' when pos is outside
    [1, #t+1] (incl. negatives and non-integer floats) instead of
    silently doing nothing / creating holes.
  • remove validates only an explicitly given position against
    [1, #t+1]; the default/nil position (= #t, including empty tables)
    stays unchecked; pos == #t+1 is valid and returns nil, and no longer
    wipes t[#t].

JIT parity: floor/ceil/fmod/modf/insert/remove had inline code
generation in c_gen that bypassed the natives, so the native fixes
alone left TCC/GCC on the old behavior. Add FlPushNumInt/FlMathFloor/
FlMathCeil/FlMathFmod/FlMathModf to c_runtime_header.h and route both
the JIT inline paths and the natives through the shared implementation.

Note: the runtime header is embedded in c_gen as an R"( raw string, so
the fmod error message cannot end right before a quote (')' + quote
would terminate the raw literal).

Tests: add test_math_lua54_types, test_basic_tonumber_lua54,
test_format_lua54_strict (all run on TCC/GCC/interp); rewrite
test_math_fmod_zero and the table boundary tests that encoded the old
silent/out-of-range behavior. Differential suite: 0 divergence on the
math/table/number cases; full 1515-test suite passes.

…ntics

Found by a third differential-testing pass against Lua 5.4.9 (semantics
verified against the 5.4.6 sources: lmathlib.c, lobject.c l_str2d,
lstrlib.c str_format, ltablib.c).

math library:
- math.floor/ceil now follow Lua's pushnumint: results that fit in
  int64 are returned as integer; inf/-inf/nan/values >= 2^63 stay
  float instead of always returning float.
- math.fmod distinguishes integer pairs (integer result, C truncation
  semantics; divisor zero raises, mininteger % -1 is 0) from mixed/
  float pairs (always float, divide-by-zero is NaN, no error).
- math.modf integer part truncates toward zero and returns integer when
  integral; fractional part is +0.0 when there is no fraction (integer
  values and inf), matching lua 5.4 including modf(inf)=(inf, 0.0).

tonumber:
- reject 'inf'/'nan' spellings in any case (Lua only accepts constructed
  math.huge/0/0, never literals), while accepting exponent overflow:
  tonumber('1e999') -> inf, '1e-999' -> 0.0, hex floats likewise.
  Unify the three parser paths (boost charconv decimal, strtod fallback,
  JIT inline FlTonumber) on l_str2d rules; ERANGE saturates via strtod.

string.format:
- %d %i %u %x %X %o %c now require an exact integer via the
  checkinteger rule (3.9, 1e20, inf raise 'number has no integer
  representation') instead of silently truncating; exact-value floats
  like 2.0 still work.
- unknown conversion specifiers and dangling '%' raise 'invalid
  conversion' instead of being copied through verbatim (covers %z,
  '%1', '%.*f'; Lua does not support '*' either).

table.insert / table.remove:
- insert raises 'position out of bounds' when pos is outside
  [1, #t+1] (incl. negatives and non-integer floats) instead of
  silently doing nothing / creating holes.
- remove validates only an explicitly given position against
  [1, #t+1]; the default/nil position (= #t, including empty tables)
  stays unchecked; pos == #t+1 is valid and returns nil, and no longer
  wipes t[#t].

JIT parity: floor/ceil/fmod/modf/insert/remove had inline code
generation in c_gen that bypassed the natives, so the native fixes
alone left TCC/GCC on the old behavior. Add FlPushNumInt/FlMathFloor/
FlMathCeil/FlMathFmod/FlMathModf to c_runtime_header.h and route both
the JIT inline paths and the natives through the shared implementation.

Note: the runtime header is embedded in c_gen as an R"( raw string, so
the fmod error message cannot end right before a quote (')' + quote
would terminate the raw literal).

Tests: add test_math_lua54_types, test_basic_tonumber_lua54,
test_format_lua54_strict (all run on TCC/GCC/interp); rewrite
test_math_fmod_zero and the table boundary tests that encoded the old
silent/out-of-range behavior. Differential suite: 0 divergence on the
math/table/number cases; full 1515-test suite passes.
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 94.02985% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.20%. Comparing base (6081541) to head (30a9c0d).

Files with missing lines Patch % Lines
src/compile/c_gen.cpp 76.92% 3 Missing ⚠️
src/native/basic/native_basic.cpp 95.23% 1 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #27      +/-   ##
==========================================
+ Coverage   88.07%   88.20%   +0.12%     
==========================================
  Files         124      124              
  Lines       25234    25248      +14     
==========================================
+ Hits        22225    22269      +44     
+ Misses       3009     2979      -30     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@esrrhs
esrrhs merged commit dafdac7 into master Oct 9, 2026
9 checks passed
@esrrhs
esrrhs deleted the fix/lua54-math-tonumber-format-table-bounds branch October 9, 2026 14:47
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

Successfully merging this pull request may close these issues.

2 participants