Skip to content

Add typed numeric literal suffixes#20

Merged
wtholliday merged 1 commit into
audulus:mainfrom
izaak:literal-suffixes
Jun 6, 2026
Merged

Add typed numeric literal suffixes#20
wtholliday merged 1 commit into
audulus:mainfrom
izaak:literal-suffixes

Conversation

@izaak

@izaak izaak commented Jun 2, 2026

Copy link
Copy Markdown

This PR introduces typed numeric suffixes for integer and floating-point literals.

Supported suffixes:

  • Integer: i32, u32
  • Integer alias: u, normalized as u32
  • Float: f32, f64

Examples:

var a: i32 = -2147483648i32
var b: u32 = 4294967295u32
var c: u32 = 1u
var d: f64 = 1.0f64
var e: f32 = 1.0f32

Suffixes force an exact literal type. Unsuffixed integer and float literal behavior is otherwise unchanged.

Design Notes

The AST now represents numeric literals as:

Expr::Int(i64, Option<IntLiteralSuffix>)
Expr::Real(String, Option<FloatLiteralSuffix>)

This removes the legacy unsigned literal AST variant while keeping literal handling simple. The u suffix is accepted as an alias for u32 and intentionally normalizes to u32; the AST does not preserve the source spelling.

Integer-valued float literals such as 1f64 are intentionally not accepted. Float suffixes require a real literal spelling such as 1.0f64.

Signed minimum values are handled as a literal-position rule: a leading unary - is considered during signed suffix range validation. This allows -2147483648i32, while still rejecting 2147483648i32.

Suffixes remain expression-literal syntax only. Suffixed integers are rejected for tuple field indices, array type sizes, and integer const declarations, preserving the current const behavior for a future dedicated const-system PR.

Why i8 and u8 Are Omitted

This PR intentionally limits suffixes to numeric types that already participate in the language's arithmetic surface.

i8 is source-reachable today, but it is mostly a byte/storage/interoperability type. It is used for characters, strings, casts, and byte-sized storage, but it does not currently participate in ordinary arithmetic, relational comparisons, or unary negation. Adding an i8 suffix would make 1i8 + 2i8 look like an expected use case even though the checker rejects that operation today.

u8 is even less settled. The type exists internally, but it is not exposed as a source primitive on main. Making u8 literals source-reachable also exposed backend consistency questions around byte loads and casts, especially in the stack backend.

For those reasons, byte-sized integer suffixes are deferred. A future PR should decide the intended byte integer model explicitly: whether i8/u8 are full arithmetic numeric types or storage/cast-oriented byte types, and then align parsing, type checking, casts, memory loads, and backend behavior around that decision.

Implementation

Suffixes now flow through:

  • Lexing and parsing
  • Type checking and range validation
  • Safety checking
  • Monomorphization
  • Pretty-printing
  • JIT, LLVM, VM, and stack codegen

Validation

This adds coverage for:

  • Valid suffix execution across JIT, VM, and stack backends
  • i32 and u32 boundary checks
  • f32/f64 type mismatch checks
  • Invalid suffix diagnostics, including 1i64, 1i8, 1u8, integer-valued float suffixes such as 1f64, and float literals with integer suffixes such as 1.0u
  • Rejection of suffixes in tuple indices, array type sizes, and const declarations

Verified with:

cargo test --workspace

@wtholliday wtholliday merged commit 9b31681 into audulus:main Jun 6, 2026
4 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.

2 participants