std.core.conv
std.core.conv — Type Conversion Intrinsics
Section titled “std.core.conv — Type Conversion Intrinsics”Profile: :core
Spec: SPEC-026
Janus does not silently coerce between integer types. Every conversion is an explicit
call. std.core.conv provides three generic conversion functions backed by dedicated
LLVM intrinsics. The compiler injects type-specific constants at monomorphization time —
no runtime dispatch, no boxing.
as[T] — Widening / Same-Width Pass-Through
Section titled “as[T] — Widening / Same-Width Pass-Through”func as[T](value: i64) -> T do @intrinsic("as", value)endZero-cost identity or widening cast. No LLVM instruction is emitted — the value flows through unchanged.
Use when: You want an explicit, readable type annotation at a widening boundary.
func run() !i64 do let narrow = toInt[u8](200)? return as[i64](narrow) // widen u8 → i64 explicitlyendtruncate[T] — Unchecked Bit-Mask Truncation
Section titled “truncate[T] — Unchecked Bit-Mask Truncation”func truncate[T](value: i64) -> T do @intrinsic("truncate", value)endRetains only the low N bits of value, where N is the bit-width of T. Equivalent to
value & ((1 << N) - 1). No error on overflow — values wrap silently.
The lowerer injects the bit_width constant from the concrete type at monomorphization:
| Type | bit_width |
|---|---|
u8, i8 | 8 |
u16, i16 | 16 |
u32, i32 | 32 |
u64, i64 | 64 |
Use when: You want intentional wrapping — bitmask operations, hash functions, protocol byte extraction.
func main() do let masked = truncate[u8](0x1FF) // 511 → low 8 bits = 255 print_int(masked) // 255
let wrapped = truncate[u8](256) // 0x100 → low 8 bits = 0 print_int(wrapped) // 0endtoInt[T] — Checked Narrowing with Range Enforcement
Section titled “toInt[T] — Checked Narrowing with Range Enforcement”func toInt[T](value: i64) !T do @intrinsic("int_cast_checked", value)endNarrows value to type T. Returns an error union !T — either the value, or an
error if it falls outside T’s representable range.
The lowerer injects min and max from the concrete type’s range:
| Type | min | max |
|---|---|---|
u8 | 0 | 255 |
i8 | -128 | 127 |
u16 | 0 | 65535 |
i16 | -32768 | 32767 |
u32 | 0 | 4294967295 |
i32 | -2147483648 | 2147483647 |
i64 | -9223372036854775808 | 9223372036854775807 |
u64 | 0 | 18446744073709551615 |
Use when: You are narrowing from user input, protocol data, or array indices and correctness requires range verification.
The run() wrapper pattern
Section titled “The run() wrapper pattern”? propagation is only valid in functions that return !T. Never use ? on toInt
directly inside main() — main() has a void/i32 return type and ? would emit
an incompatible ret { i8, i64 } in LLVM.
Always wrap in a helper:
func toInt[T](value: i64) !T do @intrinsic("int_cast_checked", value)end
func parse_byte(n: i64) !i64 do return toInt[u8](n)? // OK: this function returns !i64end
func main() do let b = parse_byte(200) catch -1 print_int(b) // 200
let bad = parse_byte(999) catch -1 print_int(bad) // -1 (out of range → catch sentinel)endRoundtrip: toInt → as
Section titled “Roundtrip: toInt → as”Narrowing and widening back is lossless for values within the target type’s range:
func run() !i64 do let narrow = toInt[u8](200)? // u8, value=200 return as[i64](narrow) // i64, value=200end
func main() do print_int(run() catch -1) // 200endDesign: Why i64 Input for All Three?
Section titled “Design: Why i64 Input for All Three?”The :core integer model treats i64 as the universal integer type. Type-specific
operations on u8, i16, etc. are always entered through an explicit conversion call.
This makes the conversion intent visible at every call site — a direct expression of
Syntactic Honesty.
Design: Why Intrinsics Rather Than Zig FFI?
Section titled “Design: Why Intrinsics Rather Than Zig FFI?”The lowerer injects type constants after monomorphization, before LLVM emission. A
regular extern call would receive these as runtime arguments. The intrinsic path lets
LLVM emit optimal code — a simple and for truncate, a two-compare bounds check for
toInt — with the constants folded at compile time.