Skip to content

fix: preserve full 256-bit precision for integer literals#55

Merged
refcell merged 6 commits into
mainfrom
nox/int-literals-bug
Mar 9, 2026
Merged

fix: preserve full 256-bit precision for integer literals#55
refcell merged 6 commits into
mainfrom
nox/int-literals-bug

Conversation

@N0xMare
Copy link
Copy Markdown
Collaborator

@N0xMare N0xMare commented Mar 8, 2026

Summary

Fixes #40 — Integer literals >2^64 were silently truncated to u64.

Also upgrades typeck const evaluation from u64 to full U256 precision.


Commit 1: fix: preserve full 256-bit precision for integer literals

Problem

Lit::Int(u64) in the AST meant the parser accumulated lexer bytes into u128 then cast to u64, destroying precision. Any literal value above 18,446,744,073,709,551,615 was silently wrong — blocking MAX_UINT, realistic token amounts (e.g. 10^21 wei), and address literals.

The lexer already produced correct [u8; 32] big-endian bytes via decimal_to_bytes32(). The parser threw them away.

Fix

  • Lit::Int(u64, ...)Lit::Int([u8; 32], ...) — carry raw bytes through the AST
  • Parser — copy lexer bytes directly instead of the lossy u128→u64 accumulation
  • IR lowering — values <2^63 use SmallInt(i64) (same path as before), values ≥2^63 use LargeInt(hex_string) which codegen already handles correctly
  • Typeck eval_const_expr — overflow guard for values >u64 (superseded by commit 2)
  • All other downstream consumers (array index/length extraction) updated

No behavioral change for small values — values 0 to 2^63-1 follow the exact same SmallInt code path.

Tests added (commit 1)

  • 3 parser tests: 2^64 preserved, MAX_UINT bytes correct, small value unchanged
  • 4 e2e bytecode tests: 2^64, 10^21, 2^256-1, and 42 all produce correct PUSH instructions

Commit 2: feat(typeck): upgrade const eval to full U256 precision

Problem

eval_const_expr worked on u64 — constants >2^64 returned 0, arithmetic overflowed at 10^19, and shifts were masked with & 63 instead of % 256.

Fix

  • ConstValue.value: u64[u8; 32] (big-endian 256-bit)
  • eval_const_expr: rewritten to use ruint::aliases::U256 arithmetic internally
    • All arithmetic uses 256-bit wrapping (matches EVM semantics)
    • Shifts check r >= 256 → return zero (correct EVM behavior)
    • Division/modulo use is_zero() guard
    • Lit::Int passes [u8; 32] through directly — no truncation
    • Lit::Hex/Bin properly pads into 32 bytes

Self-contained in typeckConstValue is not consumed outside the typeck crate. IR lowering re-evaluates constants from AST expressions independently.

Tests added (commit 2)

  • Large literal >u64::MAX: verifies exact bytes
  • Const arithmetic (A * A): verifies computed value
  • Shift left 128: verifies correct 256-bit result (was zero with u64)
  • Small u8 value: verifies no regression

All 670+ workspace tests pass.

@netlify
Copy link
Copy Markdown

netlify Bot commented Mar 8, 2026

Deploy Preview for edgelang ready!

Name Link
🔨 Latest commit 1e4a3d7
🔍 Latest deploy log https://app.netlify.com/projects/edgelang/deploys/69aec61c059760000857d213
😎 Deploy Preview https://deploy-preview-55--edgelang.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@brockelmore
Copy link
Copy Markdown
Collaborator

lgtm pending merge of main and resolution of that conflict

@refcell refcell merged commit 76aa326 into main Mar 9, 2026
16 checks passed
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.

fix: integer literals truncated to u64, breaking values larger than 2^64

3 participants