Wado

WEP: 128-bit Integer Types (i128/u128)

Context

Wado needs 128-bit integer types for:

  1. Cryptographic operations: EC cryptography, hash functions, and big-integer arithmetic require 128-bit intermediate results
  2. High-precision timestamps: Nanosecond timestamps over long periods
  3. 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:

  1. Compiler builtin: Treat i128/u128 as primitive types with special-cased codegen
  2. Struct + operator overloading: Implement as regular structs using the trait system

Decision

Implement i128/u128 as structs with operator overloading.

Rationale

  1. Aligns with Wado philosophy: Minimize hard-coded compiler logic; define builtins in Wado source files
  2. Most operations need software implementation anyway: Only add/sub/mul have native Wasm support
  3. Leverages existing trait system: Uses Add, Sub, Mul, Eq, Ord traits from WEP-2026-01-18
  4. Incremental implementation: Can be developed and tested step by step
  5. Maintainability: All implementation in Wado code, not scattered across compiler modules
  6. 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:

Operator Traits

i128/u128 implement the following traits:

Implementation Plan

  1. Add Wasm wide arithmetic builtins to builtin.wado
  2. Implement arithmetic operator traits (WEP-2026-01-18 prerequisite)
  3. Define i128/u128 structs with trait implementations
  4. Add compiler support for as i128 / as u128 casts

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

  1. Consistent design: Uses same trait system as other types
  2. Maintainable: Implementation in Wado, not scattered in compiler
  3. Extensible: Pattern can be used for other wide types (u256, etc.)
  4. Validates operator overloading: Good test case for the trait system
  5. Available in prelude: No explicit import needed, like primitive types

Negative

  1. Depends on operator overloading: Cannot implement until arithmetic traits are complete

References