Summary
This proposal adds support for unsigned integer types (u8, u16, u32, u64, usize) to Loom. These types mirror their signed counterparts (i8, i16, i32, i64, isize) but represent only non-negative values and offer greater upper bounds. Unsigned integers are essential for bare-metal programming, allowing precise and safe manipulation of memory, indices, and binary data.
Motivation
Loom is designed for bare-metal targets with extreme fine control. Unsigned integers are critical for the following reasons:
- Memory-mapped I/O and device registers rarely use negative values.
- Bit manipulation is more predictable without sign-extension.
- Network packets and protocol fields are commonly unsigned.
- Sizes, lengths, and indexes are naturally non-negative.
- Unsigned types allow a greater positive range with the same bit width.
Without unsigned support, developers are forced into workarounds or unsafe casts that violate Loom’s design principles of precision and control.
Guide-Level Explanation
Loom would support the following types:
| Signed |
Unsigned |
Description |
i8 |
u8 |
8-bit integer |
i16 |
u16 |
16-bit integer |
i32 |
u32 |
32-bit integer |
i64 |
u64 |
64-bit integer |
isize |
usize |
Pointer-width int |
Example
pub func main(argc: []i64, argv: []string) -> i64 {
var length -> u32 = 1024u32;
var port -> u16 = 8080u16;
var mask -> u8 = 0xFFu8;
print("Length: %d\n", length);
print("Port: %d\n", port);
print("Mask: %d\n", mask);
return 0;
}
Casting
All conversions between signed and unsigned integers must be explicit:
var x -> i32 = -42;
var y -> u32 = x as u32; // wraps or truncates explicitly
Overflow Semantics
Loom may introduce distinct operators for handling overflow:
a +% b; // wrapping add
a +! b; // checked add, traps on overflow
a +? b; // saturating add
⸻
Reference-Level Explanation
Grammar Changes
Extend the type grammar to include:
Type ::= "i8" | "i16" | "i32" | "i64"
| "u8" | "u16" | "u32" | "u64"
| "isize" | "usize"
Support typed literals using suffixes:
let maxByte -> u8 = 255u8;
let big -> u64 = 0xFFFF_FFFF_FFFF_FFFFu64;
Type System
• Add u8, u16, u32, u64, usize as primitive types.
• Ensure arithmetic, comparison, and bitwise operators support unsigned operands.
• Prevent implicit coercion between signed and unsigned types.
Codegen
• Use unsigned IR and machine instructions (e.g., movzx, unsigned div, cmp).
• For usize, emit pointer-width integer types depending on target (32-bit, 64-bit, etc.).
Diagnostics
• Warn on implicit conversions between signed/unsigned types.
• Provide clear errors for type mismatches and undefined overflow behavior.
⸻
Drawbacks
• Adds complexity to the language and the type system.
• Requires developers to understand the difference between signed and unsigned arithmetic.
• Increases the burden on tooling (type checker, codegen, etc.).
⸻
Alternatives
• Do not include unsigned types (rejected for low-level systems use).
• Emulate unsigned types through libraries or annotations (not viable for low-level performance).
• Rely only on usize for indexing (too limited and ambiguous for general use).
⸻
Prior Art
• C: Longtime support for unsigned types; implicit coercion leads to bugs.
• Rust: Strictly separates signed/unsigned, with explicit conversions and overflow operators.
• Zig: Heavy use of unsigned types with robust safety and performance semantics.
• Java: Lacks native unsigned types, causing issues in systems work.
⸻
Unresolved Questions
• Should usize always match pointer width or be configurable?
• Should Loom provide overflow operators (+%, +!, +?) as language features or compiler intrinsics?
• Should type inference prefer signed or unsigned when ambiguous?
⸻
Future Directions
• Add support for wider unsigned types (u128, u256) for crypto or VM applications.
• Define compile-time arithmetic evaluation with overflow semantics.
• Enable bitfield and register-mapped structs for embedded/firmware programming.
• Introduce trait-like interfaces for generic arithmetic over integer types.
20250806-unsigned-integer-typesSummary
This proposal adds support for unsigned integer types (
u8,u16,u32,u64,usize) to Loom. These types mirror their signed counterparts (i8,i16,i32,i64,isize) but represent only non-negative values and offer greater upper bounds. Unsigned integers are essential for bare-metal programming, allowing precise and safe manipulation of memory, indices, and binary data.Motivation
Loom is designed for bare-metal targets with extreme fine control. Unsigned integers are critical for the following reasons:
Without unsigned support, developers are forced into workarounds or unsafe casts that violate Loom’s design principles of precision and control.
Guide-Level Explanation
Loom would support the following types:
i8u8i16u16i32u32i64u64isizeusizeExample
Casting
All conversions between signed and unsigned integers must be explicit:
Overflow Semantics
Loom may introduce distinct operators for handling overflow:
⸻
Reference-Level Explanation
Grammar Changes
Extend the type grammar to include:
Support typed literals using suffixes:
Type System
• Add u8, u16, u32, u64, usize as primitive types.
• Ensure arithmetic, comparison, and bitwise operators support unsigned operands.
• Prevent implicit coercion between signed and unsigned types.
Codegen
• Use unsigned IR and machine instructions (e.g., movzx, unsigned div, cmp).
• For usize, emit pointer-width integer types depending on target (32-bit, 64-bit, etc.).
Diagnostics
• Warn on implicit conversions between signed/unsigned types.
• Provide clear errors for type mismatches and undefined overflow behavior.
⸻
Drawbacks
• Adds complexity to the language and the type system.
• Requires developers to understand the difference between signed and unsigned arithmetic.
• Increases the burden on tooling (type checker, codegen, etc.).
⸻
Alternatives
• Do not include unsigned types (rejected for low-level systems use).
• Emulate unsigned types through libraries or annotations (not viable for low-level performance).
• Rely only on usize for indexing (too limited and ambiguous for general use).
⸻
Prior Art
• C: Longtime support for unsigned types; implicit coercion leads to bugs.
• Rust: Strictly separates signed/unsigned, with explicit conversions and overflow operators.
• Zig: Heavy use of unsigned types with robust safety and performance semantics.
• Java: Lacks native unsigned types, causing issues in systems work.
⸻
Unresolved Questions
• Should usize always match pointer width or be configurable?
• Should Loom provide overflow operators (+%, +!, +?) as language features or compiler intrinsics?
• Should type inference prefer signed or unsigned when ambiguous?
⸻
Future Directions
• Add support for wider unsigned types (u128, u256) for crypto or VM applications.
• Define compile-time arithmetic evaluation with overflow semantics.
• Enable bitfield and register-mapped structs for embedded/firmware programming.
• Introduce trait-like interfaces for generic arithmetic over integer types.