WEP: 128-bit Integer Types (i128/u128)
Context
Wado needs 128-bit integer types for:
- Cryptographic operations: EC cryptography, hash functions, and big-integer arithmetic require 128-bit intermediate results
- High-precision timestamps: Nanosecond timestamps over long periods
- Large identifiers: UUIDs and other 128-bit identifiers
WebAssembly Wide Arithmetic proposal provides native instructions for efficient 128-bit operations:
| Instruction | Signature | Description |
|---|---|---|
i64.add128 |
[i64 i64 i64 i64] → [i64 i64] |
128-bit addition |
i64.sub128 |
[i64 i64 i64 i64] → [i64 i64] |
128-bit subtraction |
i64.mul_wide_u |
[i64 i64] → [i64 i64] |
64×64→128 unsigned multiplication |
i64.mul_wide_s |
[i64 i64] → [i64 i64] |
64×64→128 signed multiplication |
Division, remainder, shifts, bitwise operations, and comparisons require software implementation.
Design Alternatives
Two implementation approaches were considered:
- Compiler builtin: Treat i128/u128 as primitive types with special-cased codegen
- Struct + operator overloading: Implement as regular structs using the trait system
Decision
Implement i128/u128 as structs with operator overloading.
Rationale
- Aligns with Wado philosophy: Minimize hard-coded compiler logic; define builtins in Wado source files
- Most operations need software implementation anyway: Only add/sub/mul have native Wasm support
- Leverages existing trait system: Uses
Add,Sub,Mul,Eq,Ordtraits from WEP-2026-01-18 - Incremental implementation: Can be developed and tested step by step
- Maintainability: All implementation in Wado code, not scattered across compiler modules
- Component Model compatibility: WIT has no 128-bit integer types (max is
s64/u64), so struct representation maps naturally to WIT records for cross-component interoperability
Type Definitions
/// Signed 128-bit integer
pub struct i128 {
low: u64,
high: i64, // sign bit is in high
}
/// Unsigned 128-bit integer
pub struct u128 {
low: u64,
high: u64,
}
The low/high split matches Wasm wide arithmetic conventions.
Usage
// Construction via cast
let a = 42 as i128;
let b = 100 as u128;
// Arithmetic (via operator overloading traits)
let sum = a + b;
let product = a * b;
// Comparison
if a < b { ... }
// Conversion
let x = a as i64; // truncates to the low 64 bits
let f = a as f64; // correctly rounded (ties to even)
Builtin Functions
The following builtins map directly to Wasm wide arithmetic instructions:
builtin::i64_add128→i64.add128builtin::i64_sub128→i64.sub128builtin::i64_mul_wide_u→i64.mul_wide_ubuiltin::i64_mul_wide_s→i64.mul_wide_s
Operator Traits
i128/u128 implement the following traits:
- Arithmetic:
Add,Sub,Mul,Div,Rem,Neg - Bitwise:
BitAnd,BitOr,BitXor,BitNot,Shl,Shr - Comparison:
Eq,Ord - Display:
Display
Implementation Plan
- Add Wasm wide arithmetic builtins to
builtin.wado - Implement arithmetic operator traits (WEP-2026-01-18 prerequisite)
- Define i128/u128 structs with trait implementations
- Add compiler support for
as i128/as u128casts
Implementation Status
| Task | Status |
|---|---|
Wide arithmetic builtins (i64_add128, etc.) |
✅ |
u128/i128 struct definitions |
✅ |
Add/Sub trait implementations |
✅ |
Neg trait for i128 |
✅ |
Eq/Ord trait implementations |
✅ |
from_u64()/from_i64() constructors |
✅ |
low()/high() accessors |
✅ |
| i32 → u64 cast codegen fix | ✅ |
Mul trait implementations |
✅ |
Div/Rem trait implementations |
✅ |
Bitwise traits (BitAnd, BitOr, BitXor) |
✅ |
BitNot trait implementations |
✅ |
Shl/Shr trait implementations |
✅ |
u128_to_string/i128_to_string functions |
✅ |
Literal coercion (let x: u128 = 42) |
✅ |
as i128 / as u128 casts |
✅ |
to_string() methods for stringify |
✅ |
Casts from i128/u128 (as f64, as i64, …) |
✅ |
TryFrom checked conversions |
✅ |
All features are implemented. i128/u128 types are fully functional.
Implementation
The types are defined in lib/core/prelude/int128.wado and re-exported from the prelude:
// lib/core/prelude.wado
pub use { i128, u128 } from "./prelude/int128.wado";
This makes i128 and u128 available in all modules without explicit imports (same as String, List, etc.).
Consequences
Positive
- Consistent design: Uses same trait system as other types
- Maintainable: Implementation in Wado, not scattered in compiler
- Extensible: Pattern can be used for other wide types (u256, etc.)
- Validates operator overloading: Good test case for the trait system
- Available in prelude: No explicit import needed, like primitive types
Negative
- Depends on operator overloading: Cannot implement until arithmetic traits are complete
