Skip to content

CEP-0066: Extended Integer Widths

Draft

Draft proposal for primitive integer widths beyond the V1 i1..i128 and u1..u128 range. V1 keeps the accepted integer-width ceiling unchanged.

Summary

Catalyst should eventually decide whether primitive integer type identities may use arbitrary positive bit widths above 128, such as u256, i257, or u4096.

V1 already has target-independent primitive integer type identities for i1..i128 and u1..u128, with runtime use of non-standard widths gated by target support. This proposal extends that model to a larger compiler-supported width range instead of treating i128 and u128 as a permanent language ceiling.

Motivation

Extended integer widths are useful for cryptography, fixed-width protocols, packed binary formats, wide counters, multiprecision algorithms, SIMD-adjacent bit packing, and generated code that wants exact storage widths without wrapping values in user-defined limb structs.

They also line up with Catalyst's existing integer model. The source-level primitive constructors are already parameterized:

fn SignedInteger(comptime bits: comptime_int) Type
fn UnsignedInteger(comptime bits: comptime_int) Type

If bits is a compile-time value, a hard ceiling at 128 is a policy choice rather than a syntax requirement. A larger bounded range would keep the current shape while making the primitive factories more honest about their long-term direction.

Proposed Direction

Future Catalyst should allow primitive integer type identities for a deterministic compiler-supported range above 128 bits. The exact maximum is part of this proposal's acceptance work; candidate policies include:

  • a fixed language minimum such as i1..i4096 and u1..u4096;
  • a Zig-like larger implementation limit with a required minimum;
  • a target-independent type-identity range that is wider than the runtime lowering baseline.

The primitive factories remain the canonical construction path:

const WideCounter = UnsignedInteger(257)
const SignedHashLane = SignedInteger(512)

Source spellings such as u257 and i512 should be aliases for those same canonical type identities when their widths are within the accepted range.

Integer literal suffixes should accept the extended primitive names:

const mask = 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffffu128
const lane = 1u257

Primitive equality, ordering, arithmetic, bitwise operations if accepted, reflection, and layout metadata should continue to operate over the canonical primitive type identities rather than introducing separate "big integer" wrapper types.

Example

Possible future shape:

const Tag = UnsignedInteger(257)

const top: Tag = 1u257 << 256

fn bump(value: Tag) Tag {
  return value + 1u257
}

This is draft future syntax and semantics. V1 rejects primitive integer widths above 128.

Runtime Lowering

Extended integer type identities should not imply that every target can store or compute every width at runtime.

The target model should report at least:

  • maximum native or backend-supported bit-precise integer width;
  • whether non-power-of-two widths are supported in storage;
  • whether arithmetic on extended widths is native, helper-lowered, or unsupported;
  • whether ABI exposure of extended widths is supported;
  • whether C emission can use C23 _BitInt(N) / unsigned _BitInt(N) for the requested width.

If a width is valid as a Catalyst Type value but cannot be lowered for a selected target use, the compiler should emit a target-support diagnostic at the runtime-use boundary. This preserves the current V1 distinction between type identity and backend capability.

Comptime Interaction

comptime_int remains a comptime-only exact integer value family, not a runtime integer type. Extended primitive integer widths should be representable from comptime_int only when the value fits the destination type.

If the accepted runtime integer-width range exceeds the minimum portable comptime_int range, the proposal must also decide one of these policies:

  • raise the required minimum comptime_int range;
  • require implementations to support enough comptime_int precision to construct every accepted primitive integer value;
  • allow some extended-width values to be constructed through typed operations without every possible value being portable as a standalone comptime_int literal.

The simplest acceptance path is to set the extended integer maximum no higher than the required portable comptime_int magnitude unless the comptime range is raised at the same time.

V1 Compatibility

V1 remains unchanged:

  • valid primitive integer type identities are i1..i128, u1..u128, isize, and usize;
  • valid integer literal suffixes are i1..i128, u1..u128, isize, and usize;
  • runtime use of non-standard widths inside the V1 range remains target-gated;
  • comptime_int has the accepted bounded exact range documented in Built-In Types.

Code that uses widths above 128 is rejected until this CEP is accepted and the active reference docs are updated.

Open Questions

  • What exact minimum and maximum width range should Catalyst guarantee?
  • Should source aliases such as u4096 be accepted for every supported width, or should very wide widths require factory spelling?
  • Should the prelude continue to publish only familiar aliases while the lexer/parser recognize numeric suffix names structurally?
  • Should primitive numeric contract examples use comptime bits: comptime_int instead of u8 before this proposal can be accepted?
  • How should extended-width values appear in reflection and diagnostics?
  • Which backends must support helper-lowered arithmetic before the feature is useful?
  • Should ABI exposure of extended widths require explicit representation attributes?